diff options
Diffstat (limited to 'src/cmd/compile/internal/typecheck/const.go')
-rw-r--r-- | src/cmd/compile/internal/typecheck/const.go | 944 |
1 files changed, 944 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/typecheck/const.go b/src/cmd/compile/internal/typecheck/const.go new file mode 100644 index 0000000000..54d70cb835 --- /dev/null +++ b/src/cmd/compile/internal/typecheck/const.go @@ -0,0 +1,944 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typecheck + +import ( + "fmt" + "go/constant" + "go/token" + "math" + "math/big" + "strings" + "unicode" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/types" + "cmd/internal/src" +) + +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) + } + 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(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 v + } + + 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 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 v + } + + 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 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. +// +// For explicit conversions, t must be non-nil, and integer-to-string +// conversions are allowed. +// +// For implicit conversions (e.g., assignments), t may be nil; if so, +// n is converted to its default type. +// +// If there's an error converting n to t, context is used in the error +// message. +func convlit1(n ir.Node, t *types.Type, explicit bool, context func() string) ir.Node { + if explicit && t == nil { + base.Fatalf("explicit conversion missing type") + } + if t != nil && t.IsUntyped() { + base.Fatalf("bad conversion to untyped: %v", t) + } + + if n == nil || n.Type() == nil { + // Allow sloppy callers. + return n + } + if !n.Type().IsUntyped() { + // Already typed; nothing to do. + return n + } + + // Nil is technically not a constant, so handle it specially. + if n.Type().Kind() == types.TNIL { + if n.Op() != ir.ONIL { + base.Fatalf("unexpected op: %v (%v)", n, n.Op()) + } + n = ir.Copy(n) + if t == nil { + base.Errorf("use of untyped nil") + n.SetDiag(true) + n.SetType(nil) + return n + } + + if !t.HasNil() { + // Leave for caller to handle. + return n + } + + n.SetType(t) + return n + } + + if t == nil || !ir.OKForConst[t.Kind()] { + t = defaultType(n.Type()) + } + + switch n.Op() { + default: + base.Fatalf("unexpected untyped expression: %v", n) + + case ir.OLITERAL: + v := convertVal(n.Val(), t, explicit) + if v.Kind() == constant.Unknown { + n = ir.NewConstExpr(n.Val(), n) + break + } + n = ir.NewConstExpr(v, n) + n.SetType(t) + return n + + 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 := n.(*ir.UnaryExpr) + n.X = convlit(n.X, ot) + if n.X.Type() == nil { + n.SetType(nil) + return n + } + n.SetType(t) + return n + + 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 + } + + var l, r ir.Node + switch n := n.(type) { + case *ir.BinaryExpr: + n.X = convlit(n.X, ot) + n.Y = convlit(n.Y, ot) + l, r = n.X, n.Y + case *ir.LogicalExpr: + n.X = convlit(n.X, ot) + n.Y = convlit(n.Y, ot) + l, r = n.X, n.Y + } + + if l.Type() == nil || r.Type() == nil { + n.SetType(nil) + return n + } + if !types.Identical(l.Type(), r.Type()) { + base.Errorf("invalid operation: %v (mismatched types %v and %v)", n, l.Type(), r.Type()) + n.SetType(nil) + return n + } + + n.SetType(t) + return n + + case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE: + n := n.(*ir.BinaryExpr) + if !t.IsBoolean() { + break + } + n.SetType(t) + return n + + case ir.OLSH, ir.ORSH: + n := n.(*ir.BinaryExpr) + n.X = convlit1(n.X, t, explicit, nil) + n.SetType(n.X.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 + } + + if !n.Diag() { + if !t.Broke() { + if explicit { + base.Errorf("cannot convert %L to type %v", n, t) + } else if context != nil { + base.Errorf("cannot use %L as type %v in %s", n, t, context()) + } else { + base.Errorf("cannot use %L as type %v", n, t) + } + } + n.SetDiag(true) + } + n.SetType(nil) + return n +} + +func operandType(op ir.Op, t *types.Type) *types.Type { + switch op { + case ir.OCOMPLEX: + if t.IsComplex() { + return types.FloatForComplex(t) + } + case ir.OREAL, ir.OIMAG: + if t.IsFloat() { + return types.ComplexForFloat(t) + } + default: + if okfor[op][t.Kind()] { + return t + } + } + return nil +} + +// convertVal converts v into a representation appropriate for t. If +// no such representation exists, it returns Val{} instead. +// +// If explicit is true, then conversions from integer to string are +// also allowed. +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 constant.String: + if t.IsString() { + return v + } + + case constant.Int: + if explicit && t.IsString() { + return tostr(v) + } + fallthrough + case constant.Float, constant.Complex: + switch { + case t.IsInteger(): + v = toint(v) + overflow(v, t) + return v + case t.IsFloat(): + v = toflt(v) + v = truncfltlit(v, t) + return v + case t.IsComplex(): + v = tocplx(v) + v = trunccmplxlit(v, t) + return v + } + } + + return constant.MakeUnknown() +} + +func tocplx(v constant.Value) constant.Value { + return constant.ToComplex(v) +} + +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 = constant.Real(v) + } + + return constant.ToFloat(v) +} + +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) + } + + if v := constant.ToInt(v); v.Kind() == constant.Int { + return v + } + + // 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 := ir.BigFloat(v) + if f.MantExp(nil) > 2*ir.ConstPrec { + 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) + } + } + + // Prevent follow-on errors. + // TODO(mdempsky): Use constant.MakeUnknown() instead. + return constant.MakeInt64(1) +} + +// 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.IsUntyped() { + return false + } + if v.Kind() == constant.Int && constant.BitLen(v) > ir.ConstPrec { + base.Errorf("integer too large") + return true + } + if ir.ConstOverflow(v, t) { + base.Errorf("constant %v overflows %v", types.FmtConst(v, false), t) + return true + } + return false +} + +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 = constant.MakeString(string(r)) + } + return v +} + +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 { + // Pick off just the opcodes that can be constant evaluated. + switch n.Op() { + case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT: + n := n.(*ir.UnaryExpr) + nl := n.X + if nl.Op() == ir.OLITERAL { + var prec uint + if n.Type().IsUnsigned() { + prec = uint(n.Type().Size() * 8) + } + return OrigConst(n, constant.UnaryOp(tokenForOp[n.Op()], nl.Val(), prec)) + } + + case ir.OADD, ir.OSUB, ir.OMUL, ir.ODIV, ir.OMOD, ir.OOR, ir.OXOR, ir.OAND, ir.OANDNOT: + n := n.(*ir.BinaryExpr) + nl, nr := n.X, n.Y + if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL { + rval := nr.Val() + + // check for divisor underflow in complex division (see issue 20227) + if n.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 (n.Op() == ir.ODIV || n.Op() == ir.OMOD) && constant.Sign(rval) == 0 { + base.Errorf("division by zero") + n.SetType(nil) + return n + } + + tok := tokenForOp[n.Op()] + if n.Op() == ir.ODIV && n.Type().IsInteger() { + tok = token.QUO_ASSIGN // integer division + } + return OrigConst(n, constant.BinaryOp(nl.Val(), tok, rval)) + } + + case ir.OOROR, ir.OANDAND: + n := n.(*ir.LogicalExpr) + nl, nr := n.X, n.Y + if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL { + return OrigConst(n, constant.BinaryOp(nl.Val(), tokenForOp[n.Op()], nr.Val())) + } + + case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE: + n := n.(*ir.BinaryExpr) + nl, nr := n.X, n.Y + if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL { + return OrigBool(n, constant.Compare(nl.Val(), tokenForOp[n.Op()], nr.Val())) + } + + case ir.OLSH, ir.ORSH: + n := n.(*ir.BinaryExpr) + nl, nr := n.X, n.Y + 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[n.Op()], uint(s))) + } + + case ir.OCONV, ir.ORUNESTR: + n := n.(*ir.ConvExpr) + nl := n.X + if ir.OKForConst[n.Type().Kind()] && nl.Op() == ir.OLITERAL { + return OrigConst(n, convertVal(nl.Val(), n.Type(), true)) + } + + case ir.OCONVNOP: + n := n.(*ir.ConvExpr) + nl := n.X + if ir.OKForConst[n.Type().Kind()] && nl.Op() == ir.OLITERAL { + // set so n.Orig gets OCONV instead of OCONVNOP + n.SetOp(ir.OCONV) + return OrigConst(n, nl.Val()) + } + + case ir.OADDSTR: + // Merge adjacent constants in the argument list. + n := n.(*ir.AddStringExpr) + s := n.List + 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, ir.StringVal(c)) + } + 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 := i + for i2 < len(s) && ir.IsConst(s[i2], constant.String) { + strs = append(strs, ir.StringVal(s[i2])) + i2++ + } + + nl := ir.Copy(n).(*ir.AddStringExpr) + nl.List.Set(s[i:i2]) + newList = append(newList, OrigConst(nl, constant.MakeString(strings.Join(strs, "")))) + i = i2 - 1 + } else { + newList = append(newList, s[i]) + } + } + + nn := ir.Copy(n).(*ir.AddStringExpr) + nn.List.Set(newList) + return nn + + case ir.OCAP, ir.OLEN: + n := n.(*ir.UnaryExpr) + nl := n.X + switch nl.Type().Kind() { + case types.TSTRING: + if ir.IsConst(nl, constant.String) { + return OrigInt(n, int64(len(ir.StringVal(nl)))) + } + case types.TARRAY: + if !anyCallOrChan(nl) { + return OrigInt(n, nl.Type().NumElem()) + } + } + + case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: + n := n.(*ir.UnaryExpr) + return OrigInt(n, evalunsafe(n)) + + case ir.OREAL: + n := n.(*ir.UnaryExpr) + nl := n.X + if nl.Op() == ir.OLITERAL { + return OrigConst(n, constant.Real(nl.Val())) + } + + case ir.OIMAG: + n := n.(*ir.UnaryExpr) + nl := n.X + if nl.Op() == ir.OLITERAL { + return OrigConst(n, constant.Imag(nl.Val())) + } + + case ir.OCOMPLEX: + n := n.(*ir.BinaryExpr) + nl, nr := n.X, n.Y + if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL { + return OrigConst(n, makeComplex(nl.Val(), nr.Val())) + } + } + + return n +} + +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)) + } + return constant.Make(i) +} + +func makeFloat64(f float64) constant.Value { + if math.IsInf(f, 0) { + base.Fatalf("infinity is not a valid constant") + } + v := constant.MakeFloat64(f) + v = constant.ToFloat(v) // workaround #42641 (MakeFloat64(0).Kind() returns Int, not Float) + return v +} + +func makeComplex(real, imag constant.Value) constant.Value { + return constant.BinaryOp(constant.ToFloat(real), token.ADD, constant.MakeImag(constant.ToFloat(imag))) +} + +func square(x constant.Value) constant.Value { + return constant.BinaryOp(x, token.MUL, x) +} + +// 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", +} + +// OrigConst returns an OLITERAL with orig n and value v. +func OrigConst(n ir.Node, v constant.Value) ir.Node { + lno := ir.SetPos(n) + v = convertVal(v, n.Type(), false) + base.Pos = lno + + switch v.Kind() { + case constant.Int: + if constant.BitLen(v) <= ir.ConstPrec { + 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 + } + + return ir.NewConstExpr(v, n) +} + +func OrigBool(n ir.Node, v bool) ir.Node { + return OrigConst(n, constant.MakeBool(v)) +} + +func OrigInt(n ir.Node, v int64) ir.Node { + return OrigConst(n, constant.MakeInt64(v)) +} + +// defaultlit on both nodes simultaneously; +// if they're both ideal going in they better +// get the same type going out. +// 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 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()) + return l, r + } + + if !r.Type().IsUntyped() { + l = convlit(l, r.Type()) + return l, r + } + + if !force { + return l, r + } + + // Can't mix bool with non-bool, string with non-string, or nil with anything (untyped). + if l.Type().IsBoolean() != r.Type().IsBoolean() { + return l, r + } + if l.Type().IsString() != r.Type().IsString() { + return l, r + } + if ir.IsNil(l) || ir.IsNil(r) { + return l, r + } + + t := defaultType(mixUntyped(l.Type(), r.Type())) + l = convlit(l, t) + r = convlit(r, t) + return l, r +} + +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") + } + + if rank(t2) > rank(t1) { + return t2 + } + return t1 +} + +func defaultType(t *types.Type) *types.Type { + if !t.IsUntyped() || t.Kind() == types.TNIL { + return t + } + + switch t { + case types.UntypedBool: + return types.Types[types.TBOOL] + case types.UntypedString: + return types.Types[types.TSTRING] + case types.UntypedInt: + return types.Types[types.TINT] + case types.UntypedRune: + return types.RuneType + case types.UntypedFloat: + return types.Types[types.TFLOAT64] + case types.UntypedComplex: + return types.Types[types.TCOMPLEX128] + } + + base.Fatalf("bad type %v", t) + return nil +} + +// IndexConst checks if Node n contains a constant expression +// representable as a non-negative int and returns its value. +// 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 ir.Node) int64 { + if n.Op() != ir.OLITERAL { + return -1 + } + if !n.Type().IsInteger() && n.Type().Kind() != types.TIDEAL { + return -1 + } + + v := toint(n.Val()) + if v.Kind() != constant.Int || constant.Sign(v) < 0 { + return -1 + } + if ir.ConstOverflow(v, types.Types[types.TINT]) { + return -2 + } + return ir.IntVal(types.Types[types.TINT], v) +} + +// anyCallOrChan reports whether n contains any calls or channel operations. +func anyCallOrChan(n ir.Node) bool { + return ir.Any(n, func(n ir.Node) bool { + 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 + } + return false + }) +} + +// A constSet represents a set of Go constant expressions. +type constSet struct { + m map[constSetKey]src.XPos +} + +type constSetKey struct { + typ *types.Type + val interface{} +} + +// add adds constant expression n to s. If a constant expression of +// equal value and identical type has already been added, then add +// reports an error about the duplicate value. +// +// pos provides position information for where expression n occurred +// (in case n does not have its own position information). what and +// where are used in the error message. +// +// n must not be an untyped constant. +func (s *constSet) add(pos src.XPos, n ir.Node, what, where string) { + if conv := n; conv.Op() == ir.OCONVIFACE { + conv := conv.(*ir.ConvExpr) + if conv.Implicit() { + n = conv.X + } + } + + if !ir.IsConstNode(n) { + return + } + if n.Type().IsUntyped() { + base.Fatalf("%v is untyped", n) + } + + // Consts are only duplicates if they have the same value and + // identical types. + // + // In general, we have to use types.Identical to test type + // identity, because == gives false negatives for anonymous + // types and the byte/uint8 and rune/int32 builtin type + // aliases. However, this is not a problem here, because + // constant expressions are always untyped or have a named + // type, and we explicitly handle the builtin type aliases + // below. + // + // This approach may need to be revisited though if we fix + // #21866 by treating all type aliases like byte/uint8 and + // rune/int32. + + typ := n.Type() + switch typ { + case types.ByteType: + typ = types.Types[types.TUINT8] + case types.RuneType: + typ = types.Types[types.TINT32] + } + k := constSetKey{typ, ir.ConstValue(n)} + + if ir.HasUniquePos(n) { + pos = n.Pos() + } + + if s.m == nil { + s.m = make(map[constSetKey]src.XPos) + } + + if prevPos, isDup := s.m[k]; isDup { + base.ErrorfAt(pos, "duplicate %s %s in %s\n\tprevious %s at %v", + what, nodeAndVal(n), where, + what, base.FmtPos(prevPos)) + } else { + s.m[k] = pos + } +} + +// nodeAndVal reports both an expression and its constant value, if +// the latter is non-obvious. +// +// TODO(mdempsky): This could probably be a fmt.go flag. +func nodeAndVal(n ir.Node) string { + show := fmt.Sprint(n) + val := ir.ConstValue(n) + if s := fmt.Sprintf("%#v", val); show != s { + show += " (value " + s + ")" + } + return show +} + +// evalunsafe evaluates a package unsafe operation and returns the result. +func evalunsafe(n ir.Node) int64 { + switch n.Op() { + case ir.OALIGNOF, ir.OSIZEOF: + n := n.(*ir.UnaryExpr) + n.X = Expr(n.X) + n.X = DefaultLit(n.X, nil) + tr := n.X.Type() + if tr == nil { + return 0 + } + types.CalcSize(tr) + if n.Op() == ir.OALIGNOF { + return int64(tr.Align) + } + return tr.Width + + case ir.OOFFSETOF: + // must be a selector. + n := n.(*ir.UnaryExpr) + if n.X.Op() != ir.OXDOT { + base.Errorf("invalid expression %v", n) + return 0 + } + sel := n.X.(*ir.SelectorExpr) + + // Remember base of selector to find it back after dot insertion. + // Since r->left may be mutated by typechecking, check it explicitly + // first to track it correctly. + sel.X = Expr(sel.X) + sbase := sel.X + + tsel := Expr(sel) + n.X = tsel + if tsel.Type() == nil { + return 0 + } + switch tsel.Op() { + case ir.ODOT, ir.ODOTPTR: + break + case ir.OCALLPART: + base.Errorf("invalid expression %v: argument is a method value", n) + return 0 + default: + base.Errorf("invalid expression %v", n) + return 0 + } + + // Sum offsets for dots until we reach sbase. + var v int64 + var next ir.Node + for r := tsel; r != sbase; r = next { + switch r.Op() { + case ir.ODOTPTR: + // For Offsetof(s.f), s may itself be a pointer, + // but accessing f must not otherwise involve + // indirection via embedded pointer types. + r := r.(*ir.SelectorExpr) + if r.X != sbase { + base.Errorf("invalid expression %v: selector implies indirection of embedded %v", n, r.X) + return 0 + } + fallthrough + case ir.ODOT: + r := r.(*ir.SelectorExpr) + v += r.Offset + next = r.X + default: + ir.Dump("unsafenmagic", tsel) + base.Fatalf("impossible %v node after dot insertion", r.Op()) + } + } + return v + } + + base.Fatalf("unexpected op %v", n.Op()) + return 0 +} |