go/ssa: make Builtin capable of representing non-spec-defined intrinsics.

Also, define ssa:wrapnilchk intrinsic to check and gracefully
fail when a T method is dynamically invoked via a nil *T receiver.
+ Test.

A follow-up CL will add another intrinsic, ssa:memclr.

+ minor cleanups.

LGTM=gri
R=gri
CC=golang-codereviews
https://golang.org/cl/101170044
diff --git a/go/pointer/gen.go b/go/pointer/gen.go
index 850591f..bcb7448 100644
--- a/go/pointer/gen.go
+++ b/go/pointer/gen.go
@@ -531,7 +531,7 @@
 // genBuiltinCall generates contraints for a call to a built-in.
 func (a *analysis) genBuiltinCall(instr ssa.CallInstruction, cgn *cgnode) {
 	call := instr.Common()
-	switch call.Value.(*ssa.Builtin).Object().Name() {
+	switch call.Value.(*ssa.Builtin).Name() {
 	case "append":
 		// Safe cast: append cannot appear in a go or defer statement.
 		a.genAppend(instr.(*ssa.Call), cgn)
@@ -553,6 +553,9 @@
 		// to its arg, so make sure we create nodes for it.
 		a.valueNode(call.Args[0])
 
+	case "ssa:wrapnilchk":
+		a.copy(a.valueNode(instr.Value()), a.valueNode(call.Args[0]), 1)
+
 	default:
 		// No-ops: close len cap real imag complex print println delete.
 	}
diff --git a/go/ssa/builder.go b/go/ssa/builder.go
index 4007dc1..3249a72 100644
--- a/go/ssa/builder.go
+++ b/go/ssa/builder.go
@@ -57,6 +57,7 @@
 	tByte       = types.Typ[types.Byte]
 	tInt        = types.Typ[types.Int]
 	tInvalid    = types.Typ[types.Invalid]
+	tString     = types.Typ[types.String]
 	tUntypedNil = types.Typ[types.UntypedNil]
 	tRangeIter  = &opaqueType{nil, "iter"} // the type of all "range" iterators
 	tEface      = new(types.Interface)
@@ -597,7 +598,7 @@
 		// Universal built-in or nil?
 		switch obj := obj.(type) {
 		case *types.Builtin:
-			return &Builtin{object: obj, sig: fn.Pkg.typeOf(e).(*types.Signature)}
+			return &Builtin{name: obj.Name(), sig: fn.Pkg.typeOf(e).(*types.Signature)}
 		case *types.Nil:
 			return nilConst(fn.Pkg.typeOf(e))
 		}
@@ -1414,7 +1415,7 @@
 	for _, st := range states {
 		if st.Dir == types.RecvOnly {
 			tElem := st.Chan.Type().Underlying().(*types.Chan).Elem()
-			vars = append(vars, newVar("", tElem))
+			vars = append(vars, anonVar(tElem))
 		}
 	}
 	sel.setType(types.NewTuple(vars...))
@@ -1489,7 +1490,7 @@
 		// A blocking select must match some case.
 		// (This should really be a runtime.errorString, not a string.)
 		fn.emit(&Panic{
-			X: emitConv(fn, NewConst(exact.MakeString("blocking select matched no case"), types.Typ[types.String]), tEface),
+			X: emitConv(fn, stringConst("blocking select matched no case"), tEface),
 		})
 		fn.currentBlock = fn.newBasicBlock("unreachable")
 	}
diff --git a/go/ssa/const.go b/go/ssa/const.go
index 1a4a4d0..44a1ee7 100644
--- a/go/ssa/const.go
+++ b/go/ssa/const.go
@@ -25,7 +25,7 @@
 // intConst returns an 'int' constant that evaluates to i.
 // (i is an int64 in case the host is narrower than the target.)
 func intConst(i int64) *Const {
-	return NewConst(exact.MakeInt64(i), types.Typ[types.Int])
+	return NewConst(exact.MakeInt64(i), tInt)
 }
 
 // nilConst returns a nil constant of the specified type, which may
@@ -35,6 +35,11 @@
 	return NewConst(nil, typ)
 }
 
