New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
siebenmann opened this Issue Apr 9, 2018 · 2 comments

Comments

2 participants
@siebenmann

siebenmann commented Apr 9, 2018

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.

@teh-cmc

This comment has been minimized.

Owner

teh-cmc commented Apr 15, 2018

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.

@teh-cmc

This comment has been minimized.

Owner

teh-cmc commented Apr 15, 2018

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.

@teh-cmc teh-cmc closed this Apr 15, 2018

@teh-cmc teh-cmc changed the title from chapter 2: How you can get duplicated go.itab interface definitions to chapter2: How you can get duplicated go.itab interface definitions Apr 17, 2018

Ferrodata added a commit to Ferrodata/go-internals-de that referenced this issue Oct 29, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment