1
0
mirror of https://github.com/golang/go synced 2024-09-30 18:18:32 -06:00

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("%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"

View File

@ -62,8 +62,8 @@ func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) {
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 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp
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
}
}

View File

@ -25,7 +25,6 @@ const (
Unknown Kind = iota
// non-numeric values
Nil
Bool
String
@ -53,7 +52,6 @@ type Value interface {
type (
unknownVal struct{}
nilVal struct{}
boolVal bool
stringVal string
int64Val int64
@ -63,7 +61,6 @@ type (
)
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 (floatVal) Kind() Kind { return Float }
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 floatVal) String() string { return x.val.String() }
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 @@ func normComplex(re, im *big.Rat) Value {
// 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 @@ func match(x, y Value) (_, _ Value) {
// ord(x) <= ord(y)
switch x := x.(type) {
case unknownVal, nilVal, boolVal, stringVal, complexVal:
case unknownVal, boolVal, stringVal, complexVal:
return x, y
case int64Val:

View File

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

View File

@ -174,12 +174,28 @@ func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, i
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.

View File

@ -13,27 +13,62 @@ import (
"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 @@ func (check *checker) assignVar(lhs ast.Expr, x *operand) Type {
// 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

View File

@ -381,10 +381,8 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
// 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

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) {
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 @@ func (conf *Config) check(pkgPath string, fset *token.FileSet, files []*ast.File
// 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)
}
}

View File

@ -50,7 +50,7 @@ func (check *checker) conversion(x *operand, T Type) {
// 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)
}
}

View File

@ -89,7 +89,8 @@ func (check *checker) unary(x *operand, op token.Token) {
}
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 @@ func (check *checker) unary(x *operand, op token.Token) {
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 @@ func (check *checker) unary(x *operand, op token.Token) {
}
if !check.op(unaryOpPredicates, x, op) {
goto Error
x.mode = invalid
return
}
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)
// 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 @@ func isRepresentableConst(x exact.Value, conf *Config, as BasicKind, rounded *ex
case exact.String:
return as == String || as == UntypedString
case exact.Nil:
return as == UntypedNil || as == UnsafePointer
default:
unreachable()
}
@ -328,16 +327,24 @@ func isRepresentableConst(x exact.Value, conf *Config, as BasicKind, rounded *ex
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 @@ 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.
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 @@ func (check *checker) convertUntyped(x *operand, target Type) {
// 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 @@ func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token) {
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
}

View File

@ -233,17 +233,17 @@ func consolidateMultiples(list []embeddedType) []embeddedType {
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 @@ func MissingMethod(typ Type, T *Interface, static bool) (method *Func, wrongType
// 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 @@ func MissingMethod(typ Type, T *Interface, static bool) (method *Func, wrongType
// 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
}

View File

@ -266,3 +266,10 @@ type Builtin struct {
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" }

View File

@ -184,13 +184,13 @@ func (x *operand) setConst(tok token.Token, lit string) {
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.

View File

@ -49,6 +49,11 @@ func isString(typ Type) bool {
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 @@ func isConstType(typ Type) bool {
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 @@ func isComparable(typ Type) bool {
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
}

View File

@ -322,7 +322,7 @@ func make1() {
_ = 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)

View File

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

View File

@ -61,7 +61,7 @@ const (
_ 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 @@ const (
_ 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 @@ const (
_ 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 @@ const (
_ 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 @@ const (
_ 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 @@ const (
_ 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 @@ const (
_ 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 @@ const (
_ 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 @@ const (
_ 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 @@ const (
_ 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 @@ const (
_ 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)

View File

@ -12,7 +12,7 @@ func indexes() {
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 @@ func struct_literals() {
_ = 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 @@ func array_literals() {
_ = 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 @@ func array_literals() {
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 @@ func slice_literals() {
_ = 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 @@ func slice_literals() {
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}
}

View File

@ -67,10 +67,10 @@ func assignments1() {
// 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

View File

@ -97,6 +97,10 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool)
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 @@ func (check *checker) typ(e ast.Expr, def *Named, cycleOk bool) Type {
}
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 @@ func (check *checker) typOrNil(e ast.Expr) Type {
check.errorf(x.pos(), "%s used as type", &x)
case typexpr:
return x.typ
case constant:
case value:
if x.isNil() {
return nil
}

View File

@ -80,7 +80,6 @@ var predeclaredConsts = [...]struct {
{"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 defPredeclaredConsts() {
}
}
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 @@ func init() {
defPredeclaredTypes()
defPredeclaredConsts()
defPredeclaredNil()
defPredeclaredFuncs()
universeIota = Universe.Lookup("iota").(*Const)

View File

@ -637,9 +637,12 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value {
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 {

View File

@ -31,7 +31,7 @@ func intConst(i int64) *Const {
// 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 zeroConst(t types.Type) *Const {
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 @@ func (c *Const) Pos() token.Pos {
// 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

View File

@ -363,7 +363,7 @@ type Parameter struct {
//
// 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.
//