go.tools/go/types: represent built-ins as Objects (rather than types)
This change affects the API: Func objects now always have a *Signature as type (never a *Builtin). Instead, built-ins now appear as *Builtin objects. Only the built-in name is exposed, other fields are now private to go/types. Several bugs are fixed: - correctly checking for built-ins permitted in statement context - expression statements that are calls are not type-checked twice anymore - go/defer statements report call types and provide good error messages now This CL will briefly break the build until CL 13848043 is submitted. R=adonovan CC=golang-dev https://golang.org/cl/13813043
This commit is contained in:
		
							parent
							
								
									3371b79a96
								
							
						
					
					
						commit
						a05da76c7b
					
				|  | @ -185,7 +185,5 @@ func IsAssignableTo(V, T Type) bool { | |||
| 
 | ||||
| // BUG(gri): Some built-ins don't check parameters fully, yet (e.g. append).
 | ||||
| // BUG(gri): Use of labels is only partially checked.
 | ||||
| // BUG(gri): Unused variables and imports are not reported.
 | ||||
| // BUG(gri): Interface vs non-interface comparisons are not correctly implemented.
 | ||||
| // BUG(gri): Switch statements don't check duplicate cases for all types for which it is required.
 | ||||
| // BUG(gri): Some built-ins may not be callable if in statement-context.
 | ||||
|  |  | |||
|  | @ -16,13 +16,11 @@ import ( | |||
| // TODO(gri): Several built-ins are missing assignment checks. As a result,
 | ||||
| //            non-constant shift arguments may not be properly type-checked.
 | ||||
| 
 | ||||
| // builtin typechecks a built-in call. The built-in type is bin, and the result
 | ||||
| // of the call is returned via x. If the call has type errors, the returned x is
 | ||||
| // marked as invalid (x.mode == invalid).
 | ||||
| // builtin typechecks a call to a built-in and returns the result via x.
 | ||||
| // If the call has type errors, the returned x is marked as invalid.
 | ||||
| //
 | ||||
| func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin) { | ||||
| func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) { | ||||
| 	args := call.Args | ||||
| 	id := bin.id | ||||
| 
 | ||||
| 	// declare before goto's
 | ||||
| 	var arg0 ast.Expr // first argument, if present
 | ||||
|  | @ -30,9 +28,10 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin) { | |||
| 	// check argument count
 | ||||
| 	n := len(args) | ||||
| 	msg := "" | ||||
| 	bin := predeclaredFuncs[id] | ||||
| 	if n < bin.nargs { | ||||
| 		msg = "not enough" | ||||
| 	} else if !bin.isVariadic && n > bin.nargs { | ||||
| 	} else if !bin.variadic && n > bin.nargs { | ||||
| 		msg = "too many" | ||||
| 	} | ||||
| 	if msg != "" { | ||||
|  |  | |||
|  | @ -9,11 +9,26 @@ package types | |||
| import ( | ||||
| 	"go/ast" | ||||
| 	"go/token" | ||||
| 
 | ||||
| 	"code.google.com/p/go.tools/go/exact" | ||||
| ) | ||||
| 
 | ||||
| func (check *checker) call(x *operand, e *ast.CallExpr) { | ||||
| // exprKind describes the kind of an expression;
 | ||||
| // the kind determines if an expression is valid
 | ||||
| // in 'statement context'.
 | ||||
| type exprKind int | ||||
| 
 | ||||
| const ( | ||||
| 	conversion exprKind = iota | ||||
| 	expression | ||||
| 	statement | ||||
| ) | ||||
| 
 | ||||
| func (check *checker) call(x *operand, e *ast.CallExpr) exprKind { | ||||
| 	check.exprOrType(x, e.Fun) | ||||
| 	if x.mode == invalid { | ||||
| 
 | ||||
| 	switch x.mode { | ||||
| 	case invalid: | ||||
| 		// We don't have a valid call or conversion but we have a list of arguments.
 | ||||
| 		// Typecheck them independently for better partial type information in
 | ||||
| 		// the presence of type errors.
 | ||||
|  | @ -22,10 +37,9 @@ func (check *checker) call(x *operand, e *ast.CallExpr) { | |||
| 		} | ||||
| 		x.mode = invalid | ||||
| 		x.expr = e | ||||
| 		return | ||||
| 	} | ||||
| 		return statement | ||||
| 
 | ||||
| 	if x.mode == typexpr { | ||||
| 	case typexpr: | ||||
| 		// conversion
 | ||||
| 		T := x.typ | ||||
| 		x.mode = invalid | ||||
|  | @ -44,11 +58,23 @@ func (check *checker) call(x *operand, e *ast.CallExpr) { | |||
| 			check.errorf(e.Args[n-1].Pos(), "too many arguments in conversion to %s", T) | ||||
| 		} | ||||
| 		x.expr = e | ||||
| 		return | ||||
| 	} | ||||
| 		return conversion | ||||
| 
 | ||||
| 	if sig, ok := x.typ.Underlying().(*Signature); ok { | ||||
| 	case builtin: | ||||
| 		id, _ := exact.Int64Val(x.val) | ||||
| 		check.builtin(x, e, builtinId(id)) | ||||
| 		return predeclaredFuncs[id].kind | ||||
| 
 | ||||
| 	default: | ||||
| 		// function/method call
 | ||||
| 		sig, _ := x.typ.Underlying().(*Signature) | ||||
| 		if sig == nil { | ||||
| 			check.invalidOp(x.pos(), "cannot call non-function %s", x) | ||||
| 			x.mode = invalid | ||||
| 			x.expr = e | ||||
| 			return statement | ||||
| 		} | ||||
| 
 | ||||
| 		passSlice := false | ||||
| 		if e.Ellipsis.IsValid() { | ||||
| 			// last argument is of the form x...
 | ||||
|  | @ -116,17 +142,9 @@ func (check *checker) call(x *operand, e *ast.CallExpr) { | |||
| 			x.typ = sig.results | ||||
| 		} | ||||
| 		x.expr = e | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if bin, ok := x.typ.(*Builtin); ok { | ||||
| 		check.builtin(x, e, bin) | ||||
| 		return | ||||
| 		return statement | ||||
| 	} | ||||
| 
 | ||||
| 	check.invalidOp(x.pos(), "cannot call non-function %s", x) | ||||
| 	x.mode = invalid | ||||
| 	x.expr = e | ||||
| } | ||||
| 
 | ||||
| // argument checks passing of argument x to the i'th parameter of the given signature.
 | ||||
|  | @ -218,6 +236,10 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) { | |||
| 			case *Func: | ||||
| 				x.mode = value | ||||
| 				x.typ = exp.typ | ||||
| 			case *Builtin: | ||||
| 				x.mode = builtin | ||||
| 				x.typ = exp.typ | ||||
| 				x.val = exact.MakeInt64(int64(exp.id)) | ||||
| 			default: | ||||
| 				unreachable() | ||||
| 			} | ||||
|  |  | |||
|  | @ -248,9 +248,7 @@ func TestCheck(t *testing.T) { | |||
| 	// the construction of the Universe var.
 | ||||
| 	if !testBuiltinsDeclared { | ||||
| 		testBuiltinsDeclared = true | ||||
| 		// Pkg == nil for Universe objects
 | ||||
| 		def(NewFunc(token.NoPos, nil, "assert", &Builtin{_Assert, "assert", 1, false, true})) | ||||
| 		def(NewFunc(token.NoPos, nil, "trace", &Builtin{_Trace, "trace", 0, true, true})) | ||||
| 		defPredeclaredTestFuncs() | ||||
| 	} | ||||
| 
 | ||||
| 	// If explicit test files are specified, only check those.
 | ||||
|  |  | |||
|  | @ -844,13 +844,13 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64 | |||
| // 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.
 | ||||
| //
 | ||||
| func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) { | ||||
| func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind { | ||||
| 	if trace { | ||||
| 		check.trace(e.Pos(), "%s", e) | ||||
| 		check.indent++ | ||||
| 	} | ||||
| 
 | ||||
| 	check.expr0(x, e, hint) | ||||
| 	kind := check.expr0(x, e, hint) | ||||
| 
 | ||||
| 	// convert x into a user-friendly set of values
 | ||||
| 	record := true | ||||
|  | @ -885,12 +885,14 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) { | |||
| 		check.indent-- | ||||
| 		check.trace(e.Pos(), "=> %s", x) | ||||
| 	} | ||||
| 
 | ||||
| 	return kind | ||||
| } | ||||
| 
 | ||||
| // expr0 contains the core of type checking of expressions.
 | ||||
| // Must only be called by rawExpr.
 | ||||
| //
 | ||||
| func (check *checker) expr0(x *operand, e ast.Expr, hint Type) { | ||||
| func (check *checker) expr0(x *operand, e ast.Expr, hint Type) exprKind { | ||||
| 	// make sure x has a valid state in case of bailout
 | ||||
| 	// (was issue 5770)
 | ||||
| 	x.mode = invalid | ||||
|  | @ -1070,7 +1072,9 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) { | |||
| 		x.typ = typ | ||||
| 
 | ||||
| 	case *ast.ParenExpr: | ||||
| 		check.rawExpr(x, e.X, nil) | ||||
| 		kind := check.rawExpr(x, e.X, nil) | ||||
| 		x.expr = e | ||||
| 		return kind | ||||
| 
 | ||||
| 	case *ast.SelectorExpr: | ||||
| 		check.selector(x, e) | ||||
|  | @ -1130,7 +1134,7 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) { | |||
| 			x.mode = valueok | ||||
| 			x.typ = typ.elt | ||||
| 			x.expr = e | ||||
| 			return | ||||
| 			return expression | ||||
| 		} | ||||
| 
 | ||||
| 		if !valid { | ||||
|  | @ -1246,7 +1250,7 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) { | |||
| 		x.typ = T | ||||
| 
 | ||||
| 	case *ast.CallExpr: | ||||
| 		check.call(x, e) | ||||
| 		return check.call(x, e) | ||||
| 
 | ||||
| 	case *ast.StarExpr: | ||||
| 		check.exprOrType(x, e.X) | ||||
|  | @ -1274,6 +1278,10 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) { | |||
| 		if x.mode == invalid { | ||||
| 			goto Error | ||||
| 		} | ||||
| 		if e.Op == token.ARROW { | ||||
| 			x.expr = e | ||||
| 			return statement // receive operators may appear in statement context
 | ||||
| 		} | ||||
| 
 | ||||
| 	case *ast.BinaryExpr: | ||||
| 		check.binary(x, e.X, e.Y, e.Op) | ||||
|  | @ -1305,11 +1313,12 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) { | |||
| 
 | ||||
| 	// everything went well
 | ||||
| 	x.expr = e | ||||
| 	return | ||||
| 	return expression | ||||
| 
 | ||||
| Error: | ||||
| 	x.mode = invalid | ||||
| 	x.expr = e | ||||
| 	return statement // avoid follow-up errors
 | ||||
| } | ||||
| 
 | ||||
| // typeAssertion checks that x.(T) is legal; xtyp must be the type of x.
 | ||||
|  | @ -1332,14 +1341,19 @@ func (check *checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface, | |||
| //
 | ||||
| func (check *checker) expr(x *operand, e ast.Expr) { | ||||
| 	check.rawExpr(x, e, nil) | ||||
| 	var msg string | ||||
| 	switch x.mode { | ||||
| 	default: | ||||
| 		return | ||||
| 	case novalue: | ||||
| 		check.errorf(x.pos(), "%s used as value", x) | ||||
| 		x.mode = invalid | ||||
| 		msg = "%s used as value" | ||||
| 	case builtin: | ||||
| 		msg = "%s must be called" | ||||
| 	case typexpr: | ||||
| 		check.errorf(x.pos(), "%s is not an expression", x) | ||||
| 		x.mode = invalid | ||||
| 		msg = "%s is not an expression" | ||||
| 	} | ||||
| 	check.errorf(x.pos(), msg, x) | ||||
| 	x.mode = invalid | ||||
| } | ||||
| 
 | ||||
| // exprWithHint typechecks expression e and initializes x with the expression value.
 | ||||
|  | @ -1349,14 +1363,19 @@ func (check *checker) expr(x *operand, e ast.Expr) { | |||
| func (check *checker) exprWithHint(x *operand, e ast.Expr, hint Type) { | ||||
| 	assert(hint != nil) | ||||
| 	check.rawExpr(x, e, hint) | ||||
| 	var msg string | ||||
| 	switch x.mode { | ||||
| 	default: | ||||
| 		return | ||||
| 	case novalue: | ||||
| 		check.errorf(x.pos(), "%s used as value", x) | ||||
| 		x.mode = invalid | ||||
| 		msg = "%s used as value" | ||||
| 	case builtin: | ||||
| 		msg = "%s must be called" | ||||
| 	case typexpr: | ||||
| 		check.errorf(x.pos(), "%s is not an expression", x) | ||||
| 		x.mode = invalid | ||||
| 		msg = "%s is not an expression" | ||||
| 	} | ||||
| 	check.errorf(x.pos(), msg, x) | ||||
| 	x.mode = invalid | ||||
| } | ||||
| 
 | ||||
| // exprOrType typechecks expression or type e and initializes x with the expression value or type.
 | ||||
|  |  | |||
|  | @ -187,13 +187,18 @@ func (obj *Var) Anonymous() bool { return obj.anonymous } | |||
| func (obj *Var) String() string  { return obj.toString("var", obj.typ) } | ||||
| 
 | ||||
| // A Func represents a declared function, concrete method, or abstract
 | ||||
| // (interface) method.  Its Type() is a *Signature.
 | ||||
| // (interface) method.  Its Type() is always a *Signature.
 | ||||
| // An abstract method may belong to many interfaces due to embedding.
 | ||||
| type Func struct { | ||||
| 	object | ||||
| } | ||||
| 
 | ||||
| func NewFunc(pos token.Pos, pkg *Package, name string, typ Type) *Func { | ||||
| func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func { | ||||
| 	// don't store a nil signature
 | ||||
| 	var typ Type | ||||
| 	if sig != nil { | ||||
| 		typ = sig | ||||
| 	} | ||||
| 	return &Func{object{nil, pos, pkg, name, typ, false}} | ||||
| } | ||||
| 
 | ||||
|  | @ -206,7 +211,8 @@ func (obj *Func) FullName() string { | |||
| } | ||||
| 
 | ||||
| func (obj *Func) fullname(buf *bytes.Buffer) { | ||||
| 	if sig, _ := obj.typ.(*Signature); sig != nil { // may be a *Builtin
 | ||||
| 	if obj.typ != nil { | ||||
| 		sig := obj.typ.(*Signature) | ||||
| 		if recv := sig.Recv(); recv != nil { | ||||
| 			buf.WriteByte('(') | ||||
| 			if _, ok := recv.Type().(*Interface); ok { | ||||
|  | @ -232,8 +238,8 @@ func (obj *Func) String() string { | |||
| 	var buf bytes.Buffer | ||||
| 	buf.WriteString("func ") | ||||
| 	obj.fullname(&buf) | ||||
| 	if sig, _ := obj.typ.(*Signature); sig != nil { | ||||
| 		writeSignature(&buf, sig) | ||||
| 	if obj.typ != nil { | ||||
| 		writeSignature(&buf, obj.typ.(*Signature)) | ||||
| 	} | ||||
| 	return buf.String() | ||||
| } | ||||
|  | @ -244,7 +250,19 @@ type Label struct { | |||
| } | ||||
| 
 | ||||
| func NewLabel(pos token.Pos, name string) *Label { | ||||
| 	return &Label{object{nil, pos, nil, name, nil, false}} | ||||
| 	return &Label{object{pos: pos, name: name}} | ||||
| } | ||||
| 
 | ||||
| func (obj *Label) String() string { return fmt.Sprintf("label %s", obj.Name()) } | ||||
| 
 | ||||
| // A Builtin represents a built-in function.
 | ||||
| // Builtins don't have a valid type.
 | ||||
| type Builtin struct { | ||||
| 	object | ||||
| 
 | ||||
| 	id builtinId | ||||
| } | ||||
| 
 | ||||
| func newBuiltin(id builtinId) *Builtin { | ||||
| 	return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid]}, id} | ||||
| } | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ type operandMode int | |||
| const ( | ||||
| 	invalid  operandMode = iota // operand is invalid
 | ||||
| 	novalue                     // operand represents no value (result of a function call w/o result)
 | ||||
| 	builtin                     // operand is a built-in function
 | ||||
| 	typexpr                     // operand is a type
 | ||||
| 	constant                    // operand is a constant; the operand's typ is a Basic type
 | ||||
| 	variable                    // operand is an addressable variable
 | ||||
|  | @ -31,6 +32,7 @@ const ( | |||
| var operandModeString = [...]string{ | ||||
| 	invalid:  "invalid", | ||||
| 	novalue:  "no value", | ||||
| 	builtin:  "builtin", | ||||
| 	typexpr:  "type", | ||||
| 	constant: "constant", | ||||
| 	variable: "variable", | ||||
|  | @ -40,7 +42,8 @@ var operandModeString = [...]string{ | |||
| 
 | ||||
| // An operand represents an intermediate value during type checking.
 | ||||
| // Operands have an (addressing) mode, the expression evaluating to
 | ||||
| // the operand, the operand's type, and a value if mode == constant.
 | ||||
| // the operand, the operand's type, and a value if mode == constant
 | ||||
| // or builtin. The built-in id is encoded as an exact Int64 in val.
 | ||||
| // The zero value of operand is a ready to use invalid operand.
 | ||||
| //
 | ||||
| type operand struct { | ||||
|  |  | |||
|  | @ -32,11 +32,7 @@ func (check *checker) isTerminating(s ast.Stmt, label string) bool { | |||
| 		if call, _ := s.X.(*ast.CallExpr); call != nil { | ||||
| 			if id, _ := call.Fun.(*ast.Ident); id != nil { | ||||
| 				if obj := check.topScope.LookupParent(id.Name); obj != nil { | ||||
| 					// TODO(gri) Predeclared functions should be modelled as objects
 | ||||
| 					//           rather then ordinary functions that have a predeclared
 | ||||
| 					//           function type. This would simplify code here and else-
 | ||||
| 					//           where.
 | ||||
| 					if f, _ := obj.(*Func); f != nil && f.typ == predeclaredFunctions[_Panic] { | ||||
| 					if b, _ := obj.(*Builtin); b != nil && b.id == _Panic { | ||||
| 						return true | ||||
| 					} | ||||
| 				} | ||||
|  |  | |||
|  | @ -134,7 +134,6 @@ func TestStdfixed(t *testing.T) { | |||
| 		"bug223.go", "bug413.go", "bug459.go", // TODO(gri) complete initialization checks
 | ||||
| 		"bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore
 | ||||
| 		"bug250.go",    // TODO(gri) fix recursive interfaces
 | ||||
| 		"bug376.go",    // TODO(gri) built-ins must be called (no built-in function expressions)
 | ||||
| 		"issue3924.go", // TODO(gri) && and || produce bool result (not untyped bool)
 | ||||
| 		"issue4847.go", // TODO(gri) initialization cycle error not found
 | ||||
| 	) | ||||
|  |  | |||
|  | @ -71,6 +71,22 @@ func assignOp(op token.Token) token.Token { | |||
| 	return token.ILLEGAL | ||||
| } | ||||
| 
 | ||||
| func (check *checker) suspendedCall(keyword string, call *ast.CallExpr) { | ||||
| 	var x operand | ||||
| 	var msg string | ||||
| 	switch check.rawExpr(&x, call, nil) { | ||||
| 	case conversion: | ||||
| 		msg = "requires function call, not conversion" | ||||
| 	case expression: | ||||
| 		msg = "discards result of" | ||||
| 	case statement: | ||||
| 		return | ||||
| 	default: | ||||
| 		panic("unreachable") | ||||
| 	} | ||||
| 	check.errorf(x.pos(), "%s %s %s", keyword, msg, &x) | ||||
| } | ||||
| 
 | ||||
| // stmt typechecks statement s.
 | ||||
| func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) { | ||||
| 	// statements cannot use iota in general
 | ||||
|  | @ -99,35 +115,14 @@ func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) { | |||
| 		check.stmt(s.Stmt, fallthroughOk) | ||||
| 
 | ||||
| 	case *ast.ExprStmt: | ||||
| 		// spec: "With the exception of specific built-in functions,
 | ||||
| 		// function and method calls and receive operations can appear
 | ||||
| 		// in statement context. Such statements may be parenthesized."
 | ||||
| 		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
 | ||||
| 			// (Caution: This evaluates e.Fun twice, once here and once
 | ||||
| 			//           below as part of s.X. Perhaps this can be avoided.)
 | ||||
| 			check.expr(&x, e.Fun) | ||||
| 			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) | ||||
| 		if x.mode == typexpr { | ||||
| 			check.errorf(x.pos(), "%s is not an expression", &x) | ||||
| 		if check.rawExpr(&x, s.X, nil) != statement { | ||||
| 			check.errorf(s.X.Pos(), "%s is not used", s.X) | ||||
| 		} | ||||
| 		return | ||||
| 
 | ||||
| 	case *ast.SendStmt: | ||||
| 		var ch, x operand | ||||
|  | @ -195,14 +190,10 @@ func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) { | |||
| 		} | ||||
| 
 | ||||
| 	case *ast.GoStmt: | ||||
| 		var x operand | ||||
| 		check.call(&x, s.Call) | ||||
| 		// TODO(gri) If a builtin is called, the builtin must be valid in this context.
 | ||||
| 		check.suspendedCall("go", s.Call) | ||||
| 
 | ||||
| 	case *ast.DeferStmt: | ||||
| 		var x operand | ||||
| 		check.call(&x, s.Call) | ||||
| 		// TODO(gri) If a builtin is called, the builtin must be valid in this context.
 | ||||
| 		check.suspendedCall("defer", s.Call) | ||||
| 
 | ||||
| 	case *ast.ReturnStmt: | ||||
| 		sig := check.funcSig | ||||
|  |  | |||
|  | @ -161,11 +161,9 @@ func _() { | |||
| } | ||||
| 
 | ||||
| // Method calls with value receivers | ||||
| // TODO(gri) For internal reasons, these expressions are evaluated twice, | ||||
| //           hence the double (equal) error messages for now. Fix this. | ||||
| func _() { | ||||
| 	T0{}.v0() | ||||
| 	T0 /* ERROR "not in method set" */ /* ERROR "not in method set" */ {}.p0() | ||||
| 	T0 /* ERROR "not in method set" */ {}.p0() | ||||
| 
 | ||||
| 	(&T0{}).v0() | ||||
| 	(&T0{}).p0() | ||||
|  | @ -175,7 +173,7 @@ func _() { | |||
| 	// no values for T2 | ||||
| 
 | ||||
| 	T3{}.v0() | ||||
| 	T3 /* ERROR "not in method set" */ /* ERROR "not in method set" */ {}.p0() | ||||
| 	T3 /* ERROR "not in method set" */ {}.p0() | ||||
| 	T3{}.v1() | ||||
| 	T3{}.p1() | ||||
| 	T3{}.v2() | ||||
|  |  | |||
|  | @ -149,18 +149,20 @@ func selects() { | |||
| 
 | ||||
| func gos() { | ||||
| 	go 1 /* ERROR "expected function/method call" */ | ||||
| 	go int /* ERROR "go requires function call, not conversion" */ (0) | ||||
| 	go gos() | ||||
| 	var c chan int | ||||
| 	go close(c) | ||||
| 	go len(c) // TODO(gri) this should not be legal | ||||
| 	go len /* ERROR "go discards result" */ (c) | ||||
| } | ||||
| 
 | ||||
| func defers() { | ||||
| 	defer 1 /* ERROR "expected function/method call" */ | ||||
| 	defer int /* ERROR "defer requires function call, not conversion" */ (0) | ||||
| 	defer defers() | ||||
| 	var c chan int | ||||
| 	defer close(c) | ||||
| 	defer len(c) // TODO(gri) this should not be legal | ||||
| 	defer len /* ERROR "defer discards result" */ (c) | ||||
| } | ||||
| 
 | ||||
| func switches0() { | ||||
|  |  | |||
|  | @ -236,50 +236,6 @@ func (s *Signature) Results() *Tuple { return s.results } | |||
| // IsVariadic reports whether the signature s is variadic.
 | ||||
| func (s *Signature) IsVariadic() bool { return s.isVariadic } | ||||
| 
 | ||||
| // builtinId is an id of a builtin function.
 | ||||
| type builtinId int | ||||
| 
 | ||||
| // Predeclared builtin functions.
 | ||||
| const ( | ||||
| 	// Universe scope
 | ||||
| 	_Append builtinId = iota | ||||
| 	_Cap | ||||
| 	_Close | ||||
| 	_Complex | ||||
| 	_Copy | ||||
| 	_Delete | ||||
| 	_Imag | ||||
| 	_Len | ||||
| 	_Make | ||||
| 	_New | ||||
| 	_Panic | ||||
| 	_Print | ||||
| 	_Println | ||||
| 	_Real | ||||
| 	_Recover | ||||
| 
 | ||||
| 	// Unsafe package
 | ||||
| 	_Alignof | ||||
| 	_Offsetof | ||||
| 	_Sizeof | ||||
| 
 | ||||
| 	// Testing support
 | ||||
| 	_Assert | ||||
| 	_Trace | ||||
| ) | ||||
| 
 | ||||
| // A Builtin represents the type of a built-in function.
 | ||||
| type Builtin struct { | ||||
| 	id          builtinId | ||||
| 	name        string | ||||
| 	nargs       int // number of arguments (minimum if variadic)
 | ||||
| 	isVariadic  bool | ||||
| 	isStatement bool // true if the built-in is valid as an expression statement
 | ||||
| } | ||||
| 
 | ||||
| // Name returns the name of the built-in function b.
 | ||||
| func (b *Builtin) Name() string { return b.name } | ||||
| 
 | ||||
| // An Interface represents an interface type.
 | ||||
| type Interface struct { | ||||
| 	methods []*Func         // methods declared with or embedded in this interface
 | ||||
|  |  | |||
|  | @ -89,9 +89,14 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool) | |||
| 		x.mode = variable | ||||
| 
 | ||||
| 	case *Func: | ||||
| 		obj.used = true // use dot-imported function
 | ||||
| 		obj.used = true | ||||
| 		x.mode = value | ||||
| 
 | ||||
| 	case *Builtin: | ||||
| 		obj.used = true // for built-ins defined by package unsafe
 | ||||
| 		x.mode = builtin | ||||
| 		x.val = exact.MakeInt64(int64(obj.id)) | ||||
| 
 | ||||
| 	default: | ||||
| 		unreachable() | ||||
| 	} | ||||
|  |  | |||
|  | @ -19,7 +19,6 @@ var ( | |||
| 	universeIota *Const | ||||
| ) | ||||
| 
 | ||||
| // Predeclared types, indexed by BasicKind.
 | ||||
| var Typ = [...]*Basic{ | ||||
| 	Invalid: {Invalid, 0, 0, "invalid type"}, | ||||
| 
 | ||||
|  | @ -56,41 +55,7 @@ var aliases = [...]*Basic{ | |||
| 	{Rune, IsInteger, 4, "rune"}, | ||||
| } | ||||
| 
 | ||||
| var predeclaredConstants = [...]*Const{ | ||||
| 	NewConst(token.NoPos, nil, "true", Typ[UntypedBool], exact.MakeBool(true)), | ||||
| 	NewConst(token.NoPos, nil, "false", Typ[UntypedBool], exact.MakeBool(false)), | ||||
| 	NewConst(token.NoPos, nil, "iota", Typ[UntypedInt], exact.MakeInt64(0)), | ||||
| 	NewConst(token.NoPos, nil, "nil", Typ[UntypedNil], exact.MakeNil()), | ||||
| } | ||||
| 
 | ||||
| var predeclaredFunctions = [...]*Builtin{ | ||||
| 	{_Append, "append", 1, true, false}, | ||||
| 	{_Cap, "cap", 1, false, false}, | ||||
| 	{_Close, "close", 1, false, true}, | ||||
| 	{_Complex, "complex", 2, false, false}, | ||||
| 	{_Copy, "copy", 2, false, true}, | ||||
| 	{_Delete, "delete", 2, false, true}, | ||||
| 	{_Imag, "imag", 1, false, false}, | ||||
| 	{_Len, "len", 1, false, false}, | ||||
| 	{_Make, "make", 1, true, false}, | ||||
| 	{_New, "new", 1, false, false}, | ||||
| 	{_Panic, "panic", 1, false, true}, | ||||
| 	{_Print, "print", 0, true, true}, | ||||
| 	{_Println, "println", 0, true, true}, | ||||
| 	{_Real, "real", 1, false, false}, | ||||
| 	{_Recover, "recover", 0, false, true}, | ||||
| 
 | ||||
| 	{_Alignof, "Alignof", 1, false, false}, | ||||
| 	{_Offsetof, "Offsetof", 1, false, false}, | ||||
| 	{_Sizeof, "Sizeof", 1, false, false}, | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	Universe = NewScope(nil) | ||||
| 	Unsafe = NewPackage("unsafe", "unsafe", NewScope(Universe)) | ||||
| 	Unsafe.complete = true | ||||
| 
 | ||||
| 	// predeclared types
 | ||||
| func defPredeclaredTypes() { | ||||
| 	for _, t := range Typ { | ||||
| 		def(NewTypeName(token.NoPos, nil, t.name, t)) | ||||
| 	} | ||||
|  | @ -98,24 +63,116 @@ func init() { | |||
| 		def(NewTypeName(token.NoPos, nil, t.name, t)) | ||||
| 	} | ||||
| 
 | ||||
| 	// error type
 | ||||
| 	{ | ||||
| 		// Error has a nil package in its qualified name since it is in no package
 | ||||
| 		res := NewVar(token.NoPos, nil, "", Typ[String]) | ||||
| 		sig := &Signature{results: NewTuple(res)} | ||||
| 		err := NewFunc(token.NoPos, nil, "Error", sig) | ||||
| 		typ := &Named{underlying: &Interface{methods: []*Func{err}}, complete: true} | ||||
| 		sig.recv = NewVar(token.NoPos, nil, "", typ) | ||||
| 		def(NewTypeName(token.NoPos, nil, "error", typ)) | ||||
| 	} | ||||
| 	// Error has a nil package in its qualified name since it is in no package
 | ||||
| 	res := NewVar(token.NoPos, nil, "", Typ[String]) | ||||
| 	sig := &Signature{results: NewTuple(res)} | ||||
| 	err := NewFunc(token.NoPos, nil, "Error", sig) | ||||
| 	typ := &Named{underlying: &Interface{methods: []*Func{err}}, complete: true} | ||||
| 	sig.recv = NewVar(token.NoPos, nil, "", typ) | ||||
| 	def(NewTypeName(token.NoPos, nil, "error", typ)) | ||||
| } | ||||
| 
 | ||||
| 	for _, c := range predeclaredConstants { | ||||
| 		def(c) | ||||
| 	} | ||||
| var predeclaredConsts = [...]struct { | ||||
| 	name string | ||||
| 	kind BasicKind | ||||
| 	val  exact.Value | ||||
| }{ | ||||
| 	{"true", UntypedBool, exact.MakeBool(true)}, | ||||
| 	{"false", UntypedBool, exact.MakeBool(false)}, | ||||
| 	{"iota", UntypedInt, exact.MakeInt64(0)}, | ||||
| 	{"nil", UntypedNil, exact.MakeNil()}, | ||||
| } | ||||
| 
 | ||||
| 	for _, f := range predeclaredFunctions { | ||||
| 		def(NewFunc(token.NoPos, nil, f.name, f)) | ||||
| func defPredeclaredConsts() { | ||||
| 	for _, c := range predeclaredConsts { | ||||
| 		def(NewConst(token.NoPos, nil, c.name, Typ[c.kind], c.val)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // A builtinId is the id of a builtin function.
 | ||||
| type builtinId int | ||||
| 
 | ||||
| const ( | ||||
| 	// universe scope
 | ||||
| 	_Append builtinId = iota | ||||
| 	_Cap | ||||
| 	_Close | ||||
| 	_Complex | ||||
| 	_Copy | ||||
| 	_Delete | ||||
| 	_Imag | ||||
| 	_Len | ||||
| 	_Make | ||||
| 	_New | ||||
| 	_Panic | ||||
| 	_Print | ||||
| 	_Println | ||||
| 	_Real | ||||
| 	_Recover | ||||
| 
 | ||||
| 	// package unsafe
 | ||||
| 	_Alignof | ||||
| 	_Offsetof | ||||
| 	_Sizeof | ||||
| 
 | ||||
| 	// testing support
 | ||||
| 	_Assert | ||||
| 	_Trace | ||||
| ) | ||||
| 
 | ||||
| var predeclaredFuncs = [...]struct { | ||||
| 	name     string | ||||
| 	nargs    int | ||||
| 	variadic bool | ||||
| 	kind     exprKind | ||||
| }{ | ||||
| 	_Append:  {"append", 1, true, expression}, | ||||
| 	_Cap:     {"cap", 1, false, expression}, | ||||
| 	_Close:   {"close", 1, false, statement}, | ||||
| 	_Complex: {"complex", 2, false, expression}, | ||||
| 	_Copy:    {"copy", 2, false, statement}, | ||||
| 	_Delete:  {"delete", 2, false, statement}, | ||||
| 	_Imag:    {"imag", 1, false, expression}, | ||||
| 	_Len:     {"len", 1, false, expression}, | ||||
| 	_Make:    {"make", 1, true, expression}, | ||||
| 	_New:     {"new", 1, false, expression}, | ||||
| 	_Panic:   {"panic", 1, false, statement}, | ||||
| 	_Print:   {"print", 0, true, statement}, | ||||
| 	_Println: {"println", 0, true, statement}, | ||||
| 	_Real:    {"real", 1, false, expression}, | ||||
| 	_Recover: {"recover", 0, false, statement}, | ||||
| 
 | ||||
| 	_Alignof:  {"Alignof", 1, false, expression}, | ||||
| 	_Offsetof: {"Offsetof", 1, false, expression}, | ||||
| 	_Sizeof:   {"Sizeof", 1, false, expression}, | ||||
| 
 | ||||
| 	_Assert: {"assert", 1, false, statement}, | ||||
| 	_Trace:  {"trace", 0, true, statement}, | ||||
| } | ||||
| 
 | ||||
| func defPredeclaredFuncs() { | ||||
| 	for i := range predeclaredFuncs { | ||||
| 		id := builtinId(i) | ||||
| 		if id == _Assert || id == _Trace { | ||||
| 			continue // only define these in testing environment
 | ||||
| 		} | ||||
| 		def(newBuiltin(id)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func defPredeclaredTestFuncs() { | ||||
| 	def(newBuiltin(_Assert)) | ||||
| 	def(newBuiltin(_Trace)) | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	Universe = NewScope(nil) | ||||
| 	Unsafe = NewPackage("unsafe", "unsafe", NewScope(Universe)) | ||||
| 	Unsafe.complete = true | ||||
| 
 | ||||
| 	defPredeclaredTypes() | ||||
| 	defPredeclaredConsts() | ||||
| 	defPredeclaredFuncs() | ||||
| 
 | ||||
| 	universeIota = Universe.Lookup("iota").(*Const) | ||||
| } | ||||
|  | @ -141,7 +198,7 @@ func def(obj Object) { | |||
| 		switch obj := obj.(type) { | ||||
| 		case *TypeName: | ||||
| 			obj.pkg = Unsafe | ||||
| 		case *Func: | ||||
| 		case *Builtin: | ||||
| 			obj.pkg = Unsafe | ||||
| 		default: | ||||
| 			unreachable() | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue