// index checks an index/size expression arg for validity. // If length >= 0, it is the upper bound for arg. // TODO(gri): Do we need iota? func (check *checker) index(arg ast.Expr, length int64, iota int) (i int64, ok bool) { var x operand check.expr(&x, arg, nil, iota) // an untyped constant must be representable as Int check.convertUntyped(&x, Typ[Int]) if x.mode == invalid { return } // the index/size must be of integer type if !isInteger(x.typ) { check.invalidArg(x.pos(), "%s must be integer", &x) return } // a constant index/size i must be 0 <= i < length if x.mode == constant { if exact.Sign(x.val) < 0 { check.invalidArg(x.pos(), "%s must not be negative", &x) return } i, ok = exact.Int64Val(x.val) if !ok || length >= 0 && i >= length { check.errorf(x.pos(), "index %s is out of bounds", &x) return i, false } // 0 <= i [ && i < length ] return i, true } return -1, true }
// conversion typechecks the type conversion conv to type typ. iota is the current // value of iota or -1 if iota doesn't have a value in the current context. The result // of the conversion is returned via x. If the conversion has type errors, the returned // x is marked as invalid (x.mode == invalid). // func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota int) { // all conversions have one argument if len(conv.Args) != 1 { check.invalidOp(conv.Pos(), "%s conversion requires exactly one argument", conv) goto Error } // evaluate argument check.expr(x, conv.Args[0], nil, iota) if x.mode == invalid { goto Error } if x.mode == constant && isConstType(typ) { // constant conversion typ := typ.Underlying().(*Basic) // For now just implement string(x) where x is an integer, // as a temporary work-around for issue 4982, which is a // common issue. if typ.kind == String { switch { case x.isInteger(): codepoint := int64(-1) if i, ok := exact.Int64Val(x.val); ok { codepoint = i } // If codepoint < 0 the absolute value is too large (or unknown) for // conversion. This is the same as converting any other out-of-range // value - let string(codepoint) do the work. x.val = exact.MakeString(string(codepoint)) case isString(x.typ): // nothing to do default: goto ErrorMsg } } // TODO(gri) verify the remaining conversions. } else { // non-constant conversion if !x.isConvertible(check.ctxt, typ) { goto ErrorMsg } x.mode = value } // the conversion argument types are final; for now we just use x.typ // TODO(gri) Fix this. For untyped constants, the type should be typ. check.updateExprType(x.expr, x.typ, true) check.conversions[conv] = true // for cap/len checking x.expr = conv x.typ = typ return ErrorMsg: check.invalidOp(conv.Pos(), "cannot convert %s to %s", x, typ) Error: x.mode = invalid x.expr = conv }
func isRepresentableConst(x exact.Value, ctxt *Context, as BasicKind) bool { switch x.Kind() { case exact.Unknown: return true case exact.Bool: return as == Bool || as == UntypedBool case exact.Int: if x, ok := exact.Int64Val(x); ok { switch as { case Int: var s = uint(ctxt.sizeof(Typ[as])) * 8 return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1 case Int8: const s = 8 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int16: const s = 16 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int32: const s = 32 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int64: return true case Uint, Uintptr: if s := uint(ctxt.sizeof(Typ[as])) * 8; s < 64 { return 0 <= x && x <= int64(1)<<s-1 } return 0 <= x case Uint8: const s = 8 return 0 <= x && x <= 1<<s-1 case Uint16: const s = 16 return 0 <= x && x <= 1<<s-1 case Uint32: const s = 32 return 0 <= x && x <= 1<<s-1 case Uint64: return 0 <= x case Float32: return true // TODO(gri) fix this case Float64: return true // TODO(gri) fix this case Complex64: return true // TODO(gri) fix this case Complex128: return true // TODO(gri) fix this case UntypedInt, UntypedFloat, UntypedComplex: return true } } n := exact.BitLen(x) switch as { case Uint, Uintptr: var s = uint(ctxt.sizeof(Typ[as])) * 8 return exact.Sign(x) >= 0 && n <= int(s) case Uint64: return exact.Sign(x) >= 0 && n <= 64 case Float32: return true // TODO(gri) fix this case Float64: return true // TODO(gri) fix this case Complex64: return true // TODO(gri) fix this case Complex128: return true // TODO(gri) fix this case UntypedInt, UntypedFloat, UntypedComplex: return true } case exact.Float: switch as { case Float32: return true // TODO(gri) fix this case Float64: return true // TODO(gri) fix this case Complex64: return true // TODO(gri) fix this case Complex128: return true // TODO(gri) fix this case UntypedFloat, UntypedComplex: return true } case exact.Complex: switch as { case Complex64: return true // TODO(gri) fix this case Complex128: return true // TODO(gri) fix this case UntypedComplex: return true } case exact.String: return as == String || as == UntypedString case exact.Nil: return as == UntypedNil || as == UnsafePointer default: unreachable() } return false }
// rawExpr typechecks expression e and initializes x with the expression // value or type. If an error occurred, x.mode is set to invalid. // If hint != nil, it is the type of a composite literal element. // iota >= 0 indicates that the expression is part of a constant declaration. // cycleOk indicates whether it is ok for a type expression to refer to itself. // func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycleOk bool) { if trace { c := "" if cycleOk { c = " тиБ" } check.trace(e.Pos(), "%s%s", e, c) defer check.untrace("=> %s", x) } // record final type of x if untyped, notify clients of type otherwise defer check.callExpr(x) switch e := e.(type) { case *ast.BadExpr: goto Error // error was reported before case *ast.Ident: if e.Name == "_" { check.invalidOp(e.Pos(), "cannot use _ as value or type") goto Error } obj := check.lookup(e) if obj == nil { goto Error // error was reported before } check.object(obj, cycleOk) switch obj := obj.(type) { case *Package: check.errorf(e.Pos(), "use of package %s not in selector", obj.Name) goto Error case *Const: if obj.typ == Typ[Invalid] { goto Error } x.mode = constant if obj == universeIota { if iota < 0 { check.invalidAST(e.Pos(), "cannot use iota outside constant declaration") goto Error } x.val = exact.MakeInt64(int64(iota)) } else { x.val = obj.val // may be nil if we don't know the constant value } case *TypeName: x.mode = typexpr if !cycleOk && obj.typ.Underlying() == nil { check.errorf(obj.spec.Pos(), "illegal cycle in declaration of %s", obj.Name) x.expr = e x.typ = Typ[Invalid] return // don't goto Error - need x.mode == typexpr } case *Var: x.mode = variable case *Func: x.mode = value default: unreachable() } x.typ = obj.Type() case *ast.Ellipsis: // ellipses are handled explicitly where they are legal // (array composite literals and parameter lists) check.errorf(e.Pos(), "invalid use of '...'") goto Error case *ast.BasicLit: x.setConst(e.Kind, e.Value) if x.mode == invalid { check.invalidAST(e.Pos(), "invalid literal %v", e.Value) goto Error } case *ast.FuncLit: if sig, ok := check.typ(e.Type, false).(*Signature); ok { x.mode = value x.typ = sig check.later(nil, sig, e.Body) } else { check.invalidAST(e.Pos(), "invalid function literal %s", e) goto Error } case *ast.CompositeLit: typ := hint openArray := false if e.Type != nil { // [...]T array types may only appear with composite literals. // Check for them here so we don't have to handle ... in general. typ = nil if atyp, _ := e.Type.(*ast.ArrayType); atyp != nil && atyp.Len != nil { if ellip, _ := atyp.Len.(*ast.Ellipsis); ellip != nil && ellip.Elt == nil { // We have an "open" [...]T array type. // Create a new ArrayType with unknown length (-1) // and finish setting it up after analyzing the literal. typ = &Array{len: -1, elt: check.typ(atyp.Elt, cycleOk)} openArray = true } } if typ == nil { typ = check.typ(e.Type, false) } } if typ == nil { check.errorf(e.Pos(), "missing type in composite literal") goto Error } switch utyp := typ.Deref().Underlying().(type) { case *Struct: if len(e.Elts) == 0 { break } fields := utyp.fields if _, ok := e.Elts[0].(*ast.KeyValueExpr); ok { // all elements must have keys visited := make([]bool, len(fields)) for _, e := range e.Elts { kv, _ := e.(*ast.KeyValueExpr) if kv == nil { check.errorf(e.Pos(), "mixture of field:value and value elements in struct literal") continue } key, _ := kv.Key.(*ast.Ident) if key == nil { check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key) continue } i := utyp.fieldIndex(check.pkg, key.Name) if i < 0 { check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name) continue } // 0 <= i < len(fields) if visited[i] { check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name) continue } visited[i] = true check.expr(x, kv.Value, nil, iota) check.register(key, fields[i]) etyp := fields[i].typ if !check.assignment(x, etyp) { if x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp) } continue } } } else { // no element must have a key for i, e := range e.Elts { if kv, _ := e.(*ast.KeyValueExpr); kv != nil { check.errorf(kv.Pos(), "mixture of field:value and value elements in struct literal") continue } check.expr(x, e, nil, iota) if i >= len(fields) { check.errorf(x.pos(), "too many values in struct literal") break // cannot continue } // i < len(fields) etyp := fields[i].typ if !check.assignment(x, etyp) { if x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp) } continue } } if len(e.Elts) < len(fields) { check.errorf(e.Rbrace, "too few values in struct literal") // ok to continue } } case *Array: n := check.indexedElts(e.Elts, utyp.elt, utyp.len, iota) // if we have an "open" [...]T array, set the length now that we know it if openArray { utyp.len = n } case *Slice: check.indexedElts(e.Elts, utyp.elt, -1, iota) case *Map: visited := make(map[interface{}]bool, len(e.Elts)) for _, e := range e.Elts { kv, _ := e.(*ast.KeyValueExpr) if kv == nil { check.errorf(e.Pos(), "missing key in map literal") continue } check.compositeLitKey(kv.Key) check.expr(x, kv.Key, nil, iota) if !check.assignment(x, utyp.key) { if x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.key) } continue } if x.mode == constant { if visited[x.val] { check.errorf(x.pos(), "duplicate key %s in map literal", x.val) continue } visited[x.val] = true } check.expr(x, kv.Value, utyp.elt, iota) if !check.assignment(x, utyp.elt) { if x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.elt) } continue } } default: check.errorf(e.Pos(), "%s is not a valid composite literal type", typ) goto Error } x.mode = value x.typ = typ case *ast.ParenExpr: check.rawExpr(x, e.X, nil, iota, cycleOk) case *ast.SelectorExpr: sel := e.Sel.Name // If the identifier refers to a package, handle everything here // so we don't need a "package" mode for operands: package names // can only appear in qualified identifiers which are mapped to // selector expressions. if ident, ok := e.X.(*ast.Ident); ok { if pkg, ok := check.lookup(ident).(*Package); ok { exp := pkg.scope.Lookup(sel) if exp == nil { check.errorf(e.Pos(), "%s not declared by package %s", sel, ident) goto Error } else if !ast.IsExported(exp.Name()) { // gcimported package scopes contain non-exported // objects such as types used in partially exported // objects - do not accept them check.errorf(e.Pos(), "%s not exported by package %s", sel, ident) goto Error } check.register(e.Sel, exp) // Simplified version of the code for *ast.Idents: // - imported packages use types.Scope and types.Objects // - imported objects are always fully initialized switch exp := exp.(type) { case *Const: assert(exp.Val != nil) x.mode = constant x.typ = exp.typ x.val = exp.val case *TypeName: x.mode = typexpr x.typ = exp.typ case *Var: x.mode = variable x.typ = exp.typ case *Func: x.mode = value x.typ = exp.typ default: unreachable() } x.expr = e return } } check.exprOrType(x, e.X, iota, false) if x.mode == invalid { goto Error } res := lookupField(x.typ, check.pkg, sel) utyp := x.typ.Underlying() if res.mode == invalid { check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel) goto Error } if x.mode == typexpr { // method expression sig, ok := res.typ.(*Signature) if !ok { check.invalidOp(e.Pos(), "%s has no method %s", x, sel) goto Error } // the receiver type becomes the type of the first function // argument of the method expression's function type // TODO(gri) at the moment, method sets don't correctly track // pointer vs non-pointer receivers => typechecker is too lenient var params []*Var if sig.params != nil { params = sig.params.vars } x.mode = value m := x.typ.Deref().(*Named).methods.Lookup(check.pkg, sel) check.register(e.Sel, m) x.typ = &Signature{ params: NewTuple(append([]*Var{{typ: x.typ}}, params...)...), results: sig.results, isVariadic: sig.isVariadic, } } else { // regular selector x.mode = res.mode if res.index == nil { // method var method Object if t, ok := x.typ.Deref().(*Named); ok { method = t.methods.Lookup(check.pkg, sel) } if method == nil { if t, ok := utyp.Deref().(*Interface); ok { method = t.methods.Lookup(check.pkg, sel) } } if method != nil { check.register(e.Sel, method) } else if false { // TODO(sqs): fix this - it occurs when we aren't propagating the methods of an embedded interface. fmt.Printf("nil method: %s", e.Sel) } } else { // field if s, ok := utyp.Deref().(*Struct); ok { check.register(e.Sel, s.Field(res.index[0])) } } x.typ = res.typ } case *ast.IndexExpr: check.expr(x, e.X, nil, iota) if x.mode == invalid { goto Error } valid := false length := int64(-1) // valid if >= 0 switch typ := x.typ.Underlying().(type) { case *Basic: if isString(typ) { valid = true if x.mode == constant { length = int64(len(exact.StringVal(x.val))) } // an indexed string always yields a byte value // (not a constant) even if the string and the // index are constant x.mode = value x.typ = Typ[Byte] } case *Array: valid = true length = typ.len if x.mode != variable { x.mode = value } x.typ = typ.elt case *Pointer: if typ, _ := typ.base.Underlying().(*Array); typ != nil { valid = true length = typ.len x.mode = variable x.typ = typ.elt } case *Slice: valid = true x.mode = variable x.typ = typ.elt case *Map: var key operand check.expr(&key, e.Index, nil, iota) if !check.assignment(&key, typ.key) { if key.mode != invalid { check.invalidOp(key.pos(), "cannot use %s as map index of type %s", &key, typ.key) } goto Error } x.mode = valueok x.typ = typ.elt x.expr = e return } if !valid { check.invalidOp(x.pos(), "cannot index %s", x) goto Error } if e.Index == nil { check.invalidAST(e.Pos(), "missing index expression for %s", x) return } check.index(e.Index, length, iota) // ok to continue case *ast.SliceExpr: check.expr(x, e.X, nil, iota) if x.mode == invalid { goto Error } valid := false length := int64(-1) // valid if >= 0 switch typ := x.typ.Underlying().(type) { case *Basic: if isString(typ) { valid = true if x.mode == constant { length = int64(len(exact.StringVal(x.val))) + 1 // +1 for slice } // a sliced string always yields a string value // of the same type as the original string (not // a constant) even if the string and the indices // are constant x.mode = value // x.typ doesn't change, but if it is an untyped // string it becomes string (see also issue 4913). if typ.kind == UntypedString { x.typ = Typ[String] } } case *Array: valid = true length = typ.len + 1 // +1 for slice if x.mode != variable { check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x) goto Error } x.typ = &Slice{elt: typ.elt} case *Pointer: if typ, _ := typ.base.Underlying().(*Array); typ != nil { valid = true length = typ.len + 1 // +1 for slice x.mode = variable x.typ = &Slice{elt: typ.elt} } case *Slice: valid = true x.mode = variable // x.typ doesn't change } if !valid { check.invalidOp(x.pos(), "cannot slice %s", x) goto Error } lo := int64(0) if e.Low != nil { if i, ok := check.index(e.Low, length, iota); ok && i >= 0 { lo = i } } hi := int64(-1) if e.High != nil { if i, ok := check.index(e.High, length, iota); ok && i >= 0 { hi = i } } else if length >= 0 { hi = length } if lo >= 0 && hi >= 0 && lo > hi { check.errorf(e.Low.Pos(), "inverted slice range: %d > %d", lo, hi) // ok to continue } case *ast.TypeAssertExpr: check.expr(x, e.X, nil, iota) if x.mode == invalid { goto Error } var T *Interface if T, _ = x.typ.Underlying().(*Interface); T == nil { check.invalidOp(x.pos(), "%s is not an interface", x) goto Error } // x.(type) expressions are handled explicitly in type switches if e.Type == nil { check.errorf(e.Pos(), "use of .(type) outside type switch") goto Error } typ := check.typ(e.Type, false) if typ == Typ[Invalid] { goto Error } if method, wrongType := missingMethod(typ, T); method != nil { var msg string if wrongType { msg = "%s cannot have dynamic type %s (wrong type for method %s)" } else { msg = "%s cannot have dynamic type %s (missing method %s)" } check.errorf(e.Type.Pos(), msg, x, typ, method.name) // ok to continue } x.mode = valueok x.expr = e x.typ = typ case *ast.CallExpr: check.exprOrType(x, e.Fun, iota, false) if x.mode == invalid { goto Error } else if x.mode == typexpr { check.conversion(x, e, x.typ, iota) } else if sig, ok := x.typ.Underlying().(*Signature); ok { // check parameters // If we have a trailing ... at the end of the parameter // list, the last argument must match the parameter type // []T of a variadic function parameter x ...T. passSlice := false if e.Ellipsis.IsValid() { if sig.isVariadic { passSlice = true } else { check.errorf(e.Ellipsis, "cannot use ... in call to %s", e.Fun) // ok to continue } } // If we have a single argument that is a function call // we need to handle it separately. Determine if this // is the case without checking the argument. var call *ast.CallExpr if len(e.Args) == 1 { call, _ = unparen(e.Args[0]).(*ast.CallExpr) } n := 0 // parameter count if call != nil { // We have a single argument that is a function call. check.expr(x, call, nil, -1) if x.mode == invalid { goto Error // TODO(gri): we can do better } if t, ok := x.typ.(*Tuple); ok { // multiple result values n = t.Len() for i := 0; i < n; i++ { obj := t.At(i) x.mode = value x.expr = nil // TODO(gri) can we do better here? (for good error messages) x.typ = obj.typ check.argument(sig, i, nil, x, passSlice && i+1 == n) } } else { // single result value n = 1 check.argument(sig, 0, nil, x, passSlice) } } else { // We don't have a single argument or it is not a function call. n = len(e.Args) for i, arg := range e.Args { check.argument(sig, i, arg, x, passSlice && i+1 == n) } } // determine if we have enough arguments if sig.isVariadic { // a variadic function accepts an "empty" // last argument: count one extra n++ } if n < sig.params.Len() { check.errorf(e.Fun.Pos(), "too few arguments in call to %s", e.Fun) // ok to continue } // determine result switch sig.results.Len() { case 0: x.mode = novalue case 1: x.mode = value x.typ = sig.results.vars[0].typ default: x.mode = value x.typ = sig.results } } else if bin, ok := x.typ.(*Builtin); ok { check.builtin(x, e, bin, iota) } else { check.invalidOp(x.pos(), "cannot call non-function %s", x) goto Error } case *ast.StarExpr: check.exprOrType(x, e.X, iota, true) switch x.mode { case invalid: goto Error case typexpr: x.typ = &Pointer{base: x.typ} default: if typ, ok := x.typ.Underlying().(*Pointer); ok { x.mode = variable x.typ = typ.base } else { check.invalidOp(x.pos(), "cannot indirect %s", x) goto Error } } case *ast.UnaryExpr: check.expr(x, e.X, nil, iota) if x.mode == invalid { goto Error } check.unary(x, e.Op) if x.mode == invalid { goto Error } case *ast.BinaryExpr: check.binary(x, e.X, e.Y, e.Op, iota) if x.mode == invalid { goto Error } case *ast.KeyValueExpr: // key:value expressions are handled in composite literals check.invalidAST(e.Pos(), "no key:value expected") goto Error case *ast.ArrayType: if e.Len != nil { check.expr(x, e.Len, nil, iota) if x.mode == invalid { goto Error } if x.mode != constant { if x.mode != invalid { check.errorf(x.pos(), "array length %s must be constant", x) } goto Error } if !x.isInteger() { check.errorf(x.pos(), "array length %s must be integer", x) goto Error } n, ok := exact.Int64Val(x.val) if !ok || n < 0 { check.errorf(x.pos(), "invalid array length %s", x) goto Error } x.typ = &Array{len: n, elt: check.typ(e.Elt, cycleOk)} } else { x.typ = &Slice{elt: check.typ(e.Elt, true)} } x.mode = typexpr case *ast.StructType: x.mode = typexpr fields, tags := check.collectFields(e.Fields, cycleOk) x.typ = &Struct{fields: fields, tags: tags} case *ast.FuncType: params, isVariadic := check.collectParams(e.Params, true) results, _ := check.collectParams(e.Results, false) x.mode = typexpr x.typ = &Signature{recv: nil, params: NewTuple(params...), results: NewTuple(results...), isVariadic: isVariadic} case *ast.InterfaceType: x.mode = typexpr x.typ = &Interface{methods: check.collectMethods(e.Methods)} case *ast.MapType: x.mode = typexpr x.typ = &Map{key: check.typ(e.Key, true), elt: check.typ(e.Value, true)} case *ast.ChanType: x.mode = typexpr x.typ = &Chan{dir: e.Dir, elt: check.typ(e.Value, true)} default: if debug { check.dump("expr = %v (%T)", e, e) } unreachable() } // everything went well x.expr = e return Error: x.mode = invalid x.expr = e }
// object typechecks an object by assigning it a type. // func (check *checker) object(obj Object, cycleOk bool) { switch obj := obj.(type) { case *Package: // nothing to do case *Const: if obj.typ != nil { return // already checked } // The obj.Val field for constants is initialized to its respective // iota value (type int) by the parser. // If the object's type is Typ[Invalid], the object value is ignored. // If the object's type is valid, the object value must be a legal // constant value; it may be nil to indicate that we don't know the // value of the constant (e.g., in: "const x = float32("foo")" we // know that x is a constant and has type float32, but we don't // have a value due to the error in the conversion). if obj.visited { check.errorf(obj.Pos(), "illegal cycle in initialization of constant %s", obj.Name) obj.typ = Typ[Invalid] return } obj.visited = true spec := obj.spec iota, ok := exact.Int64Val(obj.val) assert(ok) obj.val = exact.MakeUnknown() // determine spec for type and initialization expressions init := spec if len(init.Values) == 0 { init = check.initspecs[spec] } check.valueSpec(spec.Pos(), obj, spec.Names, init, int(iota)) case *Var: if obj.typ != nil { return // already checked } if obj.visited { check.errorf(obj.Pos(), "illegal cycle in initialization of variable %s", obj.Name) obj.typ = Typ[Invalid] return } obj.visited = true switch d := obj.decl.(type) { case *ast.Field: unreachable() // function parameters are always typed when collected case *ast.ValueSpec: check.valueSpec(d.Pos(), obj, d.Names, d, 0) case *ast.AssignStmt: unreachable() // assign1to1 sets the type for failing short var decls default: unreachable() // see also function newObj } case *TypeName: if obj.typ != nil { return // already checked } typ := &Named{obj: obj} obj.typ = typ // "mark" object so recursion terminates typ.underlying = check.typ(obj.spec.Type, cycleOk).Underlying() // typecheck associated method signatures if scope := check.methods[obj]; scope != nil { switch t := typ.underlying.(type) { case *Struct: // struct fields must not conflict with methods for _, f := range t.fields { if m := scope.Lookup(f.name); m != nil { check.errorf(m.Pos(), "type %s has both field and method named %s", obj.Name, f.name) // ok to continue } f.FieldOf = obj } case *Interface: // methods cannot be associated with an interface type for _, m := range scope.Entries { recv := m.(*Func).decl.Recv.List[0].Type check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name) // ok to continue } } // typecheck method signatures var methods ObjSet for _, obj := range scope.Entries { m := obj.(*Func) sig := check.typ(m.decl.Type, cycleOk).(*Signature) params, _ := check.collectParams(m.decl.Recv, false) sig.recv = params[0] // the parser/assocMethod ensure there is exactly one parameter m.typ = sig assert(methods.Insert(obj) == nil) check.later(m, sig, m.decl.Body) } typ.methods = methods delete(check.methods, obj) // we don't need this scope anymore } case *Func: if obj.typ != nil { return // already checked } fdecl := obj.decl // methods are typechecked when their receivers are typechecked if fdecl.Recv == nil { sig := check.typ(fdecl.Type, cycleOk).(*Signature) if 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 } obj.typ = sig check.later(obj, sig, fdecl.Body) } default: unreachable() } }