Example #1
0
// 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)
		}
	}
}
Example #2
0
// 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()
	}
}