| // 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 gc | |
| import ( | |
| "cmd/compile/internal/types" | |
| "cmd/internal/src" | |
| "fmt" | |
| "math/big" | |
| "strings" | |
| ) | |
| // Ctype describes the constant kind of an "ideal" (untyped) constant. | |
| type Ctype uint8 | |
| const ( | |
| CTxxx Ctype = iota | |
| CTINT | |
| CTRUNE | |
| CTFLT | |
| CTCPLX | |
| CTSTR | |
| CTBOOL | |
| CTNIL | |
| ) | |
| 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) { | |
| 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()) | |
| } | |
| } | |
| 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 | |
| } | |
| // 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) | |
| } | |
| // 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) { | |
| // 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 fv | |
| } | |
| // 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) { | |
| // 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 cv | |
| } | |
| // 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) } | |
| // 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 *Node, t *types.Type, explicit bool, context func() string) *Node { | |
| if explicit && t == nil { | |
| Fatalf("explicit conversion missing type") | |
| } | |
| if t != nil && t.IsUntyped() { | |
| 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 | |
| } | |
| if n.Op == OLITERAL { | |
| // Can't always set n.Type directly on OLITERAL nodes. | |
| // See discussion on CL 20813. | |
| n = n.rawcopy() | |
| } | |
| // Nil is technically not a constant, so handle it specially. | |
| if n.Type.Etype == TNIL { | |
| if t == nil { | |
| yyerror("use of untyped nil") | |
| n.SetDiag(true) | |
| n.Type = nil | |
| return n | |
| } | |
| if !t.HasNil() { | |
| // Leave for caller to handle. | |
| return n | |
| } | |
| n.Type = t | |
| return n | |
| } | |
| if t == nil || !okforconst[t.Etype] { | |
| t = defaultType(n.Type) | |
| } | |
| switch n.Op { | |
| default: | |
| Fatalf("unexpected untyped expression: %v", n) | |
| case OLITERAL: | |
| v := convertVal(n.Val(), t, explicit) | |
| if v.U == nil { | |
| break | |
| } | |
| n.SetVal(v) | |
| n.Type = t | |
| return n | |
| case OPLUS, ONEG, OBITNOT, ONOT, OREAL, 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 | |
| return n | |
| } | |
| n.Type = t | |
| return n | |
| case OADD, OSUB, OMUL, ODIV, OMOD, OOR, OXOR, OAND, OANDNOT, OOROR, OANDAND, 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 | |
| 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 | |
| return n | |
| } | |
| n.Type = t | |
| return n | |
| case OEQ, ONE, OLT, OLE, OGT, OGE: | |
| if !t.IsBoolean() { | |
| break | |
| } | |
| n.Type = 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 | |
| } | |
| return n | |
| } | |
| if !n.Diag() { | |
| if !t.Broke() { | |
| if explicit { | |
| yyerror("cannot convert %L to type %v", n, t) | |
| } else if context != nil { | |
| yyerror("cannot use %L as type %v in %s", n, t, context()) | |
| } else { | |
| yyerror("cannot use %L as type %v", n, t) | |
| } | |
| } | |
| n.SetDiag(true) | |
| } | |
| n.Type = nil | |
| return n | |
| } | |
| func operandType(op Op, t *types.Type) *types.Type { | |
| switch op { | |
| case OCOMPLEX: | |
| if t.IsComplex() { | |
| return floatForComplex(t) | |
| } | |
| case OREAL, OIMAG: | |
| if t.IsFloat() { | |
| return complexForFloat(t) | |
| } | |
| default: | |
| if okfor[op][t.Etype] { | |
| 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 Val, t *types.Type, explicit bool) Val { | |
| switch ct := v.Ctype(); ct { | |
| case CTBOOL: | |
| if t.IsBoolean() { | |
| return v | |
| } | |
| case CTSTR: | |
| if t.IsString() { | |
| return v | |
| } | |
| case CTINT, CTRUNE: | |
| if explicit && t.IsString() { | |
| return tostr(v) | |
| } | |
| fallthrough | |
| case CTFLT, CTCPLX: | |
| switch { | |
| case t.IsInteger(): | |
| v = toint(v) | |
| overflow(v, t) | |
| return v | |
| case t.IsFloat(): | |
| v = toflt(v) | |
| v = Val{truncfltlit(v.U.(*Mpflt), t)} | |
| return v | |
| case t.IsComplex(): | |
| v = tocplx(v) | |
| v = Val{trunccmplxlit(v.U.(*Mpcplx), t)} | |
| return v | |
| } | |
| } | |
| return Val{} | |
| } | |
| 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 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()) | |
| } | |
| v.U = f | |
| } | |
| return 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 | |
| } | |
| 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 | |
| case *Mpcplx: | |
| i := new(Mpint) | |
| if !i.SetFloat(&u.Real) || u.Imag.CmpFloat64(0) != 0 { | |
| yyerror("constant %v truncated to integer", u.GoString()) | |
| } | |
| v.U = i | |
| } | |
| return v | |
| } | |
| 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 | |
| } | |
| func overflow(v Val, t *types.Type) bool { | |
| // v has already been converted | |
| // to appropriate form for t. | |
| if t == nil || t.Etype == TIDEAL { | |
| return false | |
| } | |
| // Only uintptrs may be converted to pointers, which cannot overflow. | |
| if t.IsPtr() || t.IsUnsafePtr() { | |
| return false | |
| } | |
| if doesoverflow(v, t) { | |
| yyerror("constant %v overflows %v", v, 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()) | |
| } | |
| v.U = 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 | |
| // 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)) | |
| } | |
| 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 OEQ, ONE, OLT, OLE, OGT, OGE: | |
| if nl.Op == OLITERAL && nr.Op == OLITERAL { | |
| setboolconst(n, compareOp(nl.Val(), op, nr.Val())) | |
| } | |
| case OLSH, ORSH: | |
| if nl.Op == OLITERAL && nr.Op == OLITERAL { | |
| setconst(n, shiftOp(nl.Val(), op, nr.Val())) | |
| } | |
| case OCONV, ORUNESTR: | |
| if okforconst[n.Type.Etype] && nl.Op == OLITERAL { | |
| setconst(n, convertVal(nl.Val(), n.Type, true)) | |
| } | |
| case OCONVNOP: | |
| if okforconst[n.Type.Etype] && nl.Op == OLITERAL { | |
| // set so n.Orig gets OCONV instead of OCONVNOP | |
| n.Op = OCONV | |
| setconst(n, nl.Val()) | |
| } | |
| case 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 | |
| var strs []string | |
| i2 := i1 | |
| for i2 < len(s) && Isconst(s[i2], CTSTR) { | |
| 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:]...) | |
| } | |
| } | |
| if len(s) == 1 && Isconst(s[0], CTSTR) { | |
| n.Op = OLITERAL | |
| n.SetVal(s[0].Val()) | |
| } else { | |
| n.List.Set(s) | |
| } | |
| case OCAP, OLEN: | |
| switch nl.Type.Etype { | |
| case TSTRING: | |
| if Isconst(nl, CTSTR) { | |
| setintconst(n, int64(len(nl.StringVal()))) | |
| } | |
| case 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 | |
| } | |
| 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 | |
| } | |
| 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 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 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 | |
| } | |
| } | |
| Fatalf("compareOp: bad comparison: %v %v %v", x, op, y) | |
| panic("unreachable") | |
| } | |
| 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 | |
| } | |
| Fatalf("cmpZero: want comparison operator, got %v", op) | |
| panic("unreachable") | |
| } | |
| 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} | |
| } | |
| Fatalf("binaryOp: bad operation: %v %v %v", x, op, y) | |
| panic("unreachable") | |
| } | |
| 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 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} | |
| } | |
| // 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) | |
| } | |
| // Check range. | |
| 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)}) | |
| } | |
| } | |
| } | |
| 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) | |
| 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 | |
| } | |
| // 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 *Node, r *Node, force bool) (*Node, *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 l.isNil() || r.isNil() { | |
| return l, r | |
| } | |
| 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 | |
| } | |
| Fatalf("bad type %v", t) | |
| panic("unreachable") | |
| } | |
| func mixUntyped(t1, t2 *types.Type) *types.Type { | |
| t := t1 | |
| if ctype(t2) > ctype(t1) { | |
| t = t2 | |
| } | |
| return t | |
| } | |
| func defaultType(t *types.Type) *types.Type { | |
| if !t.IsUntyped() || t.Etype == TNIL { | |
| return t | |
| } | |
| switch t { | |
| case types.UntypedBool: | |
| return types.Types[TBOOL] | |
| case types.UntypedString: | |
| return types.Types[TSTRING] | |
| case types.UntypedInt: | |
| return types.Types[TINT] | |
| case types.UntypedRune: | |
| return types.Runetype | |
| case types.UntypedFloat: | |
| return types.Types[TFLOAT64] | |
| case types.UntypedComplex: | |
| return types.Types[TCOMPLEX128] | |
| } | |
| 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 | |
| } | |
| } | |
| } | |
| return false | |
| } | |
| // 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 *Node) int64 { | |
| if n.Op != OLITERAL { | |
| 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 { | |
| return -1 | |
| } | |
| if vi.Cmp(maxintval[TINT]) > 0 { | |
| return -2 | |
| } | |
| return vi.Int64() | |
| } | |
| // isGoConst reports whether n is a Go language constant (as opposed to a | |
| // compile-time constant). | |
| // | |
| // 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 hascallchan(n *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: | |
| return true | |
| } | |
| if hascallchan(n.Left) || hascallchan(n.Right) { | |
| return true | |
| } | |
| for _, n1 := range n.List.Slice() { | |
| if hascallchan(n1) { | |
| return true | |
| } | |
| } | |
| for _, n2 := range n.Rlist.Slice() { | |
| if hascallchan(n2) { | |
| 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 *Node, what, where string) { | |
| if n.Op == OCONVIFACE && n.Implicit() { | |
| n = n.Left | |
| } | |
| if !n.isGoConst() { | |
| return | |
| } | |
| if n.Type.IsUntyped() { | |
| 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[TUINT8] | |
| case types.Runetype: | |
| typ = types.Types[TINT32] | |
| } | |
| k := constSetKey{typ, n.Val().Interface()} | |
| if hasUniquePos(n) { | |
| pos = n.Pos | |
| } | |
| if s.m == nil { | |
| s.m = make(map[constSetKey]src.XPos) | |
| } | |
| if prevPos, isDup := s.m[k]; isDup { | |
| yyerrorl(pos, "duplicate %s %s in %s\n\tprevious %s at %v", | |
| what, nodeAndVal(n), where, | |
| what, linestr(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 *Node) string { | |
| show := n.String() | |
| val := n.Val().Interface() | |
| if s := fmt.Sprintf("%#v", val); show != s { | |
| show += " (value " + s + ")" | |
| } | |
| return show | |
| } |