]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/go/types/resolver.go
[dev.typeparams] merge dev.regabi (618e3c1) into dev.typeparams
[gostls13.git] / src / go / types / resolver.go
index b637f8b8cacf75793f564660081bc0c741daab1f..4f09237692837c3fb81275e7e9149feb0653bede 100644 (file)
@@ -19,11 +19,11 @@ import (
 type declInfo struct {
        file      *Scope        // scope of file containing this declaration
        lhs       []*Var        // lhs of n:1 variable declarations, or nil
-       typ       ast.Expr      // type, or nil
-       init      ast.Expr      // init/orig expression, or nil
+       vtyp      ast.Expr      // type, or nil (for const and var declarations only)
+       init      ast.Expr      // init/orig expression, or nil (for const and var declarations only)
        inherited bool          // if set, the init expression is inherited from a previous constant declaration
+       tdecl     *ast.TypeSpec // type declaration, or nil
        fdecl     *ast.FuncDecl // func declaration, or nil
-       alias     bool          // type alias declaration
 
        // The deps field tracks initialization expression dependencies.
        deps map[Object]bool // lazily initialized
@@ -216,7 +216,13 @@ func (check *Checker) collectObjects() {
                pkgImports[imp] = true
        }
 
-       var methods []*Func // list of methods with non-blank _ names
+       type methodInfo struct {
+               obj  *Func      // method
+               ptr  bool       // true if pointer receiver
+               recv *ast.Ident // receiver type name
+       }
+       var methods []methodInfo // collected methods with valid receivers and 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.
@@ -230,6 +236,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
@@ -324,7 +331,7 @@ func (check *Checker) collectObjects() {
                                                init = d.init[i]
                                        }
 
-                                       d := &declInfo{file: fileScope, typ: d.typ, init: init, inherited: d.inherited}
+                                       d := &declInfo{file: fileScope, vtyp: d.typ, init: init, inherited: d.inherited}
                                        check.declarePkgObj(name, obj, d)
                                }
 
@@ -339,7 +346,7 @@ func (check *Checker) collectObjects() {
                                        // The lhs elements are only set up after the for loop below,
                                        // but that's ok because declareVar only collects the declInfo
                                        // for a later phase.
-                                       d1 = &declInfo{file: fileScope, lhs: lhs, typ: d.spec.Type, init: d.spec.Values[0]}
+                                       d1 = &declInfo{file: fileScope, lhs: lhs, vtyp: d.spec.Type, init: d.spec.Values[0]}
                                }
 
                                // declare all variables
@@ -354,26 +361,44 @@ func (check *Checker) collectObjects() {
                                                if i < len(d.spec.Values) {
                                                        init = d.spec.Values[i]
                                                }
-                                               di = &declInfo{file: fileScope, typ: d.spec.Type, init: init}
+                                               di = &declInfo{file: fileScope, vtyp: d.spec.Type, init: init}
                                        }
 
                                        check.declarePkgObj(name, obj, di)
                                }
                        case typeDecl:
                                obj := NewTypeName(d.spec.Name.Pos(), pkg, d.spec.Name.Name, nil)
-                               check.declarePkgObj(d.spec.Name, obj, &declInfo{file: fileScope, typ: d.spec.Type, alias: d.spec.Assign.IsValid()})
+                               check.declarePkgObj(d.spec.Name, obj, &declInfo{file: fileScope, tdecl: d.spec})
                        case funcDecl:
                                info := &declInfo{file: fileScope, fdecl: d.decl}
                                name := d.decl.Name.Name
                                obj := NewFunc(d.decl.Name.Pos(), pkg, name, nil)
