From b14f328a62114bcf7494e8f83220679c7be17fb3 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 26 Sep 2018 20:35:05 -0700 Subject: [PATCH] go/internal/gccgoimporter: port recent changes from stdlib version This CL brings over the following changes from the std lib: https://golang.org/cl/137857 https://golang.org/cl/137935 https://golang.org/cl/137975 There are no further code changes except that the importer test cases are split between importer_test.go and importer19_test.go to support multiple Go versions. Updates golang/go#27856. Change-Id: I625def738c22c24c6659af37c3871038fdd8b981 Reviewed-on: https://go-review.googlesource.com/137995 Reviewed-by: Ian Lance Taylor --- go/internal/gccgoimporter/importer19_test.go | 3 +- go/internal/gccgoimporter/importer_test.go | 4 + go/internal/gccgoimporter/parser.go | 240 ++++++++++++------ go/internal/gccgoimporter/testdata/alias.gox | 4 - go/internal/gccgoimporter/testdata/aliases.go | 65 +++++ .../gccgoimporter/testdata/aliases.gox | 33 +++ .../gccgoimporter/testdata/issue27856.go | 9 + .../gccgoimporter/testdata/issue27856.gox | 9 + 8 files changed, 285 insertions(+), 82 deletions(-) delete mode 100644 go/internal/gccgoimporter/testdata/alias.gox create mode 100644 go/internal/gccgoimporter/testdata/aliases.go create mode 100644 go/internal/gccgoimporter/testdata/aliases.gox create mode 100644 go/internal/gccgoimporter/testdata/issue27856.go create mode 100644 go/internal/gccgoimporter/testdata/issue27856.gox diff --git a/go/internal/gccgoimporter/importer19_test.go b/go/internal/gccgoimporter/importer19_test.go index 0caa2256..222d66ca 100644 --- a/go/internal/gccgoimporter/importer19_test.go +++ b/go/internal/gccgoimporter/importer19_test.go @@ -7,7 +7,8 @@ package gccgoimporter var aliasTests = []importerTest{ - {pkgpath: "alias", name: "IntAlias2", want: "type IntAlias2 = Int"}, + {pkgpath: "aliases", name: "A14", want: "type A14 = func(int, T0) chan T2"}, + {pkgpath: "aliases", name: "C0", want: "type C0 struct{f1 C1; f2 C1}"}, } func init() { diff --git a/go/internal/gccgoimporter/importer_test.go b/go/internal/gccgoimporter/importer_test.go index 02c100a6..b04711da 100644 --- a/go/internal/gccgoimporter/importer_test.go +++ b/go/internal/gccgoimporter/importer_test.go @@ -104,7 +104,11 @@ var importerTests = []importerTest{ {pkgpath: "unicode", name: "IsUpper", want: "func IsUpper(r rune) bool"}, {pkgpath: "unicode", name: "MaxRune", want: "const MaxRune untyped rune", wantval: "1114111"}, {pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import", "math..import"}}, + // moved to importer19_test.go (they import interface types) + // {pkgpath: "aliases", name: "A14", want: "type A14 = func(int, T0) chan T2"}, + // {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}"}, } func TestGoxImporter(t *testing.T) { diff --git a/go/internal/gccgoimporter/parser.go b/go/internal/gccgoimporter/parser.go index 18a18632..d95d0166 100644 --- a/go/internal/gccgoimporter/parser.go +++ b/go/internal/gccgoimporter/parser.go @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package gccgoimporter +// This is a verbatim copy of $GOROOT/src/go/internal/gccgoimporter/parser.go +// with a small modification in parseInterface to support older Go versions. -// This is a verbatim copy of $GOROOT/src/go/internal/gccgoimporter/parser.go. +package gccgoimporter import ( "bytes" @@ -28,7 +29,7 @@ type parser struct { pkgname string // name of imported package pkg *types.Package // reference to imported package imports map[string]*types.Package // package path -> package object - typeMap map[int]types.Type // type number -> type + typeList []types.Type // type number -> type initdata InitData // package init priority data } @@ -40,7 +41,7 @@ func (p *parser) init(filename string, src io.Reader, imports map[string]*types. p.scanner.Filename = filename // for good error messages p.next() p.imports = imports - p.typeMap = make(map[int]types.Type) + p.typeList = make([]types.Type, 1 /* type numbers start at 1 */, 16) } type importError struct { @@ -380,52 +381,80 @@ func (p *parser) parseConst(pkg *types.Package) *types.Const { return types.NewConst(token.NoPos, pkg, name, typ, val) } +// reserved is a singleton type used to fill type map slots that have +// been reserved (i.e., for which a type number has been parsed) but +// which don't have their actual type yet. When the type map is updated, +// the actual type must replace a reserved entry (or we have an internal +// error). Used for self-verification only - not required for correctness. +var reserved = new(struct{ types.Type }) + +// reserve reserves the type map entry n for future use. +func (p *parser) reserve(n int) { + if n != len(p.typeList) { + p.errorf("invalid type number %d (out of sync)", n) + } + p.typeList = append(p.typeList, reserved) +} + +// update sets the type map entries for the given type numbers nlist to t. +func (p *parser) update(t types.Type, nlist []int) { + for _, n := range nlist { + if p.typeList[n] != reserved { + p.errorf("typeMap[%d] not reserved", n) + } + p.typeList[n] = t + } +} + // NamedType = TypeName [ "=" ] Type { Method } . // TypeName = ExportedName . // Method = "func" "(" Param ")" Name ParamList ResultList ";" . -func (p *parser) parseNamedType(n int) types.Type { +func (p *parser) parseNamedType(nlist []int) types.Type { pkg, name := p.parseExportedName() scope := pkg.Scope() - - if p.tok == '=' { - // type alias - p.next() - typ := p.parseType(pkg) - if obj := scope.Lookup(name); obj != nil { - typ = obj.Type() // use previously imported type - if typ == nil { - p.errorf("%v (type alias) used in cycle", obj) - } - } else { - obj = types.NewTypeName(token.NoPos, pkg, name, typ) - scope.Insert(obj) - } - p.typeMap[n] = typ - return typ + obj := scope.Lookup(name) + if obj != nil && obj.Type() == nil { + p.errorf("%v has nil type", obj) } - // named type - obj := scope.Lookup(name) + // type alias + if p.tok == '=' { + p.next() + if obj != nil { + // use the previously imported (canonical) type + t := obj.Type() + p.update(t, nlist) + p.parseType(pkg) // discard + return t + } + t := p.parseType(pkg, nlist...) + obj = types.NewTypeName(token.NoPos, pkg, name, t) + scope.Insert(obj) + return t + } + + // defined type if obj == nil { - // a named type may be referred to before the underlying type - // is known - set it up + // A named type may be referred to before the underlying type + // is known - set it up. tname := types.NewTypeName(token.NoPos, pkg, name, nil) types.NewNamed(tname, nil, nil) scope.Insert(tname) obj = tname } - typ := obj.Type() - p.typeMap[n] = typ + // use the previously imported (canonical), or newly created type + t := obj.Type() + p.update(t, nlist) - nt, ok := typ.(*types.Named) + nt, ok := t.(*types.Named) if !ok { // This can happen for unsafe.Pointer, which is a TypeName holding a Basic type. pt := p.parseType(pkg) - if pt != typ { + if pt != t { p.error("unexpected underlying type for non-named TypeName") } - return typ + return t } underlying := p.parseType(pkg) @@ -451,41 +480,70 @@ func (p *parser) parseNamedType(n int) types.Type { return nt } -func (p *parser) parseInt() int64 { +func (p *parser) parseInt64() int64 { lit := p.expect(scanner.Int) - n, err := strconv.ParseInt(lit, 10, 0) + n, err := strconv.ParseInt(lit, 10, 64) if err != nil { p.error(err) } return n } +func (p *parser) parseInt() int { + lit := p.expect(scanner.Int) + n, err := strconv.ParseInt(lit, 10, 0 /* int */) + if err != nil { + p.error(err) + } + return int(n) +} + // ArrayOrSliceType = "[" [ int ] "]" Type . -func (p *parser) parseArrayOrSliceType(pkg *types.Package) types.Type { +func (p *parser) parseArrayOrSliceType(pkg *types.Package, nlist []int) types.Type { p.expect('[') if p.tok == ']' { p.next() - return types.NewSlice(p.parseType(pkg)) + + t := new(types.Slice) + p.update(t, nlist) + + *t = *types.NewSlice(p.parseType(pkg)) + return t } - n := p.parseInt() + t := new(types.Array) + p.update(t, nlist) + + len := p.parseInt64() p.expect(']') - return types.NewArray(p.parseType(pkg), n) + + *t = *types.NewArray(p.parseType(pkg), len) + return t } // MapType = "map" "[" Type "]" Type . -func (p *parser) parseMapType(pkg *types.Package) types.Type { +func (p *parser) parseMapType(pkg *types.Package, nlist []int) types.Type { p.expectKeyword("map") + + t := new(types.Map) + p.update(t, nlist) + p.expect('[') key := p.parseType(pkg) p.expect(']') elem := p.parseType(pkg) - return types.NewMap(key, elem) + + *t = *types.NewMap(key, elem) + return t } // ChanType = "chan" ["<-" | "-<"] Type . -func (p *parser) parseChanType(pkg *types.Package) types.Type { +func (p *parser) parseChanType(pkg *types.Package, nlist []int) types.Type { p.expectKeyword("chan") + + t := new(types.Chan) + p.update(t, nlist) + dir := types.SendRecv switch p.tok { case '-': @@ -502,13 +560,17 @@ func (p *parser) parseChanType(pkg *types.Package) types.Type { } } - return types.NewChan(dir, p.parseType(pkg)) + *t = *types.NewChan(dir, p.parseType(pkg)) + return t } // StructType = "struct" "{" { Field } "}" . -func (p *parser) parseStructType(pkg *types.Package) types.Type { +func (p *parser) parseStructType(pkg *types.Package, nlist []int) types.Type { p.expectKeyword("struct") + t := new(types.Struct) + p.update(t, nlist) + var fields []*types.Var var tags []string @@ -521,7 +583,8 @@ func (p *parser) parseStructType(pkg *types.Package) types.Type { } p.expect('}') - return types.NewStruct(fields, tags) + *t = *types.NewStruct(fields, tags) + return t } // ParamList = "(" [ { Parameter "," } Parameter ] ")" . @@ -564,10 +627,15 @@ func (p *parser) parseResultList(pkg *types.Package) *types.Tuple { } // FunctionType = ParamList ResultList . -func (p *parser) parseFunctionType(pkg *types.Package) *types.Signature { +func (p *parser) parseFunctionType(pkg *types.Package, nlist []int) *types.Signature { + t := new(types.Signature) + p.update(t, nlist) + params, isVariadic := p.parseParamList(pkg) results := p.parseResultList(pkg) - return types.NewSignature(nil, params, results, isVariadic) + + *t = *types.NewSignature(nil, params, results, isVariadic) + return t } // Func = Name FunctionType . @@ -579,13 +647,16 @@ func (p *parser) parseFunc(pkg *types.Package) *types.Func { p.discardDirectiveWhileParsingTypes(pkg) return nil } - return types.NewFunc(token.NoPos, pkg, name, p.parseFunctionType(pkg)) + return types.NewFunc(token.NoPos, pkg, name, p.parseFunctionType(pkg, nil)) } // InterfaceType = "interface" "{" { ("?" Type | Func) ";" } "}" . -func (p *parser) parseInterfaceType(pkg *types.Package) types.Type { +func (p *parser) parseInterfaceType(pkg *types.Package, nlist []int) types.Type { p.expectKeyword("interface") + t := new(types.Interface) + p.update(t, nlist) + var methods []*types.Func var embeddeds []types.Type @@ -602,53 +673,61 @@ func (p *parser) parseInterfaceType(pkg *types.Package) types.Type { } p.expect('}') - return newInterface(methods, embeddeds) + *t = *newInterface(methods, embeddeds) + return t } // PointerType = "*" ("any" | Type) . -func (p *parser) parsePointerType(pkg *types.Package) types.Type { +func (p *parser) parsePointerType(pkg *types.Package, nlist []int) types.Type { p.expect('*') if p.tok == scanner.Ident { p.expectKeyword("any") - return types.Typ[types.UnsafePointer] + t := types.Typ[types.UnsafePointer] + p.update(t, nlist) + return t } - return types.NewPointer(p.parseType(pkg)) + + t := new(types.Pointer) + p.update(t, nlist) + + *t = *types.NewPointer(p.parseType(pkg)) + + return t } -// TypeDefinition = NamedType | MapType | ChanType | StructType | InterfaceType | PointerType | ArrayOrSliceType | FunctionType . -func (p *parser) parseTypeDefinition(pkg *types.Package, n int) types.Type { - var t types.Type +// TypeSpec = NamedType | MapType | ChanType | StructType | InterfaceType | PointerType | ArrayOrSliceType | FunctionType . +func (p *parser) parseTypeSpec(pkg *types.Package, nlist []int) types.Type { switch p.tok { case scanner.String: - t = p.parseNamedType(n) + return p.parseNamedType(nlist) case scanner.Ident: switch p.lit { case "map": - t = p.parseMapType(pkg) + return p.parseMapType(pkg, nlist) case "chan": - t = p.parseChanType(pkg) + return p.parseChanType(pkg, nlist) case "struct": - t = p.parseStructType(pkg) + return p.parseStructType(pkg, nlist) case "interface": - t = p.parseInterfaceType(pkg) + return p.parseInterfaceType(pkg, nlist) } case '*': - t = p.parsePointerType(pkg) + return p.parsePointerType(pkg, nlist) case '[': - t = p.parseArrayOrSliceType(pkg) + return p.parseArrayOrSliceType(pkg, nlist) case '(': - t = p.parseFunctionType(pkg) + return p.parseFunctionType(pkg, nlist) } - p.typeMap[n] = t - return t + p.errorf("expected type name or literal, got %s", scanner.TokenString(p.tok)) + return nil } const ( @@ -702,29 +781,36 @@ func lookupBuiltinType(typ int) types.Type { }[typ] } -// Type = "<" "type" ( "-" int | int [ TypeDefinition ] ) ">" . -func (p *parser) parseType(pkg *types.Package) (t types.Type) { +// Type = "<" "type" ( "-" int | int [ TypeSpec ] ) ">" . +// +// parseType updates the type map to t for all type numbers n. +// +func (p *parser) parseType(pkg *types.Package, n ...int) (t types.Type) { p.expect('<') p.expectKeyword("type") switch p.tok { case scanner.Int: - n := p.parseInt() - + n1 := p.parseInt() if p.tok == '>' { - t = p.typeMap[int(n)] + t = p.typeList[n1] + if t == reserved { + p.errorf("invalid type cycle, type %d not yet defined", n1) + } + p.update(t, n) } else { - t = p.parseTypeDefinition(pkg, int(n)) + p.reserve(n1) + t = p.parseTypeSpec(pkg, append(n, n1)) } case '-': p.next() - n := p.parseInt() - t = lookupBuiltinType(int(n)) + n1 := p.parseInt() + t = lookupBuiltinType(n1) + p.update(t, n) default: p.errorf("expected type number, got %s (%q)", scanner.TokenString(p.tok), p.lit) - return nil } p.expect('>') @@ -737,7 +823,7 @@ func (p *parser) parsePackageInit() PackageInit { initfunc := p.parseUnquotedString() priority := -1 if p.version == "v1" { - priority = int(p.parseInt()) + priority = p.parseInt() } return PackageInit{Name: name, InitFunc: initfunc, Priority: priority} } @@ -783,7 +869,7 @@ func (p *parser) parseInitDataDirective() { case "priority": p.next() - p.initdata.Priority = int(p.parseInt()) + p.initdata.Priority = p.parseInt() p.expect(';') case "init": @@ -797,8 +883,8 @@ func (p *parser) parseInitDataDirective() { p.next() // The graph data is thrown away for now. for p.tok != ';' && p.tok != scanner.EOF { - p.parseInt() - p.parseInt() + p.parseInt64() + p.parseInt64() } p.expect(';') @@ -900,7 +986,7 @@ func (p *parser) parsePackage() *types.Package { for p.tok != scanner.EOF { p.parseDirective() } - for _, typ := range p.typeMap { + for _, typ := range p.typeList { if it, ok := typ.(*types.Interface); ok { it.Complete() } diff --git a/go/internal/gccgoimporter/testdata/alias.gox b/go/internal/gccgoimporter/testdata/alias.gox deleted file mode 100644 index ced7d84c..00000000 --- a/go/internal/gccgoimporter/testdata/alias.gox +++ /dev/null @@ -1,4 +0,0 @@ -v1; -package alias; -pkgpath alias; -type >>>) < type 114>; M2 () ; }>>; diff --git a/go/internal/gccgoimporter/testdata/aliases.go b/go/internal/gccgoimporter/testdata/aliases.go new file mode 100644 index 00000000..cfb59b3e --- /dev/null +++ b/go/internal/gccgoimporter/testdata/aliases.go @@ -0,0 +1,65 @@ +package aliases + +type ( + T0 [10]int + T1 []byte + T2 struct { + x int + } + T3 interface { + m() T2 + } + T4 func(int, T0) chan T2 +) + +// basic aliases +type ( + Ai = int + A0 = T0 + A1 = T1 + A2 = T2 + A3 = T3 + A4 = T4 + + A10 = [10]int + A11 = []byte + A12 = struct { + x int + } + A13 = interface { + m() A2 + } + A14 = func(int, A0) chan A2 +) + +// alias receiver types +func (T0) m1() {} +func (A0) m2() {} + +// alias receiver types (long type declaration chains) +type ( + V0 = V1 + V1 = (V2) + V2 = (V3) + V3 = T0 +) + +func (V1) n() {} + +// cycles +type C0 struct { + f1 C1 + f2 C2 +} + +type ( + C1 *C0 + C2 = C1 +) + +type ( + C5 struct { + f *C6 + } + C6 = C5 +) diff --git a/go/internal/gccgoimporter/testdata/aliases.gox b/go/internal/gccgoimporter/testdata/aliases.gox new file mode 100644 index 00000000..2428c068 --- /dev/null +++ b/go/internal/gccgoimporter/testdata/aliases.gox @@ -0,0 +1,33 @@ +v2; +package aliases; +prefix go; +package aliases go.aliases go.aliases; +type > + func (? ) .go.aliases.m1 (); + func (? ) .go.aliases.m2 (); + func (? >>>) .go.aliases.n (); +>>; +type >>>; +type >>; +type >>; +type ; }>>; +type ; }>>>; }>>; +type , ? ) >>>; +type ; +type ; }>>>; +type , ? ) >>>>; +type >; +type >>; .go.aliases.f2 >; }>>; +type ; +type ; +type >>; }>>; +type ; +type ; +type ; +type ; +type ; +type ; +type >; +type ; +type ; +type ; diff --git a/go/internal/gccgoimporter/testdata/issue27856.go b/go/internal/gccgoimporter/testdata/issue27856.go new file mode 100644 index 00000000..bf361e1c --- /dev/null +++ b/go/internal/gccgoimporter/testdata/issue27856.go @@ -0,0 +1,9 @@ +package lib + +type M struct { + E E +} +type F struct { + _ *M +} +type E = F diff --git a/go/internal/gccgoimporter/testdata/issue27856.gox b/go/internal/gccgoimporter/testdata/issue27856.gox new file mode 100644 index 00000000..6665e640 --- /dev/null +++ b/go/internal/gccgoimporter/testdata/issue27856.gox @@ -0,0 +1,9 @@ +v2; +package main; +pkgpath main; +import runtime runtime "runtime"; +init runtime runtime..import sys runtime_internal_sys..import; +init_graph 0 1; +type ; }>>>; }>>>; +type ; +type ;