+// stringConst returns a 'string' constant that evaluates to s.
+func stringConst(s string) *Const {
+	return NewConst(exact.MakeString(s), tString)
+}
+
 // zeroConst returns a new "zero" constant of the specified type,
 // which must not be an array or struct type: the zero values of
 // aggregates are well-defined but cannot be represented by Const.
diff --git a/go/ssa/interp/ops.go b/go/ssa/interp/ops.go
index 5452285..86aaee0 100644
--- a/go/ssa/interp/ops.go
+++ b/go/ssa/interp/ops.go
@@ -1059,6 +1059,16 @@
 
 	case "recover":
 		return doRecover(caller)
+
+	case "ssa:wrapnilchk":
+		recv := args[0]
+		if recv.(*value) == nil {
+			recvType := args[1]
+			methodName := args[2]
+			panic(fmt.Sprintf("value method (%s).%s called using nil *%s pointer",
+				recvType, methodName, recvType))
+		}
+		return recv
 	}
 
 	panic("unknown built-in: " + fn.Name())
diff --git a/go/ssa/interp/testdata/coverage.go b/go/ssa/interp/testdata/coverage.go
index ca6b174..3ad49f2 100644
--- a/go/ssa/interp/testdata/coverage.go
+++ b/go/ssa/interp/testdata/coverage.go
@@ -673,3 +673,20 @@
 		panic(count)
 	}
 }
+
+// Test that a nice error is issue by indirection wrappers.
+func init() {
+	var ptr *T
+	var i I = ptr
+
+	defer func() {
+		r := fmt.Sprint(recover())
+		// Exact error varies by toolchain:
+		if r != "runtime error: value method (main.T).f called using nil *main.T pointer" &&
+			r != "value method main.T.f called using nil *T pointer" {
+			panic("want panic from call with nil receiver, got " + r)
+		}
+	}()
+	i.f()
+	panic("unreachable")
+}
diff --git a/go/ssa/promote.go b/go/ssa/promote.go
index fa8c755..f110436 100644
--- a/go/ssa/promote.go
+++ b/go/ssa/promote.go
@@ -264,12 +264,30 @@
 	fn.addSpilledParam(recv)
 	createParams(fn, start)
 
+	indices := meth.Index()
+
 	var v Value = fn.Locals[0] // spilled receiver
 	if isPointer(meth.Recv()) {
-		// TODO(adonovan): consider emitting a nil-pointer
-		// check here with a nice error message, like gc does.
-		// We could define a new builtin for the purpose.
 		v = emitLoad(fn, v)
+
+		// For simple indirection wrappers, perform an informative nil-check:
+		// "value method (T).f called using nil *T pointer"
+		if len(indices) == 1 && !isPointer(recvType(obj)) {
+			var c Call
+			c.Call.Value = &Builtin{
+				name: "ssa:wrapnilchk",
+				sig: types.NewSignature(nil, nil,
+					types.NewTuple(anonVar(meth.Recv()), anonVar(tString), anonVar(tString)),
+					types.NewTuple(anonVar(meth.Recv())), false),
+			}
+			c.Call.Args = []Value{
+				v,
+				stringConst(deref(meth.Recv()).String()),
+				stringConst(meth.Obj().Name()),
+			}
+			c.setType(v.Type())
+			v = fn.emit(&c)
+		}
 	}
 
 	// Invariant: v is a pointer, either
@@ -280,7 +298,6 @@
 	// Load) in preference to value extraction (Field possibly
 	// preceded by Load).
 
-	indices := meth.Index()
 	v = emitImplicitSelections(fn, v, indices[:len(indices)-1])
 
 	// Invariant: v is a pointer, either
diff --git a/go/ssa/ssa.go b/go/ssa/ssa.go
index 404bf92..b1e582f 100644
--- a/go/ssa/ssa.go
+++ b/go/ssa/ssa.go
@@ -440,14 +440,23 @@
 // Builtins are immutable values.  Builtins do not have addresses.
 // Builtins can only appear in CallCommon.Func.
 //