-                               if d.decl.Recv == nil {
+                               if !d.decl.IsMethod() {
                                        // regular function
+                                       if d.decl.Recv != nil {
+                                               check.error(d.decl.Recv, _BadRecv, "method is missing receiver")
+                                               // treat as function
+                                       }
+                                       if name == "init" || (name == "main" && check.pkg.name == "main") {
+                                               code := _InvalidInitDecl
+                                               if name == "main" {
+                                                       code = _InvalidMainDecl
+                                               }
+                                               if d.decl.Type.TParams != nil {
+                                                       check.softErrorf(d.decl.Type.TParams, code, "func %s must have no type parameters", name)
+                                               }
+                                               if t := d.decl.Type; t.Params.NumFields() != 0 || t.Results != nil {
+                                                       // TODO(rFindley) Should this be a hard error?
+                                                       check.softErrorf(d.decl, code, "func %s must have no arguments and no return values", name)
+                                               }
+                                       }
                                        if name == "init" {
                                                // don't declare init functions in the package scope - they are invisible
                                                obj.parent = pkg.scope
                                                check.recordDef(d.decl.Name, obj)
                                                // init functions must have a body
                                                if d.decl.Body == nil {
+                                                       // TODO(gri) make this error message consistent with the others above
                                                        check.softErrorf(obj, _MissingInitBody, "missing function body")
                                                }
                                        } else {
@@ -381,11 +406,18 @@ func (check *Checker) collectObjects() {
                                        }
                                } else {
                                        // method
-                                       // (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.)
-                                       if name != "_" {
-                                               methods = append(methods, obj)
+
+                                       // TODO(rFindley) earlier versions of this code checked that methods
+                                       //                have no type parameters, but this is checked later
+                                       //                when type checking the function type. Confirm that
+                                       //                we don't need to check tparams here.
+
+                                       ptr, recv, _ := check.unpackRecv(d.decl.Recv.List[0].Type, false)
+                                       // (Methods with invalid receiver cannot be associated to a type, and
+                                       // methods with blank _ names are never found; no need to collect any
+                                       // of them. They will still be type-checked with all the other functions.)
+                                       if recv != nil && name != "_" {
+                                               methods = append(methods, methodInfo{obj, ptr, recv})
                                        }
                                        check.recordDef(d.decl.Name, obj)
                                }
@@ -400,7 +432,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 {
@@ -423,31 +455,87 @@ func (check *Checker) collectObjects() {
                return // nothing to do
        }
        check.methods = make(map[*TypeName][]*Func)
-       for _, f := range methods {
-               fdecl := check.objMap[f].fdecl
-               if list := fdecl.Recv.List; len(list) > 0 {
-                       // f is a method.
-                       // Determine the receiver base type and associate f with it.
-                       ptr, base := check.resolveBaseTypeName(list[0].Type)
-                       if base != nil {
-                               f.hasPtrRecv = ptr
-                               check.methods[base] = append(check.methods[base], f)
+       for i := range methods {
+               m := &methods[i]
+               // Determine the receiver base type and associate m with it.
+               ptr, base := check.resolveBaseTypeName(m.ptr, m.recv)
+               if base != nil {
+                       m.obj.hasPtrRecv = ptr
+                       check.methods[base] = append(check.methods[base], m.obj)
+               }
+       }
+}
+
+// unpackRecv unpacks a receiver type and returns its components: ptr indicates whether
+// rtyp is a pointer receiver, rname is the receiver type name, and tparams are its
+// type parameters, if any. The type parameters are only unpacked if unpackParams is
+// set. If rname is nil, the receiver is unusable (i.e., the source has a bug which we
+// cannot easily work around).
+func (check *Checker) unpackRecv(rtyp ast.Expr, unpackParams bool) (ptr bool, rname *ast.Ident, tparams []*ast.Ident) {
+L: // unpack receiver type
+       // This accepts invalid receivers such as ***T and does not
+       // work for other invalid receivers, but we don't care. The
+       // validity of receiver expressions is checked elsewhere.
+       for {
+               switch t := rtyp.(type) {
+               case *ast.ParenExpr:
+                       rtyp = t.X
+               case *ast.StarExpr:
+                       ptr = true
+                       rtyp = t.X
+               default:
+                       break L
+               }
+       }
+
+       // unpack type parameters, if any
+       switch ptyp := rtyp.(type) {
+       case *ast.IndexExpr:
+               panic("unimplemented")
+       case *ast.CallExpr:
+               rtyp = ptyp.Fun
+               if unpackParams {
+                       for _, arg := range ptyp.Args {
+                               var par *ast.Ident
+                               switch arg := arg.(type) {
+                               case *ast.Ident:
+                                       par = arg
+                               case *ast.BadExpr:
+                                       // ignore - error already reported by parser
+                               case nil:
+                                       check.invalidAST(ptyp, "parameterized receiver contains nil parameters")
+                               default:
+                                       check.errorf(arg, _Todo, "receiver type parameter %s must be an identifier", arg)
+                               }
+                               if par == nil {
+                                       par = &ast.Ident{NamePos: arg.Pos(), Name: "_"}
+                               }
+                               tparams = append(tparams, par)
                        }
                }
        }
+
+       // unpack receiver name
+       if name, _ := rtyp.(*ast.Ident); name != nil {
+               rname = name
+       }
+
+       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
 // name exists, the returned base is nil.
-func (check *Checker) resolveBaseTypeName(typ ast.Expr) (ptr bool, base *TypeName) {
+func (check *Checker) resolveBaseTypeName(seenPtr bool, name *ast.Ident) (ptr bool, base *TypeName) {
        // Algorithm: Starting from a type expression, which may be a name,
        // we follow that type through alias declarations until we reach a
        // non-alias type name. If we encounter anything but pointer types or
        // parentheses we're done. If we encounter more than one pointer type
        // we're done.
+       ptr = seenPtr
        var seen map[*TypeName]bool
+       var typ ast.Expr = name
        for {
                typ = unparen(typ)
 
@@ -487,13 +575,13 @@ func (check *Checker) resolveBaseTypeName(typ ast.Expr) (ptr bool, base *TypeNam
 
                // we're done if tdecl defined tname as a new type
                // (rather than an alias)
-               tdecl := check.objMap[tname] // must exist for objects in package scope
-               if !tdecl.alias {
+               tdecl := check.objMap[tname].tdecl // must exist for objects in package scope
+               if !tdecl.Assign.IsValid() {
                        return ptr, tname
                }
 
                // otherwise, continue resolving
-               typ = tdecl.typ
+               typ = tdecl.Type
                if seen == nil {
                        seen = make(map[*TypeName]bool)
                }
@@ -515,7 +603,7 @@ func (check *Checker) packageObjects() {
        // add new methods to already type-checked types (from a prior Checker.Files call)
        for _, obj := range objList {
                if obj, _ := obj.(*TypeName); obj != nil && obj.typ != nil {
-                       check.addMethodDecls(obj)
+                       check.collectMethods(obj)
                }
        }
 
@@ -529,7 +617,7 @@ func (check *Checker) packageObjects() {
        // phase 1
        for _, obj := range objList {
                // If we have a type alias, collect it for the 2nd phase.
-               if tname, _ := obj.(*TypeName); tname != nil && check.objMap[tname].alias {
+               if tname, _ := obj.(*TypeName); tname != nil && check.objMap[tname].tdecl.Assign.IsValid() {
                        aliasList = append(aliasList, tname)
                        continue
                }