aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/types2/expr.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/types2/expr.go')
-rw-r--r--src/cmd/compile/internal/types2/expr.go128
1 files changed, 86 insertions, 42 deletions
diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go
index a1a626fb33..584b8ee6a0 100644
--- a/src/cmd/compile/internal/types2/expr.go
+++ b/src/cmd/compile/internal/types2/expr.go
@@ -59,11 +59,16 @@ the type (and constant value, if any) is recorded via Info.Types, if present.
type opPredicates map[syntax.Operator]func(Type) bool
-var unaryOpPredicates = opPredicates{
- syntax.Add: isNumeric,
- syntax.Sub: isNumeric,
- syntax.Xor: isInteger,
- syntax.Not: isBoolean,
+var unaryOpPredicates opPredicates
+
+func init() {
+ // Setting unaryOpPredicates in init avoids declaration cycles.
+ unaryOpPredicates = opPredicates{
+ syntax.Add: isNumeric,
+ syntax.Sub: isNumeric,
+ syntax.Xor: isInteger,
+ syntax.Not: isBoolean,
+ }
}
func (check *Checker) op(m opPredicates, x *operand, op syntax.Operator) bool {
@@ -96,9 +101,7 @@ func (check *Checker) overflow(x *operand) {
what := "" // operator description, if any
if op, _ := x.expr.(*syntax.Operation); op != nil {
pos = op.Pos()
- if int(op.Op) < len(op2str) {
- what = op2str[op.Op]
- }
+ what = opName(op)
}
if x.val.Kind() == constant.Unknown {
@@ -112,20 +115,42 @@ func (check *Checker) overflow(x *operand) {
// Typed constants must be representable in
// their type after each constant operation.
if isTyped(x.typ) {
- check.representable(x, x.typ.Basic())
+ check.representable(x, asBasic(x.typ))
return
}
// Untyped integer values must not grow arbitrarily.
- const limit = 4 * 512 // 512 is the constant precision - we need more because old tests had no limits
- if x.val.Kind() == constant.Int && constant.BitLen(x.val) > limit {
+ const prec = 512 // 512 is the constant precision
+ if x.val.Kind() == constant.Int && constant.BitLen(x.val) > prec {
check.errorf(pos, "constant %s overflow", what)
x.val = constant.MakeUnknown()
}
}
-// This is only used for operations that may cause overflow.
-var op2str = [...]string{
+// opName returns the name of an operation, or the empty string.
+// For now, only operations that might overflow are handled.
+// TODO(gri) Expand this to a general mechanism giving names to
+// nodes?
+func opName(e *syntax.Operation) string {
+ op := int(e.Op)
+ if e.Y == nil {
+ if op < len(op2str1) {
+ return op2str1[op]
+ }
+ } else {
+ if op < len(op2str2) {
+ return op2str2[op]
+ }
+ }
+ return ""
+}
+
+// Entries must be "" or end with a space.
+var op2str1 = [...]string{
+ syntax.Xor: "bitwise complement",
+}
+
+var op2str2 = [...]string{
syntax.Add: "addition",
syntax.Sub: "subtraction",
syntax.Xor: "bitwise XOR",
@@ -153,7 +178,7 @@ func (check *Checker) unary(x *operand, e *syntax.Operation) {
return
case syntax.Recv:
- typ := x.typ.Chan()
+ typ := asChan(x.typ)
if typ == nil {
check.invalidOpf(x, "cannot receive from non-channel %s", x)
x.mode = invalid
@@ -523,7 +548,7 @@ func (check *Checker) updateExprType(x syntax.Expr, typ Type, final bool) {
// If the new type is not final and still untyped, just
// update the recorded type.
if !final && isUntyped(typ) {
- old.typ = typ.Basic()
+ old.typ = asBasic(typ)
check.untyped[x] = old
return
}
@@ -595,7 +620,7 @@ func (check *Checker) convertUntyped(x *operand, target Type) {
// TODO(gri) We should not need this because we have the code
// for Sum types in convertUntypedInternal. But at least one
// test fails. Investigate.
- if t := target.TypeParam(); t != nil {
+ if t := asTypeParam(target); t != nil {
types := t.Bound().allTypes
if types == nil {
goto Error
@@ -636,7 +661,7 @@ func (check *Checker) convertUntypedInternal(x *operand, target Type) {
}
// typed target
- switch t := optype(target.Under()).(type) {
+ switch t := optype(target).(type) {
case *Basic:
if x.mode == constant_ {
check.representable(x, t)
@@ -796,12 +821,25 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) {
check.invalidOpf(y, "shift count %s must be integer", y)
x.mode = invalid
return
+ } else if !isUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) {
+ check.invalidOpf(y, "signed shift count %s requires go1.13 or later", y)
+ x.mode = invalid
+ return
}
if x.mode == constant_ {
if y.mode == constant_ {
+ // if either x or y has an unknown value, the result is unknown
+ if x.val.Kind() == constant.Unknown || y.val.Kind() == constant.Unknown {
+ x.val = constant.MakeUnknown()
+ // ensure the correct type - see comment below
+ if !isInteger(x.typ) {
+ x.typ = Typ[UntypedInt]
+ }
+ return
+ }
// rhs must be within reasonable bounds in constant shifts
- const shiftBound = 1023 - 1 + 52 // so we can express smallestFloat64
+ const shiftBound = 1023 - 1 + 52 // so we can express smallestFloat64 (see issue #44057)
s, ok := constant.Uint64Val(y.val)
if !ok || s > shiftBound {
check.invalidOpf(y, "invalid shift count %s", y)
@@ -863,20 +901,25 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) {
x.mode = value
}
-var binaryOpPredicates = opPredicates{
- syntax.Add: isNumericOrString,
- syntax.Sub: isNumeric,
- syntax.Mul: isNumeric,
- syntax.Div: isNumeric,
- syntax.Rem: isInteger,
+var binaryOpPredicates opPredicates
- syntax.And: isInteger,
- syntax.Or: isInteger,
- syntax.Xor: isInteger,
- syntax.AndNot: isInteger,
+func init() {
+ // Setting binaryOpPredicates in init avoids declaration cycles.
+ binaryOpPredicates = opPredicates{
+ syntax.Add: isNumericOrString,
+ syntax.Sub: isNumeric,
+ syntax.Mul: isNumeric,
+ syntax.Div: isNumeric,
+ syntax.Rem: isInteger,
- syntax.AndAnd: isBoolean,
- syntax.OrOr: isBoolean,
+ syntax.And: isInteger,
+ syntax.Or: isInteger,
+ syntax.Xor: isInteger,
+ syntax.AndNot: isInteger,
+
+ syntax.AndAnd: isBoolean,
+ syntax.OrOr: isBoolean,
+ }
}
// If e != nil, it must be the binary expression; it may be nil for non-constant expressions
@@ -1156,6 +1199,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
}
switch e.Kind {
case syntax.IntLit, syntax.FloatLit, syntax.ImagLit:
+ check.langCompat(e)
// The max. mantissa precision for untyped numeric values
// is 512 bits, or 4048 bits for each of the two integer
// parts of a fraction for floating-point numbers that are
@@ -1224,7 +1268,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
case hint != nil:
// no composite literal type present - use hint (element type of enclosing type)
typ = hint
- base, _ = deref(typ.Under()) // *T implies &T{}
+ base, _ = deref(under(typ)) // *T implies &T{}
default:
// TODO(gri) provide better error messages depending on context
@@ -1232,7 +1276,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
goto Error
}
- switch utyp := optype(base.Under()).(type) {
+ switch utyp := optype(base).(type) {
case *Struct:
if len(e.ElemList) == 0 {
break
@@ -1362,7 +1406,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
duplicate := false
// if the key is of interface type, the type is also significant when checking for duplicates
xkey := keyVal(x.val)
- if utyp.key.Interface() != nil {
+ if asInterface(utyp.key) != nil {
for _, vtyp := range visited[xkey] {
if check.identical(vtyp, x.typ) {
duplicate = true
@@ -1431,7 +1475,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
}
if x.mode == value {
- if sig := x.typ.Signature(); sig != nil && len(sig.tparams) > 0 {
+ if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 {
// function instantiation
check.funcInst(x, e)
return expression
@@ -1441,7 +1485,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
// ordinary index expression
valid := false
length := int64(-1) // valid if >= 0
- switch typ := optype(x.typ.Under()).(type) {
+ switch typ := optype(x.typ).(type) {
case *Basic:
if isString(typ) {
valid = true
@@ -1464,7 +1508,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
x.typ = typ.elem
case *Pointer:
- if typ := typ.base.Array(); typ != nil {
+ if typ := asArray(typ.base); typ != nil {
valid = true
length = typ.len
x.mode = variable
@@ -1494,7 +1538,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
nmaps := 0 // number of map types in sum type
if typ.is(func(t Type) bool {
var e Type
- switch t := t.Under().(type) {
+ switch t := under(t).(type) {
case *Basic:
if isString(t) {
e = universeByte
@@ -1502,7 +1546,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
case *Array:
e = t.elem
case *Pointer:
- if t := t.base.Array(); t != nil {
+ if t := asArray(t.base); t != nil {
e = t.elem
}
case *Slice:
@@ -1603,7 +1647,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
valid := false
length := int64(-1) // valid if >= 0
- switch typ := optype(x.typ.Under()).(type) {
+ switch typ := optype(x.typ).(type) {
case *Basic:
if isString(typ) {
if e.Full {
@@ -1631,7 +1675,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
x.typ = &Slice{elem: typ.elem}
case *Pointer:
- if typ := typ.base.Array(); typ != nil {
+ if typ := asArray(typ.base); typ != nil {
valid = true
length = typ.len
x.typ = &Slice{elem: typ.elem}
@@ -1704,7 +1748,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
if x.mode == invalid {
goto Error
}
- xtyp, _ := x.typ.Under().(*Interface)
+ xtyp, _ := under(x.typ).(*Interface)
if xtyp == nil {
check.errorf(x, "%s is not an interface type", x)
goto Error
@@ -1763,7 +1807,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
case typexpr:
x.typ = &Pointer{base: x.typ}
default:
- if typ := x.typ.Pointer(); typ != nil {
+ if typ := asPointer(x.typ); typ != nil {
x.mode = variable
x.typ = typ.base
} else {