Example #1
0
func (tc *typechecker) resolve(obj *ast.Object) {
	// check for declaration cycles
	if tc.cyclemap[obj] {
		tc.Errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name)
		obj.Kind = ast.Bad
		return
	}
	tc.cyclemap[obj] = true
	defer func() {
		tc.cyclemap[obj] = false, false
	}()

	// resolve non-type objects
	typ, _ := obj.Type.(*Type)
	if typ == nil {
		switch obj.Kind {
		case ast.Bad:
			// ignore

		case ast.Con:
			tc.declConst(obj)

		case ast.Var:
			tc.declVar(obj)
			obj.Type = tc.typeFor(nil, obj.Decl.(*ast.ValueSpec).Type, false)

		case ast.Fun:
			obj.Type = NewType(Function)
			t := obj.Decl.(*ast.FuncDecl).Type
			tc.declSignature(obj.Type.(*Type), nil, t.Params, t.Results)

		default:
			// type objects have non-nil types when resolve is called
			if debug {
				fmt.Printf("kind = %s\n", obj.Kind)
			}
			panic("unreachable")
		}
		return
	}

	// resolve type objects
	if typ.Form == Unresolved {
		tc.typeFor(typ, typ.Obj.Decl.(*ast.TypeSpec).Type, false)

		// provide types for all methods
		for _, obj := range typ.Scope.Objects {
			if obj.Kind == ast.Fun {
				assert(obj.Type == nil)
				obj.Type = NewType(Method)
				f := obj.Decl.(*ast.FuncDecl)
				t := f.Type
				tc.declSignature(obj.Type.(*Type), f.Recv, t.Params, t.Results)
			}
		}
	}
}
Example #2
0
// checkObj type checks an object.
func (c *checker) checkObj(obj *ast.Object, ref bool) {
	if obj.Type != nil {
		// object has already been type checked
		return
	}

	switch obj.Kind {
	case ast.Bad:
		// ignore

	case ast.Con:
		// TODO(gri) complete this

	case ast.Typ:
		typ := &Name{Obj: obj}
		obj.Type = typ // "mark" object so recursion terminates
		typ.Underlying = Underlying(c.makeType(obj.Decl.(*ast.TypeSpec).Type, ref))

	case ast.Var:
		// TODO(gri) complete this

	case ast.Fun:
		// TODO(gri) complete this

	default:
		panic("unreachable")
	}
}
Example #3
0
// checkObj type checks an object.
func (c *checker) checkObj(obj *ast.Object, ref bool) {
	if obj.Type != nil {
		// object has already been type checked
		return
	}

	switch obj.Kind {
	case ast.Bad:
		// ignore

	case ast.Con:
		// TODO(gri) complete this

	case ast.Typ:
		typ := &Name{Obj: obj}
		obj.Type = typ // "mark" object so recursion terminates
		typ.Underlying = Underlying(c.makeType(obj.Decl.(*ast.TypeSpec).Type, ref))

	case ast.Var:
		// TODO(gri) complete this

	case ast.Fun:
		fdecl := obj.Decl.(*ast.FuncDecl)
		ftyp := c.makeType(fdecl.Type, ref).(*Func)
		obj.Type = ftyp
		if fdecl.Recv != nil {
			recvField := fdecl.Recv.List[0]
			if len(recvField.Names) > 0 {
				ftyp.Recv = recvField.Names[0].Obj
			} else {
				ftyp.Recv = ast.NewObj(ast.Var, "_")
				ftyp.Recv.Decl = recvField
			}
			c.checkObj(ftyp.Recv, ref)
			// TODO(axw) add method to a list in the receiver type.
		}
		// TODO(axw) check function body, if non-nil.

	default:
		panic("unreachable")
	}
}
Example #4
0
func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident, typ ast.Expr, rhs []ast.Expr, iota int) {
	if len(lhs) == 0 {
		check.invalidAST(pos, "missing lhs in declaration")
		return
	}

	// determine type for all of lhs, if any
	// (but only set it for the object we typecheck!)
	var t Type
	if typ != nil {
		t = check.typ(typ, false)
	}

	// len(lhs) > 0
	if len(lhs) == len(rhs) {
		// check only lhs and rhs corresponding to obj
		var l, r ast.Expr
		for i, name := range lhs {
			if name.Obj == obj {
				l = lhs[i]
				r = rhs[i]
				break
			}
		}
		assert(l != nil)
		obj.Type = t
		check.assign1to1(l, r, nil, true, iota)
		return
	}

	// there must be a type or initialization expressions
	if t == nil && len(rhs) == 0 {
		check.invalidAST(pos, "missing type or initialization expression")
		t = Typ[Invalid]
	}

	// if we have a type, mark all of lhs
	if t != nil {
		for _, name := range lhs {
			name.Obj.Type = t
		}
	}

	// check initial values, if any
	if len(rhs) > 0 {
		// TODO(gri) should try to avoid this conversion
		lhx := make([]ast.Expr, len(lhs))
		for i, e := range lhs {
			lhx[i] = e
		}
		check.assignNtoM(lhx, rhs, true, iota)
	}
}
Example #5
0
File: check.go Project: hfeeki/go
func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident, typ ast.Expr, rhs []ast.Expr, iota int) {
	if len(lhs) == 0 {
		check.invalidAST(pos, "missing lhs in declaration")
		return
	}

	var t Type
	if typ != nil {
		t = check.typ(typ, false)
	}

	// len(lhs) >= 1
	if len(lhs) == len(rhs) {
		// check only corresponding lhs and rhs
		var l, r ast.Expr
		for i, ident := range lhs {
			if ident.Obj == obj {
				l = lhs[i]
				r = rhs[i]
				break
			}
		}
		assert(l != nil)
		obj.Type = t
		// check rhs
		var x operand
		check.expr(&x, r, t, iota)
		// assign to lhs
		check.assignment(l, &x, true)
		return
	}

	if t != nil {
		for _, name := range lhs {
			name.Obj.Type = t
		}
	}

	// check initial values, if any
	if len(rhs) > 0 {
		// TODO(gri) should try to avoid this conversion
		lhx := make([]ast.Expr, len(lhs))
		for i, e := range lhs {
			lhx[i] = e
		}
		check.assignNtoM(lhx, rhs, true, iota)
	}
}
Example #6
0
func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (params ObjList, isVariadic bool) {
	if list == nil {
		return
	}
	var last *ast.Object
	for i, field := range list.List {
		ftype := field.Type
		if t, _ := ftype.(*ast.Ellipsis); t != nil {
			ftype = t.Elt
			if variadicOk && i == len(list.List)-1 {
				isVariadic = true
			} else {
				check.invalidAST(field.Pos(), "... not permitted")
				// ok to continue
			}
		}
		// the parser ensures that f.Tag is nil and we don't
		// care if a constructed AST contains a non-nil tag
		typ := check.typ(ftype, true)
		if len(field.Names) > 0 {
			// named parameter
			for _, name := range field.Names {
				obj := name.Obj
				obj.Type = typ
				params = append(params, obj)
				last = obj
			}
		} else {
			// anonymous parameter
			obj := ast.NewObj(ast.Var, "")
			obj.Type = typ
			params = append(params, obj)
			last = obj
		}
	}
	// For a variadic function, change the last parameter's object type
	// from T to []T (this is the type used inside the function), but
	// keep a copy of the object with the original type T in the params
	// list (this is the externally visible type).
	if isVariadic {
		// if isVariadic is set, last must exist and len(params) > 0
		copy := *last
		last.Type = &Slice{Elt: last.Type.(Type)}
		params[len(params)-1] = &copy
	}
	return
}
Example #7
0
// convertUntyped takes an object, and, if it is untyped, gives it
// a named builtin type: bool, rune, int, float64, complex128 or string.
func maybeConvertUntyped(obj *ast.Object) bool {
	switch obj.Type {
	case Bool.Underlying:
		obj.Type = Bool
	case Rune.Underlying:
		obj.Type = Rune
	case Int.Underlying:
		obj.Type = Int
	case Float64.Underlying:
		obj.Type = Float64
	case Complex128.Underlying:
		obj.Type = Complex128
	case String.Underlying:
		obj.Type = String
	default:
		return false
	}
	return true
}
Example #8
0
// object typechecks an object by assigning it a type; obj.Type must be nil.
// Callers must check obj.Type before calling object; this eliminates a call
// for each identifier that has been typechecked already, a common scenario.
//
func (check *checker) object(obj *ast.Object, cycleOk bool) {
	assert(obj.Type == nil)

	switch obj.Kind {
	case ast.Bad, ast.Pkg:
		// nothing to do

	case ast.Con, ast.Var:
		// The obj.Data field for constants and variables is initialized
		// to the respective (hypothetical, for variables) iota value by
		// the parser. The object's fields can be in one of the following
		// states:
		// Type != nil  =>  the constant value is Data
		// Type == nil  =>  the object is not typechecked yet, and Data can be:
		// Data is int  =>  Data is the value of iota for this declaration
		// Data == nil  =>  the object's expression is being evaluated
		if obj.Data == nil {
			check.errorf(obj.Pos(), "illegal cycle in initialization of %s", obj.Name)
			obj.Type = Typ[Invalid]
			return
		}
		spec := obj.Decl.(*ast.ValueSpec)
		iota := obj.Data.(int)
		obj.Data = nil
		// determine initialization expressions
		values := spec.Values
		if len(values) == 0 && obj.Kind == ast.Con {
			values = check.initexprs[spec]
		}
		check.valueSpec(spec.Pos(), obj, spec.Names, spec.Type, values, iota)

	case ast.Typ:
		typ := &NamedType{Obj: obj}
		obj.Type = typ // "mark" object so recursion terminates
		typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk))
		// typecheck associated method signatures
		if obj.Data != nil {
			scope := obj.Data.(*ast.Scope)
			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
					}
				}
			case *Interface:
				// methods cannot be associated with an interface type
				for _, m := range scope.Objects {
					recv := m.Decl.(*ast.FuncDecl).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
			for _, obj := range scope.Objects {
				mdecl := obj.Decl.(*ast.FuncDecl)
				sig := check.typ(mdecl.Type, cycleOk).(*Signature)
				params, _ := check.collectParams(mdecl.Recv, false)
				sig.Recv = params[0] // the parser/assocMethod ensure there is exactly one parameter
				obj.Type = sig
				check.later(obj, sig, mdecl.Body)
			}
		}

	case ast.Fun:
		fdecl := obj.Decl.(*ast.FuncDecl)
		// 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:
		panic("unreachable")
	}
}
Example #9
0
File: check.go Project: mm120/gcc
// object typechecks an object by assigning it a type; obj.Type must be nil.
// Callers must check obj.Type before calling object; this eliminates a call
// for each identifier that has been typechecked already, a common scenario.
//
func (check *checker) object(obj *ast.Object, cycleOk bool) {
	assert(obj.Type == nil)

	switch obj.Kind {
	case ast.Bad, ast.Pkg:
		// nothing to do

	case ast.Con, ast.Var:
		// The obj.Data field for constants and variables is initialized
		// to the respective (hypothetical, for variables) iota value by
		// the parser. The object's fields can be in one of the following
		// states:
		// Type != nil  =>  the constant value is Data
		// Type == nil  =>  the object is not typechecked yet, and Data can be:
		// Data is int  =>  Data is the value of iota for this declaration
		// Data == nil  =>  the object's expression is being evaluated
		if obj.Data == nil {
			check.errorf(obj.Pos(), "illegal cycle in initialization of %s", obj.Name)
			obj.Type = Typ[Invalid]
			return
		}
		spec := obj.Decl.(*ast.ValueSpec)
		iota := obj.Data.(int)
		obj.Data = nil
		// determine initialization expressions
		values := spec.Values
		if len(values) == 0 && obj.Kind == ast.Con {
			values = check.initexprs[spec]
		}
		check.valueSpec(spec.Pos(), obj, spec.Names, spec.Type, values, iota)

	case ast.Typ:
		typ := &NamedType{Obj: obj}
		obj.Type = typ // "mark" object so recursion terminates
		typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk))
		// typecheck associated method signatures
		if obj.Data != nil {
			scope := obj.Data.(*ast.Scope)
			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
			case *Interface:
				// methods cannot be associated with an interface type
				for _, m := range scope.Objects {
					recv := m.Decl.(*ast.FuncDecl).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
			for _, m := range scope.Objects {
				mdecl := m.Decl.(*ast.FuncDecl)
				// TODO(gri) At the moment, the receiver is type-checked when checking
				// the method body. Also, we don't properly track if the receiver is
				// a pointer (i.e., currently, method sets are too large). FIX THIS.
				mtyp := check.typ(mdecl.Type, cycleOk).(*Signature)
				m.Type = mtyp
			}
		}

	case ast.Fun:
		fdecl := obj.Decl.(*ast.FuncDecl)
		if fdecl.Recv != nil {
			// This will ensure that the method base type is
			// type-checked
			check.collectFields(token.FUNC, fdecl.Recv, true)
		}
		ftyp := check.typ(fdecl.Type, cycleOk).(*Signature)
		obj.Type = ftyp
		check.function(ftyp, fdecl.Body)

	default:
		panic("unreachable")
	}
}
Example #10
0
File: stmt.go Project: Lao16/gcc
// stmt typechecks statement s.
func (check *checker) stmt(s ast.Stmt) {
	switch s := s.(type) {
	case *ast.BadStmt, *ast.EmptyStmt:
		// ignore

	case *ast.DeclStmt:
		check.decl(s.Decl)

	case *ast.LabeledStmt:
		// TODO(gri) anything to do with label itself?
		check.stmt(s.Stmt)

	case *ast.ExprStmt:
		var x operand
		used := false
		switch e := unparen(s.X).(type) {
		case *ast.CallExpr:
			// function calls are permitted
			used = true
			// but some builtins are excluded
			check.expr(&x, e.Fun, nil, -1)
			if x.mode != invalid {
				if b, ok := x.typ.(*builtin); ok && !b.isStatement {
					used = false
				}
			}
		case *ast.UnaryExpr:
			// receive operations are permitted
			if e.Op == token.ARROW {
				used = true
			}
		}
		if !used {
			check.errorf(s.Pos(), "%s not used", s.X)
			// ok to continue
		}
		check.rawExpr(&x, s.X, nil, -1, false)
		if x.mode == typexpr {
			check.errorf(x.pos(), "%s is not an expression", &x)
		}

	case *ast.SendStmt:
		var ch, x operand
		check.expr(&ch, s.Chan, nil, -1)
		check.expr(&x, s.Value, nil, -1)
		if ch.mode == invalid || x.mode == invalid {
			return
		}
		if tch, ok := underlying(ch.typ).(*Chan); !ok || tch.Dir&ast.SEND == 0 || !x.isAssignable(tch.Elt) {
			check.invalidOp(ch.pos(), "cannot send %s to channel %s", &x, &ch)
		}

	case *ast.IncDecStmt:
		var op token.Token
		switch s.Tok {
		case token.INC:
			op = token.ADD
		case token.DEC:
			op = token.SUB
		default:
			check.invalidAST(s.TokPos, "unknown inc/dec operation %s", s.Tok)
			return
		}
		var x, y operand
		check.expr(&x, s.X, nil, -1)
		check.expr(&y, &ast.BasicLit{ValuePos: x.pos(), Kind: token.INT, Value: "1"}, nil, -1) // use x's position
		check.binary(&x, &y, op, nil)
		check.assign1to1(s.X, nil, &x, false, -1)

	case *ast.AssignStmt:
		switch s.Tok {
		case token.ASSIGN, token.DEFINE:
			if len(s.Lhs) == 0 {
				check.invalidAST(s.Pos(), "missing lhs in assignment")
				return
			}
			check.assignNtoM(s.Lhs, s.Rhs, s.Tok == token.DEFINE, -1)
		default:
			// assignment operations
			if len(s.Lhs) != 1 || len(s.Rhs) != 1 {
				check.errorf(s.TokPos, "assignment operation %s requires single-valued expressions", s.Tok)
				return
			}
			// TODO(gri) make this conversion more efficient
			var op token.Token
			switch s.Tok {
			case token.ADD_ASSIGN:
				op = token.ADD
			case token.SUB_ASSIGN:
				op = token.SUB
			case token.MUL_ASSIGN:
				op = token.MUL
			case token.QUO_ASSIGN:
				op = token.QUO
			case token.REM_ASSIGN:
				op = token.REM
			case token.AND_ASSIGN:
				op = token.AND
			case token.OR_ASSIGN:
				op = token.OR
			case token.XOR_ASSIGN:
				op = token.XOR
			case token.SHL_ASSIGN:
				op = token.SHL
			case token.SHR_ASSIGN:
				op = token.SHR
			case token.AND_NOT_ASSIGN:
				op = token.AND_NOT
			default:
				check.invalidAST(s.TokPos, "unknown assignment operation %s", s.Tok)
				return
			}
			var x, y operand
			check.expr(&x, s.Lhs[0], nil, -1)
			check.expr(&y, s.Rhs[0], nil, -1)
			check.binary(&x, &y, op, nil)
			check.assign1to1(s.Lhs[0], nil, &x, false, -1)
		}

	case *ast.GoStmt:
		check.call(s.Call)

	case *ast.DeferStmt:
		check.call(s.Call)

	case *ast.ReturnStmt:
		sig := check.functypes[len(check.functypes)-1]
		if n := len(sig.Results); n > 0 {
			// TODO(gri) should not have to compute lhs, named every single time - clean this up
			lhs := make([]ast.Expr, n)
			named := false // if set, function has named results
			for i, res := range sig.Results {
				if len(res.Name) > 0 {
					// a blank (_) result parameter is a named result parameter!
					named = true
				}
				name := ast.NewIdent(res.Name)
				name.NamePos = s.Pos()
				name.Obj = res
				lhs[i] = name
			}
			if len(s.Results) > 0 || !named {
				// TODO(gri) assignNtoM should perhaps not require len(lhs) > 0
				check.assignNtoM(lhs, s.Results, false, -1)
			}
		} else if len(s.Results) > 0 {
			check.errorf(s.Pos(), "no result values expected")
		}

	case *ast.BranchStmt:
		unimplemented()

	case *ast.BlockStmt:
		check.stmtList(s.List)

	case *ast.IfStmt:
		check.optionalStmt(s.Init)
		var x operand
		check.expr(&x, s.Cond, nil, -1)
		if !isBoolean(x.typ) {
			check.errorf(s.Cond.Pos(), "non-boolean condition in if statement")
		}
		check.stmt(s.Body)
		check.optionalStmt(s.Else)

	case *ast.SwitchStmt:
		check.optionalStmt(s.Init)
		var x operand
		if s.Tag != nil {
			check.expr(&x, s.Tag, nil, -1)
		} else {
			// TODO(gri) should provide a position (see IncDec) for good error messages
			x.mode = constant
			x.typ = Typ[UntypedBool]
			x.val = true
		}

		check.multipleDefaults(s.Body.List)
		for _, s := range s.Body.List {
			clause, _ := s.(*ast.CaseClause)
			if clause == nil {
				continue // error reported before
			}
			for _, expr := range clause.List {
				var y operand
				check.expr(&y, expr, nil, -1)
				// TODO(gri) x and y must be comparable
			}
			check.stmtList(clause.Body)
		}

	case *ast.TypeSwitchStmt:
		check.optionalStmt(s.Init)

		// A type switch guard must be of the form:
		//
		//     TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
		//
		// The parser is checking syntactic correctness;
		// remaining syntactic errors are considered AST errors here.
		// TODO(gri) better factoring of error handling (invalid ASTs)
		//
		var lhs *ast.Object // lhs identifier object or nil
		var rhs ast.Expr
		switch guard := s.Assign.(type) {
		case *ast.ExprStmt:
			rhs = guard.X
		case *ast.AssignStmt:
			if len(guard.Lhs) != 1 || guard.Tok != token.DEFINE || len(guard.Rhs) != 1 {
				check.invalidAST(s.Pos(), "incorrect form of type switch guard")
				return
			}
			ident, _ := guard.Lhs[0].(*ast.Ident)
			if ident == nil {
				check.invalidAST(s.Pos(), "incorrect form of type switch guard")
				return
			}
			lhs = ident.Obj
			rhs = guard.Rhs[0]
		default:
			check.invalidAST(s.Pos(), "incorrect form of type switch guard")
			return
		}

		// rhs must be of the form: expr.(type) and expr must be an interface
		expr, _ := rhs.(*ast.TypeAssertExpr)
		if expr == nil || expr.Type != nil {
			check.invalidAST(s.Pos(), "incorrect form of type switch guard")
			return
		}
		var x operand
		check.expr(&x, expr.X, nil, -1)
		if x.mode == invalid {
			return
		}
		var T *Interface
		if T, _ = underlying(x.typ).(*Interface); T == nil {
			check.errorf(x.pos(), "%s is not an interface", &x)
			return
		}

		check.multipleDefaults(s.Body.List)
		for _, s := range s.Body.List {
			clause, _ := s.(*ast.CaseClause)
			if clause == nil {
				continue // error reported before
			}
			// Check each type in this type switch case.
			var typ Type
			for _, expr := range clause.List {
				typ = check.typOrNil(expr, false)
				if typ != nil && typ != Typ[Invalid] {
					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(expr.Pos(), msg, &x, typ, method.Name)
						// ok to continue
					}
				}
			}
			// If lhs exists, set its type for each clause.
			if lhs != nil {
				// In clauses with a case listing exactly one type, the variable has that type;
				// otherwise, the variable has the type of the expression in the TypeSwitchGuard.
				if len(clause.List) != 1 || typ == nil {
					typ = x.typ
				}
				lhs.Type = typ
			}
			check.stmtList(clause.Body)
		}

		// There is only one object (lhs) associated with a lhs identifier, but that object
		// assumes different types for different clauses. Set it to nil when we are done so
		// that the type cannot be used by mistake.
		if lhs != nil {
			lhs.Type = nil
		}

	case *ast.SelectStmt:
		check.multipleDefaults(s.Body.List)
		for _, s := range s.Body.List {
			clause, _ := s.(*ast.CommClause)
			if clause == nil {
				continue // error reported before
			}
			check.optionalStmt(clause.Comm) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt)
			check.stmtList(clause.Body)
		}

	case *ast.ForStmt:
		check.optionalStmt(s.Init)
		if s.Cond != nil {
			var x operand
			check.expr(&x, s.Cond, nil, -1)
			if !isBoolean(x.typ) {
				check.errorf(s.Cond.Pos(), "non-boolean condition in for statement")
			}
		}
		check.optionalStmt(s.Post)
		check.stmt(s.Body)

	case *ast.RangeStmt:
		// check expression to iterate over
		decl := s.Tok == token.DEFINE
		var x operand
		check.expr(&x, s.X, nil, -1)
		if x.mode == invalid {
			// if we don't have a declaration, we can still check the loop's body
			if !decl {
				check.stmt(s.Body)
			}
			return
		}

		// determine key/value types
		var key, val Type
		switch typ := underlying(x.typ).(type) {
		case *Basic:
			if isString(typ) {
				key = Typ[UntypedInt]
				val = Typ[UntypedRune]
			}
		case *Array:
			key = Typ[UntypedInt]
			val = typ.Elt
		case *Slice:
			key = Typ[UntypedInt]
			val = typ.Elt
		case *Pointer:
			if typ, _ := underlying(typ.Base).(*Array); typ != nil {
				key = Typ[UntypedInt]
				val = typ.Elt
			}
		case *Map:
			key = typ.Key
			val = typ.Elt
		case *Chan:
			key = typ.Elt
			if typ.Dir&ast.RECV == 0 {
				check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
				// ok to continue
			}
			if s.Value != nil {
				check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x)
				// ok to continue
			}
		}

		if key == nil {
			check.errorf(x.pos(), "cannot range over %s", &x)
			// if we don't have a declaration, we can still check the loop's body
			if !decl {
				check.stmt(s.Body)
			}
			return
		}

		// check assignment to/declaration of iteration variables
		// TODO(gri) The error messages/positions are not great here,
		//           they refer to the expression in the range clause.
		//           Should give better messages w/o too much code
		//           duplication (assignment checking).
		if s.Key != nil {
			x.typ = key
			check.assign1to1(s.Key, nil, &x, decl, -1)
		} else {
			check.invalidAST(s.Pos(), "range clause requires index iteration variable")
			// ok to continue
		}
		if s.Value != nil {
			x.typ = val
			check.assign1to1(s.Value, nil, &x, decl, -1)
		}

		check.stmt(s.Body)

	default:
		check.errorf(s.Pos(), "invalid statement")
	}
}
Example #11
0
// checkObj type checks an object.
func (c *checker) checkObj(obj *ast.Object, ref bool) {
	if obj.Type != nil {
		// object has already been type checked
		return
	}

	switch obj.Kind {
	case ast.Bad:
		// ignore

	case ast.Con:
		valspec := obj.Decl.(*ast.ValueSpec)
		if valspec.Type != nil {
			obj.Type = c.makeType(valspec.Type, ref)
			for _, name := range valspec.Names {
				name.Obj.Type = obj.Type
			}
		}
		if valspec.Values != nil {
			oldData := Iota.Data
			for i, name := range valspec.Names {
				if name.Obj != nil {
					c.checkExpr(valspec.Values[i], []*ast.Ident{name})
					iotaValue := name.Obj.Data.(int)
					Iota.Data = Const{big.NewInt(int64(iotaValue))}
					constValue := c.evalConst(valspec.Values[i])
					typ := name.Obj.Type.(Type)
					name.Obj.Data = constValue.Convert(&typ)
				} else {
					c.checkExpr(valspec.Values[i], nil)
				}
			}
			if oldData != nil {
				Iota.Data = oldData
			}
		}

	case ast.Typ:
		typ := &Name{Package: c.pkgid, Obj: obj}
		obj.Type = typ // "mark" object so recursion terminates
		typ.Underlying = Underlying(c.makeType(obj.Decl.(*ast.TypeSpec).Type, ref))
		if methobjs := c.methods[obj]; methobjs != nil {
			methobjs.Sort()
			typ.Methods = methobjs

			// Check for instances of field and method with same name.
			if s, ok := typ.Underlying.(*Struct); ok {
				for _, m := range methobjs {
					if _, ok := s.FieldIndices[m.Name]; ok {
						c.errorf(m.Pos(), "type %s has both field and method named %s", obj.Name, m.Name)
					}
				}
			}

			// methods cannot be associated with an interface type
			// (do this check after sorting for reproducible error positions - needed for testing)
			if _, ok := typ.Underlying.(*Interface); ok {
				for _, m := range methobjs {
					recv := m.Decl.(*ast.FuncDecl).Recv.List[0].Type
					c.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name)
				}
			}
		}
		for _, m := range typ.Methods {
			c.checkObj(m, ref)
		}

	case ast.Var:
		var names []*ast.Ident
		var values []ast.Expr
		var typexpr ast.Expr
		switch x := obj.Decl.(type) {
		case *ast.ValueSpec:
			names = x.Names
			values = x.Values
			typexpr = x.Type
		case *ast.Field:
			names = x.Names
			typexpr = x.Type
		case *ast.AssignStmt:
			c.checkStmt(x)
			if obj.Type == nil {
				panic("obj.Type == nil")
			}
		default:
			panic(fmt.Sprintf("unimplemented (%T)", x))
		}
		if names != nil { // nil for anonymous field
			var typ Type
			if typexpr != nil {
				typ = c.makeType(typexpr, ref)
				for i, name := range names {
					if name.Obj != nil {
						name.Obj.Type = typ
					} else {
						names[i] = nil
					}
				}
			}
			if len(values) == 1 && len(names) > 1 {
				// multi-value assignment
				c.checkExpr(values[0], names)
			} else if len(values) == len(names) {
				for i, name := range names {
					if name.Obj != nil {
						c.checkExpr(values[i], []*ast.Ident{name})
						maybeConvertUntyped(name.Obj)
					} else {
						c.checkExpr(values[i], nil)
					}
				}
			}
		}

	case ast.Fun:
		fndecl := obj.Decl.(*ast.FuncDecl)
		obj.Type = c.makeType(fndecl.Type, ref)
		fn := obj.Type.(*Func)
		if fndecl.Recv != nil {
			recvField := fndecl.Recv.List[0]
			names := recvField.Names
			if len(recvField.Names) > 0 {
				fn.Recv = recvField.Names[0].Obj
			} else {
				fn.Recv = ast.NewObj(ast.Var, "_")
				fn.Recv.Decl = recvField
				name := &ast.Ident{Name: "_", Obj: fn.Recv}
				recvField.Names = []*ast.Ident{name}
			}
			c.checkObj(fn.Recv, ref)
			recvField.Names = names
		} else {
			// Only check body of non-method functions. We check method
			// bodies later, to avoid references to incomplete types.
			c.checkFunc(fndecl.Body, fn)
		}

	default:
		panic("unreachable")
	}
}
Example #12
0
// obj type checks an object.
func (check *checker) obj(obj *ast.Object, cycleOk bool) {
	if trace {
		fmt.Printf("obj(%s)\n", obj.Name)
	}

	if obj.Type != nil {
		// object has already been type checked
		return
	}

	switch obj.Kind {
	case ast.Bad, ast.Pkg:
		// nothing to do

	case ast.Con:
		if obj.Data == nil {
			check.errorf(obj.Pos(), "illegal cycle in initialization of %s", obj.Name)
			return
		}
		spec, ok := obj.Decl.(*ast.ValueSpec)
		assert(ok)
		// The Data stored with the constant is the value of iota for that
		// ast.ValueSpec. Use it for the evaluation of the initialization
		// expressions.
		iota := obj.Data.(int)
		obj.Data = nil
		check.decl(spec.Pos(), obj, spec.Names, spec.Type, check.specValues(spec), iota)

	case ast.Var:
		// TODO(gri) missing cycle detection
		spec, ok := obj.Decl.(*ast.ValueSpec)
		if !ok {
			// TODO(gri) the assertion fails for "x, y := 1, 2, 3" it seems
			fmt.Printf("var = %s\n", obj.Name)
		}
		assert(ok)
		check.decl(spec.Pos(), obj, spec.Names, spec.Type, spec.Values, 0)

	case ast.Typ:
		typ := &NamedType{Obj: obj}
		obj.Type = typ // "mark" object so recursion terminates
		typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk))
		// collect associated methods, if any
		if obj.Data != nil {
			scope := obj.Data.(*ast.Scope)
			// struct fields must not conflict with methods
			if t, ok := typ.Underlying.(*Struct); ok {
				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)
					}
				}
			}
			// collect methods
			methods := make(ObjList, len(scope.Objects))
			i := 0
			for _, m := range scope.Objects {
				methods[i] = m
				i++
			}
			methods.Sort()
			typ.Methods = methods
			// methods cannot be associated with an interface type
			// (do this check after sorting for reproducible error positions - needed for testing)
			if _, ok := typ.Underlying.(*Interface); ok {
				for _, m := range methods {
					recv := m.Decl.(*ast.FuncDecl).Recv.List[0].Type
					check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name)
				}
			}
		}

	case ast.Fun:
		fdecl := obj.Decl.(*ast.FuncDecl)
		ftyp := check.typ(fdecl.Type, cycleOk).(*Signature)
		obj.Type = ftyp
		if fdecl.Recv != nil {
			// TODO(gri) handle method receiver
		}
		check.stmt(fdecl.Body)

	default:
		panic("unreachable")
	}
}
Example #13
0
// checkObj type checks an object.
func (c *checker) checkObj(obj *ast.Object, ref bool) {
	if obj.Type != nil {
		// object has already been type checked
		return
	}

	switch obj.Kind {
	case ast.Bad:
		// ignore

	case ast.Con:
		valspec := obj.Decl.(*ast.ValueSpec)
		if valspec.Type != nil {
			obj.Type = c.makeType(valspec.Type, ref)
			for _, name := range valspec.Names {
				name.Obj.Type = obj.Type
			}
		}
		if valspec.Values != nil {
			for i, name := range valspec.Names {
				if name.Obj != nil {
					c.checkExpr(valspec.Values[i], []*ast.Ident{name})
				} else {
					c.checkExpr(valspec.Values[i], nil)
				}
			}
		}

	case ast.Typ:
		typ := &Name{Obj: obj}
		obj.Type = typ // "mark" object so recursion terminates
		if methobjs := c.methods[obj]; methobjs != nil {
			methobjs.Sort()
			typ.Methods = methobjs
		}
		typ.Underlying = Underlying(c.makeType(obj.Decl.(*ast.TypeSpec).Type, ref))
		for _, m := range typ.Methods {
			c.checkObj(m, ref)
		}

	case ast.Var:
		var names []*ast.Ident
		var values []ast.Expr
		var typexpr ast.Expr
		switch x := obj.Decl.(type) {
		case *ast.ValueSpec:
			names = x.Names
			values = x.Values
			typexpr = x.Type
		case *ast.Field:
			names = x.Names
			typexpr = x.Type
		case *ast.AssignStmt:
			c.checkStmt(x)
			if obj.Type == nil {
				panic("obj.Type == nil")
			}
		default:
			panic(fmt.Sprintf("unimplemented (%T)", x))
		}
		if names != nil { // nil for anonymous field
			var typ Type
			if typexpr != nil {
				typ = c.makeType(typexpr, ref)
				for i, name := range names {
					if name.Obj != nil {
						name.Obj.Type = typ
					} else {
						names[i] = nil
					}
				}
			}
			if len(values) == 1 && len(names) > 1 {
				// multi-value assignment
				c.checkExpr(values[0], names)
			} else if len(values) == len(names) {
				for i, name := range names {
					if name.Obj != nil {
						c.checkExpr(values[i], []*ast.Ident{name})
					} else {
						c.checkExpr(values[i], nil)
					}
				}
			}
		}

	case ast.Fun:
		fndecl := obj.Decl.(*ast.FuncDecl)
		obj.Type = c.makeType(fndecl.Type, ref)
		fn := obj.Type.(*Func)
		if fndecl.Recv != nil {
			recvField := fndecl.Recv.List[0]
			if len(recvField.Names) > 0 {
				fn.Recv = recvField.Names[0].Obj
			} else {
				fn.Recv = ast.NewObj(ast.Var, "_")
				fn.Recv.Decl = recvField
			}
			c.checkObj(fn.Recv, ref)
		} else {
			// Only check body of non-method functions. We check method
			// bodies later, to avoid references to incomplete types.
			if fndecl.Body != nil {
				c.checkStmt(fndecl.Body)
			}
		}

	default:
		panic("unreachable")
	}
}