diff --git a/oracle/callees.go b/oracle/callees.go index 1e0d65e1..a9ee1890 100644 --- a/oracle/callees.go +++ b/oracle/callees.go @@ -41,8 +41,10 @@ func callees(o *Oracle, qpos *QueryPos) (queryResult, error) { } // Reject calls to built-ins. - if b, ok := qpos.info.TypeOf(call.Fun).(*types.Builtin); ok { - return nil, o.errorf(call, "this is a call to the built-in '%s' operator", b.Name()) + if id, ok := unparen(call.Fun).(*ast.Ident); ok { + if b, ok := qpos.info.ObjectOf(id).(*types.Builtin); ok { + return nil, o.errorf(call, "this is a call to the built-in '%s' operator", b.Name()) + } } buildSSA(o) diff --git a/ssa/builder.go b/ssa/builder.go index 045b7a4b..58eb0015 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -265,15 +265,15 @@ func (b *builder) exprN(fn *Function, e ast.Expr) Value { } // builtin emits to fn SSA instructions to implement a call to the -// built-in function called name with the specified arguments +// built-in function obj with the specified arguments // and return type. It returns the value defined by the result. // // The result is nil if no special handling was required; in this case // the caller should treat this like an ordinary library function // call. // -func (b *builder) builtin(fn *Function, name string, args []ast.Expr, typ types.Type, pos token.Pos) Value { - switch name { +func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ types.Type, pos token.Pos) Value { + switch obj.Name() { case "make": switch typ.Underlying().(type) { case *types.Slice: @@ -554,9 +554,8 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value { } // Call to "intrinsic" built-ins, e.g. new, make, panic. if id, ok := e.Fun.(*ast.Ident); ok { - obj := fn.Pkg.objectOf(id) - if _, ok := fn.Prog.builtins[obj]; ok { - if v := b.builtin(fn, id.Name, e.Args, typ, e.Lparen); v != nil { + if obj, ok := fn.Pkg.objectOf(id).(*types.Builtin); ok { + if v := b.builtin(fn, obj, e.Args, typ, e.Lparen); v != nil { return v } } @@ -639,7 +638,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value { case *ast.Ident: obj := fn.Pkg.objectOf(e) // Universal built-in? - if obj.Pkg() == nil { + if obj, ok := obj.(*types.Builtin); ok { return fn.Prog.builtins[obj] } // Package-level func or var? @@ -1750,7 +1749,7 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value } else { // length = len(x). var c Call - c.Call.Value = fn.Prog.builtins[types.Universe.Lookup("len")] + c.Call.Value = fn.Prog.builtins[types.Universe.Lookup("len").(*types.Builtin)] c.Call.Args = []Value{x} c.setType(tInt) length = fn.emit(&c) diff --git a/ssa/create.go b/ssa/create.go index 47e51160..fb6de474 100644 --- a/ssa/create.go +++ b/ssa/create.go @@ -43,7 +43,7 @@ func NewProgram(fset *token.FileSet, mode BuilderMode) *Program { Fset: fset, imported: make(map[string]*Package), packages: make(map[*types.Package]*Package), - builtins: make(map[types.Object]*Builtin), + builtins: make(map[*types.Builtin]*Builtin), boundMethodWrappers: make(map[*types.Func]*Function), ifaceMethodWrappers: make(map[*types.Func]*Function), mode: mode, @@ -51,7 +51,7 @@ func NewProgram(fset *token.FileSet, mode BuilderMode) *Program { // Create Values for built-in functions. for _, name := range types.Universe.Names() { - if obj, ok := types.Universe.Lookup(name).(*types.Func); ok { + if obj, ok := types.Universe.Lookup(name).(*types.Builtin); ok { prog.builtins[obj] = &Builtin{obj} } } diff --git a/ssa/source.go b/ssa/source.go index f490a323..8f539128 100644 --- a/ssa/source.go +++ b/ssa/source.go @@ -178,18 +178,13 @@ func (prog *Program) packageLevelValue(obj types.Object) Value { return nil } -// FuncValue returns the SSA Value denoted by the source-level named -// function obj. The result may be a *Function or a *Builtin, or nil -// if not found. +// FuncValue returns the Function denoted by the source-level named +// function obj. // -func (prog *Program) FuncValue(obj *types.Func) Value { - // Universal built-in? - if v, ok := prog.builtins[obj]; ok { - return v - } +func (prog *Program) FuncValue(obj *types.Func) *Function { // Package-level function or declared method? if v := prog.packageLevelValue(obj); v != nil { - return v + return v.(*Function) } // Interface method wrapper? meth := recvType(obj).MethodSet().Lookup(obj.Pkg(), obj.Name()) diff --git a/ssa/source_test.go b/ssa/source_test.go index fc72290f..366aac0e 100644 --- a/ssa/source_test.go +++ b/ssa/source_test.go @@ -103,28 +103,19 @@ func TestObjValueLookup(t *testing.T) { } func checkFuncValue(t *testing.T, prog *ssa.Program, obj *types.Func) { - v := prog.FuncValue(obj) - // fmt.Printf("FuncValue(%s) = %s\n", obj, v) // debugging - if v == nil { + fn := prog.FuncValue(obj) + // fmt.Printf("FuncValue(%s) = %s\n", obj, fn) // debugging + if fn == nil { t.Errorf("FuncValue(%s) == nil", obj) return } - // v must be an *ssa.Function or *ssa.Builtin. - v2, _ := v.(interface { - Object() types.Object - }) - if v2 == nil { - t.Errorf("FuncValue(%s) = %s %T; has no Object() method", - obj, v.Name(), v) - return - } - if vobj := v2.Object(); vobj != obj { + if fnobj := fn.Object(); fnobj != obj { t.Errorf("FuncValue(%s).Object() == %s; value was %s", - obj, vobj, v.Name()) + obj, fnobj, fn.Name()) return } - if !types.IsIdentical(v.Type(), obj.Type()) { - t.Errorf("FuncValue(%s).Type() == %s", obj, v.Type()) + if !types.IsIdentical(fn.Type(), obj.Type()) { + t.Errorf("FuncValue(%s).Type() == %s", obj, fn.Type()) return } } diff --git a/ssa/ssa.go b/ssa/ssa.go index 2610eadc..d80098a6 100644 --- a/ssa/ssa.go +++ b/ssa/ssa.go @@ -25,7 +25,7 @@ type Program struct { Fset *token.FileSet // position information for the files of this Program imported map[string]*Package // all importable Packages, keyed by import path packages map[*types.Package]*Package // all loaded Packages, keyed by object - builtins map[types.Object]*Builtin // all built-in functions, keyed by typechecker objects. + builtins map[*types.Builtin]*Builtin // all built-in functions, keyed by typechecker objects. mode BuilderMode // set of mode bits for SSA construction methodsMu sync.Mutex // guards the following maps: @@ -401,12 +401,14 @@ type Global struct { // Builtins are immutable values. Builtins do not have addresses. // Builtins can only appear in CallCommon.Func. // -// Type() returns a *types.Builtin. -// Built-in functions may have polymorphic or variadic types that are -// not expressible in Go's type system. +// Object() returns a *types.Builtin. +// +// Type() returns types.Typ[types.Invalid], since built-in functions +// may have polymorphic or variadic types that are not expressible in +// Go's type system. // type Builtin struct { - object *types.Func // canonical types.Universe object for this built-in + object *types.Builtin // canonical types.Universe object for this built-in } // Value-defining instructions ----------------------------------------