go.tools/go/types: nil is not a constant + misc. cleanups

- removed support for nil constants from go/exact
- instead define a singleton Nil Object (the nil _value_)
- in assignments, follow more closely spec wording
  (pending spec CL 14415043)
- removed use of goto in checker.unary
- cleanup around handling of isRepresentable for
  constants, with better error messages
- fix missing checks in checker.convertUntyped
- added isTyped (== !isUntyped) and isInterface predicates
- fixed hasNil predicate: unsafe.Pointer also has nil
- adjusted ssa per adonovan
- implememted types.Implements (wrapper arounfd types.MissingMethod)
- use types.Implements in vet (and fix a bug)

R=adonovan, r
CC=golang-dev
https://golang.org/cl/14438052
diff --git a/cmd/vet/testdata/print.go b/cmd/vet/testdata/print.go
index 9168d4c..232c7b3 100644
--- a/cmd/vet/testdata/print.go
+++ b/cmd/vet/testdata/print.go
@@ -124,7 +124,7 @@
 	fmt.Printf("%t", notstringerarrayv)        // ERROR "arg notstringerarrayv for printf verb %t of wrong type"
 	fmt.Printf("%q", notstringerarrayv)        // ERROR "arg notstringerarrayv for printf verb %q of wrong type"
 	fmt.Printf("%d", Formatter(true))          // ERROR "arg Formatter\(true\) for printf verb %d of wrong type"
-	fmt.Printf("%s", nonemptyinterface)        // ERROR "for printf verb %s of wrong type" (Disabled temporarily because of bug in IsAssignableTo)
+	fmt.Printf("%s", nonemptyinterface)        // correct (the dynamic type of nonemptyinterface may be a stringer)
 	fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type"
 	fmt.Println()                              // not an error
 	fmt.Println("%s", "hi")                    // ERROR "possible formatting directive in Println call"
diff --git a/cmd/vet/types.go b/cmd/vet/types.go
index a80939f..41ce644 100644
--- a/cmd/vet/types.go
+++ b/cmd/vet/types.go
@@ -62,8 +62,8 @@
 
 var (
 	stringerMethodType = types.New("func() string")
-	errorType          = types.New("interface{ Error() string }")
-	stringerType       = types.New("interface{ String() string }")
+	errorType          = types.New("interface{ Error() string }").(*types.Interface)
+	stringerType       = types.New("interface{ String() string }").(*types.Interface)
 	// One day this might work. See issue 6259.
 	// formatterType   = types.New("interface{Format(f fmt.State, c rune)}")
 )
@@ -103,9 +103,9 @@
 	if hasMethod(typ, "Format") {
 		return true
 	}
