diff options
Diffstat (limited to 'src/cmd/compile/internal/gc/const.go')
-rw-r--r-- | src/cmd/compile/internal/gc/const.go | 1349 |
1 files changed, 472 insertions, 877 deletions
diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go index b92c8d66b5..4beb85245f 100644 --- a/src/cmd/compile/internal/gc/const.go +++ b/src/cmd/compile/internal/gc/const.go @@ -5,217 +5,87 @@ package gc import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" "cmd/compile/internal/types" "cmd/internal/src" "fmt" + "go/constant" + "go/token" + "math" "math/big" "strings" + "unicode" ) -// Ctype describes the constant kind of an "ideal" (untyped) constant. -type Ctype uint8 - const ( - CTxxx Ctype = iota - - CTINT - CTRUNE - CTFLT - CTCPLX - CTSTR - CTBOOL - CTNIL + // Maximum size in bits for big.Ints before signalling + // overflow and also mantissa precision for big.Floats. + Mpprec = 512 ) -type Val struct { - // U contains one of: - // bool bool when Ctype() == CTBOOL - // *Mpint int when Ctype() == CTINT, rune when Ctype() == CTRUNE - // *Mpflt float when Ctype() == CTFLT - // *Mpcplx pair of floats when Ctype() == CTCPLX - // string string when Ctype() == CTSTR - // *Nilval when Ctype() == CTNIL - U interface{} -} - -func (v Val) Ctype() Ctype { - switch x := v.U.(type) { - default: - Fatalf("unexpected Ctype for %T", v.U) - panic("unreachable") - case nil: - return CTxxx - case *NilVal: - return CTNIL - case bool: - return CTBOOL - case *Mpint: - if x.Rune { - return CTRUNE - } - return CTINT - case *Mpflt: - return CTFLT - case *Mpcplx: - return CTCPLX - case string: - return CTSTR - } -} - -func eqval(a, b Val) bool { - if a.Ctype() != b.Ctype() { - return false - } - switch x := a.U.(type) { - default: - Fatalf("unexpected Ctype for %T", a.U) - panic("unreachable") - case *NilVal: - return true - case bool: - y := b.U.(bool) - return x == y - case *Mpint: - y := b.U.(*Mpint) - return x.Cmp(y) == 0 - case *Mpflt: - y := b.U.(*Mpflt) - return x.Cmp(y) == 0 - case *Mpcplx: - y := b.U.(*Mpcplx) - return x.Real.Cmp(&y.Real) == 0 && x.Imag.Cmp(&y.Imag) == 0 - case string: - y := b.U.(string) - return x == y - } -} - -// Interface returns the constant value stored in v as an interface{}. -// It returns int64s for ints and runes, float64s for floats, -// complex128s for complex values, and nil for constant nils. -func (v Val) Interface() interface{} { - switch x := v.U.(type) { +func bigFloatVal(v constant.Value) *big.Float { + f := new(big.Float) + f.SetPrec(Mpprec) + switch u := constant.Val(v).(type) { + case int64: + f.SetInt64(u) + case *big.Int: + f.SetInt(u) + case *big.Float: + f.Set(u) + case *big.Rat: + f.SetRat(u) default: - Fatalf("unexpected Interface for %T", v.U) - panic("unreachable") - case *NilVal: - return nil - case bool, string: - return x - case *Mpint: - return x.Int64() - case *Mpflt: - return x.Float64() - case *Mpcplx: - return complex(x.Real.Float64(), x.Imag.Float64()) + base.Fatalf("unexpected: %v", u) } + return f } -type NilVal struct{} - -// Int64Val returns n as an int64. -// n must be an integer or rune constant. -func (n *Node) Int64Val() int64 { - if !Isconst(n, CTINT) { - Fatalf("Int64Val(%v)", n) - } - return n.Val().U.(*Mpint).Int64() -} - -// CanInt64 reports whether it is safe to call Int64Val() on n. -func (n *Node) CanInt64() bool { - if !Isconst(n, CTINT) { - return false +func roundFloat(v constant.Value, sz int64) constant.Value { + switch sz { + case 4: + f, _ := constant.Float32Val(v) + return makeFloat64(float64(f)) + case 8: + f, _ := constant.Float64Val(v) + return makeFloat64(f) } - - // if the value inside n cannot be represented as an int64, the - // return value of Int64 is undefined - return n.Val().U.(*Mpint).CmpInt64(n.Int64Val()) == 0 -} - -// BoolVal returns n as a bool. -// n must be a boolean constant. -func (n *Node) BoolVal() bool { - if !Isconst(n, CTBOOL) { - Fatalf("BoolVal(%v)", n) - } - return n.Val().U.(bool) -} - -// StringVal returns the value of a literal string Node as a string. -// n must be a string constant. -func (n *Node) StringVal() string { - if !Isconst(n, CTSTR) { - Fatalf("StringVal(%v)", n) - } - return n.Val().U.(string) + base.Fatalf("unexpected size: %v", sz) + panic("unreachable") } // truncate float literal fv to 32-bit or 64-bit precision // according to type; return truncated value. -func truncfltlit(oldv *Mpflt, t *types.Type) *Mpflt { - if t == nil { - return oldv - } - - if overflow(Val{oldv}, t) { +func truncfltlit(v constant.Value, t *types.Type) constant.Value { + if t.IsUntyped() || overflow(v, t) { // If there was overflow, simply continuing would set the // value to Inf which in turn would lead to spurious follow-on // errors. Avoid this by returning the existing value. - return oldv - } - - fv := newMpflt() - - // convert large precision literal floating - // into limited precision (float64 or float32) - switch t.Etype { - case types.TFLOAT32: - fv.SetFloat64(oldv.Float32()) - case types.TFLOAT64: - fv.SetFloat64(oldv.Float64()) - default: - Fatalf("truncfltlit: unexpected Etype %v", t.Etype) + return v } - return fv + return roundFloat(v, t.Size()) } // truncate Real and Imag parts of Mpcplx to 32-bit or 64-bit // precision, according to type; return truncated value. In case of -// overflow, calls yyerror but does not truncate the input value. -func trunccmplxlit(oldv *Mpcplx, t *types.Type) *Mpcplx { - if t == nil { - return oldv - } - - if overflow(Val{oldv}, t) { +// overflow, calls Errorf but does not truncate the input value. +func trunccmplxlit(v constant.Value, t *types.Type) constant.Value { + if t.IsUntyped() || overflow(v, t) { // If there was overflow, simply continuing would set the // value to Inf which in turn would lead to spurious follow-on // errors. Avoid this by returning the existing value. - return oldv - } - - cv := newMpcmplx() - - switch t.Etype { - case types.TCOMPLEX64: - cv.Real.SetFloat64(oldv.Real.Float32()) - cv.Imag.SetFloat64(oldv.Imag.Float32()) - case types.TCOMPLEX128: - cv.Real.SetFloat64(oldv.Real.Float64()) - cv.Imag.SetFloat64(oldv.Imag.Float64()) - default: - Fatalf("trunccplxlit: unexpected Etype %v", t.Etype) + return v } - return cv + fsz := t.Size() / 2 + return makeComplex(roundFloat(constant.Real(v), fsz), roundFloat(constant.Imag(v), fsz)) } // TODO(mdempsky): Replace these with better APIs. -func convlit(n *Node, t *types.Type) *Node { return convlit1(n, t, false, nil) } -func defaultlit(n *Node, t *types.Type) *Node { return convlit1(n, t, false, nil) } +func convlit(n ir.Node, t *types.Type) ir.Node { return convlit1(n, t, false, nil) } +func defaultlit(n ir.Node, t *types.Type) ir.Node { return convlit1(n, t, false, nil) } // convlit1 converts an untyped expression n to type t. If n already // has a type, convlit1 has no effect. @@ -228,35 +98,38 @@ func defaultlit(n *Node, t *types.Type) *Node { return convlit1(n, t, false, nil // // If there's an error converting n to t, context is used in the error // message. -func convlit1(n *Node, t *types.Type, explicit bool, context func() string) *Node { +func convlit1(n ir.Node, t *types.Type, explicit bool, context func() string) ir.Node { if explicit && t == nil { - Fatalf("explicit conversion missing type") + base.Fatalf("explicit conversion missing type") } if t != nil && t.IsUntyped() { - Fatalf("bad conversion to untyped: %v", t) + base.Fatalf("bad conversion to untyped: %v", t) } - if n == nil || n.Type == nil { + if n == nil || n.Type() == nil { // Allow sloppy callers. return n } - if !n.Type.IsUntyped() { + if !n.Type().IsUntyped() { // Already typed; nothing to do. return n } - if n.Op == OLITERAL { + if n.Op() == ir.OLITERAL || n.Op() == ir.ONIL { // Can't always set n.Type directly on OLITERAL nodes. // See discussion on CL 20813. - n = n.rawcopy() + n = n.RawCopy() } // Nil is technically not a constant, so handle it specially. - if n.Type.Etype == TNIL { + if n.Type().Etype == types.TNIL { + if n.Op() != ir.ONIL { + base.Fatalf("unexpected op: %v (%v)", n, n.Op()) + } if t == nil { - yyerror("use of untyped nil") + base.Errorf("use of untyped nil") n.SetDiag(true) - n.Type = nil + n.SetType(nil) return n } @@ -265,77 +138,77 @@ func convlit1(n *Node, t *types.Type, explicit bool, context func() string) *Nod return n } - n.Type = t + n.SetType(t) return n } - if t == nil || !okforconst[t.Etype] { - t = defaultType(n.Type) + if t == nil || !ir.OKForConst[t.Etype] { + t = defaultType(n.Type()) } - switch n.Op { + switch n.Op() { default: - Fatalf("unexpected untyped expression: %v", n) + base.Fatalf("unexpected untyped expression: %v", n) - case OLITERAL: + case ir.OLITERAL: v := convertVal(n.Val(), t, explicit) - if v.U == nil { + if v.Kind() == constant.Unknown { break } + n.SetType(t) n.SetVal(v) - n.Type = t return n - case OPLUS, ONEG, OBITNOT, ONOT, OREAL, OIMAG: - ot := operandType(n.Op, t) + case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.OREAL, ir.OIMAG: + ot := operandType(n.Op(), t) if ot == nil { n = defaultlit(n, nil) break } - n.Left = convlit(n.Left, ot) - if n.Left.Type == nil { - n.Type = nil + n.SetLeft(convlit(n.Left(), ot)) + if n.Left().Type() == nil { + n.SetType(nil) return n } - n.Type = t + n.SetType(t) return n - case OADD, OSUB, OMUL, ODIV, OMOD, OOR, OXOR, OAND, OANDNOT, OOROR, OANDAND, OCOMPLEX: - ot := operandType(n.Op, t) + case ir.OADD, ir.OSUB, ir.OMUL, ir.ODIV, ir.OMOD, ir.OOR, ir.OXOR, ir.OAND, ir.OANDNOT, ir.OOROR, ir.OANDAND, ir.OCOMPLEX: + ot := operandType(n.Op(), t) if ot == nil { n = defaultlit(n, nil) break } - n.Left = convlit(n.Left, ot) - n.Right = convlit(n.Right, ot) - if n.Left.Type == nil || n.Right.Type == nil { - n.Type = nil + n.SetLeft(convlit(n.Left(), ot)) + n.SetRight(convlit(n.Right(), ot)) + if n.Left().Type() == nil || n.Right().Type() == nil { + n.SetType(nil) return n } - if !types.Identical(n.Left.Type, n.Right.Type) { - yyerror("invalid operation: %v (mismatched types %v and %v)", n, n.Left.Type, n.Right.Type) - n.Type = nil + if !types.Identical(n.Left().Type(), n.Right().Type()) { + base.Errorf("invalid operation: %v (mismatched types %v and %v)", n, n.Left().Type(), n.Right().Type()) + n.SetType(nil) return n } - n.Type = t + n.SetType(t) return n - case OEQ, ONE, OLT, OLE, OGT, OGE: + case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE: if !t.IsBoolean() { break } - n.Type = t + n.SetType(t) return n - case OLSH, ORSH: - n.Left = convlit1(n.Left, t, explicit, nil) - n.Type = n.Left.Type - if n.Type != nil && !n.Type.IsInteger() { - yyerror("invalid operation: %v (shift of type %v)", n, n.Type) - n.Type = nil + case ir.OLSH, ir.ORSH: + n.SetLeft(convlit1(n.Left(), t, explicit, nil)) + n.SetType(n.Left().Type()) + if n.Type() != nil && !n.Type().IsInteger() { + base.Errorf("invalid operation: %v (shift of type %v)", n, n.Type()) + n.SetType(nil) } return n } @@ -343,26 +216,26 @@ func convlit1(n *Node, t *types.Type, explicit bool, context func() string) *Nod if !n.Diag() { if !t.Broke() { if explicit { - yyerror("cannot convert %L to type %v", n, t) + base.Errorf("cannot convert %L to type %v", n, t) } else if context != nil { - yyerror("cannot use %L as type %v in %s", n, t, context()) + base.Errorf("cannot use %L as type %v in %s", n, t, context()) } else { - yyerror("cannot use %L as type %v", n, t) + base.Errorf("cannot use %L as type %v", n, t) } } n.SetDiag(true) } - n.Type = nil + n.SetType(nil) return n } -func operandType(op Op, t *types.Type) *types.Type { +func operandType(op ir.Op, t *types.Type) *types.Type { switch op { - case OCOMPLEX: + case ir.OCOMPLEX: if t.IsComplex() { return floatForComplex(t) } - case OREAL, OIMAG: + case ir.OREAL, ir.OIMAG: if t.IsFloat() { return complexForFloat(t) } @@ -379,24 +252,24 @@ func operandType(op Op, t *types.Type) *types.Type { // // If explicit is true, then conversions from integer to string are // also allowed. -func convertVal(v Val, t *types.Type, explicit bool) Val { - switch ct := v.Ctype(); ct { - case CTBOOL: +func convertVal(v constant.Value, t *types.Type, explicit bool) constant.Value { + switch ct := v.Kind(); ct { + case constant.Bool: if t.IsBoolean() { return v } - case CTSTR: + case constant.String: if t.IsString() { return v } - case CTINT, CTRUNE: + case constant.Int: if explicit && t.IsString() { return tostr(v) } fallthrough - case CTFLT, CTCPLX: + case constant.Float, constant.Complex: switch { case t.IsInteger(): v = toint(v) @@ -404,646 +277,383 @@ func convertVal(v Val, t *types.Type, explicit bool) Val { return v case t.IsFloat(): v = toflt(v) - v = Val{truncfltlit(v.U.(*Mpflt), t)} + v = truncfltlit(v, t) return v case t.IsComplex(): v = tocplx(v) - v = Val{trunccmplxlit(v.U.(*Mpcplx), t)} + v = trunccmplxlit(v, t) return v } } - return Val{} + return constant.MakeUnknown() } -func tocplx(v Val) Val { - switch u := v.U.(type) { - case *Mpint: - c := newMpcmplx() - c.Real.SetInt(u) - c.Imag.SetFloat64(0.0) - v.U = c - - case *Mpflt: - c := newMpcmplx() - c.Real.Set(u) - c.Imag.SetFloat64(0.0) - v.U = c - } - - return v +func tocplx(v constant.Value) constant.Value { + return constant.ToComplex(v) } -func toflt(v Val) Val { - switch u := v.U.(type) { - case *Mpint: - f := newMpflt() - f.SetInt(u) - v.U = f - - case *Mpcplx: - f := newMpflt() - f.Set(&u.Real) - if u.Imag.CmpFloat64(0) != 0 { - yyerror("constant %v truncated to real", u.GoString()) +func toflt(v constant.Value) constant.Value { + if v.Kind() == constant.Complex { + if constant.Sign(constant.Imag(v)) != 0 { + base.Errorf("constant %v truncated to real", v) } - v.U = f + v = constant.Real(v) } - return v + return constant.ToFloat(v) } -func toint(v Val) Val { - switch u := v.U.(type) { - case *Mpint: - if u.Rune { - i := new(Mpint) - i.Set(u) - v.U = i +func toint(v constant.Value) constant.Value { + if v.Kind() == constant.Complex { + if constant.Sign(constant.Imag(v)) != 0 { + base.Errorf("constant %v truncated to integer", v) } + v = constant.Real(v) + } - case *Mpflt: - i := new(Mpint) - if !i.SetFloat(u) { - if i.checkOverflow(0) { - yyerror("integer too large") - } else { - // The value of u cannot be represented as an integer; - // so we need to print an error message. - // Unfortunately some float values cannot be - // reasonably formatted for inclusion in an error - // message (example: 1 + 1e-100), so first we try to - // format the float; if the truncation resulted in - // something that looks like an integer we omit the - // value from the error message. - // (See issue #11371). - var t big.Float - t.Parse(u.GoString(), 10) - if t.IsInt() { - yyerror("constant truncated to integer") - } else { - yyerror("constant %v truncated to integer", u.GoString()) - } - } - } - v.U = i + if v := constant.ToInt(v); v.Kind() == constant.Int { + return v + } - case *Mpcplx: - i := new(Mpint) - if !i.SetFloat(&u.Real) || u.Imag.CmpFloat64(0) != 0 { - yyerror("constant %v truncated to integer", u.GoString()) + // The value of v cannot be represented as an integer; + // so we need to print an error message. + // Unfortunately some float values cannot be + // reasonably formatted for inclusion in an error + // message (example: 1 + 1e-100), so first we try to + // format the float; if the truncation resulted in + // something that looks like an integer we omit the + // value from the error message. + // (See issue #11371). + f := bigFloatVal(v) + if f.MantExp(nil) > 2*Mpprec { + base.Errorf("integer too large") + } else { + var t big.Float + t.Parse(fmt.Sprint(v), 0) + if t.IsInt() { + base.Errorf("constant truncated to integer") + } else { + base.Errorf("constant %v truncated to integer", v) } - - v.U = i } - return v + // Prevent follow-on errors. + // TODO(mdempsky): Use constant.MakeUnknown() instead. + return constant.MakeInt64(1) } -func doesoverflow(v Val, t *types.Type) bool { - switch u := v.U.(type) { - case *Mpint: - if !t.IsInteger() { - Fatalf("overflow: %v integer constant", t) - } - return u.Cmp(minintval[t.Etype]) < 0 || u.Cmp(maxintval[t.Etype]) > 0 - - case *Mpflt: - if !t.IsFloat() { - Fatalf("overflow: %v floating-point constant", t) - } - return u.Cmp(minfltval[t.Etype]) <= 0 || u.Cmp(maxfltval[t.Etype]) >= 0 - - case *Mpcplx: - if !t.IsComplex() { - Fatalf("overflow: %v complex constant", t) - } - return u.Real.Cmp(minfltval[t.Etype]) <= 0 || u.Real.Cmp(maxfltval[t.Etype]) >= 0 || - u.Imag.Cmp(minfltval[t.Etype]) <= 0 || u.Imag.Cmp(maxfltval[t.Etype]) >= 0 - } - - return false +// doesoverflow reports whether constant value v is too large +// to represent with type t. +func doesoverflow(v constant.Value, t *types.Type) bool { + switch { + case t.IsInteger(): + bits := uint(8 * t.Size()) + if t.IsUnsigned() { + x, ok := constant.Uint64Val(v) + return !ok || x>>bits != 0 + } + x, ok := constant.Int64Val(v) + if x < 0 { + x = ^x + } + return !ok || x>>(bits-1) != 0 + case t.IsFloat(): + switch t.Size() { + case 4: + f, _ := constant.Float32Val(v) + return math.IsInf(float64(f), 0) + case 8: + f, _ := constant.Float64Val(v) + return math.IsInf(f, 0) + } + case t.IsComplex(): + ft := floatForComplex(t) + return doesoverflow(constant.Real(v), ft) || doesoverflow(constant.Imag(v), ft) + } + base.Fatalf("doesoverflow: %v, %v", v, t) + panic("unreachable") } -func overflow(v Val, t *types.Type) bool { +// overflow reports whether constant value v is too large +// to represent with type t, and emits an error message if so. +func overflow(v constant.Value, t *types.Type) bool { // v has already been converted // to appropriate form for t. - if t == nil || t.Etype == TIDEAL { + if t.IsUntyped() { return false } - - // Only uintptrs may be converted to pointers, which cannot overflow. - if t.IsPtr() || t.IsUnsafePtr() { - return false + if v.Kind() == constant.Int && constant.BitLen(v) > Mpprec { + base.Errorf("integer too large") + return true } - if doesoverflow(v, t) { - yyerror("constant %v overflows %v", v, t) + base.Errorf("constant %v overflows %v", ir.FmtConst(v, 0), t) return true } - return false - } -func tostr(v Val) Val { - switch u := v.U.(type) { - case *Mpint: - var r rune = 0xFFFD - if u.Cmp(minintval[TINT32]) >= 0 && u.Cmp(maxintval[TINT32]) <= 0 { - r = rune(u.Int64()) +func tostr(v constant.Value) constant.Value { + if v.Kind() == constant.Int { + r := unicode.ReplacementChar + if x, ok := constant.Uint64Val(v); ok && x <= unicode.MaxRune { + r = rune(x) } - v.U = string(r) + v = constant.MakeString(string(r)) } - return v } -func consttype(n *Node) Ctype { - if n == nil || n.Op != OLITERAL { - return CTxxx - } - return n.Val().Ctype() -} - -func Isconst(n *Node, ct Ctype) bool { - t := consttype(n) - - // If the caller is asking for CTINT, allow CTRUNE too. - // Makes life easier for back ends. - return t == ct || (ct == CTINT && t == CTRUNE) -} - -// evconst rewrites constant expressions into OLITERAL nodes. -func evconst(n *Node) { - nl, nr := n.Left, n.Right +var tokenForOp = [...]token.Token{ + ir.OPLUS: token.ADD, + ir.ONEG: token.SUB, + ir.ONOT: token.NOT, + ir.OBITNOT: token.XOR, + + ir.OADD: token.ADD, + ir.OSUB: token.SUB, + ir.OMUL: token.MUL, + ir.ODIV: token.QUO, + ir.OMOD: token.REM, + ir.OOR: token.OR, + ir.OXOR: token.XOR, + ir.OAND: token.AND, + ir.OANDNOT: token.AND_NOT, + ir.OOROR: token.LOR, + ir.OANDAND: token.LAND, + + ir.OEQ: token.EQL, + ir.ONE: token.NEQ, + ir.OLT: token.LSS, + ir.OLE: token.LEQ, + ir.OGT: token.GTR, + ir.OGE: token.GEQ, + + ir.OLSH: token.SHL, + ir.ORSH: token.SHR, +} + +// evalConst returns a constant-evaluated expression equivalent to n. +// If n is not a constant, evalConst returns n. +// Otherwise, evalConst returns a new OLITERAL with the same value as n, +// and with .Orig pointing back to n. +func evalConst(n ir.Node) ir.Node { + nl, nr := n.Left(), n.Right() // Pick off just the opcodes that can be constant evaluated. - switch op := n.Op; op { - case OPLUS, ONEG, OBITNOT, ONOT: - if nl.Op == OLITERAL { - setconst(n, unaryOp(op, nl.Val(), n.Type)) + switch op := n.Op(); op { + case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT: + if nl.Op() == ir.OLITERAL { + var prec uint + if n.Type().IsUnsigned() { + prec = uint(n.Type().Size() * 8) + } + return origConst(n, constant.UnaryOp(tokenForOp[op], nl.Val(), prec)) } - case OADD, OSUB, OMUL, ODIV, OMOD, OOR, OXOR, OAND, OANDNOT, OOROR, OANDAND: - if nl.Op == OLITERAL && nr.Op == OLITERAL { - setconst(n, binaryOp(nl.Val(), op, nr.Val())) + case ir.OADD, ir.OSUB, ir.OMUL, ir.ODIV, ir.OMOD, ir.OOR, ir.OXOR, ir.OAND, ir.OANDNOT, ir.OOROR, ir.OANDAND: + if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL { + rval := nr.Val() + + // check for divisor underflow in complex division (see issue 20227) + if op == ir.ODIV && n.Type().IsComplex() && constant.Sign(square(constant.Real(rval))) == 0 && constant.Sign(square(constant.Imag(rval))) == 0 { + base.Errorf("complex division by zero") + n.SetType(nil) + return n + } + if (op == ir.ODIV || op == ir.OMOD) && constant.Sign(rval) == 0 { + base.Errorf("division by zero") + n.SetType(nil) + return n + } + + tok := tokenForOp[op] + if op == ir.ODIV && n.Type().IsInteger() { + tok = token.QUO_ASSIGN // integer division + } + return origConst(n, constant.BinaryOp(nl.Val(), tok, rval)) } - case OEQ, ONE, OLT, OLE, OGT, OGE: - if nl.Op == OLITERAL && nr.Op == OLITERAL { - setboolconst(n, compareOp(nl.Val(), op, nr.Val())) + case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE: + if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL { + return origBoolConst(n, constant.Compare(nl.Val(), tokenForOp[op], nr.Val())) } - case OLSH, ORSH: - if nl.Op == OLITERAL && nr.Op == OLITERAL { - setconst(n, shiftOp(nl.Val(), op, nr.Val())) + case ir.OLSH, ir.ORSH: + if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL { + // shiftBound from go/types; "so we can express smallestFloat64" + const shiftBound = 1023 - 1 + 52 + s, ok := constant.Uint64Val(nr.Val()) + if !ok || s > shiftBound { + base.Errorf("invalid shift count %v", nr) + n.SetType(nil) + break + } + return origConst(n, constant.Shift(toint(nl.Val()), tokenForOp[op], uint(s))) } - case OCONV, ORUNESTR: - if okforconst[n.Type.Etype] && nl.Op == OLITERAL { - setconst(n, convertVal(nl.Val(), n.Type, true)) + case ir.OCONV, ir.ORUNESTR: + if ir.OKForConst[n.Type().Etype] && nl.Op() == ir.OLITERAL { + return origConst(n, convertVal(nl.Val(), n.Type(), true)) } - case OCONVNOP: - if okforconst[n.Type.Etype] && nl.Op == OLITERAL { + case ir.OCONVNOP: + if ir.OKForConst[n.Type().Etype] && nl.Op() == ir.OLITERAL { // set so n.Orig gets OCONV instead of OCONVNOP - n.Op = OCONV - setconst(n, nl.Val()) + n.SetOp(ir.OCONV) + return origConst(n, nl.Val()) } - case OADDSTR: + case ir.OADDSTR: // Merge adjacent constants in the argument list. - s := n.List.Slice() - for i1 := 0; i1 < len(s); i1++ { - if Isconst(s[i1], CTSTR) && i1+1 < len(s) && Isconst(s[i1+1], CTSTR) { - // merge from i1 up to but not including i2 + s := n.List().Slice() + need := 0 + for i := 0; i < len(s); i++ { + if i == 0 || !ir.IsConst(s[i-1], constant.String) || !ir.IsConst(s[i], constant.String) { + // Can't merge s[i] into s[i-1]; need a slot in the list. + need++ + } + } + if need == len(s) { + return n + } + if need == 1 { + var strs []string + for _, c := range s { + strs = append(strs, c.StringVal()) + } + return origConst(n, constant.MakeString(strings.Join(strs, ""))) + } + newList := make([]ir.Node, 0, need) + for i := 0; i < len(s); i++ { + if ir.IsConst(s[i], constant.String) && i+1 < len(s) && ir.IsConst(s[i+1], constant.String) { + // merge from i up to but not including i2 var strs []string - i2 := i1 - for i2 < len(s) && Isconst(s[i2], CTSTR) { + i2 := i + for i2 < len(s) && ir.IsConst(s[i2], constant.String) { strs = append(strs, s[i2].StringVal()) i2++ } - nl := *s[i1] - nl.Orig = &nl - nl.SetVal(Val{strings.Join(strs, "")}) - s[i1] = &nl - s = append(s[:i1+1], s[i2:]...) + nl := origConst(s[i], constant.MakeString(strings.Join(strs, ""))) + nl.SetOrig(nl) // it's bigger than just s[i] + newList = append(newList, nl) + i = i2 - 1 + } else { + newList = append(newList, s[i]) } } - if len(s) == 1 && Isconst(s[0], CTSTR) { - n.Op = OLITERAL - n.SetVal(s[0].Val()) - } else { - n.List.Set(s) - } + n = ir.Copy(n) + n.PtrList().Set(newList) + return n - case OCAP, OLEN: - switch nl.Type.Etype { - case TSTRING: - if Isconst(nl, CTSTR) { - setintconst(n, int64(len(nl.StringVal()))) + case ir.OCAP, ir.OLEN: + switch nl.Type().Etype { + case types.TSTRING: + if ir.IsConst(nl, constant.String) { + return origIntConst(n, int64(len(nl.StringVal()))) } - case TARRAY: + case types.TARRAY: if !hascallchan(nl) { - setintconst(n, nl.Type.NumElem()) - } - } - - case OALIGNOF, OOFFSETOF, OSIZEOF: - setintconst(n, evalunsafe(n)) - - case OREAL, OIMAG: - if nl.Op == OLITERAL { - var re, im *Mpflt - switch u := nl.Val().U.(type) { - case *Mpint: - re = newMpflt() - re.SetInt(u) - // im = 0 - case *Mpflt: - re = u - // im = 0 - case *Mpcplx: - re = &u.Real - im = &u.Imag - default: - Fatalf("impossible") - } - if n.Op == OIMAG { - if im == nil { - im = newMpflt() - } - re = im + return origIntConst(n, nl.Type().NumElem()) } - setconst(n, Val{re}) } - case OCOMPLEX: - if nl.Op == OLITERAL && nr.Op == OLITERAL { - // make it a complex literal - c := newMpcmplx() - c.Real.Set(toflt(nl.Val()).U.(*Mpflt)) - c.Imag.Set(toflt(nr.Val()).U.(*Mpflt)) - setconst(n, Val{c}) - } - } -} - -func match(x, y Val) (Val, Val) { - switch { - case x.Ctype() == CTCPLX || y.Ctype() == CTCPLX: - return tocplx(x), tocplx(y) - case x.Ctype() == CTFLT || y.Ctype() == CTFLT: - return toflt(x), toflt(y) - } - - // Mixed int/rune are fine. - return x, y -} + case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: + return origIntConst(n, evalunsafe(n)) -func compareOp(x Val, op Op, y Val) bool { - x, y = match(x, y) - - switch x.Ctype() { - case CTBOOL: - x, y := x.U.(bool), y.U.(bool) - switch op { - case OEQ: - return x == y - case ONE: - return x != y + case ir.OREAL: + if nl.Op() == ir.OLITERAL { + return origConst(n, constant.Real(nl.Val())) } - case CTINT, CTRUNE: - x, y := x.U.(*Mpint), y.U.(*Mpint) - return cmpZero(x.Cmp(y), op) - - case CTFLT: - x, y := x.U.(*Mpflt), y.U.(*Mpflt) - return cmpZero(x.Cmp(y), op) - - case CTCPLX: - x, y := x.U.(*Mpcplx), y.U.(*Mpcplx) - eq := x.Real.Cmp(&y.Real) == 0 && x.Imag.Cmp(&y.Imag) == 0 - switch op { - case OEQ: - return eq - case ONE: - return !eq + case ir.OIMAG: + if nl.Op() == ir.OLITERAL { + return origConst(n, constant.Imag(nl.Val())) } - case CTSTR: - x, y := x.U.(string), y.U.(string) - switch op { - case OEQ: - return x == y - case ONE: - return x != y - case OLT: - return x < y - case OLE: - return x <= y - case OGT: - return x > y - case OGE: - return x >= y + case ir.OCOMPLEX: + if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL { + return origConst(n, makeComplex(nl.Val(), nr.Val())) } } - Fatalf("compareOp: bad comparison: %v %v %v", x, op, y) - panic("unreachable") + return n } -func cmpZero(x int, op Op) bool { - switch op { - case OEQ: - return x == 0 - case ONE: - return x != 0 - case OLT: - return x < 0 - case OLE: - return x <= 0 - case OGT: - return x > 0 - case OGE: - return x >= 0 +func makeInt(i *big.Int) constant.Value { + if i.IsInt64() { + return constant.Make(i.Int64()) // workaround #42640 (Int64Val(Make(big.NewInt(10))) returns (10, false), not (10, true)) } - - Fatalf("cmpZero: want comparison operator, got %v", op) - panic("unreachable") + return constant.Make(i) } -func binaryOp(x Val, op Op, y Val) Val { - x, y = match(x, y) - -Outer: - switch x.Ctype() { - case CTBOOL: - x, y := x.U.(bool), y.U.(bool) - switch op { - case OANDAND: - return Val{U: x && y} - case OOROR: - return Val{U: x || y} - } - - case CTINT, CTRUNE: - x, y := x.U.(*Mpint), y.U.(*Mpint) - - u := new(Mpint) - u.Rune = x.Rune || y.Rune - u.Set(x) - switch op { - case OADD: - u.Add(y) - case OSUB: - u.Sub(y) - case OMUL: - u.Mul(y) - case ODIV: - if y.CmpInt64(0) == 0 { - yyerror("division by zero") - return Val{} - } - u.Quo(y) - case OMOD: - if y.CmpInt64(0) == 0 { - yyerror("division by zero") - return Val{} - } - u.Rem(y) - case OOR: - u.Or(y) - case OAND: - u.And(y) - case OANDNOT: - u.AndNot(y) - case OXOR: - u.Xor(y) - default: - break Outer - } - return Val{U: u} - - case CTFLT: - x, y := x.U.(*Mpflt), y.U.(*Mpflt) - - u := newMpflt() - u.Set(x) - switch op { - case OADD: - u.Add(y) - case OSUB: - u.Sub(y) - case OMUL: - u.Mul(y) - case ODIV: - if y.CmpFloat64(0) == 0 { - yyerror("division by zero") - return Val{} - } - u.Quo(y) - default: - break Outer - } - return Val{U: u} - - case CTCPLX: - x, y := x.U.(*Mpcplx), y.U.(*Mpcplx) - - u := newMpcmplx() - u.Real.Set(&x.Real) - u.Imag.Set(&x.Imag) - switch op { - case OADD: - u.Real.Add(&y.Real) - u.Imag.Add(&y.Imag) - case OSUB: - u.Real.Sub(&y.Real) - u.Imag.Sub(&y.Imag) - case OMUL: - u.Mul(y) - case ODIV: - if !u.Div(y) { - yyerror("complex division by zero") - return Val{} - } - default: - break Outer - } - return Val{U: u} +func makeFloat64(f float64) constant.Value { + if math.IsInf(f, 0) { + base.Fatalf("infinity is not a valid constant") } - - Fatalf("binaryOp: bad operation: %v %v %v", x, op, y) - panic("unreachable") + v := constant.MakeFloat64(f) + v = constant.ToFloat(v) // workaround #42641 (MakeFloat64(0).Kind() returns Int, not Float) + return v } -func unaryOp(op Op, x Val, t *types.Type) Val { - switch op { - case OPLUS: - switch x.Ctype() { - case CTINT, CTRUNE, CTFLT, CTCPLX: - return x - } - - case ONEG: - switch x.Ctype() { - case CTINT, CTRUNE: - x := x.U.(*Mpint) - u := new(Mpint) - u.Rune = x.Rune - u.Set(x) - u.Neg() - return Val{U: u} - - case CTFLT: - x := x.U.(*Mpflt) - u := newMpflt() - u.Set(x) - u.Neg() - return Val{U: u} - - case CTCPLX: - x := x.U.(*Mpcplx) - u := newMpcmplx() - u.Real.Set(&x.Real) - u.Imag.Set(&x.Imag) - u.Real.Neg() - u.Imag.Neg() - return Val{U: u} - } - - case OBITNOT: - switch x.Ctype() { - case CTINT, CTRUNE: - x := x.U.(*Mpint) - - u := new(Mpint) - u.Rune = x.Rune - if t.IsSigned() || t.IsUntyped() { - // Signed values change sign. - u.SetInt64(-1) - } else { - // Unsigned values invert their bits. - u.Set(maxintval[t.Etype]) - } - u.Xor(x) - return Val{U: u} - } - - case ONOT: - return Val{U: !x.U.(bool)} - } - - Fatalf("unaryOp: bad operation: %v %v", op, x) - panic("unreachable") +func makeComplex(real, imag constant.Value) constant.Value { + return constant.BinaryOp(constant.ToFloat(real), token.ADD, constant.MakeImag(constant.ToFloat(imag))) } -func shiftOp(x Val, op Op, y Val) Val { - if x.Ctype() != CTRUNE { - x = toint(x) - } - y = toint(y) - - u := new(Mpint) - u.Set(x.U.(*Mpint)) - u.Rune = x.U.(*Mpint).Rune - switch op { - case OLSH: - u.Lsh(y.U.(*Mpint)) - case ORSH: - u.Rsh(y.U.(*Mpint)) - default: - Fatalf("shiftOp: bad operator: %v", op) - panic("unreachable") - } - return Val{U: u} +func square(x constant.Value) constant.Value { + return constant.BinaryOp(x, token.MUL, x) } -// setconst rewrites n as an OLITERAL with value v. -func setconst(n *Node, v Val) { - // If constant folding failed, mark n as broken and give up. - if v.U == nil { - n.Type = nil - return - } - - // Ensure n.Orig still points to a semantically-equivalent - // expression after we rewrite n into a constant. - if n.Orig == n { - n.Orig = n.sepcopy() - } - - *n = Node{ - Op: OLITERAL, - Pos: n.Pos, - Orig: n.Orig, - Type: n.Type, - Xoffset: BADWIDTH, - } - n.SetVal(v) - if vt := idealType(v.Ctype()); n.Type.IsUntyped() && n.Type != vt { - Fatalf("untyped type mismatch, have: %v, want: %v", n.Type, vt) - } +// For matching historical "constant OP overflow" error messages. +// TODO(mdempsky): Replace with error messages like go/types uses. +var overflowNames = [...]string{ + ir.OADD: "addition", + ir.OSUB: "subtraction", + ir.OMUL: "multiplication", + ir.OLSH: "shift", + ir.OXOR: "bitwise XOR", + ir.OBITNOT: "bitwise complement", +} - // Check range. +// origConst returns an OLITERAL with orig n and value v. +func origConst(n ir.Node, v constant.Value) ir.Node { lno := setlineno(n) - overflow(v, n.Type) - lineno = lno - - if !n.Type.IsUntyped() { - switch v.Ctype() { - // Truncate precision for non-ideal float. - case CTFLT: - n.SetVal(Val{truncfltlit(v.U.(*Mpflt), n.Type)}) - // Truncate precision for non-ideal complex. - case CTCPLX: - n.SetVal(Val{trunccmplxlit(v.U.(*Mpcplx), n.Type)}) + v = convertVal(v, n.Type(), false) + base.Pos = lno + + switch v.Kind() { + case constant.Int: + if constant.BitLen(v) <= Mpprec { + break + } + fallthrough + case constant.Unknown: + what := overflowNames[n.Op()] + if what == "" { + base.Fatalf("unexpected overflow: %v", n.Op()) } + base.ErrorfAt(n.Pos(), "constant %v overflow", what) + n.SetType(nil) + return n } -} - -func setboolconst(n *Node, v bool) { - setconst(n, Val{U: v}) -} - -func setintconst(n *Node, v int64) { - u := new(Mpint) - u.SetInt64(v) - setconst(n, Val{u}) -} -// nodlit returns a new untyped constant with value v. -func nodlit(v Val) *Node { - n := nod(OLITERAL, nil, nil) + orig := n + n = ir.NodAt(orig.Pos(), ir.OLITERAL, nil, nil) + n.SetOrig(orig) + n.SetType(orig.Type()) n.SetVal(v) - n.Type = idealType(v.Ctype()) return n } -func idealType(ct Ctype) *types.Type { - switch ct { - case CTSTR: - return types.UntypedString - case CTBOOL: - return types.UntypedBool - case CTINT: - return types.UntypedInt - case CTRUNE: - return types.UntypedRune - case CTFLT: - return types.UntypedFloat - case CTCPLX: - return types.UntypedComplex - case CTNIL: - return types.Types[TNIL] - } - Fatalf("unexpected Ctype: %v", ct) - return nil +func origBoolConst(n ir.Node, v bool) ir.Node { + return origConst(n, constant.MakeBool(v)) +} + +func origIntConst(n ir.Node, v int64) ir.Node { + return origConst(n, constant.MakeInt64(v)) } // defaultlit on both nodes simultaneously; @@ -1052,17 +662,17 @@ func idealType(ct Ctype) *types.Type { // force means must assign concrete (non-ideal) type. // The results of defaultlit2 MUST be assigned back to l and r, e.g. // n.Left, n.Right = defaultlit2(n.Left, n.Right, force) -func defaultlit2(l *Node, r *Node, force bool) (*Node, *Node) { - if l.Type == nil || r.Type == nil { +func defaultlit2(l ir.Node, r ir.Node, force bool) (ir.Node, ir.Node) { + if l.Type() == nil || r.Type() == nil { return l, r } - if !l.Type.IsUntyped() { - r = convlit(r, l.Type) + if !l.Type().IsUntyped() { + r = convlit(r, l.Type()) return l, r } - if !r.Type.IsUntyped() { - l = convlit(l, r.Type) + if !r.Type().IsUntyped() { + l = convlit(l, r.Type()) return l, r } @@ -1071,93 +681,77 @@ func defaultlit2(l *Node, r *Node, force bool) (*Node, *Node) { } // Can't mix bool with non-bool, string with non-string, or nil with anything (untyped). - if l.Type.IsBoolean() != r.Type.IsBoolean() { + if l.Type().IsBoolean() != r.Type().IsBoolean() { return l, r } - if l.Type.IsString() != r.Type.IsString() { + if l.Type().IsString() != r.Type().IsString() { return l, r } - if l.isNil() || r.isNil() { + if ir.IsNil(l) || ir.IsNil(r) { return l, r } - t := defaultType(mixUntyped(l.Type, r.Type)) + t := defaultType(mixUntyped(l.Type(), r.Type())) l = convlit(l, t) r = convlit(r, t) return l, r } -func ctype(t *types.Type) Ctype { - switch t { - case types.UntypedBool: - return CTBOOL - case types.UntypedString: - return CTSTR - case types.UntypedInt: - return CTINT - case types.UntypedRune: - return CTRUNE - case types.UntypedFloat: - return CTFLT - case types.UntypedComplex: - return CTCPLX +func mixUntyped(t1, t2 *types.Type) *types.Type { + if t1 == t2 { + return t1 + } + + rank := func(t *types.Type) int { + switch t { + case types.UntypedInt: + return 0 + case types.UntypedRune: + return 1 + case types.UntypedFloat: + return 2 + case types.UntypedComplex: + return 3 + } + base.Fatalf("bad type %v", t) + panic("unreachable") } - Fatalf("bad type %v", t) - panic("unreachable") -} -func mixUntyped(t1, t2 *types.Type) *types.Type { - t := t1 - if ctype(t2) > ctype(t1) { - t = t2 + if rank(t2) > rank(t1) { + return t2 } - return t + return t1 } func defaultType(t *types.Type) *types.Type { - if !t.IsUntyped() || t.Etype == TNIL { + if !t.IsUntyped() || t.Etype == types.TNIL { return t } switch t { case types.UntypedBool: - return types.Types[TBOOL] + return types.Types[types.TBOOL] case types.UntypedString: - return types.Types[TSTRING] + return types.Types[types.TSTRING] case types.UntypedInt: - return types.Types[TINT] + return types.Types[types.TINT] case types.UntypedRune: return types.Runetype case types.UntypedFloat: - return types.Types[TFLOAT64] + return types.Types[types.TFLOAT64] case types.UntypedComplex: - return types.Types[TCOMPLEX128] + return types.Types[types.TCOMPLEX128] } - Fatalf("bad type %v", t) + base.Fatalf("bad type %v", t) return nil } -func smallintconst(n *Node) bool { - if n.Op == OLITERAL && Isconst(n, CTINT) && n.Type != nil { - switch simtype[n.Type.Etype] { - case TINT8, - TUINT8, - TINT16, - TUINT16, - TINT32, - TUINT32, - TBOOL: - return true - - case TIDEAL, TINT64, TUINT64, TPTR: - v, ok := n.Val().U.(*Mpint) - if ok && v.Cmp(minintval[TINT32]) >= 0 && v.Cmp(maxintval[TINT32]) <= 0 { - return true - } - } +func smallintconst(n ir.Node) bool { + if n.Op() == ir.OLITERAL { + v, ok := constant.Int64Val(n.Val()) + return ok && int64(int32(v)) == v } - return false } @@ -1166,21 +760,22 @@ func smallintconst(n *Node) bool { // If n is not a constant expression, not representable as an // integer, or negative, it returns -1. If n is too large, it // returns -2. -func indexconst(n *Node) int64 { - if n.Op != OLITERAL { +func indexconst(n ir.Node) int64 { + if n.Op() != ir.OLITERAL { + return -1 + } + if !n.Type().IsInteger() && n.Type().Etype != types.TIDEAL { return -1 } - v := toint(n.Val()) // toint returns argument unchanged if not representable as an *Mpint - vi, ok := v.U.(*Mpint) - if !ok || vi.CmpInt64(0) < 0 { + v := toint(n.Val()) + if v.Kind() != constant.Int || constant.Sign(v) < 0 { return -1 } - if vi.Cmp(maxintval[TINT]) > 0 { + if doesoverflow(v, types.Types[types.TINT]) { return -2 } - - return vi.Int64() + return ir.Int64Val(types.Types[types.TINT], v) } // isGoConst reports whether n is a Go language constant (as opposed to a @@ -1188,47 +783,47 @@ func indexconst(n *Node) int64 { // // Expressions derived from nil, like string([]byte(nil)), while they // may be known at compile time, are not Go language constants. -func (n *Node) isGoConst() bool { - return n.Op == OLITERAL && n.Val().Ctype() != CTNIL +func isGoConst(n ir.Node) bool { + return n.Op() == ir.OLITERAL } -func hascallchan(n *Node) bool { +func hascallchan(n ir.Node) bool { if n == nil { return false } - switch n.Op { - case OAPPEND, - OCALL, - OCALLFUNC, - OCALLINTER, - OCALLMETH, - OCAP, - OCLOSE, - OCOMPLEX, - OCOPY, - ODELETE, - OIMAG, - OLEN, - OMAKE, - ONEW, - OPANIC, - OPRINT, - OPRINTN, - OREAL, - ORECOVER, - ORECV: + switch n.Op() { + case ir.OAPPEND, + ir.OCALL, + ir.OCALLFUNC, + ir.OCALLINTER, + ir.OCALLMETH, + ir.OCAP, + ir.OCLOSE, + ir.OCOMPLEX, + ir.OCOPY, + ir.ODELETE, + ir.OIMAG, + ir.OLEN, + ir.OMAKE, + ir.ONEW, + ir.OPANIC, + ir.OPRINT, + ir.OPRINTN, + ir.OREAL, + ir.ORECOVER, + ir.ORECV: return true } - if hascallchan(n.Left) || hascallchan(n.Right) { + if hascallchan(n.Left()) || hascallchan(n.Right()) { return true } - for _, n1 := range n.List.Slice() { + for _, n1 := range n.List().Slice() { if hascallchan(n1) { return true } } - for _, n2 := range n.Rlist.Slice() { + for _, n2 := range n.Rlist().Slice() { if hascallchan(n2) { return true } @@ -1256,16 +851,16 @@ type constSetKey struct { // where are used in the error message. // // n must not be an untyped constant. -func (s *constSet) add(pos src.XPos, n *Node, what, where string) { - if n.Op == OCONVIFACE && n.Implicit() { - n = n.Left +func (s *constSet) add(pos src.XPos, n ir.Node, what, where string) { + if n.Op() == ir.OCONVIFACE && n.Implicit() { + n = n.Left() } - if !n.isGoConst() { + if !isGoConst(n) { return } - if n.Type.IsUntyped() { - Fatalf("%v is untyped", n) + if n.Type().IsUntyped() { + base.Fatalf("%v is untyped", n) } // Consts are only duplicates if they have the same value and @@ -1283,17 +878,17 @@ func (s *constSet) add(pos src.XPos, n *Node, what, where string) { // #21866 by treating all type aliases like byte/uint8 and // rune/int32. - typ := n.Type + typ := n.Type() switch typ { case types.Bytetype: - typ = types.Types[TUINT8] + typ = types.Types[types.TUINT8] case types.Runetype: - typ = types.Types[TINT32] + typ = types.Types[types.TINT32] } - k := constSetKey{typ, n.Val().Interface()} + k := constSetKey{typ, ir.ConstValue(n)} if hasUniquePos(n) { - pos = n.Pos + pos = n.Pos() } if s.m == nil { @@ -1301,9 +896,9 @@ func (s *constSet) add(pos src.XPos, n *Node, what, where string) { } if prevPos, isDup := s.m[k]; isDup { - yyerrorl(pos, "duplicate %s %s in %s\n\tprevious %s at %v", + base.ErrorfAt(pos, "duplicate %s %s in %s\n\tprevious %s at %v", what, nodeAndVal(n), where, - what, linestr(prevPos)) + what, base.FmtPos(prevPos)) } else { s.m[k] = pos } @@ -1313,9 +908,9 @@ func (s *constSet) add(pos src.XPos, n *Node, what, where string) { // the latter is non-obvious. // // TODO(mdempsky): This could probably be a fmt.go flag. -func nodeAndVal(n *Node) string { +func nodeAndVal(n ir.Node) string { show := n.String() - val := n.Val().Interface() + val := ir.ConstValue(n) if s := fmt.Sprintf("%#v", val); show != s { show += " (value " + s + ")" } |