diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go index 3374f818991..025dcbf2c84 100644 --- a/src/go/types/assignments.go +++ b/src/go/types/assignments.go @@ -7,7 +7,6 @@ package types import ( - "errors" "go/ast" "go/token" ) @@ -46,27 +45,30 @@ func (check *Checker) assignment(x *operand, T Type, context string) { } target = Default(x.typ) } - if err := check.canConvertUntyped(x, target); err != nil { + newType, val, code := check.implicitTypeAndValue(x, target) + if code != 0 { msg := check.sprintf("cannot use %s as %s value in %s", x, target, context) - code := _IncompatibleAssign - var ierr Error - if errors.As(err, &ierr) { - // Preserve these inner errors, as they are informative. - switch ierr.go116code { - case _TruncatedFloat: - msg += " (truncated)" - code = ierr.go116code - case _NumericOverflow: - msg += " (overflows)" - code = ierr.go116code - } + switch code { + case _TruncatedFloat: + msg += " (truncated)" + case _NumericOverflow: + msg += " (overflows)" + default: + code = _IncompatibleAssign } check.error(x, code, msg) x.mode = invalid return } + if val != nil { + x.val = val + check.updateExprVal(x.expr, val) + } + if newType != x.typ { + x.typ = newType + check.updateExprType(x.expr, newType, false) + } } - // x.typ is typed // A generic (non-instantiated) function value cannot be assigned to a variable. if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 { diff --git a/src/go/types/expr.go b/src/go/types/expr.go index dcccd87c89b..1deda99aaff 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -338,17 +338,18 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c // representable checks that a constant operand is representable in the given // basic type. func (check *Checker) representable(x *operand, typ *Basic) { - if err := check.isRepresentable(x, typ); err != nil { + if v, code := check.representation(x, typ); code != 0 { + check.invalidConversion(code, x, typ) x.mode = invalid - check.err(err) + } else if v != nil { + x.val = v } } -func (check *Checker) isRepresentable(x *operand, typ *Basic) error { +func (check *Checker) representation(x *operand, typ *Basic) (constant.Value, errorCode) { assert(x.mode == constant_) - if !representableConst(x.val, check, typ, &x.val) { - var msg string - var code errorCode + v := x.val + if !representableConst(x.val, check, typ, &v) { if isNumeric(x.typ) && isNumeric(typ) { // numeric conversion : error msg // @@ -358,19 +359,25 @@ func (check *Checker) isRepresentable(x *operand, typ *Basic) error { // float -> float : overflows // if !isInteger(x.typ) && isInteger(typ) { - msg = "%s truncated to %s" - code = _TruncatedFloat + return nil, _TruncatedFloat } else { - msg = "%s overflows %s" - code = _NumericOverflow + return nil, _NumericOverflow } - } else { - msg = "cannot convert %s to %s" - code = _InvalidConstVal } - return check.newErrorf(x, code, false, msg, x, typ) + return nil, _InvalidConstVal } - return nil + return v, 0 +} + +func (check *Checker) invalidConversion(code errorCode, x *operand, target Type) { + msg := "cannot convert %s to %s" + switch code { + case _TruncatedFloat: + msg = "%s truncated to %s" + case _NumericOverflow: + msg = "%s overflows %s" + } + check.errorf(x, code, msg, x, target) } // updateExprType updates the type of x to typ and invokes itself @@ -506,16 +513,29 @@ func (check *Checker) updateExprVal(x ast.Expr, val constant.Value) { // convertUntyped attempts to set the type of an untyped value to the target type. func (check *Checker) convertUntyped(x *operand, target Type) { - if err := check.canConvertUntyped(x, target); err != nil { + newType, val, code := check.implicitTypeAndValue(x, target) + if code != 0 { + check.invalidConversion(code, x, target.Underlying()) x.mode = invalid - check.err(err) + return + } + if val != nil { + x.val = val + check.updateExprVal(x.expr, val) + } + if newType != x.typ { + x.typ = newType + check.updateExprType(x.expr, newType, false) } } -func (check *Checker) canConvertUntyped(x *operand, target Type) error { +// implicitTypeAndValue 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 a nil Type. +func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, constant.Value, errorCode) { target = expand(target) if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] { - return nil + return x.typ, nil, 0 } if isUntyped(target) { @@ -524,43 +544,23 @@ func (check *Checker) canConvertUntyped(x *operand, target Type) error { tkind := target.(*Basic).kind if isNumeric(x.typ) && isNumeric(target) { if xkind < tkind { - x.typ = target - check.updateExprType(x.expr, target, false) + return target, nil, 0 } } else if xkind != tkind { - return check.newErrorf(x, _InvalidUntypedConversion, false, "cannot convert %s to %s", x, target) + return nil, nil, _InvalidUntypedConversion } - return nil + return x.typ, nil, 0 } - 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, _InvalidUntypedConversion, false, "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) { + switch t := optype(target).(type) { case *Basic: - assert(x.mode != constant_) + if x.mode == constant_ { + v, code := check.representation(x, t) + if code != 0 { + return nil, nil, code + } + return target, v, code + } // Non-constant untyped values may appear as the // result of comparisons (untyped bool), intermediate // (delayed-checked) rhs operands of shifts, and as @@ -568,26 +568,39 @@ func (check *Checker) implicitType(x *operand, target Type) Type { switch x.typ.(*Basic).kind { case UntypedBool: if !isBoolean(target) { - return nil + return nil, nil, _InvalidUntypedConversion } case UntypedInt, UntypedRune, UntypedFloat, UntypedComplex: if !isNumeric(target) { - return nil + return nil, nil, _InvalidUntypedConversion } 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 + return nil, nil, _InvalidUntypedConversion } case UntypedNil: // Unsafe.Pointer is a basic type that includes nil. if !hasNil(target) { - return nil + return nil, nil, _InvalidUntypedConversion } + // TODO(rFindley) return UntypedNil here (golang.org/issues/13061). default: - return nil + return nil, nil, _InvalidUntypedConversion + } + case *Sum: + ok := t.is(func(t Type) bool { + target, _, _ := check.implicitTypeAndValue(x, t) + return target != nil + }) + if !ok { + return nil, nil, _InvalidUntypedConversion + } + // keep nil untyped (was bug #39755) + if x.isNil() { + return Typ[UntypedNil], nil, 0 } case *Interface: // Values must have concrete dynamic types. If the value is nil, @@ -595,24 +608,24 @@ func (check *Checker) implicitType(x *operand, target Type) Type { // need the dynamic type for argument checking of say, print // functions) if x.isNil() { - return Typ[UntypedNil] + return Typ[UntypedNil], nil, 0 } // cannot assign untyped values to non-empty interfaces check.completeInterface(token.NoPos, t) if !t.Empty() { - return nil + return nil, nil, _InvalidUntypedConversion } - return Default(x.typ) + return Default(x.typ), nil, 0 case *Pointer, *Signature, *Slice, *Map, *Chan: if !x.isNil() { - return nil + return nil, nil, _InvalidUntypedConversion } // Keep nil untyped - see comment for interfaces, above. - return Typ[UntypedNil] + return Typ[UntypedNil], nil, 0 default: - return nil + return nil, nil, _InvalidUntypedConversion } - return target + return target, nil, 0 } func (check *Checker) comparison(x, y *operand, op token.Token) { diff --git a/src/go/types/operand.go b/src/go/types/operand.go index 336babcadcb..8f9c9d09bfc 100644 --- a/src/go/types/operand.go +++ b/src/go/types/operand.go @@ -242,20 +242,15 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er // x is an untyped value representable by a value of type T. if isUntyped(Vu) { - // TODO(rFindley) synchronize this block of code with types2 - switch t := Tu.(type) { - case *Basic: - if x.mode == constant_ { - return representableConst(x.val, check, t, nil), _IncompatibleAssign - } - case *Sum: + if t, ok := Tu.(*Sum); ok { return t.is(func(t Type) bool { // TODO(gri) this could probably be more efficient ok, _ := x.assignableTo(check, t, reason) return ok }), _IncompatibleAssign } - return check.implicitType(x, Tu) != nil, _IncompatibleAssign + newType, _, _ := check.implicitTypeAndValue(x, Tu) + return newType != nil, _IncompatibleAssign } // Vu is typed diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go index 85e2b9a0cad..02332749671 100644 --- a/src/go/types/predicates.go +++ b/src/go/types/predicates.go @@ -74,12 +74,8 @@ func isUntyped(typ Type) bool { return !isTyped(typ) } -func isOrdered(typ Type) bool { return is(typ, IsOrdered) } - -func isConstType(typ Type) bool { - t := asBasic(typ) - return t != nil && t.info&IsConstType != 0 -} +func isOrdered(typ Type) bool { return is(typ, IsOrdered) } +func isConstType(typ Type) bool { return is(typ, IsConstType) } // IsInterface reports whether typ is an interface type. func IsInterface(typ Type) bool {