diff --git a/src/go/types/decl.go b/src/go/types/decl.go index 079c74575a..aabba91279 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -660,11 +660,7 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) { sig := new(Signature) obj.typ = sig // guard against cycles fdecl := decl.fdecl - check.funcType(sig, fdecl.Recv, fdecl.TParams, fdecl.Type) - if sig.recv == nil && obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) { - check.errorf(fdecl.Pos(), "func init must have no arguments and no return values") - // ok to continue - } + check.funcType(sig, fdecl.Recv, obj.scope, obj.tparams, fdecl.Type) // function body must be type-checked after global declarations // (functions implemented elsewhere have no body) @@ -787,7 +783,9 @@ 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) + if s.TParams != 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 21d008e990..be8558410c 100644 --- a/src/go/types/object.go +++ b/src/go/types/object.go @@ -213,7 +213,6 @@ 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 } @@ -301,7 +300,9 @@ func (*Var) isDependency() {} // a variable may be a dependency of an initializa // An abstract method may belong to many interfaces due to embedding. type Func struct { object - hasPtrRecv bool // only valid for methods that don't have a type yet + hasPtrRecv bool // only valid for methods that don't have a type yet + scope *Scope // type parameter scope; or nil + tparams []*TypeName // type parameters from left to right; or nil } // NewFunc returns a new function with the given signature, representing @@ -312,7 +313,7 @@ func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func { if sig != nil { typ = sig } - return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, false} + return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, false, nil, nil} } // FullName returns the package- or receiver-type-qualified name of diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go index 4c6599c6c7..eb315c6dcf 100644 --- a/src/go/types/resolver.go +++ b/src/go/types/resolver.go @@ -215,6 +215,7 @@ func (check *Checker) collectObjects() { } var methods []*Func // list of methods with non-blank _ names + var fileScopes []*Scope for fileNo, file := range check.files { // The package identifier denotes the current package, // but there is no corresponding package object. @@ -228,6 +229,7 @@ func (check *Checker) collectObjects() { pos, end = token.Pos(f.Base()), token.Pos(f.Base()+f.Size()) } fileScope := NewScope(check.pkg.scope, pos, end, check.filename(fileNo)) + fileScopes = append(fileScopes, fileScope) check.recordScope(file, fileScope) // determine file directory, necessary to resolve imports @@ -386,7 +388,9 @@ 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) + if s.TParams != nil { + obj.scope, obj.tparams = check.collectTypeParams(pkg.scope, s, s.TParams) + } check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, tdecl: s}) default: @@ -400,18 +404,32 @@ func (check *Checker) collectObjects() { if d.Recv == nil { // regular function if name == "init" { + if d.TParams != nil { + check.softErrorf(d.TParams.Pos(), "func init must have no type parameters") + } + if t := d.Type; t.Params.NumFields() != 0 || t.Results != nil { + check.softErrorf(d.Pos(), "func init must have no arguments and no return values") + } // don't declare init functions in the package scope - they are invisible obj.parent = pkg.scope check.recordDef(d.Name, obj) // init functions must have a body if d.Body == nil { + // TODO(gri) make this error message consistent with the others above check.softErrorf(obj.pos, "missing function body") } } else { + if d.TParams != nil { + obj.scope, obj.tparams = check.collectTypeParams(pkg.scope, d, d.TParams) + } check.declare(pkg.scope, d.Name, obj, token.NoPos) } } else { // method + if d.TParams != nil { + // TODO(gri) should this be done in the parser (and this an invalidAST error)? + check.softErrorf(d.TParams.Pos(), "method must have no type parameters") + } // (Methods with blank _ names are never found; no need to collect // them for later type association. They will still be type-checked // with all the other functions.) @@ -435,7 +453,7 @@ func (check *Checker) collectObjects() { } // verify that objects in package and file scopes have different names - for _, scope := range check.pkg.scope.children /* file scopes */ { + for _, scope := range fileScopes { for _, obj := range scope.elems { if alt := pkg.scope.Lookup(obj.Name()); alt != nil { if pkg, ok := obj.(*PkgName); ok { @@ -473,10 +491,6 @@ 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) diff --git a/src/go/types/testdata/typeparams.go2 b/src/go/types/testdata/typeparams.go2 index 51226b6302..25d2701737 100644 --- a/src/go/types/testdata/typeparams.go2 +++ b/src/go/types/testdata/typeparams.go2 @@ -111,3 +111,15 @@ var _ = f8(1, 2.3, 3.4, 4 /* ERROR does not match */ ) var _ = f8(int, float64)(1, 2.3, 3.4, 4) var _ = f8(int, float64)(0, 0, nil...) // test case for #18268 + +// init function and methods cannot have type parameters + +func init() {} +func init(/* ERROR func init must have no type parameters */ type)() {} +func init(/* ERROR func init must have no type parameters */ type P)() {} + +type T struct {} + +func (T) m1() {} +func (T) m2( /* ERROR method must have no type parameters */ type)() {} +func (T) m3( /* ERROR method must have no type parameters */ type P)() {} diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 811873be2a..c5c24e49d9 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -143,13 +143,11 @@ 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) { +func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, scope *Scope, tparams []*TypeName, ftyp *ast.FuncType) { // type parameters are in a scope enclosing the function 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 + if tparams != nil { check.scope = scope + // TODO(gri) push/pop both (type parameter and function) scopes // defer check.closeScope() } @@ -209,10 +207,6 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, tpar *ast } } sig.recv = recv - // A method cannot have type parameters - this should be checked by the parser. - if len(tparams) > 0 { - check.invalidAST(tpar.Pos(), "method cannot have type parameters") - } } sig.scope = scope @@ -301,7 +295,7 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type { case *ast.FuncType: typ := new(Signature) def.setUnderlying(typ) - check.funcType(typ, nil, nil, e) + check.funcType(typ, nil, nil, nil, e) return typ case *ast.InterfaceType: