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
This commit is contained in:
Robert Griesemer 2013-10-09 14:17:25 -07:00
parent 9c8d9fe736
commit f50f6c858a
24 changed files with 213 additions and 109 deletions

View File

@ -124,7 +124,7 @@ func PrintfTests() {
fmt.Printf("%t", notstringerarrayv) // ERROR "arg notstringerarrayv for printf verb %t of wrong type" 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("%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("%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.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() // not an error
fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call" fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call"

View File

@ -62,8 +62,8 @@ func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) {
var ( var (
stringerMethodType = types.New("func() string") stringerMethodType = types.New("func() string")
errorType = types.New("interface{ Error() string }") errorType = types.New("interface{ Error() string }").(*types.Interface)
stringerType = types.New("interface{ String() string }") stringerType = types.New("interface{ String() string }").(*types.Interface)
// One day this might work. See issue 6259. // One day this might work. See issue 6259.
// formatterType = types.New("interface{Format(f fmt.State, c rune)}") // formatterType = types.New("interface{Format(f fmt.State, c rune)}")
) )
@ -103,9 +103,9 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp
if hasMethod(typ, "Format") { if hasMethod(typ, "Format") {
return true 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 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 return true
} }
} }

View File

@ -25,7 +25,6 @@ const (
Unknown Kind = iota Unknown Kind = iota
// non-numeric values // non-numeric values
Nil
Bool Bool
String String
@ -53,7 +52,6 @@ type Value interface {
type ( type (
unknownVal struct{} unknownVal struct{}
nilVal struct{}
boolVal bool boolVal bool
stringVal string stringVal string
int64Val int64 int64Val int64
@ -63,7 +61,6 @@ type (
) )
func (unknownVal) Kind() Kind { return Unknown } func (unknownVal) Kind() Kind { return Unknown }
func (nilVal) Kind() Kind { return Nil }
func (boolVal) Kind() Kind { return Bool } func (boolVal) Kind() Kind { return Bool }
func (stringVal) Kind() Kind { return String } func (stringVal) Kind() Kind { return String }
func (int64Val) Kind() Kind { return Int } func (int64Val) Kind() Kind { return Int }
@ -72,7 +69,6 @@ func (floatVal) Kind() Kind { return Float }
func (complexVal) Kind() Kind { return Complex } func (complexVal) Kind() Kind { return Complex }
func (unknownVal) String() string { return "unknown" } 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 boolVal) String() string { return fmt.Sprintf("%v", bool(x)) }
func (x stringVal) String() string { return strconv.Quote(string(x)) } func (x stringVal) String() string { return strconv.Quote(string(x)) }
func (x int64Val) String() string { return strconv.FormatInt(int64(x), 10) } func (x int64Val) String() string { return strconv.FormatInt(int64(x), 10) }
@ -81,7 +77,6 @@ func (x floatVal) String() string { return x.val.String() }
func (x complexVal) String() string { return fmt.Sprintf("(%s + %si)", x.re, x.im) } func (x complexVal) String() string { return fmt.Sprintf("(%s + %si)", x.re, x.im) }
func (unknownVal) implementsValue() {} func (unknownVal) implementsValue() {}
func (nilVal) implementsValue() {}
func (boolVal) implementsValue() {} func (boolVal) implementsValue() {}
func (stringVal) implementsValue() {} func (stringVal) implementsValue() {}
func (int64Val) implementsValue() {} func (int64Val) implementsValue() {}
@ -122,9 +117,6 @@ func normComplex(re, im *big.Rat) Value {
// MakeUnknown returns the Unknown value. // MakeUnknown returns the Unknown value.
func MakeUnknown() Value { return unknownVal{} } func MakeUnknown() Value { return unknownVal{} }
// MakeNil returns the Nil value.
func MakeNil() Value { return nilVal{} }
// MakeBool returns the Bool value for x. // MakeBool returns the Bool value for x.
func MakeBool(b bool) Value { return boolVal(b) } func MakeBool(b bool) Value { return boolVal(b) }
@ -447,7 +439,7 @@ func match(x, y Value) (_, _ Value) {
// ord(x) <= ord(y) // ord(x) <= ord(y)
switch x := x.(type) { switch x := x.(type) {
case unknownVal, nilVal, boolVal, stringVal, complexVal: case unknownVal, boolVal, stringVal, complexVal:
return x, y return x, y
case int64Val: case int64Val:

View File

@ -112,8 +112,6 @@ func val(lit string) Value {
switch lit { switch lit {
case "?": case "?":
return MakeUnknown() return MakeUnknown()
case "nil":
return MakeNil()
case "true": case "true":
return MakeBool(true) return MakeBool(true)
case "false": case "false":

View File

@ -174,12 +174,28 @@ func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, i
return pkg, err return pkg, err
} }
// IsAssignableTo reports whether a value of type V // IsAssignableTo reports whether a value of type V is assignable to a variable of type T.
// is assignable to a variable of type T.
func IsAssignableTo(V, T Type) bool { func IsAssignableTo(V, T Type) bool {
x := operand{mode: value, typ: V} x := operand{mode: value, typ: V}
return x.isAssignableTo(nil, T) // config not needed for non-constant x 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): 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): Switch statements don't check duplicate cases for all types for which it is required.

View File

@ -13,27 +13,62 @@ import (
"code.google.com/p/go.tools/go/exact" "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 // if necessary by attempting to convert untyped values to the appropriate
// type. If x.mode == invalid upon return, then assignment has already // type. If x.mode == invalid upon return, then assignment has already
// issued an error message and the caller doesn't have to report another. // issued an error message and the caller doesn't have to report another.
// TODO(gri) This latter behavior is for historic reasons and complicates // Use T == nil to indicate assignment to an untyped blank identifier.
// callers. Needs to be cleaned up. //
func (check *checker) assignment(x *operand, to Type) bool { // TODO(gri) Should find a better way to handle in-band errors.
if x.mode == invalid { // TODO(gri) The T == nil mechanism is not yet used - should simplify callers eventually.
return false //
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) assert(t.Len() > 1)
check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x) check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x)
x.mode = invalid x.mode = invalid
return false 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) { func (check *checker) initConst(lhs *Const, x *operand) {
@ -120,7 +155,7 @@ func (check *checker) assignVar(lhs ast.Expr, x *operand) Type {
// If the lhs is untyped, determine the default type. // If the lhs is untyped, determine the default type.
// The spec is unclear about this, but gc appears to // The spec is unclear about this, but gc appears to
// do this. // 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 typ := x.typ
if isUntyped(typ) { if isUntyped(typ) {
// convert untyped types to default types // convert untyped types to default types

View File

@ -381,10 +381,8 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
// includes the methods of typ. // includes the methods of typ.
// Variables are addressable, so we can always take their // Variables are addressable, so we can always take their
// address. // address.
if _, ok := typ.(*Pointer); !ok { if _, ok := typ.(*Pointer); !ok && !isInterface(typ) {
if _, ok := typ.Underlying().(*Interface); !ok { typ = &Pointer{base: typ}
typ = &Pointer{base: typ}
}
} }
} }
// If we created a synthetic pointer type above, we will throw // If we created a synthetic pointer type above, we will throw

View File

@ -79,7 +79,7 @@ func (check *checker) recordTypeAndValue(x ast.Expr, typ Type, val exact.Value)
} }
func (check *checker) recordCommaOkTypes(x ast.Expr, t1, t2 Type) { 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 { if m := check.Types; m != nil {
assert(m[x] != nil) // should have been recorded already assert(m[x] != nil) // should have been recorded already
pos := x.Pos() pos := x.Pos()
@ -181,8 +181,8 @@ func (conf *Config) check(pkgPath string, fset *token.FileSet, files []*ast.File
// remaining untyped expressions must indeed be untyped // remaining untyped expressions must indeed be untyped
if debug { if debug {
for x, info := range check.untyped { for x, info := range check.untyped {
if !isUntyped(info.typ) { if isTyped(info.typ) {
check.dump("%s: %s (type %s) is not untyped", x.Pos(), x, info.typ) check.dump("%s: %s (type %s) is typed", x.Pos(), x, info.typ)
panic(0) panic(0)
} }
} }

View File

@ -50,7 +50,7 @@ func (check *checker) conversion(x *operand, T Type) {
// For conversions to interfaces, use the argument type's // For conversions to interfaces, use the argument type's
// default type instead. Keep untyped nil for untyped nil // default type instead. Keep untyped nil for untyped nil
// arguments. // arguments.
if _, ok := T.Underlying().(*Interface); ok { if isInterface(T) {
final = defaultType(x.typ) final = defaultType(x.typ)
} }
} }

View File

@ -89,7 +89,8 @@ func (check *checker) unary(x *operand, op token.Token) {
} }
if x.mode != variable { if x.mode != variable {
check.invalidOp(x.pos(), "cannot take address of %s", x) check.invalidOp(x.pos(), "cannot take address of %s", x)
goto Error x.mode = invalid
return
} }
x.typ = &Pointer{base: x.typ} x.typ = &Pointer{base: x.typ}
return return
@ -98,11 +99,13 @@ func (check *checker) unary(x *operand, op token.Token) {
typ, ok := x.typ.Underlying().(*Chan) typ, ok := x.typ.Underlying().(*Chan)
if !ok { if !ok {
check.invalidOp(x.pos(), "cannot receive from non-channel %s", x) check.invalidOp(x.pos(), "cannot receive from non-channel %s", x)
goto Error x.mode = invalid
return
} }
if typ.dir&ast.RECV == 0 { if typ.dir&ast.RECV == 0 {
check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x) check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x)
goto Error x.mode = invalid
return
} }
x.mode = valueok x.mode = valueok
x.typ = typ.elt x.typ = typ.elt
@ -110,7 +113,8 @@ func (check *checker) unary(x *operand, op token.Token) {
} }
if !check.op(unaryOpPredicates, x, op) { if !check.op(unaryOpPredicates, x, op) {
goto Error x.mode = invalid
return
} }
if x.mode == constant { if x.mode == constant {
@ -122,16 +126,14 @@ func (check *checker) unary(x *operand, op token.Token) {
x.val = exact.UnaryOp(op, x.val, size) x.val = exact.UnaryOp(op, x.val, size)
// Typed constants must be representable in // Typed constants must be representable in
// their type after each constant operation. // their type after each constant operation.
check.isRepresentable(x, typ) if isTyped(typ) {
check.isRepresentableAs(x, typ)
}
return return
} }
x.mode = value x.mode = value
// x.typ remains unchanged // x.typ remains unchanged
return
Error:
x.mode = invalid
} }
func isShift(op token.Token) bool { func isShift(op token.Token) bool {
@ -318,9 +320,6 @@ func isRepresentableConst(x exact.Value, conf *Config, as BasicKind, rounded *ex
case exact.String: case exact.String:
return as == String || as == UntypedString return as == String || as == UntypedString
case exact.Nil:
return as == UntypedNil || as == UnsafePointer
default: default:
unreachable() unreachable()
} }
@ -328,16 +327,24 @@ func isRepresentableConst(x exact.Value, conf *Config, as BasicKind, rounded *ex
return false return false
} }
// isRepresentable checks that a constant operand is representable in the given type. // isRepresentableAs checks that a constant operand is representable in the given basic type.
func (check *checker) isRepresentable(x *operand, typ *Basic) { func (check *checker) isRepresentableAs(x *operand, typ *Basic) {
if x.mode != constant || isUntyped(typ) { assert(x.mode == constant)
return
}
if !isRepresentableConst(x.val, check.conf, typ.kind, &x.val) { if !isRepresentableConst(x.val, check.conf, typ.kind, &x.val) {
var msg string var msg string
if isNumeric(x.typ) && isNumeric(typ) { 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 { } else {
msg = "cannot convert %s to %s" msg = "cannot convert %s to %s"
} }
@ -457,7 +464,7 @@ func (check *checker) updateExprType(x ast.Expr, typ Type, final bool) {
// convertUntyped attempts to set the type of an untyped value to the target type. // convertUntyped attempts to set the type of an untyped value to the target type.
func (check *checker) convertUntyped(x *operand, 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 return
} }
@ -481,15 +488,38 @@ func (check *checker) convertUntyped(x *operand, target Type) {
// typed target // typed target
switch t := target.Underlying().(type) { 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: case *Basic:
check.isRepresentable(x, t) if x.mode == constant {
if x.mode == invalid { check.isRepresentableAs(x, t)
return // error already reported 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: case *Interface:
if !x.isNil() && t.NumMethods() > 0 /* empty interfaces are ok */ { if !x.isNil() && t.NumMethods() > 0 /* empty interfaces are ok */ {
@ -737,7 +767,9 @@ func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token) {
x.val = exact.BinaryOp(x.val, op, y.val) x.val = exact.BinaryOp(x.val, op, y.val)
// Typed constants must be representable in // Typed constants must be representable in
// their type after each constant operation. // their type after each constant operation.
check.isRepresentable(x, typ) if isTyped(typ) {
check.isRepresentableAs(x, typ)
}
return return
} }

View File

@ -233,17 +233,17 @@ func consolidateMultiples(list []embeddedType) []embeddedType {
return list[:n] 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 // returns a missing method required by T and whether it is missing or
// just has the wrong type. // just has the wrong type.
// //
// For typ of interface type, if static is set, implements checks that all // For non-interface types V, or if static is set, V implements T if all
// methods of T are present in typ (e.g., for a conversion T(x) where x is // methods of T are present in V. Otherwise (V is an interface and static
// of type typ). Otherwise, implements only checks that methods of T which // is not set), MissingMethod only checks that methods of T which are also
// are also present in typ have matching types (e.g., for a type assertion // present in V have matching types (e.g., for a type assertion x.(T) where
// x.(T) where x is of interface type typ). // 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 // fast path for common case
if T.NumMethods() == 0 { if T.NumMethods() == 0 {
return return
@ -251,7 +251,7 @@ func MissingMethod(typ Type, T *Interface, static bool) (method *Func, wrongType
// TODO(gri) Consider using method sets here. Might be more efficient. // 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 { for _, m := range T.methods {
_, obj := lookupMethod(ityp.methods, m.pkg, m.name) _, obj := lookupMethod(ityp.methods, m.pkg, m.name)
switch { switch {
@ -268,7 +268,7 @@ func MissingMethod(typ Type, T *Interface, static bool) (method *Func, wrongType
// A concrete type implements T if it implements all methods of T. // A concrete type implements T if it implements all methods of T.
for _, m := range T.methods { for _, m := range T.methods {
obj, _, indirect := lookupFieldOrMethod(typ, m.pkg, m.name) obj, _, indirect := lookupFieldOrMethod(V, m.pkg, m.name)
if obj == nil { if obj == nil {
return m, false return m, false
} }

View File

@ -266,3 +266,10 @@ type Builtin struct {
func newBuiltin(id builtinId) *Builtin { func newBuiltin(id builtinId) *Builtin {
return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid]}, id} 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" }

View File

@ -184,13 +184,13 @@ func (x *operand) setConst(tok token.Token, lit string) {
x.val = val 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 { 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, // 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. // overlapping in functionality. Need to simplify and clean up.
// isAssignableTo reports whether x is assignable to a variable of type T. // isAssignableTo reports whether x is assignable to a variable of type T.

View File

@ -49,6 +49,11 @@ func isString(typ Type) bool {
return ok && t.info&IsString != 0 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 { func isUntyped(typ Type) bool {
t, ok := typ.Underlying().(*Basic) t, ok := typ.Underlying().(*Basic)
return ok && t.info&IsUntyped != 0 return ok && t.info&IsUntyped != 0
@ -64,6 +69,11 @@ func isConstType(typ Type) bool {
return ok && t.info&IsConstType != 0 return ok && t.info&IsConstType != 0
} }
func isInterface(typ Type) bool {
_, ok := typ.Underlying().(*Interface)
return ok
}
func isComparable(typ Type) bool { func isComparable(typ Type) bool {
switch t := typ.Underlying().(type) { switch t := typ.Underlying().(type) {
case *Basic: case *Basic:
@ -84,8 +94,11 @@ func isComparable(typ Type) bool {
return false return false
} }
// hasNil reports whether a type includes the nil value.
func hasNil(typ Type) bool { 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: case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
return true return true
} }

View File

@ -322,7 +322,7 @@ func make1() {
_ = make([]int, int /* ERROR not an expression */) _ = make([]int, int /* ERROR not an expression */)
_ = make([]int, 10, float32 /* ERROR not an expression */) _ = make([]int, 10, float32 /* ERROR not an expression */)
_ = make([]int, "foo" /* ERROR cannot convert */) _ = 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, 5, 10.0)
_ = make([]int, 0i) _ = make([]int, 0i)
_ = make([]int, 1.0) _ = make([]int, 1.0)

View File

@ -253,7 +253,7 @@ var _ = assert(_x == 3)
// special cases // special cases
const ( const (
_n0 = nil /* ERROR "invalid constant type" */ _n0 = nil /* ERROR "not constant" */
_n1 = [ /* ERROR "not constant" */ ]int{} _n1 = [ /* ERROR "not constant" */ ]int{}
) )

View File

@ -61,7 +61,7 @@ const (
_ int8 = minInt8 _ int8 = minInt8
_ int8 = maxInt8 _ int8 = maxInt8
_ int8 = maxInt8 /* ERROR "overflows" */ + 1 _ int8 = maxInt8 /* ERROR "overflows" */ + 1
_ int8 = smallestFloat64 /* ERROR "overflows" */ _ int8 = smallestFloat64 /* ERROR "truncated" */
_ = int8(minInt8 /* ERROR "cannot convert" */ - 1) _ = int8(minInt8 /* ERROR "cannot convert" */ - 1)
_ = int8(minInt8) _ = int8(minInt8)
@ -75,7 +75,7 @@ const (
_ int16 = minInt16 _ int16 = minInt16
_ int16 = maxInt16 _ int16 = maxInt16
_ int16 = maxInt16 /* ERROR "overflows" */ + 1 _ int16 = maxInt16 /* ERROR "overflows" */ + 1
_ int16 = smallestFloat64 /* ERROR "overflows" */ _ int16 = smallestFloat64 /* ERROR "truncated" */
_ = int16(minInt16 /* ERROR "cannot convert" */ - 1) _ = int16(minInt16 /* ERROR "cannot convert" */ - 1)
_ = int16(minInt16) _ = int16(minInt16)
@ -89,7 +89,7 @@ const (
_ int32 = minInt32 _ int32 = minInt32
_ int32 = maxInt32 _ int32 = maxInt32
_ int32 = maxInt32 /* ERROR "overflows" */ + 1 _ int32 = maxInt32 /* ERROR "overflows" */ + 1
_ int32 = smallestFloat64 /* ERROR "overflows" */ _ int32 = smallestFloat64 /* ERROR "truncated" */
_ = int32(minInt32 /* ERROR "cannot convert" */ - 1) _ = int32(minInt32 /* ERROR "cannot convert" */ - 1)
_ = int32(minInt32) _ = int32(minInt32)
@ -103,7 +103,7 @@ const (
_ int64 = minInt64 _ int64 = minInt64
_ int64 = maxInt64 _ int64 = maxInt64
_ int64 = maxInt64 /* ERROR "overflows" */ + 1 _ int64 = maxInt64 /* ERROR "overflows" */ + 1
_ int64 = smallestFloat64 /* ERROR "overflows" */ _ int64 = smallestFloat64 /* ERROR "truncated" */
_ = int64(minInt64 /* ERROR "cannot convert" */ - 1) _ = int64(minInt64 /* ERROR "cannot convert" */ - 1)
_ = int64(minInt64) _ = int64(minInt64)
@ -117,7 +117,7 @@ const (
_ int = minInt _ int = minInt
_ int = maxInt _ int = maxInt
_ int = maxInt /* ERROR "overflows" */ + 1 _ int = maxInt /* ERROR "overflows" */ + 1
_ int = smallestFloat64 /* ERROR "overflows" */ _ int = smallestFloat64 /* ERROR "truncated" */
_ = int(minInt /* ERROR "cannot convert" */ - 1) _ = int(minInt /* ERROR "cannot convert" */ - 1)
_ = int(minInt) _ = int(minInt)
@ -131,7 +131,7 @@ const (
_ uint8 = 0 _ uint8 = 0
_ uint8 = maxUint8 _ uint8 = maxUint8
_ uint8 = maxUint8 /* ERROR "overflows" */ + 1 _ uint8 = maxUint8 /* ERROR "overflows" */ + 1
_ uint8 = smallestFloat64 /* ERROR "overflows" */ _ uint8 = smallestFloat64 /* ERROR "truncated" */
_ = uint8(0 /* ERROR "cannot convert" */ - 1) _ = uint8(0 /* ERROR "cannot convert" */ - 1)
_ = uint8(0) _ = uint8(0)
@ -145,7 +145,7 @@ const (
_ uint16 = 0 _ uint16 = 0
_ uint16 = maxUint16 _ uint16 = maxUint16
_ uint16 = maxUint16 /* ERROR "overflows" */ + 1 _ uint16 = maxUint16 /* ERROR "overflows" */ + 1
_ uint16 = smallestFloat64 /* ERROR "overflows" */ _ uint16 = smallestFloat64 /* ERROR "truncated" */
_ = uint16(0 /* ERROR "cannot convert" */ - 1) _ = uint16(0 /* ERROR "cannot convert" */ - 1)
_ = uint16(0) _ = uint16(0)
@ -159,7 +159,7 @@ const (
_ uint32 = 0 _ uint32 = 0
_ uint32 = maxUint32 _ uint32 = maxUint32
_ uint32 = maxUint32 /* ERROR "overflows" */ + 1 _ uint32 = maxUint32 /* ERROR "overflows" */ + 1
_ uint32 = smallestFloat64 /* ERROR "overflows" */ _ uint32 = smallestFloat64 /* ERROR "truncated" */
_ = uint32(0 /* ERROR "cannot convert" */ - 1) _ = uint32(0 /* ERROR "cannot convert" */ - 1)
_ = uint32(0) _ = uint32(0)
@ -173,7 +173,7 @@ const (
_ uint64 = 0 _ uint64 = 0
_ uint64 = maxUint64 _ uint64 = maxUint64
_ uint64 = maxUint64 /* ERROR "overflows" */ + 1 _ uint64 = maxUint64 /* ERROR "overflows" */ + 1
_ uint64 = smallestFloat64 /* ERROR "overflows" */ _ uint64 = smallestFloat64 /* ERROR "truncated" */
_ = uint64(0 /* ERROR "cannot convert" */ - 1) _ = uint64(0 /* ERROR "cannot convert" */ - 1)
_ = uint64(0) _ = uint64(0)
@ -187,7 +187,7 @@ const (
_ uint = 0 _ uint = 0
_ uint = maxUint _ uint = maxUint
_ uint = maxUint /* ERROR "overflows" */ + 1 _ uint = maxUint /* ERROR "overflows" */ + 1
_ uint = smallestFloat64 /* ERROR "overflows" */ _ uint = smallestFloat64 /* ERROR "truncated" */
_ = uint(0 /* ERROR "cannot convert" */ - 1) _ = uint(0 /* ERROR "cannot convert" */ - 1)
_ = uint(0) _ = uint(0)
@ -201,7 +201,7 @@ const (
_ uintptr = 0 _ uintptr = 0
_ uintptr = maxUintptr _ uintptr = maxUintptr
_ uintptr = maxUintptr /* ERROR "overflows" */ + 1 _ uintptr = maxUintptr /* ERROR "overflows" */ + 1
_ uintptr = smallestFloat64 /* ERROR "overflows" */ _ uintptr = smallestFloat64 /* ERROR "truncated" */
_ = uintptr(0 /* ERROR "cannot convert" */ - 1) _ = uintptr(0 /* ERROR "cannot convert" */ - 1)
_ = uintptr(0) _ = uintptr(0)

View File

@ -12,7 +12,7 @@ func indexes() {
var a [10]int var a [10]int
_ = a[true /* ERROR "cannot convert" */ ] _ = a[true /* ERROR "cannot convert" */ ]
_ = a["foo" /* ERROR "cannot convert" */ ] _ = a["foo" /* ERROR "cannot convert" */ ]
_ = a[1.1 /* ERROR "overflows" */ ] _ = a[1.1 /* ERROR "truncated" */ ]
_ = a[1.0] _ = a[1.0]
_ = a[- /* ERROR "negative" */ 1] _ = a[- /* ERROR "negative" */ 1]
_ = a[- /* ERROR "negative" */ 1 :] _ = a[- /* ERROR "negative" */ 1 :]
@ -180,7 +180,7 @@ func struct_literals() {
_ = T0{1, b /* ERROR "mixture" */ : 2, 3} _ = T0{1, b /* ERROR "mixture" */ : 2, 3}
_ = T0{1, 2} /* ERROR "too few values" */ _ = T0{1, 2} /* ERROR "too few values" */
_ = T0{1, 2, 3, 4 /* ERROR "too many 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 // invalid type
type P *struct{ type P *struct{
@ -210,7 +210,7 @@ func array_literals() {
_ = A1{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ } _ = 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{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4}
_ = A1{2.0} _ = A1{2.0}
_ = A1{2.1 /* ERROR "overflows" */ } _ = A1{2.1 /* ERROR "truncated" */ }
_ = A1{"foo" /* ERROR "cannot convert" */ } _ = A1{"foo" /* ERROR "cannot convert" */ }
// indices must be integer constants // indices must be integer constants
@ -218,7 +218,7 @@ func array_literals() {
const f = 2.1 const f = 2.1
const s = "foo" const s = "foo"
_ = A1{i /* ERROR "index i must be integer constant" */ : 0} _ = 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} _ = A1{s /* ERROR "cannot convert" */ : 0}
a0 := [...]int{} a0 := [...]int{}
@ -267,7 +267,7 @@ func slice_literals() {
_ = S0{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ } _ = 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{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4}
_ = S0{2.0} _ = S0{2.0}
_ = S0{2.1 /* ERROR "overflows" */ } _ = S0{2.1 /* ERROR "truncated" */ }
_ = S0{"foo" /* ERROR "cannot convert" */ } _ = S0{"foo" /* ERROR "cannot convert" */ }
// indices must be resolved correctly // indices must be resolved correctly
@ -281,7 +281,7 @@ func slice_literals() {
const f = 2.1 const f = 2.1
const s = "foo" const s = "foo"
_ = S0{i /* ERROR "index i must be integer constant" */ : 0} _ = 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} _ = S0{s /* ERROR "cannot convert" */ : 0}
} }

View File

@ -67,10 +67,10 @@ func assignments1() {
// test cases for issue 5800 // test cases for issue 5800
var ( var (
_ int = nil /* ERROR "cannot convert nil" */ _ int = nil /* ERROR "untyped nil value" */
_ [10]int = nil /* ERROR "cannot convert nil" */ _ [10]int = nil /* ERROR "untyped nil value" */
_ []byte = nil _ []byte = nil
_ struct{} = nil /* ERROR "cannot convert nil" */ _ struct{} = nil /* ERROR "untyped nil value" */
_ func() = nil _ func() = nil
_ map[int]string = nil _ map[int]string = nil
_ chan int = nil _ chan int = nil

View File

@ -97,6 +97,10 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool)
x.mode = builtin x.mode = builtin
x.id = obj.id x.id = obj.id
case *Nil:
// no need to "use" the nil object
x.mode = value
default: default:
unreachable() unreachable()
} }
@ -118,7 +122,7 @@ func (check *checker) typ(e ast.Expr, def *Named, cycleOk bool) Type {
} }
t := check.typ0(e, def, cycleOk) 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) check.recordTypeAndValue(e, t, nil)
@ -353,7 +357,7 @@ func (check *checker) typOrNil(e ast.Expr) Type {
check.errorf(x.pos(), "%s used as type", &x) check.errorf(x.pos(), "%s used as type", &x)
case typexpr: case typexpr:
return x.typ return x.typ
case constant: case value:
if x.isNil() { if x.isNil() {
return nil return nil
} }

View File

@ -80,7 +80,6 @@ var predeclaredConsts = [...]struct {
{"true", UntypedBool, exact.MakeBool(true)}, {"true", UntypedBool, exact.MakeBool(true)},
{"false", UntypedBool, exact.MakeBool(false)}, {"false", UntypedBool, exact.MakeBool(false)},
{"iota", UntypedInt, exact.MakeInt64(0)}, {"iota", UntypedInt, exact.MakeInt64(0)},
{"nil", UntypedNil, exact.MakeNil()},
} }
func defPredeclaredConsts() { func defPredeclaredConsts() {
@ -89,6 +88,10 @@ func defPredeclaredConsts() {
} }
} }
func defPredeclaredNil() {
def(&Nil{object{name: "nil", typ: Typ[UntypedNil]}})
}
// A builtinId is the id of a builtin function. // A builtinId is the id of a builtin function.
type builtinId int type builtinId int
@ -172,6 +175,7 @@ func init() {
defPredeclaredTypes() defPredeclaredTypes()
defPredeclaredConsts() defPredeclaredConsts()
defPredeclaredNil()
defPredeclaredFuncs() defPredeclaredFuncs()
universeIota = Universe.Lookup("iota").(*Const) universeIota = Universe.Lookup("iota").(*Const)

View File

@ -637,9 +637,12 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value {
case *ast.Ident: case *ast.Ident:
obj := fn.Pkg.objectOf(e) obj := fn.Pkg.objectOf(e)
// Universal built-in? // Universal built-in or nil?
if obj, ok := obj.(*types.Builtin); ok { switch obj := obj.(type) {
case *types.Builtin:
return fn.Prog.builtins[obj] return fn.Prog.builtins[obj]
case *types.Nil:
return nilConst(fn.Pkg.typeOf(e))
} }
// Package-level func or var? // Package-level func or var?
if v := b.lookup(fn.Pkg, obj); v != nil { if v := b.lookup(fn.Pkg, obj); v != nil {

View File

@ -31,7 +31,7 @@ func intConst(i int64) *Const {
// be any reference type, including interfaces. // be any reference type, including interfaces.
// //
func nilConst(typ types.Type) *Const { 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, // zeroConst returns a new "zero" constant of the specified type,
@ -67,7 +67,9 @@ func zeroConst(t types.Type) *Const {
func (c *Const) Name() string { func (c *Const) Name() string {
var s 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) s = exact.StringVal(c.Value)
const max = 20 const max = 20
if len(s) > max { if len(s) > max {
@ -94,7 +96,7 @@ func (c *Const) Pos() token.Pos {
// IsNil returns true if this constant represents a typed or untyped nil value. // IsNil returns true if this constant represents a typed or untyped nil value.
func (c *Const) IsNil() bool { 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 // Int64 returns the numeric value of this constant truncated to fit

View File

@ -363,7 +363,7 @@ type Parameter struct {
// //
// Value holds the exact value of the constant, independent of its // Value holds the exact value of the constant, independent of its
// Type(), using the same representation as package go/exact uses for // Type(), using the same representation as package go/exact uses for
// constants. // constants, or nil for a typed nil value.
// //
// Pos() returns token.NoPos. // Pos() returns token.NoPos.
// //