-// Object() returns a *types.Builtin.
+// Name() indicates the function: one of the built-in functions from the
+// Go spec (excluding "make" and "new") or one of these ssa-defined
+// intrinsics:
+//
+//   // wrapnilchk returns ptr if non-nil, panics otherwise.
+//   // (For use in indirection wrappers.)
+//   func ssa:wrapnilchk(ptr *T, recvType, methodName string) *T
+//
+// Object() returns a *types.Builtin for built-ins defined by the spec,
+// nil for others.
 //
 // Type() returns a *types.Signature representing the effective
 // signature of the built-in for this call.
 //
 type Builtin struct {
-	object *types.Builtin // canonical types.Universe object for this built-in
-	sig    *types.Signature
+	name string
+	sig  *types.Signature
 }
 
 // Value-defining instructions  ----------------------------------------
@@ -1382,10 +1391,10 @@
 func (s *Go) Value() *Call    { return nil }
 
 func (v *Builtin) Type() types.Type        { return v.sig }
-func (v *Builtin) Name() string            { return v.object.Name() }
+func (v *Builtin) Name() string            { return v.name }
 func (*Builtin) Referrers() *[]Instruction { return nil }
 func (v *Builtin) Pos() token.Pos          { return token.NoPos }
-func (v *Builtin) Object() types.Object    { return v.object }
+func (v *Builtin) Object() types.Object    { return types.Universe.Lookup(v.name) }
 func (v *Builtin) Parent() *Function       { return nil }
 
 func (v *FreeVar) Type() types.Type          { return v.typ }
diff --git a/go/ssa/testmain.go b/go/ssa/testmain.go
index a5d8a82..448338b 100644
--- a/go/ssa/testmain.go
+++ b/go/ssa/testmain.go
@@ -14,7 +14,6 @@
 	"os"
 	"strings"
 
-	"code.google.com/p/go.tools/go/exact"
 	"code.google.com/p/go.tools/go/types"
 )
 
@@ -167,7 +166,6 @@
 		return nilConst(slice)
 	}
 
-	tString := types.Typ[types.String]
 	tPtrString := types.NewPointer(tString)
 	tPtrElem := types.NewPointer(tElem)
 	tPtrFunc := types.NewPointer(tFunc)
@@ -188,7 +186,7 @@
 		pname := fn.emit(fa)
 
 		// Emit: *pname = "testfunc"
-		emitStore(fn, pname, NewConst(exact.MakeString(testfunc.Name()), tString))
+		emitStore(fn, pname, stringConst(testfunc.Name()))
 
 		// Emit: pfunc = &pitem.F
 		fa = &FieldAddr{X: pitem, Field: 1} // .F
diff --git a/go/ssa/util.go b/go/ssa/util.go
index 56220ab..d44ddce 100644
--- a/go/ssa/util.go
+++ b/go/ssa/util.go
@@ -108,16 +108,18 @@
 	return types.NewParam(token.NoPos, nil, name, typ)
 }
 
-var (
-	lenObject  = types.Universe.Lookup("len").(*types.Builtin)
-	lenResults = types.NewTuple(newVar("", tInt))
-)
+// anonVar creates an anonymous 'var' for use in a types.Tuple.
+func anonVar(typ types.Type) *types.Var {
+	return newVar("", typ)
+}
+
+var lenResults = types.NewTuple(anonVar(tInt))
 
 // makeLen returns the len builtin specialized to type func(T)int.
 func makeLen(T types.Type) *Builtin {
-	lenParams := types.NewTuple(newVar("", T))
+	lenParams := types.NewTuple(anonVar(T))
 	return &Builtin{
-		object: lenObject,
-		sig:    types.NewSignature(nil, nil, lenParams, lenResults, false),
+		name: "len",
+		sig:  types.NewSignature(nil, nil, lenParams, lenResults, false),
 	}
 }