Skip to content

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

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

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

Comments

@siebenmann
Copy link

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
Copy link
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
Copy link
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 as completed Apr 15, 2018
@teh-cmc teh-cmc changed the title chapter 2: How you can get duplicated go.itab interface definitions chapter2: How you can get duplicated go.itab interface definitions Apr 17, 2018
icarus-sparry pushed a commit to icarus-sparry/go-internals that referenced this issue Aug 5, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants