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
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.
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
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)
}
// 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
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 {
}
} 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)
}
}
// 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 {
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)
// 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)
}
// 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)
}
}
// 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
}