How is dependency chaining avoided in Go compilation?

222 views
Skip to first unread message

John Nagle

unread,
May 20, 2013, 1:25:05 PM5/20/13
to golan...@googlegroups.com
One of the claims made for Go is this:

"Dependency Management: no header file, you just need to look at the
packages that are directly imported (no need to worry about what they
import) thus you have linear dependencies." This is one of Go's
claimed advantages over C and C++.

So how is something like this compiled?

File t1.go:

package t1

type T1 struct {
x1 int
}
...

File t2.go:

package t2
import "t1"

type T2 struct {
t1.T1 // needs t1.go to compile
x2 int
}

File t3.go:

package main
import "t2"

var item T2 // needs t1.go and t2.go to get structure layout.
...

To generate code for an instance of T2 in "main", information from
t1.go, which t3.go does not import, is needed.
Do the Go compilers look at the object file to get layout info, or what?

John Nagle




roger peppe

unread,
May 20, 2013, 1:32:45 PM5/20/13
to John Nagle, golang-nuts
Yes. The object files for a package contain information on their
dependencies as well
as information on the package itself.

Ian Lance Taylor

unread,
May 20, 2013, 1:34:08 PM5/20/13
to John Nagle, golan...@googlegroups.com
On Mon, May 20, 2013 at 10:25 AM, John Nagle <na...@animats.com> wrote:
> One of the claims made for Go is this:
>
> "Dependency Management: no header file, you just need to look at the
> packages that are directly imported (no need to worry about what they
> import) thus you have linear dependencies." This is one of Go's
> claimed advantages over C and C++.
>
> So how is something like this compiled?
>
> File t1.go:
>
> package t1
>
> type T1 struct {
> x1 int
> }
> ...
>
> File t2.go:
>
> package t2
> import "t1"
>
> type T2 struct {
> t1.T1 // needs t1.go to compile
> x2 int
> }

It doesn't need t1.go to compile. It needs t1.6 (or t1.8 or t1.o,
whatever). The generated object file contains all the data required
from t1 to compile t2.go. This exported data is currently stored in a
form that is similar to Go but is not actually Go, and as such can be
read more quickly. You can see this data yourself if you open the
object file as a text file.


> File t3.go:
>
> package main
> import "t2"
>
> var item T2 // needs t1.go and t2.go to get structure layout.
> ...
>
> To generate code for an instance of T2 in "main", information from
> t1.go, which t3.go does not import, is needed.
> Do the Go compilers look at the object file to get layout info, or what?

Yes, they look at the object file. In this case t2.6 will contain
everything that t3.go needs, including the definition of t1.T1.

Ian

John Nagle

unread,
May 21, 2013, 2:06:59 AM5/21/13
to golan...@googlegroups.com
On 5/20/2013 10:34 AM, Ian Lance Taylor wrote:
> Yes, they look at the object file. In this case t2.6 will contain
> everything that t3.go needs, including the definition of t1.T1.
>
> Ian

OK. That's useful.

In the event that something like parameterized types ever
needs to be implemented, that provides a route to an implementation.
The declaration info for a parameterized type would be stored in
the object file, and used when needed. The tables for interface
method resolution can be built when the "main" package is compiled.
(Are those built that way now?)

I don't want to get into the political issues around Go generics,
but it's worth knowing that the approach to separate compilation
could potentially support it.

John Nagle

Jesse McNelis

unread,
May 21, 2013, 2:22:47 AM5/21/13
to John Nagle, golang-nuts
On Tue, May 21, 2013 at 4:06 PM, John Nagle <na...@animats.com> wrote:
On 5/20/2013 10:34 AM, Ian Lance Taylor wrote:
> Yes, they look at the object file.  In this case t2.6 will contain
> everything that t3.go needs, including the definition of t1.T1.
>
> Ian
   I don't want to get into the political issues around Go generics,
but it's worth knowing that the approach to separate compilation
could potentially support it.

I don't think it does support it. It actually goes against it.
That is, to link t3 against t2 you need to recompile t2 with the types that t3 has specified.
If those types flow down in to t1, then t1 needs to be recompiled too.

You can't know what types are required in t1 code until you compile t3 and find out.

--
=====================
http://jessta.id.au

John Nagle

unread,
May 21, 2013, 2:30:24 AM5/21/13
to Jesse McNelis, golang-nuts
Right, but you'd generate the code for the instantiations of the
functions involved when you compiled t3. (Optimization: check
imports to see if the same instantiation already exists in an
import.)

John Nagle


Ian Lance Taylor

unread,
May 21, 2013, 9:37:33 AM5/21/13
to John Nagle, golan...@googlegroups.com
(It's a shame that people think there are political issues around Go
generics. I don't think there are.)

I agree that with any cross-package fully-compiled generics
implementation we would store the body of generic functions and types
in the export data. That still leads us in the direction either of
excess compilation, because the same generic function is compiled
multiple times and duplicates discarded at link time, or of even
slower links, because we compile the generic functions at link time.
These are not show stoppers. It's also worth considering that other
approaches are possible. For example, we could compile generic
functions once into code that uses the reflect package. That gives us
faster builds at the cost of slower run-time performance.

To answer your question, I believe that the gc compiler always build
interface method resolution tables at run-time. The gccgo compiler
builds them at compile time when possible, at run-time when not. In
general it is impossible to know which interface method tables will be
needed at compile time.

Ian
Reply all
Reply to author
Forward
0 new messages