diff options
author | Rob Findley <rfindley@google.com> | 2020-07-12 22:36:34 -0400 |
---|---|---|
committer | Robert Findley <rfindley@google.com> | 2020-08-28 16:45:21 +0000 |
commit | 42e09dc1ba1e820af44b2cbd4db0d60abb5559a2 (patch) | |
tree | 14142f321ddb8ad835fe457bf1eb5fbbd7dfc329 /src/go/types/expr.go | |
parent | ae7b6a3b779c4d6de96f59efbfed0b899c3ff6df (diff) | |
download | go-42e09dc1ba1e820af44b2cbd4db0d60abb5559a2.tar.gz go-42e09dc1ba1e820af44b2cbd4db0d60abb5559a2.zip |
go/types: factor out usage of implicit type
There was some duplication of logic interpreting the implicit type of
an operand in assignableTo and convertUntyped. Factor out this logic to
a new 'implicitType' function, which returns the implicit type of an
untyped operand when used in a context where a target type is expected.
I believe this resolves some comments about code duplication. There is
other similar code in assignable, assignableTo, and convertUntypes, but
I found it to to be sufficiently semantically distinct to not warrant
factoring out.
Change-Id: I199298a2e58fcf05344318fca0226b460c57867d
Reviewed-on: https://go-review.googlesource.com/c/go/+/242084
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Diffstat (limited to 'src/go/types/expr.go')
-rw-r--r-- | src/go/types/expr.go | 125 |
1 files changed, 67 insertions, 58 deletions
diff --git a/src/go/types/expr.go b/src/go/types/expr.go index 8503a521f6..94d98f0fbb 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -506,8 +506,6 @@ func (check *Checker) canConvertUntyped(x *operand, target Type) error { if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] { return nil } - // TODO(gri) Sloppy code - clean up. This function is central - // to assignment and expression checking. if isUntyped(target) { // both x and target are untyped @@ -519,80 +517,91 @@ func (check *Checker) canConvertUntyped(x *operand, target Type) error { check.updateExprType(x.expr, target, false) } } else if xkind != tkind { - goto Error + return check.newErrorf(x.pos(), "cannot convert %s to %s", x, target) } return nil } - // typed target + if t, ok := target.Underlying().(*Basic); ok && x.mode == constant_ { + if err := check.isRepresentable(x, t); err != nil { + return err + } + // Expression value may have been rounded - update if needed. + check.updateExprVal(x.expr, x.val) + } else { + newTarget := check.implicitType(x, target) + if newTarget == nil { + return check.newErrorf(x.pos(), "cannot convert %s to %s", x, target) + } + target = newTarget + } + x.typ = target + // Even though implicitType can return UntypedNil, this value is final: the + // predeclared identifier nil has no type. + check.updateExprType(x.expr, target, true) + return nil +} + +// implicitType returns the implicit type of x when used in a context where the +// target type is expected. If no such implicit conversion is possible, it +// returns nil. +func (check *Checker) implicitType(x *operand, target Type) Type { + assert(isUntyped(x.typ)) switch t := target.Underlying().(type) { case *Basic: - if x.mode == constant_ { - if err := check.isRepresentable(x, t); err != nil { - return err + assert(x.mode != constant_) + // 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) { + return nil } - // expression value may have been rounded - update if needed - check.updateExprVal(x.expr, x.val) - } 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 UntypedInt, UntypedRune, UntypedFloat, UntypedComplex: + if !isNumeric(target) { + return nil + } + case UntypedString: + // Non-constant untyped string values are not permitted by the spec and + // should not occur during normal typechecking passes, but this path is + // reachable via the AssignableTo API. + if !isString(target) { + return nil } + case UntypedNil: + // Unsafe.Pointer is a basic type that includes nil. + if !hasNil(target) { + return nil + } + default: + return nil } 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 + // 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(t) - if !t.Empty() { - goto Error - } - target = Default(x.typ) + return Typ[UntypedNil] + } + // cannot assign untyped values to non-empty interfaces + check.completeInterface(t) + if !t.Empty() { + return nil } + return Default(x.typ) case *Pointer, *Signature, *Slice, *Map, *Chan: if !x.isNil() { - goto Error + return nil } - // keep nil untyped - see comment for interfaces, above - target = Typ[UntypedNil] + // Keep nil untyped - see comment for interfaces, above. + return Typ[UntypedNil] default: - goto Error + return nil } - - x.typ = target - check.updateExprType(x.expr, target, true) // UntypedNils are final - return nil - -Error: - return check.newErrorf(x.pos(), "cannot convert %s to %s", x, target) + return target } func (check *Checker) comparison(x, y *operand, op token.Token) { |