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