diff --git a/src/go/internal/gccgoimporter/importer_test.go b/src/go/internal/gccgoimporter/importer_test.go index 96505b2bab..30b51db9d4 100644 --- a/src/go/internal/gccgoimporter/importer_test.go +++ b/src/go/internal/gccgoimporter/importer_test.go @@ -86,6 +86,7 @@ var importerTests = [...]importerTest{ {pkgpath: "aliases", name: "C0", want: "type C0 struct{f1 C1; f2 C1}"}, {pkgpath: "escapeinfo", name: "NewT", want: "func NewT(data []byte) *T"}, {pkgpath: "issue27856", name: "M", want: "type M struct{E F}"}, + {pkgpath: "v1reflect", name: "Type", want: "type Type interface{Align() int; AssignableTo(u Type) bool; Bits() int; ChanDir() ChanDir; Elem() Type; Field(i int) StructField; FieldAlign() int; FieldByIndex(index []int) StructField; FieldByName(name string) (StructField, bool); FieldByNameFunc(match func(string) bool) (StructField, bool); Implements(u Type) bool; In(i int) Type; IsVariadic() bool; Key() Type; Kind() Kind; Len() int; Method(int) Method; MethodByName(string) (Method, bool); Name() string; NumField() int; NumIn() int; NumMethod() int; NumOut() int; Out(i int) Type; PkgPath() string; Size() uintptr; String() string; common() *commonType; rawString() string; runtimeType() *runtimeType; uncommon() *uncommonType}"}, } func TestGoxImporter(t *testing.T) { diff --git a/src/go/internal/gccgoimporter/parser.go b/src/go/internal/gccgoimporter/parser.go index e75f15c429..7d075db4ce 100644 --- a/src/go/internal/gccgoimporter/parser.go +++ b/src/go/internal/gccgoimporter/parser.go @@ -29,9 +29,30 @@ type parser struct { imports map[string]*types.Package // package path -> package object typeList []types.Type // type number -> type typeData []string // unparsed type data (v3 and later) + fixups []fixupRecord // fixups to apply at end of parsing initdata InitData // package init priority data } +// When reading V1 export data it's possible to encounter a defined +// type N1 with an underlying defined type N2 while we are still +// reading in that defined type N2; see issue #29006 for an instance +// of this. Example: +// +// type N1 N2 +// type N2 struct { +// ... +// p *N1 +// } +// +// To handle such cases, the parser generates a fixup record (below) and +// delays setting of N1's underlying type until parsing is complete, at +// which point fixups are applied. + +type fixupRecord struct { + toUpdate *types.Named // type to modify when fixup is processed + target types.Type // type that was incomplete when fixup was created +} + func (p *parser) init(filename string, src io.Reader, imports map[string]*types.Package) { p.scanner = new(scanner.Scanner) p.initScanner(filename, src) @@ -504,7 +525,15 @@ func (p *parser) parseNamedType(nlist []int) types.Type { underlying := p.parseType(pkg) if nt.Underlying() == nil { - nt.SetUnderlying(underlying.Underlying()) + if underlying.Underlying() == nil { + if p.version != "v1" { + p.errorf("internal error: unexpected fixup required for %v", nt) + } + fix := fixupRecord{toUpdate: nt, target: underlying} + p.fixups = append(p.fixups, fix) + } else { + nt.SetUnderlying(underlying.Underlying()) + } } if p.tok == '\n' { @@ -1175,6 +1204,13 @@ func (p *parser) parsePackage() *types.Package { for p.tok != scanner.EOF { p.parseDirective() } + for _, f := range p.fixups { + if f.target.Underlying() == nil { + p.errorf("internal error: fixup can't be applied, loop required") + } + f.toUpdate.SetUnderlying(f.target.Underlying()) + } + p.fixups = nil for _, typ := range p.typeList { if it, ok := typ.(*types.Interface); ok { it.Complete() diff --git a/src/go/internal/gccgoimporter/testdata/v1reflect.gox b/src/go/internal/gccgoimporter/testdata/v1reflect.gox new file mode 100644 index 0000000000..ea468414d9 Binary files /dev/null and b/src/go/internal/gccgoimporter/testdata/v1reflect.gox differ