// nil Value.
func (tv TypeAndValue) IsValue() bool {
switch tv.mode {
- case constant_, variable, mapindex, value, commaok, commaerr:
+ case constant_, variable, mapindex, value, nilvalue, commaok, commaerr:
return true
}
return false
}
// IsNil reports whether the corresponding expression denotes the
-// predeclared value nil.
+// predeclared value nil. Depending on context, it may have been
+// given a type different from UntypedNil.
func (tv TypeAndValue) IsNil() bool {
- return tv.mode == value && tv.Type == Typ[UntypedNil]
+ return tv.mode == nilvalue
}
// Addressable reports whether the corresponding expression
{`package b4; var x interface{} = "foo"`, `"foo"`, `string`},
// uses of nil
- {`package n0; var _ *int = nil`, `nil`, `untyped nil`},
- {`package n1; var _ func() = nil`, `nil`, `untyped nil`},
- {`package n2; var _ []byte = nil`, `nil`, `untyped nil`},
- {`package n3; var _ map[int]int = nil`, `nil`, `untyped nil`},
- {`package n4; var _ chan int = nil`, `nil`, `untyped nil`},
- {`package n5; var _ interface{} = nil`, `nil`, `untyped nil`},
- {`package n6; import "unsafe"; var _ unsafe.Pointer = nil`, `nil`, `untyped nil`},
-
- {`package n10; var (x *int; _ = x == nil)`, `nil`, `untyped nil`},
- {`package n11; var (x func(); _ = x == nil)`, `nil`, `untyped nil`},
- {`package n12; var (x []byte; _ = x == nil)`, `nil`, `untyped nil`},
- {`package n13; var (x map[int]int; _ = x == nil)`, `nil`, `untyped nil`},
- {`package n14; var (x chan int; _ = x == nil)`, `nil`, `untyped nil`},
- {`package n15; var (x interface{}; _ = x == nil)`, `nil`, `untyped nil`},
- {`package n15; import "unsafe"; var (x unsafe.Pointer; _ = x == nil)`, `nil`, `untyped nil`},
-
- {`package n20; var _ = (*int)(nil)`, `nil`, `untyped nil`},
- {`package n21; var _ = (func())(nil)`, `nil`, `untyped nil`},
- {`package n22; var _ = ([]byte)(nil)`, `nil`, `untyped nil`},
- {`package n23; var _ = (map[int]int)(nil)`, `nil`, `untyped nil`},
- {`package n24; var _ = (chan int)(nil)`, `nil`, `untyped nil`},
- {`package n25; var _ = (interface{})(nil)`, `nil`, `untyped nil`},
- {`package n26; import "unsafe"; var _ = unsafe.Pointer(nil)`, `nil`, `untyped nil`},
-
- {`package n30; func f(*int) { f(nil) }`, `nil`, `untyped nil`},
- {`package n31; func f(func()) { f(nil) }`, `nil`, `untyped nil`},
- {`package n32; func f([]byte) { f(nil) }`, `nil`, `untyped nil`},
- {`package n33; func f(map[int]int) { f(nil) }`, `nil`, `untyped nil`},
- {`package n34; func f(chan int) { f(nil) }`, `nil`, `untyped nil`},
- {`package n35; func f(interface{}) { f(nil) }`, `nil`, `untyped nil`},
- {`package n35; import "unsafe"; func f(unsafe.Pointer) { f(nil) }`, `nil`, `untyped nil`},
+ {`package n0; var _ *int = nil`, `nil`, `*int`},
+ {`package n1; var _ func() = nil`, `nil`, `func()`},
+ {`package n2; var _ []byte = nil`, `nil`, `[]byte`},
+ {`package n3; var _ map[int]int = nil`, `nil`, `map[int]int`},
+ {`package n4; var _ chan int = nil`, `nil`, `chan int`},
+ {`package n5a; var _ interface{} = (*int)(nil)`, `nil`, `*int`},
+ {`package n5b; var _ interface{m()} = nil`, `nil`, `interface{m()}`},
+ {`package n6; import "unsafe"; var _ unsafe.Pointer = nil`, `nil`, `unsafe.Pointer`},
+
+ {`package n10; var (x *int; _ = x == nil)`, `nil`, `*int`},
+ {`package n11; var (x func(); _ = x == nil)`, `nil`, `func()`},
+ {`package n12; var (x []byte; _ = x == nil)`, `nil`, `[]byte`},
+ {`package n13; var (x map[int]int; _ = x == nil)`, `nil`, `map[int]int`},
+ {`package n14; var (x chan int; _ = x == nil)`, `nil`, `chan int`},
+ {`package n15a; var (x interface{}; _ = x == (*int)(nil))`, `nil`, `*int`},
+ {`package n15b; var (x interface{m()}; _ = x == nil)`, `nil`, `interface{m()}`},
+ {`package n15; import "unsafe"; var (x unsafe.Pointer; _ = x == nil)`, `nil`, `unsafe.Pointer`},
+
+ {`package n20; var _ = (*int)(nil)`, `nil`, `*int`},
+ {`package n21; var _ = (func())(nil)`, `nil`, `func()`},
+ {`package n22; var _ = ([]byte)(nil)`, `nil`, `[]byte`},
+ {`package n23; var _ = (map[int]int)(nil)`, `nil`, `map[int]int`},
+ {`package n24; var _ = (chan int)(nil)`, `nil`, `chan int`},
+ {`package n25a; var _ = (interface{})((*int)(nil))`, `nil`, `*int`},
+ {`package n25b; var _ = (interface{m()})(nil)`, `nil`, `interface{m()}`},
+ {`package n26; import "unsafe"; var _ = unsafe.Pointer(nil)`, `nil`, `unsafe.Pointer`},
+
+ {`package n30; func f(*int) { f(nil) }`, `nil`, `*int`},
+ {`package n31; func f(func()) { f(nil) }`, `nil`, `func()`},
+ {`package n32; func f([]byte) { f(nil) }`, `nil`, `[]byte`},
+ {`package n33; func f(map[int]int) { f(nil) }`, `nil`, `map[int]int`},
+ {`package n34; func f(chan int) { f(nil) }`, `nil`, `chan int`},
+ {`package n35a; func f(interface{}) { f((*int)(nil)) }`, `nil`, `*int`},
+ {`package n35b; func f(interface{m()}) { f(nil) }`, `nil`, `interface{m()}`},
+ {`package n35; import "unsafe"; func f(unsafe.Pointer) { f(nil) }`, `nil`, `unsafe.Pointer`},
// comma-ok expressions
{`package p0; var x interface{}; var _, _ = x.(int)`,
switch x.mode {
case invalid:
return // error reported before
- case constant_, variable, mapindex, value, commaok, commaerr:
+ case constant_, variable, mapindex, value, nilvalue, commaok, commaerr:
// ok
default:
// we may get here because of other problems (issue #39634, crash 12)
// 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 T == nil || IsInterface(T) {
- if T == nil && x.typ == Typ[UntypedNil] {
+ if x.isNil() {
+ if T == nil {
check.errorf(x, "use of untyped nil in %s", context)
x.mode = invalid
return
}
+ } else if T == nil || IsInterface(T) {
target = Default(x.typ)
}
check.convertUntyped(x, target)
return nil
case variable, mapindex:
// ok
+ case nilvalue:
+ check.errorf(&z, "cannot assign to nil") // default would print "untyped nil"
+ return nil
default:
if sel, ok := z.expr.(*syntax.SelectorExpr); ok {
var op operand
// given a type explicitly by a constant declaration or conversion,...".
if isUntyped(x.typ) {
final := T
- // - For conversions to interfaces, use the argument's default type.
+ // - For conversions to interfaces, except for untyped nil arguments,
+ // use the argument's default type.
// - For conversions of untyped constants to non-constant types, also
// use the default type (e.g., []byte("foo") should report string
// not []byte as type for the constant "foo").
- // - Keep untyped nil for untyped nil arguments.
// - For integer to string conversions, keep the argument type.
// (See also the TODO below.)
- if IsInterface(T) || constArg && !isConstType(T) || x.isNil() {
- final = Default(x.typ) // default type of untyped nil is untyped nil
+ if x.typ == Typ[UntypedNil] {
+ // ok
+ } else if IsInterface(T) || constArg && !isConstType(T) {
+ final = Default(x.typ)
} else if isInteger(x.typ) && isString(T) {
final = x.typ
}
}
for _, t := range unpack(types) {
- check.convertUntypedInternal(x, t)
+ x := *x // make a copy; convertUntypedInternal modifies x
+ check.convertUntypedInternal(&x, t)
if x.mode == invalid {
goto Error
}
}
- // keep nil untyped (was bug #39755)
- if x.isNil() {
- target = Typ[UntypedNil]
- }
x.typ = target
- check.updateExprType(x.expr, target, true) // UntypedNils are final
+ check.updateExprType(x.expr, target, true)
return
}
func (check *Checker) convertUntypedInternal(x *operand, target Type) {
assert(isTyped(target))
+ if x.isNil() {
+ assert(isUntyped(x.typ))
+ if hasNil(target) {
+ goto OK
+ }
+ goto Error
+ }
+
// typed target
switch t := optype(target.Under()).(type) {
case *Basic:
// 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.
+ // the value nil. Nil was handled upfront.
switch x.typ.(*Basic).kind {
case UntypedBool:
if !isBoolean(target) {
// 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
- }
- target = Typ[UntypedNil]
default:
goto Error
}
return x.mode != invalid
})
case *Interface:
- // Update operand types to the default type rather then
- // the target (interface) type: values must have concrete
- // dynamic types. If the value is nil, keep it untyped
- // (this is important for tools such as go vet which need
- // the dynamic type for argument checking of say, print
- // functions)
- if x.isNil() {
- target = Typ[UntypedNil]
- } else {
- // cannot assign untyped values to non-empty interfaces
- check.completeInterface(nopos, t)
- if !t.Empty() {
- goto Error
- }
- target = Default(x.typ)
- }
- case *Pointer, *Signature, *Slice, *Map, *Chan:
- if !x.isNil() {
- goto Error
- }
- // keep nil untyped - see comment for interfaces, above
- target = Typ[UntypedNil]
+ // Update operand types to the default type rather then the target
+ // (interface) type: values must have concrete dynamic types.
+ // Untyped nil was handled upfront.
+ check.completeInterface(nopos, t)
+ if !t.Empty() {
+ goto Error // cannot assign untyped values to non-empty interfaces
+ }
+ target = Default(x.typ)
default:
goto Error
}
+OK:
x.typ = target
- check.updateExprType(x.expr, target, true) // UntypedNils are final
+ check.updateExprType(x.expr, target, true)
return
Error:
}
case *syntax.Name:
if x.Value == "nil" {
- want = Typ[UntypedNil]
+ want = NewInterfaceType(nil, nil) // interface{}
}
}
if want != nil && !Identical(tv.Type, want) {
variable // operand is an addressable variable
mapindex // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)
value // operand is a computed value
+ nilvalue // operand is the nil value
commaok // like value, but operand may be used in a comma,ok expression
commaerr // like commaok, but second value is error, not boolean
cgofunc // operand is a cgo function
variable: "variable",
mapindex: "map index expression",
value: "value",
+ nilvalue: "nil",
commaok: "comma, ok expression",
commaerr: "comma, error expression",
cgofunc: "cgo function",
// value <expr> (<untyped kind> <mode> )
// value <expr> ( <mode> of type <typ>)
//
+// nilvalue untyped nil
+// nilvalue nil ( of type <typ>)
+//
// commaok <expr> (<untyped kind> <mode> )
// commaok <expr> ( <mode> of type <typ>)
//
// cgofunc <expr> ( <mode> of type <typ>)
//
func operandString(x *operand, qf Qualifier) string {
+ // special-case nil
+ if x.mode == nilvalue {
+ switch x.typ {
+ case nil, Typ[Invalid]:
+ return "nil (with invalid type)"
+ case Typ[UntypedNil]:
+ return "untyped nil"
+ default:
+ return fmt.Sprintf("nil (of type %s)", TypeString(x.typ, qf))
+ }
+ }
+
var buf bytes.Buffer
var expr string
x.val = val
}
-// isNil reports whether x is the nil value.
-func (x *operand) isNil() bool {
- return x.mode == value && x.typ == Typ[UntypedNil]
-}
+// isNil reports whether x is a typed or the untyped nil value.
+func (x *operand) isNil() bool { return x.mode == nilvalue }
// TODO(gri) The functions operand.assignableTo, checker.convertUntyped,
// checker.representable, and checker.assignment are
// test cases for issue 5800
var (
- _ int = nil /* ERROR "untyped nil value" */
- _ [10]int = nil /* ERROR "untyped nil value" */
+ _ int = nil /* ERROR "cannot convert untyped nil" */
+ _ [10]int = nil /* ERROR "cannot convert untyped nil" */
_ []byte = nil
- _ struct{} = nil /* ERROR "untyped nil value" */
+ _ struct{} = nil /* ERROR "cannot convert untyped nil" */
_ func() = nil
_ map[int]string = nil
_ chan int = nil
x.mode = builtin
case *Nil:
- x.mode = value
+ x.mode = nilvalue
default:
unreachable()
case typexpr:
check.instantiatedOperand(&x)
return x.typ
- case value:
- if x.isNil() {
- return nil
- }
- fallthrough
+ case nilvalue:
+ return nil
default:
check.errorf(&x, "%s is not a type", &x)
}
package p
func f() uintptr {
- return nil // ERROR "cannot use nil as type uintptr in return argument|incompatible type|cannot convert nil"
+ return nil // ERROR "cannot use nil as type uintptr in return argument|incompatible type|cannot convert untyped nil"
}