teh-cmc / go-internals

A book about the internals of the Go programming language.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

chapter2: How you can get duplicated go.itab interface definitions

siebenmann opened this issue · comments

You asked why go.itabs are dupok. I believe the fundamental reason is that Go can be statically creating iface structures outside of either the package that defines the type or the package that defines the interface.

To make this concrete, here's a scenario where I believe that Go has to create duplicate ones. Suppose that we have four packages, A, B, C, D, and a main package. A defines a concrete type T, B defines an interface I (and perhaps some functions that take it), and both C and D import A and B and statically create B.I instances from A.T instances. Now we use both C and D in our main program.

Package A and B don't know about each other, so neither can define the iface<B.I, A.T> structure. Since C and D may be used by themselves, each must define this separately; they both need it and they have no guaranteed source of it outside themselves. Then when we use both of them together in our main program, we have duplicate definitions of iface<B.I, A.T>, one from each package, so Go must make such duplicates harmless.

That makes perfect sense @siebenmann, thanks for the great explanation.

I've added an exact reproduction of what you've described here.


Layout:

$ tree
.
├── A
│   └── lib.go
├── B
│   └── lib.go
├── C
│   └── lib.go
├── D
│   └── lib.go
└── main.go

As you've described:

  • A declares a type Calc which implements B.Adder.
package A

type Calc struct{}

func (c *Calc) Add(a, b int32) int32 { return a + b }
  • B declares an Adder interface.
package B

type Adder interface{ Add(a, b int32) int32 }
  • C implements a function Add that instantiates an iface<B.Adder, *A.Calc> internally:
package C

import (
	"github.com/teh-cmc/go-internals/chapter2_interfaces/issue_7/A"
	"github.com/teh-cmc/go-internals/chapter2_interfaces/issue_7/B"
)

func Add(a, b int32) int32 {
	var adder B.Adder = &A.Calc{}
	return adder.Add(a, b)
}
  • D does the same thing as C
package D

import (
	"github.com/teh-cmc/go-internals/chapter2_interfaces/issue_7/A"
	"github.com/teh-cmc/go-internals/chapter2_interfaces/issue_7/B"
)

func Add(a, b int32) int32 {
	var adder B.Adder = &A.Calc{}
	return adder.Add(a, b)
}
  • Finally, the main package calls C.Add and D.Add:
package main

import (
	"fmt"

	"github.com/teh-cmc/go-internals/chapter2_interfaces/issue_7/C"
	"github.com/teh-cmc/go-internals/chapter2_interfaces/issue_7/D"
)

func main() {
	fmt.Println(C.Add(10, 32))
	fmt.Println(D.Add(10, 32))
}

Looking at the output of go build:

$ go build -x
# ...
packagefile github.com/teh-cmc/go-internals/chapter2_interfaces/issue_7/C=$HOME/.cache/go-build/ff/ffc441d2cc7fd2bd9e12722f11fd3407dc4280577c0c74f8cb5241d72792d554-d
packagefile github.com/teh-cmc/go-internals/chapter2_interfaces/issue_7/D=$HOME/.cache/go-build/7c/7cc79061754b8fa877646bcbf5f10814217ac3dc48fc66c5964f08dd823695de-d
# ...

We can see the linker importing the archives for both package C & D, as expected.

Now if we look for itabs in those archives:

$ go tool nm $HOME/.cache/go-build/ff/ffc441d2cc7fd2bd9e12722f11fd3407dc4280577c0c74f8cb5241d72792d554-d | grep 'itab\.'
     af7 R go.itab.*github.com/teh-cmc/go-internals/chapter2_interfaces/issue_7/A.Calc,github.com/teh-cmc/go-internals/chapter2_interfaces/issue_7/B.Adder

$ go tool nm $HOME/.cache/go-build/7c/7cc79061754b8fa877646bcbf5f10814217ac3dc48fc66c5964f08dd823695de-d | grep 'itab\.'
     af7 R go.itab.*github.com/teh-cmc/go-internals/chapter2_interfaces/issue_7/A.Calc,github.com/teh-cmc/go-internals/chapter2_interfaces/issue_7/B.Adder

We see that the itab for iface<B.Adder, *A.Calc> is indeed duplicated in both archives; so the linker will have to pick one.

I've added a link to this discussion in the relevant part of chapter 2; and will be closing this now.
Don't hesitate to add more comments even if it's closed!

Hopefully I'll take the time to update chapter 2 with all these learnings some day.