Current Path : /usr/local/go/src/cmd/compile/internal/typecheck/ |
Current File : //usr/local/go/src/cmd/compile/internal/typecheck/const.go |
// 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" "internal/types/errors" "math" "math/big" "unicode" "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/types" ) 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() { 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() { 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.Fatalf("use of untyped nil") } 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 explicit { base.Fatalf("cannot convert %L to type %v", n, t) } else if context != nil { base.Fatalf("cannot use %L as type %v in %s", n, t, context()) } else { base.Fatalf("cannot use %L as type %v", n, t) } 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) 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 { v = constant.Real(v) } return constant.ToFloat(v) } func toint(v constant.Value) constant.Value { if v.Kind() == constant.Complex { 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) } 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, } func makeFloat64(f float64) constant.Value { if math.IsInf(f, 0) { base.Fatalf("infinity is not a valid constant") } return constant.MakeFloat64(f) } func makeComplex(real, imag constant.Value) constant.Value { return constant.BinaryOp(constant.ToFloat(real), token.ADD, constant.MakeImag(constant.ToFloat(imag))) } // 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(), errors.NumericOverflow, "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().IsInterface() && !r.Type().IsInterface() { // Can't mix bool with non-bool, string with non-string. if l.Type().IsBoolean() != r.Type().IsBoolean() { return l, r } if l.Type().IsString() != r.Type().IsString() { 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 nil with anything untyped. 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) } // callOrChan reports whether n is a call or channel operation. func callOrChan(n ir.Node) bool { switch n.Op() { case ir.OAPPEND, ir.OCALL, ir.OCALLFUNC, ir.OCALLINTER, ir.OCALLMETH, ir.OCAP, ir.OCLEAR, ir.OCLOSE, ir.OCOMPLEX, ir.OCOPY, ir.ODELETE, ir.OIMAG, ir.OLEN, ir.OMAKE, ir.OMAX, ir.OMIN, ir.ONEW, ir.OPANIC, ir.OPRINT, ir.OPRINTN, ir.OREAL, ir.ORECOVER, ir.ORECV, ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESLICEDATA, ir.OUNSAFESTRING, ir.OUNSAFESTRINGDATA: return true } return false } // 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 tr.Alignment() } return tr.Size() case ir.OOFFSETOF: // must be a selector. n := n.(*ir.UnaryExpr) // ODOT and ODOTPTR are allowed in case the OXDOT transformation has // already happened (e.g. during -G=3 stenciling). if n.X.Op() != ir.OXDOT && n.X.Op() != ir.ODOT && n.X.Op() != ir.ODOTPTR { 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 // Implicit dot may already be resolved for instantiating generic function. So we // need to remove any implicit dot until we reach the first non-implicit one, it's // the right base selector. See issue #53137. var clobberBase func(n ir.Node) ir.Node clobberBase = func(n ir.Node) ir.Node { if sel, ok := n.(*ir.SelectorExpr); ok && sel.Implicit() { return clobberBase(sel.X) } return n } sbase = clobberBase(sbase) tsel := Expr(sel) n.X = tsel if tsel.Type() == nil { return 0 } switch tsel.Op() { case ir.ODOT, ir.ODOTPTR: break case ir.OMETHVALUE: 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 }