// assign1to1 typechecks a single assignment of the form lhs = rhs (if rhs != nil), or // lhs = x (if rhs == nil). If decl is set, the lhs expression must be an identifier; // if its type is not set, it is deduced from the type of x or set to Typ[Invalid] in // case of an error. // func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota int) { // Start with rhs so we have an expression type // for declarations with implicit type. if x == nil { x = new(operand) check.expr(x, rhs, nil, iota) // don't exit for declarations - we need the lhs first if x.mode == invalid && !decl { return } } // x.mode == valid || decl // lhs may be an identifier ident, _ := lhs.(*ast.Ident) // regular assignment; we know x is valid if !decl { // anything can be assigned to the blank identifier if ident != nil && ident.Name == "_" { // the rhs has its final type check.updateExprType(rhs, x.typ, true) return } var z operand check.expr(&z, lhs, nil, -1) if z.mode == invalid { return } // TODO(gri) verify that all other z.mode values // that may appear here are legal if z.mode == constant || !check.assignment(x, z.typ) { if x.mode != invalid { check.errorf(x.pos(), "cannot assign %s to %s", x, &z) } } return } // declaration with initialization; lhs must be an identifier if ident == nil { check.errorf(lhs.Pos(), "cannot declare %s", lhs) return } // Determine typ of lhs: If the object doesn't have a type // yet, determine it from the type of x; if x is invalid, // set the object type to Typ[Invalid]. var typ Type obj := check.lookup(ident) switch obj := obj.(type) { default: unreachable() case nil: // TODO(gri) is this really unreachable? unreachable() case *Const: typ = obj.Type // may already be Typ[Invalid] if typ == nil { typ = Typ[Invalid] if x.mode != invalid { typ = x.typ } obj.Type = typ } case *Var: typ = obj.Type // may already be Typ[Invalid] if typ == nil { typ = Typ[Invalid] if x.mode != invalid { typ = x.typ if isUntyped(typ) { // convert untyped types to default types if typ == Typ[UntypedNil] { check.errorf(x.pos(), "use of untyped nil") typ = Typ[Invalid] } else { typ = defaultType(typ) } } } obj.Type = typ } } // nothing else to check if we don't have a valid lhs or rhs if typ == Typ[Invalid] || x.mode == invalid { return } if !check.assignment(x, typ) { if x.mode != invalid { if x.typ != Typ[Invalid] && typ != Typ[Invalid] { check.errorf(x.pos(), "cannot initialize %s (type %s) with %s", ident.Name, typ, x) } } return } // for constants, set their value if obj, _ := obj.(*Const); obj != nil { obj.Val = exact.MakeUnknown() // failure case: we don't know the constant value if x.mode == constant { if isConstType(x.typ) { obj.Val = x.val } else if x.typ != Typ[Invalid] { check.errorf(x.pos(), "%s has invalid constant type", x) } } else if x.mode != invalid { check.errorf(x.pos(), "%s is not constant", x) } } }
// 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.Type != 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.GetPos(), "illegal cycle in initialization of constant %s", obj.Name) obj.Type = 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.Type != nil { return // already checked } if obj.visited { check.errorf(obj.GetPos(), "illegal cycle in initialization of variable %s", obj.Name) obj.Type = 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.Type != nil { return // already checked } typ := &NamedType{Obj: obj} obj.Type = typ // "mark" object so recursion terminates typ.Underlying = underlying(check.typ(obj.spec.Type, cycleOk)) // 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.GetPos(), "type %s has both field and method named %s", obj.Name, f.Name) // ok to continue } } 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 []*Method 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.Type = sig methods = append(methods, &Method{QualifiedName{check.pkg, m.Name}, sig}) 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.Type != 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" && (len(sig.Params) != 0 || len(sig.Results) != 0) { check.errorf(fdecl.Pos(), "func init must have no arguments and no return values") // ok to continue } obj.Type = sig check.later(obj, sig, fdecl.Body) } default: unreachable() } }