mirror of
https://github.com/golang/go
synced 2024-11-18 14:04:45 -07: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:
parent
9c8d9fe736
commit
f50f6c858a
2
cmd/vet/testdata/print.go
vendored
2
cmd/vet/testdata/print.go
vendored
@ -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"
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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":
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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" }
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
2
go/types/testdata/builtins.src
vendored
2
go/types/testdata/builtins.src
vendored
@ -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)
|
||||
|
2
go/types/testdata/const0.src
vendored
2
go/types/testdata/const0.src
vendored
@ -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{}
|
||||
)
|
||||
|
||||
|
22
go/types/testdata/const1.src
vendored
22
go/types/testdata/const1.src
vendored
@ -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)
|
||||
|
12
go/types/testdata/expr3.src
vendored
12
go/types/testdata/expr3.src
vendored
@ -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}
|
||||
|
||||
}
|
||||
|
6
go/types/testdata/stmt0.src
vendored
6
go/types/testdata/stmt0.src
vendored
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
//
|
||||
|
Loading…
Reference in New Issue
Block a user