-	// If we can use a string, does arg implement the Stringer or Error interface?
+	// If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
 	if t&argString != 0 {
-		if types.IsAssignableTo(typ, errorType) || types.IsAssignableTo(typ, stringerType) {
+		if types.Implements(typ, errorType, false) || types.Implements(typ, stringerType, false) {
 			return true
 		}
 	}
diff --git a/go/exact/exact.go b/go/exact/exact.go
index 3eb3941..aa691bc 100644
--- a/go/exact/exact.go
+++ b/go/exact/exact.go
@@ -25,7 +25,6 @@
 	Unknown Kind = iota
 
 	// non-numeric values
-	Nil
 	Bool
 	String
 
@@ -53,7 +52,6 @@
 
 type (
 	unknownVal struct{}
-	nilVal     struct{}
 	boolVal    bool
 	stringVal  string
 	int64Val   int64
@@ -63,7 +61,6 @@
 )
 
 func (unknownVal) Kind() Kind { return Unknown }
-func (nilVal) Kind() Kind     { return Nil }
 func (boolVal) Kind() Kind    { return Bool }
 func (stringVal) Kind() Kind  { return String }
 func (int64Val) Kind() Kind   { return Int }
@@ -72,7 +69,6 @@
 func (complexVal) Kind() Kind { return Complex }
 
 func (unknownVal) String() string   { return "unknown" }
-func (nilVal) String() string       { return "nil" }
 func (x boolVal) String() string    { return fmt.Sprintf("%v", bool(x)) }
 func (x stringVal) String() string  { return strconv.Quote(string(x)) }
 func (x int64Val) String() string   { return strconv.FormatInt(int64(x), 10) }
@@ -81,7 +77,6 @@
 func (x complexVal) String() string { return fmt.Sprintf("(%s + %si)", x.re, x.im) }
 
 func (unknownVal) implementsValue() {}
-func (nilVal) implementsValue()     {}
 func (boolVal) implementsValue()    {}
 func (stringVal) implementsValue()  {}
 func (int64Val) implementsValue()   {}
@@ -122,9 +117,6 @@
 // MakeUnknown returns the Unknown value.
 func MakeUnknown() Value { return unknownVal{} }
 
-// MakeNil returns the Nil value.
-func MakeNil() Value { return nilVal{} }
-
 // MakeBool returns the Bool value for x.
 func MakeBool(b bool) Value { return boolVal(b) }
 
@@ -447,7 +439,7 @@
 	// ord(x) <= ord(y)
 
 	switch x := x.(type) {
-	case unknownVal, nilVal, boolVal, stringVal, complexVal:
+	case unknownVal, boolVal, stringVal, complexVal:
 		return x, y
 
 	case int64Val:
diff --git a/go/exact/exact_test.go b/go/exact/exact_test.go
index aed87b7..061eec9 100644
--- a/go/exact/exact_test.go
+++ b/go/exact/exact_test.go
@@ -112,8 +112,6 @@
 	switch lit {
 	case "?":
 		return MakeUnknown()
-	case "nil":
-		return MakeNil()
 	case "true":
 		return MakeBool(true)
 	case "false":
diff --git a/go/types/api.go b/go/types/api.go
index 8e218e3..de38773 100644
--- a/go/types/api.go
+++ b/go/types/api.go
@@ -174,12 +174,28 @@
 	return pkg, err
 }
 
-// IsAssignableTo reports whether a value of type V
-// is assignable to a variable of type T.
+// IsAssignableTo reports whether a value of type V is assignable to a variable of type T.
 func IsAssignableTo(V, T Type) bool {
 	x := operand{mode: value, typ: V}
 	return x.isAssignableTo(nil, T) // config not needed for non-constant x
 }
 
+// Implements reports whether a value of type V implements T, as follows:
+//
+// 1) For non-interface types V, or if static is set, V implements T if all
+// methods of T are present in V. Informally, this reports whether V is a
+// subtype of T.
+//
+// 2) For interface types V, and if static is not set, V implements T if all
+// methods of T which are also present in V have matching types. Informally,
+// this indicates whether a type assertion x.(T) where x is of type V would
+// be legal (the concrete dynamic type of x may implement T even if V does
+// not statically implement it).
+//
+func Implements(V Type, T *Interface, static bool) bool {
+	f, _ := MissingMethod(V, T, static)
+	return f == nil
+}
+
 // 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.
diff --git a/go/types/assignments.go b/go/types/assignments.go
index 4f0db27..6a9f3ea 100644
--- a/go/types/assignments.go
+++ b/go/types/assignments.go
@@ -13,27 +13,62 @@
 	"code.google.com/p/go.tools/go/exact"
 )
 
-// assignment reports whether x can be assigned to a variable of type 'to',
+// assignment reports whether x can be assigned to a variable of type 'T',
 // if necessary by attempting to convert untyped values to the appropriate
 // type. If x.mode == invalid upon return, then assignment has already
 // issued an error message and the caller doesn't have to report another.
-// TODO(gri) This latter behavior is for historic reasons and complicates
-// callers. Needs to be cleaned up.
-func (check *checker) assignment(x *operand, to Type) bool {
-	if x.mode == invalid {
-		return false
+// Use T == nil to indicate assignment to an untyped blank identifier.
+//
+// TODO(gri) Should find a better way to handle in-band errors.
+// TODO(gri) The T == nil mechanism is not yet used - should simplify callers eventually.
+//
+func (check *checker) assignment(x *operand, T Type) bool {
+	switch x.mode {
+	case invalid:
+		return true // error reported before
+	case constant, variable, value, valueok:
+		// ok
+	default:
+		unreachable()
 	}
 
-	if t, ok := x.typ.(*Tuple); ok {
+	// x must be a single value
+	// (tuple types are never named - no need for underlying type)
+	if t, _ := x.typ.(*Tuple); t != nil {
 		assert(t.Len() > 1)
 		check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x)
 		x.mode = invalid
 		return false
 	}
 
-	check.convertUntyped(x, to)
+	// spec: "If an untyped constant is assigned to a variable of interface
+	// type or the blank identifier, the constant is first converted to type
+	// bool, rune, int, float64, complex128 or string respectively, depending
+	// on whether the value is a boolean, rune, integer, floating-point, complex,
+	// or string constant."
+	if x.mode == constant && isUntyped(x.typ) && (T == nil || isInterface(T)) {
+		check.convertUntyped(x, defaultType(x.typ))
+		if x.mode == invalid {
+			return false
+		}
+	}
 
-	return x.mode != invalid && x.isAssignableTo(check.conf, to)
+	// spec: "If a left-hand side is the blank identifier, any typed or
+	// non-constant value except for the predeclared identifier nil may
+	// be assigned to it."
+	if T == nil && (x.mode != constant || isTyped(x.typ)) && !x.isNil() {
+		return true
+	}
+
+	// If we still have an untyped x, try to convert it to T.
+	if isUntyped(x.typ) {
+		check.convertUntyped(x, T)
+		if x.mode == invalid {
+			return false
+		}
+	}
+
+	return x.isAssignableTo(check.conf, T)
 }
 
 func (check *checker) initConst(lhs *Const, x *operand) {
@@ -120,7 +155,7 @@
 		// If the lhs is untyped, determine the default type.
 		// The spec is unclear about this, but gc appears to
 		// do this.
-		// TODO(gri) This is still not correct (try: _ = nil; _ = 1<<1e3)
+		// TODO(gri) This is still not correct (_ = 1<<1e3)
 		typ := x.typ
 		if isUntyped(typ) {
 			// convert untyped types to default types
diff --git a/go/types/call.go b/go/types/call.go
index e306186..d8b499b 100644
--- a/go/types/call.go
+++ b/go/types/call.go
@@ -381,10 +381,8 @@
 					// includes the methods of typ.
 					// Variables are addressable, so we can always take their
 					// address.
-					if _, ok := typ.(*Pointer); !ok {
-						if _, ok := typ.Underlying().(*Interface); !ok {
-							typ = &Pointer{base: typ}
-						}
+					if _, ok := typ.(*Pointer); !ok && !isInterface(typ) {
+						typ = &Pointer{base: typ}
 					}
 				}
 				// If we created a synthetic pointer type above, we will throw
diff --git a/go/types/check.go b/go/types/check.go
index 6ee8e06..f0b129d 100644
--- a/go/types/check.go
+++ b/go/types/check.go
@@ -79,7 +79,7 @@
 }
 
 func (check *checker) recordCommaOkTypes(x ast.Expr, t1, t2 Type) {
-	assert(x != nil && !isUntyped(t1) && !isUntyped(t2) && isBoolean(t2))
+	assert(x != nil && isTyped(t1) && isTyped(t2) && isBoolean(t2))
 	if m := check.Types; m != nil {
 		assert(m[x] != nil) // should have been recorded already
 		pos := x.Pos()
@@ -181,8 +181,8 @@
 	// remaining untyped expressions must indeed be untyped
 	if debug {
 		for x, info := range check.untyped {
-			if !isUntyped(info.typ) {
-				check.dump("%s: %s (type %s) is not untyped", x.Pos(), x, info.typ)
+			if isTyped(info.typ) {
+				check.dump("%s: %s (type %s) is typed", x.Pos(), x, info.typ)
 				panic(0)
 			}
 		}
diff --git a/go/types/conversions.go b/go/types/conversions.go
index a32b8dc..c8f8843 100644
--- a/go/types/conversions.go
+++ b/go/types/conversions.go
@@ -50,7 +50,7 @@
 		// For conversions to interfaces, use the argument type's
 		// default type instead. Keep untyped nil for untyped nil
 		// arguments.
-		if _, ok := T.Underlying().(*Interface); ok {
+		if isInterface(T) {
 			final = defaultType(x.typ)
 		}
 	}
diff --git a/go/types/expr.go b/go/types/expr.go
index d75298b..761e709 100644
--- a/go/types/expr.go
+++ b/go/types/expr.go
@@ -89,7 +89,8 @@
 		}
 		if x.mode != variable {
 			check.invalidOp(x.pos(), "cannot take address of %s", x)
-			goto Error
+			x.mode = invalid
+			return
 		}
 		x.typ = &Pointer{base: x.typ}
 		return
@@ -98,11 +99,13 @@
 		typ, ok := x.typ.Underlying().(*Chan)
 		if !ok {
 			check.invalidOp(x.pos(), "cannot receive from non-channel %s", x)
-			goto Error
+			x.mode = invalid
+			return
 		}
 		if typ.dir&ast.RECV == 0 {
 			check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x)
-			goto Error
+			x.mode = invalid
+			return
 		}
 		x.mode = valueok
 		x.typ = typ.elt
@@ -110,7 +113,8 @@
 	}
 
 	if !check.op(unaryOpPredicates, x, op) {
-		goto Error
+		x.mode = invalid
+		return
 	}
 
 	if x.mode == constant {
@@ -122,16 +126,14 @@
 		x.val = exact.UnaryOp(op, x.val, size)
 		// Typed constants must be representable in
 		// their type after each constant operation.
-		check.isRepresentable(x, typ)
+		if isTyped(typ) {
+			check.isRepresentableAs(x, typ)
+		}
 		return
 	}
 
 	x.mode = value
 	// x.typ remains unchanged
-	return
-
-Error:
-	x.mode = invalid
 }
 
 func isShift(op token.Token) bool {
@@ -318,9 +320,6 @@
 	case exact.String:
 		return as == String || as == UntypedString
 
-	case exact.Nil:
-		return as == UntypedNil || as == UnsafePointer
-
 	default:
 		unreachable()
 	}
@@ -328,16 +327,24 @@
 	return false
 }
 
-// isRepresentable checks that a constant operand is representable in the given type.
-func (check *checker) isRepresentable(x *operand, typ *Basic) {
-	if x.mode != constant || isUntyped(typ) {
-		return
-	}
-
+// isRepresentableAs checks that a constant operand is representable in the given basic type.
+func (check *checker) isRepresentableAs(x *operand, typ *Basic) {
+	assert(x.mode == constant)
 	if !isRepresentableConst(x.val, check.conf, typ.kind, &x.val) {
 		var msg string
 		if isNumeric(x.typ) && isNumeric(typ) {
-			msg = "%s overflows (or cannot be accurately represented as) %s"
+			// numeric conversion : error msg
+			//
+			// integer -> integer : overflows
+			// integer -> float   : overflows (actually not possible)
+			// float   -> integer : truncated
+			// float   -> float   : overflows
+			//
+			if !isInteger(x.typ) && isInteger(typ) {
+				msg = "%s truncated to %s"
+			} else {
+				msg = "%s overflows %s"
+			}
 		} else {
 			msg = "cannot convert %s to %s"
 		}
@@ -457,7 +464,7 @@
 
 // convertUntyped attempts to set the type of an untyped value to the target type.
 func (check *checker) convertUntyped(x *operand, target Type) {
-	if x.mode == invalid || !isUntyped(x.typ) {
+	if x.mode == invalid || isTyped(x.typ) {
 		return
 	}
 
@@ -481,15 +488,38 @@
 
 	// typed target
 	switch t := target.Underlying().(type) {
-	case nil:
-		// We may reach here due to previous type errors.
-		// Be conservative and don't crash.
-		x.mode = invalid
-		return
 	case *Basic:
-		check.isRepresentable(x, t)
-		if x.mode == invalid {
-			return // error already reported
+		if x.mode == constant {
+			check.isRepresentableAs(x, t)
+			if x.mode == invalid {
+				return
+			}
+		} else {
+			// Non-constant untyped values may appear as the
+			// result of comparisons (untyped bool), intermediate
+			// (delayed-checked) rhs operands of shifts, and as
+			// the value nil.
+			switch x.typ.(*Basic).kind {
+			case UntypedBool:
+				if !isBoolean(target) {
+					goto Error
+				}
+			case UntypedInt, UntypedRune, UntypedFloat, UntypedComplex:
+				if !isNumeric(target) {
+					goto Error
+				}
+			case UntypedString:
+				// Non-constant untyped string values are not
+				// permitted by the spec and should not occur.
+				unreachable()
+			case UntypedNil:
+				// Unsafe.Pointer is a basic type that includes nil.
+				if !hasNil(target) {
+					goto Error
+				}
+			default:
+				goto Error
+			}
 		}
 	case *Interface:
 		if !x.isNil() && t.NumMethods() > 0 /* empty interfaces are ok */ {
@@ -737,7 +767,9 @@
 		x.val = exact.BinaryOp(x.val, op, y.val)
 		// Typed constants must be representable in
 		// their type after each constant operation.
-		check.isRepresentable(x, typ)
+		if isTyped(typ) {
+			check.isRepresentableAs(x, typ)
+		}
 		return
 	}
 
diff --git a/go/types/lookup.go b/go/types/lookup.go
index 8f8390b..398bb62 100644
--- a/go/types/lookup.go
+++ b/go/types/lookup.go
@@ -233,17 +233,17 @@
 	return list[:n]
 }
 
-// MissingMethod returns (nil, false) if typ implements T, otherwise it
+// MissingMethod returns (nil, false) if V implements T, otherwise it
 // returns a missing method required by T and whether it is missing or
 // just has the wrong type.
 //
-// For typ of interface type, if static is set, implements checks that all
-// methods of T are present in typ (e.g., for a conversion T(x) where x is
-// of type typ). Otherwise, implements only checks that methods of T which
-// are also present in typ have matching types (e.g., for a type assertion
-// x.(T) where x is of interface type typ).
+// For non-interface types V, or if static is set, V implements T if all
+// methods of T are present in V. Otherwise (V is an interface and static
+// is not set), MissingMethod only checks that methods of T which are also
+// present in V have matching types (e.g., for a type assertion x.(T) where
+// x is of interface type typ).
 //
-func MissingMethod(typ Type, T *Interface, static bool) (method *Func, wrongType bool) {
+func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) {
 	// fast path for common case
 	if T.NumMethods() == 0 {
 		return
@@ -251,7 +251,7 @@
 
 	// TODO(gri) Consider using method sets here. Might be more efficient.
 
-	if ityp, _ := typ.Underlying().(*Interface); ityp != nil {
+	if ityp, _ := V.Underlying().(*Interface); ityp != nil {
 		for _, m := range T.methods {
 			_, obj := lookupMethod(ityp.methods, m.pkg, m.name)
 			switch {
@@ -268,7 +268,7 @@
 
 	// A concrete type implements T if it implements all methods of T.
 	for _, m := range T.methods {
-		obj, _, indirect := lookupFieldOrMethod(typ, m.pkg, m.name)
+		obj, _, indirect := lookupFieldOrMethod(V, m.pkg, m.name)
 		if obj == nil {
 			return m, false
 		}
diff --git a/go/types/objects.go b/go/types/objects.go
index 020a3e1..d15e020 100644
--- a/go/types/objects.go
+++ b/go/types/objects.go
@@ -266,3 +266,10 @@
 func newBuiltin(id builtinId) *Builtin {
 	return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid]}, id}
 }
+
+// Nil represents the predeclared value nil.
+type Nil struct {
+	object
+}
+
+func (*Nil) String() string { return "nil" }
diff --git a/go/types/operand.go b/go/types/operand.go
index 6e49475..b73f87a 100644
--- a/go/types/operand.go
+++ b/go/types/operand.go
@@ -184,13 +184,13 @@
 	x.val = val
 }
 
-// isNil reports whether x is the predeclared nil constant.
+// isNil reports whether x is the nil value.
 func (x *operand) isNil() bool {
-	return x.mode == constant && x.val.Kind() == exact.Nil
+	return x.mode == value && x.typ == Typ[UntypedNil]
 }
 
 // TODO(gri) The functions operand.isAssignableTo, checker.convertUntyped,
-//           checker.isRepresentable, and checker.assignOperand are
+//           checker.isRepresentable, and checker.assignment are
 //           overlapping in functionality. Need to simplify and clean up.
 
 // isAssignableTo reports whether x is assignable to a variable of type T.
diff --git a/go/types/predicates.go b/go/types/predicates.go
index 778bf88..c9cb127 100644
--- a/go/types/predicates.go
+++ b/go/types/predicates.go
@@ -49,6 +49,11 @@
 	return ok && t.info&IsString != 0
 }
 
+func isTyped(typ Type) bool {
+	t, ok := typ.Underlying().(*Basic)
+	return !ok || t.info&IsUntyped == 0
+}
+
 func isUntyped(typ Type) bool {
 	t, ok := typ.Underlying().(*Basic)
 	return ok && t.info&IsUntyped != 0
@@ -64,6 +69,11 @@
 	return ok && t.info&IsConstType != 0
 }
 
+func isInterface(typ Type) bool {
+	_, ok := typ.Underlying().(*Interface)
+	return ok
+}
+
 func isComparable(typ Type) bool {
 	switch t := typ.Underlying().(type) {
 	case *Basic:
@@ -84,8 +94,11 @@
 	return false
 }
 
+// hasNil reports whether a type includes the nil value.
 func hasNil(typ Type) bool {
-	switch typ.Underlying().(type) {
+	switch t := typ.Underlying().(type) {
+	case *Basic:
+		return t.kind == UnsafePointer
 	case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
 		return true
 	}
diff --git a/go/types/testdata/builtins.src b/go/types/testdata/builtins.src
index 8aa6fb8..0abac1a 100644
--- a/go/types/testdata/builtins.src
+++ b/go/types/testdata/builtins.src
@@ -322,7 +322,7 @@
 	_ = make([]int, int /* ERROR not an expression */)
 	_ = make([]int, 10, float32 /* ERROR not an expression */)
 	_ = make([]int, "foo" /* ERROR cannot convert */)
-	_ = make([]int, 10, 2.3 /* ERROR overflows */)
+	_ = make([]int, 10, 2.3 /* ERROR truncated */)
 	_ = make([]int, 5, 10.0)
 	_ = make([]int, 0i)
 	_ = make([]int, 1.0)
diff --git a/go/types/testdata/const0.src b/go/types/testdata/const0.src
index 6515e2a..19e4589 100644
--- a/go/types/testdata/const0.src
+++ b/go/types/testdata/const0.src
@@ -253,7 +253,7 @@
 
 // special cases
 const (
-	_n0 = nil /* ERROR "invalid constant type" */
+	_n0 = nil /* ERROR "not constant" */
 	_n1 = [ /* ERROR "not constant" */ ]int{}
 )
 
diff --git a/go/types/testdata/const1.src b/go/types/testdata/const1.src
index df4d522..88e9fad 100644
--- a/go/types/testdata/const1.src
+++ b/go/types/testdata/const1.src
@@ -61,7 +61,7 @@
 	_ int8 = minInt8
 	_ int8 = maxInt8
 	_ int8 = maxInt8 /* ERROR "overflows" */ + 1
-	_ int8 = smallestFloat64 /* ERROR "overflows" */
+	_ int8 = smallestFloat64 /* ERROR "truncated" */
 
 	_ = int8(minInt8 /* ERROR "cannot convert" */ - 1)
 	_ = int8(minInt8)
@@ -75,7 +75,7 @@
 	_ int16 = minInt16
 	_ int16 = maxInt16
 	_ int16 = maxInt16 /* ERROR "overflows" */ + 1
-	_ int16 = smallestFloat64 /* ERROR "overflows" */
+	_ int16 = smallestFloat64 /* ERROR "truncated" */
 
 	_ = int16(minInt16 /* ERROR "cannot convert" */ - 1)
 	_ = int16(minInt16)
@@ -89,7 +89,7 @@
 	_ int32 = minInt32
 	_ int32 = maxInt32
 	_ int32 = maxInt32 /* ERROR "overflows" */ + 1
-	_ int32 = smallestFloat64 /* ERROR "overflows" */
+	_ int32 = smallestFloat64 /* ERROR "truncated" */
 
 	_ = int32(minInt32 /* ERROR "cannot convert" */ - 1)
 	_ = int32(minInt32)
@@ -103,7 +103,7 @@
 	_ int64 = minInt64
 	_ int64 = maxInt64
 	_ int64 = maxInt64 /* ERROR "overflows" */ + 1
-	_ int64 = smallestFloat64 /* ERROR "overflows" */
+	_ int64 = smallestFloat64 /* ERROR "truncated" */
 
 	_ = int64(minInt64 /* ERROR "cannot convert" */ - 1)
 	_ = int64(minInt64)
@@ -117,7 +117,7 @@
 	_ int = minInt
 	_ int = maxInt
 	_ int = maxInt /* ERROR "overflows" */ + 1
-	_ int = smallestFloat64 /* ERROR "overflows" */
+	_ int = smallestFloat64 /* ERROR "truncated" */
 
 	_ = int(minInt /* ERROR "cannot convert" */ - 1)
 	_ = int(minInt)
@@ -131,7 +131,7 @@
 	_ uint8 = 0
 	_ uint8 = maxUint8
 	_ uint8 = maxUint8 /* ERROR "overflows" */ + 1
-	_ uint8 = smallestFloat64 /* ERROR "overflows" */
+	_ uint8 = smallestFloat64 /* ERROR "truncated" */
 
 	_ = uint8(0 /* ERROR "cannot convert" */ - 1)
 	_ = uint8(0)
@@ -145,7 +145,7 @@
 	_ uint16 = 0
 	_ uint16 = maxUint16
 	_ uint16 = maxUint16 /* ERROR "overflows" */ + 1
-	_ uint16 = smallestFloat64 /* ERROR "overflows" */
+	_ uint16 = smallestFloat64 /* ERROR "truncated" */
 
 	_ = uint16(0 /* ERROR "cannot convert" */ - 1)
 	_ = uint16(0)
@@ -159,7 +159,7 @@
 	_ uint32 = 0
 	_ uint32 = maxUint32
 	_ uint32 = maxUint32 /* ERROR "overflows" */ + 1
-	_ uint32 = smallestFloat64 /* ERROR "overflows" */
+	_ uint32 = smallestFloat64 /* ERROR "truncated" */
 
 	_ = uint32(0 /* ERROR "cannot convert" */ - 1)
 	_ = uint32(0)
@@ -173,7 +173,7 @@
 	_ uint64 = 0
 	_ uint64 = maxUint64
 	_ uint64 = maxUint64 /* ERROR "overflows" */ + 1
-	_ uint64 = smallestFloat64 /* ERROR "overflows" */
+	_ uint64 = smallestFloat64 /* ERROR "truncated" */
 
 	_ = uint64(0 /* ERROR "cannot convert" */ - 1)
 	_ = uint64(0)
@@ -187,7 +187,7 @@
 	_ uint = 0
 	_ uint = maxUint
 	_ uint = maxUint /* ERROR "overflows" */ + 1
-	_ uint = smallestFloat64 /* ERROR "overflows" */
+	_ uint = smallestFloat64 /* ERROR "truncated" */
 
 	_ = uint(0 /* ERROR "cannot convert" */ - 1)
 	_ = uint(0)
@@ -201,7 +201,7 @@
 	_ uintptr = 0
 	_ uintptr = maxUintptr
 	_ uintptr = maxUintptr /* ERROR "overflows" */ + 1
-	_ uintptr = smallestFloat64 /* ERROR "overflows" */
+	_ uintptr = smallestFloat64 /* ERROR "truncated" */
 
 	_ = uintptr(0 /* ERROR "cannot convert" */ - 1)
 	_ = uintptr(0)
diff --git a/go/types/testdata/expr3.src b/go/types/testdata/expr3.src
index 32c74b6..039a5fb 100644
--- a/go/types/testdata/expr3.src
+++ b/go/types/testdata/expr3.src
@@ -12,7 +12,7 @@
 	var a [10]int
 	_ = a[true /* ERROR "cannot convert" */ ]
 	_ = a["foo" /* ERROR "cannot convert" */ ]
-	_ = a[1.1 /* ERROR "overflows" */ ]
+	_ = a[1.1 /* ERROR "truncated" */ ]
 	_ = a[1.0]
 	_ = a[- /* ERROR "negative" */ 1]
 	_ = a[- /* ERROR "negative" */ 1 :]
@@ -180,7 +180,7 @@
 	_ = T0{1, b /* ERROR "mixture" */ : 2, 3}
 	_ = T0{1, 2} /* ERROR "too few values" */
 	_ = T0{1, 2, 3, 4  /* ERROR "too many values" */ }
-	_ = T0{1, "foo" /* ERROR "cannot convert" */, 3.4  /* ERROR "overflows" */}
+	_ = T0{1, "foo" /* ERROR "cannot convert" */, 3.4  /* ERROR "truncated" */}
 
 	// invalid type
 	type P *struct{
@@ -210,7 +210,7 @@
 	_ = A1{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ }
 	_ = A1{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4}
 	_ = A1{2.0}
-	_ = A1{2.1 /* ERROR "overflows" */ }
+	_ = A1{2.1 /* ERROR "truncated" */ }
 	_ = A1{"foo" /* ERROR "cannot convert" */ }
 
 	// indices must be integer constants
@@ -218,7 +218,7 @@
 	const f = 2.1
 	const s = "foo"
 	_ = A1{i /* ERROR "index i must be integer constant" */ : 0}
-	_ = A1{f /* ERROR "cannot be .* represented" */ : 0}
+	_ = A1{f /* ERROR "truncated" */ : 0}
 	_ = A1{s /* ERROR "cannot convert" */ : 0}
 
 	a0 := [...]int{}
@@ -267,7 +267,7 @@
 	_ = S0{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ }
 	_ = S0{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4}
 	_ = S0{2.0}
-	_ = S0{2.1 /* ERROR "overflows" */ }
+	_ = S0{2.1 /* ERROR "truncated" */ }
 	_ = S0{"foo" /* ERROR "cannot convert" */ }
 
 	// indices must be resolved correctly
@@ -281,7 +281,7 @@
 	const f = 2.1
 	const s = "foo"
 	_ = S0{i /* ERROR "index i must be integer constant" */ : 0}
-	_ = S0{f /* ERROR "cannot be .* represented" */ : 0}
+	_ = S0{f /* ERROR "truncated" */ : 0}
 	_ = S0{s /* ERROR "cannot convert" */ : 0}
 
 }
diff --git a/go/types/testdata/stmt0.src b/go/types/testdata/stmt0.src
index 1939411..aecdc4c 100644
--- a/go/types/testdata/stmt0.src
+++ b/go/types/testdata/stmt0.src
@@ -67,10 +67,10 @@
 
 	// test cases for issue 5800
 	var (
-		_ int = nil /* ERROR "cannot convert nil" */
-		_ [10]int = nil /* ERROR "cannot convert nil" */
+		_ int = nil /* ERROR "untyped nil value" */
+		_ [10]int = nil /* ERROR "untyped nil value" */
 		_ []byte = nil
-		_ struct{} = nil /* ERROR "cannot convert nil" */
+		_ struct{} = nil /* ERROR "untyped nil value" */
 		_ func() = nil
 		_ map[int]string = nil
 		_ chan int = nil
diff --git a/go/types/typexpr.go b/go/types/typexpr.go
index a6ad506..acc098e 100644
--- a/go/types/typexpr.go
+++ b/go/types/typexpr.go
@@ -97,6 +97,10 @@
 		x.mode = builtin
 		x.id = obj.id
 
+	case *Nil:
+		// no need to "use" the nil object
+		x.mode = value
+
 	default:
 		unreachable()
 	}
@@ -118,7 +122,7 @@
 	}
 
 	t := check.typ0(e, def, cycleOk)
-	assert(e != nil && t != nil && !isUntyped(t))
+	assert(e != nil && t != nil && isTyped(t))
 
 	check.recordTypeAndValue(e, t, nil)
 
@@ -353,7 +357,7 @@
 		check.errorf(x.pos(), "%s used as type", &x)
 	case typexpr:
 		return x.typ
-	case constant:
+	case value:
 		if x.isNil() {
 			return nil
 		}
diff --git a/go/types/universe.go b/go/types/universe.go
index 70876c6..cf96f6d 100644
--- a/go/types/universe.go
+++ b/go/types/universe.go
@@ -80,7 +80,6 @@
 	{"true", UntypedBool, exact.MakeBool(true)},
 	{"false", UntypedBool, exact.MakeBool(false)},
 	{"iota", UntypedInt, exact.MakeInt64(0)},
-	{"nil", UntypedNil, exact.MakeNil()},
 }
 
 func defPredeclaredConsts() {
@@ -89,6 +88,10 @@
 	}
 }
 
+func defPredeclaredNil() {
+	def(&Nil{object{name: "nil", typ: Typ[UntypedNil]}})
+}
+
 // A builtinId is the id of a builtin function.
 type builtinId int
 
@@ -172,6 +175,7 @@
 
 	defPredeclaredTypes()
 	defPredeclaredConsts()
+	defPredeclaredNil()
 	defPredeclaredFuncs()
 
 	universeIota = Universe.Lookup("iota").(*Const)
diff --git a/ssa/builder.go b/ssa/builder.go
index e6dc553..a23b877 100644
--- a/ssa/builder.go
+++ b/ssa/builder.go
@@ -637,9 +637,12 @@
 
 	case *ast.Ident:
 		obj := fn.Pkg.objectOf(e)
-		// Universal built-in?
-		if obj, ok := obj.(*types.Builtin); ok {
+		// Universal built-in or nil?
+		switch obj := obj.(type) {
+		case *types.Builtin:
 			return fn.Prog.builtins[obj]
+		case *types.Nil:
+			return nilConst(fn.Pkg.typeOf(e))
 		}
 		// Package-level func or var?
 		if v := b.lookup(fn.Pkg, obj); v != nil {
diff --git a/ssa/const.go b/ssa/const.go
index 2f9acd0..6c9ee30 100644
--- a/ssa/const.go
+++ b/ssa/const.go
@@ -31,7 +31,7 @@
 // be any reference type, including interfaces.
 //
 func nilConst(typ types.Type) *Const {
-	return NewConst(exact.MakeNil(), typ)
+	return NewConst(nil, typ)
 }
 
 // zeroConst returns a new "zero" constant of the specified type,
@@ -67,7 +67,9 @@
 
 func (c *Const) Name() string {
 	var s string
-	if c.Value.Kind() == exact.String {
+	if c.Value == nil {
+		s = "nil"
+	} else if c.Value.Kind() == exact.String {
 		s = exact.StringVal(c.Value)
 		const max = 20
 		if len(s) > max {
@@ -94,7 +96,7 @@
 
 // IsNil returns true if this constant represents a typed or untyped nil value.
 func (c *Const) IsNil() bool {
-	return c.Value.Kind() == exact.Nil
+	return c.Value == nil
 }
 
 // Int64 returns the numeric value of this constant truncated to fit
diff --git a/ssa/ssa.go b/ssa/ssa.go
index dc498b6..db5465e 100644
--- a/ssa/ssa.go
+++ b/ssa/ssa.go
@@ -363,7 +363,7 @@
 //
 // Value holds the exact value of the constant, independent of its
 // Type(), using the same representation as package go/exact uses for
-// constants.
+// constants, or nil for a typed nil value.
 //
 // Pos() returns token.NoPos.
 //