diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go index 1b1f77aa7e..e721c7a705 100644 --- a/src/go/types/check_test.go +++ b/src/go/types/check_test.go @@ -101,7 +101,7 @@ var tests = [][]string{ // Go 2 tests (type parameters and contracts) {"testdata/typeparams.go2"}, - //{"testdata/typeinst.go2"}, + {"testdata/typeinst.go2"}, {"testdata/contracts.go2"}, } diff --git a/src/go/types/decl.go b/src/go/types/decl.go index f438024b22..079c74575a 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -546,6 +546,11 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) { check.validType(obj.typ, nil) }) + if obj.IsParametrized() { + assert(obj.scope != nil) + check.scope = obj.scope // push type parameter scope + } + if tdecl.Assign.IsValid() { // type alias declaration @@ -579,6 +584,10 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) { } + if obj.IsParametrized() { + check.closeScope() + } + check.addMethodDecls(obj) } @@ -778,6 +787,7 @@ func (check *Checker) declStmt(decl ast.Decl) { case *ast.TypeSpec: obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil) + obj.scope, obj.tparams = check.collectTypeParams(check.scope, s, s.TParams) // spec: "The scope of a type identifier declared inside a function // begins at the identifier in the TypeSpec and ends at the end of // the innermost containing block." diff --git a/src/go/types/object.go b/src/go/types/object.go index 374b24d1ac..21d008e990 100644 --- a/src/go/types/object.go +++ b/src/go/types/object.go @@ -213,6 +213,9 @@ func (*Const) isDependency() {} // a constant may be a dependency of an initiali // A TypeName represents a name for a (defined or alias) type. type TypeName struct { object + // TODO(gri) For Funcs, we have the type parameters on the signature. Revisit that decision. + scope *Scope // type parameter scope; or nil + tparams []*TypeName // type parameters from left to right; or nil } // NewTypeName returns a new type name denoting the given typ. @@ -223,7 +226,12 @@ type TypeName struct { // argument for NewNamed, which will set the TypeName's type as a side- // effect. func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName { - return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}} + return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, nil, nil} +} + +// IsParametrized reports whether obj is a parametrized type. +func (obj *TypeName) IsParametrized() bool { + return len(obj.tparams) > 0 } // IsAlias reports whether obj is an alias name for a type. diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go index 0841505ea0..4c6599c6c7 100644 --- a/src/go/types/resolver.go +++ b/src/go/types/resolver.go @@ -386,6 +386,7 @@ func (check *Checker) collectObjects() { case *ast.TypeSpec: obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil) + obj.scope, obj.tparams = check.collectTypeParams(pkg.scope, s, s.TParams) check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, tdecl: s}) default: @@ -471,6 +472,28 @@ func (check *Checker) collectObjects() { } } +func (check *Checker) collectTypeParams(parent *Scope, node ast.Node, list *ast.FieldList) (scope *Scope, tparams []*TypeName) { + if list.NumFields() == 0 { + return + } + + scope = NewScope(parent, node.Pos(), node.End(), "type parameters") + check.recordScope(node, scope) + + index := 0 + for _, f := range list.List { + for _, name := range f.Names { + tpar := NewTypeName(name.Pos(), check.pkg, name.Name, nil) + NewTypeParam(tpar, index) // assigns type to tpar as a side-effect + check.declare(scope, name, tpar, scope.pos) + tparams = append(tparams, tpar) + index++ + } + } + + return +} + // resolveBaseTypeName returns the non-alias base type name for typ, and whether // there was a pointer indirection to get to it. The base type name must be declared // in package scope, and there can be at most one pointer indirection. If no such type diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 831dc9cb4a..811873be2a 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -145,19 +145,20 @@ func (check *Checker) definedType(e ast.Expr, def *Named) (T Type) { // funcType type-checks a function or method type. func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, tpar *ast.FieldList, ftyp *ast.FuncType) { // type parameters are in a scope enclosing the function scope - // TODO(gri) should we always have this extra scope? - if tpar.NumFields() != 0 { - check.scope = NewScope(check.scope, token.NoPos, token.NoPos, "function type parameters") // TODO(gri) replace with check.openScope call - defer check.closeScope() - // TODO(gri) record this scope + // TODO(gri) get rid of fake ast.Ident - only here to satisfy collectTypeParams + scope, tparams := check.collectTypeParams(check.scope, new(ast.Ident), tpar) + if scope != nil { + // TODO(gri) push/pop both (type parameter and function) scopes + check.scope = scope + // defer check.closeScope() } - scope := NewScope(check.scope, token.NoPos, token.NoPos, "function") + + scope = NewScope(check.scope, token.NoPos, token.NoPos, "function") // TODO(gri) should we close this scope? scope.isFunc = true check.recordScope(ftyp, scope) recvList, _ := check.collectParams(scope, recvPar, false) - tparams := check.collectTypeParams(check.scope, tpar) params, variadic := check.collectParams(scope, ftyp.Params, true) results, _ := check.collectParams(scope, ftyp.Results, false) @@ -418,25 +419,6 @@ func (check *Checker) arrayLength(e ast.Expr) int64 { return -1 } -func (check *Checker) collectTypeParams(scope *Scope, list *ast.FieldList) (tparams []*TypeName) { - if list == nil { - return - } - - index := 0 - for _, f := range list.List { - for _, name := range f.Names { - tpar := NewTypeName(name.Pos(), check.pkg, name.Name, nil) - NewTypeParam(tpar, index) // assigns type to tpar as a side-effect - check.declare(scope, name, tpar, scope.pos) - tparams = append(tparams, tpar) - index++ - } - } - - return -} - func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) { if list == nil { return