| // 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" |
| "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 0 |
| 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{} |
| |
| // Int64 returns n as an int64. |
| // n must be an integer or rune constant. |
| func (n *Node) Int64() int64 { |
| if !Isconst(n, CTINT) { |
| Fatalf("Int64(%v)", n) |
| } |
| return n.Val().U.(*Mpint).Int64() |
| } |
| |
| // CanInt64 reports whether it is safe to call Int64() 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.Int64()) == 0 |
| } |
| |
| // Bool returns n as a bool. |
| // n must be a boolean constant. |
| func (n *Node) Bool() bool { |
| if !Isconst(n, CTBOOL) { |
| Fatalf("Bool(%v)", n) |
| } |
| return n.Val().U.(bool) |
| } |
| |
| // 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 |
| } |
| |
| // canReuseNode indicates whether it is known to be safe |
| // to reuse a Node. |
| type canReuseNode bool |
| |
| const ( |
| noReuse canReuseNode = false // not necessarily safe to reuse |
| reuseOK canReuseNode = true // safe to reuse |
| ) |
| |
| // convert n, if literal, to type t. |
| // implicit conversion. |
| // The result of convlit MUST be assigned back to n, e.g. |
| // n.Left = convlit(n.Left, t) |
| func convlit(n *Node, t *types.Type) *Node { |
| return convlit1(n, t, false, noReuse) |
| } |
| |
| // convlit1 converts n, if literal, to type t. |
| // It returns a new node if necessary. |
| // The result of convlit1 MUST be assigned back to n, e.g. |
| // n.Left = convlit1(n.Left, t, explicit, reuse) |
| func convlit1(n *Node, t *types.Type, explicit bool, reuse canReuseNode) *Node { |
| if n == nil || t == nil || n.Type == nil || t.IsUntyped() || n.Type == t { |
| return n |
| } |
| if !explicit && !n.Type.IsUntyped() { |
| return n |
| } |
| |
| if n.Op == OLITERAL && !reuse { |
| // Can't always set n.Type directly on OLITERAL nodes. |
| // See discussion on CL 20813. |
| n = n.rawcopy() |
| reuse = true |
| } |
| |
| switch n.Op { |
| default: |
| if n.Type == types.Idealbool { |
| if !t.IsBoolean() { |
| t = types.Types[TBOOL] |
| } |
| switch n.Op { |
| case ONOT: |
| n.Left = convlit(n.Left, t) |
| case OANDAND, OOROR: |
| n.Left = convlit(n.Left, t) |
| n.Right = convlit(n.Right, t) |
| } |
| n.Type = t |
| } |
| |
| if n.Type.IsUntyped() { |
| if t.IsInterface() { |
| n.Left, n.Right = defaultlit2(n.Left, n.Right, true) |
| n.Type = n.Left.Type // same as n.Right.Type per defaultlit2 |
| } else { |
| n.Left = convlit(n.Left, t) |
| n.Right = convlit(n.Right, t) |
| n.Type = t |
| } |
| } |
| |
| return n |
| |
| // target is invalid type for a constant? leave alone. |
| case OLITERAL: |
| if !okforconst[t.Etype] && n.Type.Etype != TNIL { |
| return defaultlitreuse(n, nil, reuse) |
| } |
| |
| case OLSH, ORSH: |
| n.Left = convlit1(n.Left, t, explicit && n.Left.Type.IsUntyped(), noReuse) |
| t = n.Left.Type |
| if t != nil && t.Etype == TIDEAL && n.Val().Ctype() != CTINT { |
| n.SetVal(toint(n.Val())) |
| } |
| if t != nil && !t.IsInteger() { |
| yyerror("invalid operation: %v (shift of type %v)", n, t) |
| t = nil |
| } |
| |
| n.Type = t |
| return n |
| |
| case OCOMPLEX: |
| if n.Type.Etype == TIDEAL { |
| switch t.Etype { |
| default: |
| // If trying to convert to non-complex type, |
| // leave as complex128 and let typechecker complain. |
| t = types.Types[TCOMPLEX128] |
| fallthrough |
| case types.TCOMPLEX128: |
| n.Type = t |
| n.Left = convlit(n.Left, types.Types[TFLOAT64]) |
| n.Right = convlit(n.Right, types.Types[TFLOAT64]) |
| |
| case TCOMPLEX64: |
| n.Type = t |
| n.Left = convlit(n.Left, types.Types[TFLOAT32]) |
| n.Right = convlit(n.Right, types.Types[TFLOAT32]) |
| } |
| } |
| |
| return n |
| } |
| |
| // avoid repeated calculations, errors |
| if types.Identical(n.Type, t) { |
| return n |
| } |
| |
| ct := consttype(n) |
| var et types.EType |
| if ct == 0 { |
| goto bad |
| } |
| |
| et = t.Etype |
| if et == TINTER { |
| if ct == CTNIL && n.Type == types.Types[TNIL] { |
| n.Type = t |
| return n |
| } |
| return defaultlitreuse(n, nil, reuse) |
| } |
| |
| switch ct { |
| default: |
| goto bad |
| |
| case CTNIL: |
| switch et { |
| default: |
| n.Type = nil |
| goto bad |
| |
| // let normal conversion code handle it |
| case TSTRING: |
| return n |
| |
| case TARRAY: |
| goto bad |
| |
| case TPTR, TUNSAFEPTR: |
| n.SetVal(Val{new(Mpint)}) |
| |
| case TCHAN, TFUNC, TINTER, TMAP, TSLICE: |
| break |
| } |
| |
| case CTSTR, CTBOOL: |
| if et != n.Type.Etype { |
| goto bad |
| } |
| |
| case CTINT, CTRUNE, CTFLT, CTCPLX: |
| if n.Type.Etype == TUNSAFEPTR && t.Etype != TUINTPTR { |
| goto bad |
| } |
| ct := n.Val().Ctype() |
| if isInt[et] { |
| switch ct { |
| default: |
| goto bad |
| |
| case CTCPLX, CTFLT, CTRUNE: |
| n.SetVal(toint(n.Val())) |
| fallthrough |
| |
| case CTINT: |
| overflow(n.Val(), t) |
| } |
| } else if isFloat[et] { |
| switch ct { |
| default: |
| goto bad |
| |
| case CTCPLX, CTINT, CTRUNE: |
| n.SetVal(toflt(n.Val())) |
| fallthrough |
| |
| case CTFLT: |
| n.SetVal(Val{truncfltlit(n.Val().U.(*Mpflt), t)}) |
| } |
| } else if isComplex[et] { |
| switch ct { |
| default: |
| goto bad |
| |
| case CTFLT, CTINT, CTRUNE: |
| n.SetVal(tocplx(n.Val())) |
| fallthrough |
| |
| case CTCPLX: |
| n.SetVal(Val{trunccmplxlit(n.Val().U.(*Mpcplx), t)}) |
| } |
| } else if et == types.TSTRING && (ct == CTINT || ct == CTRUNE) && explicit { |
| n.SetVal(tostr(n.Val())) |
| } else { |
| goto bad |
| } |
| } |
| |
| n.Type = t |
| return n |
| |
| bad: |
| if !n.Diag() { |
| if !t.Broke() { |
| yyerror("cannot convert %L to type %v", n, t) |
| } |
| n.SetDiag(true) |
| } |
| |
| if n.Type.IsUntyped() { |
| n = defaultlitreuse(n, nil, reuse) |
| } |
| return n |
| } |
| |
| 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 i int64 = 0xFFFD |
| if u.Cmp(minintval[TUINT32]) >= 0 && u.Cmp(maxintval[TUINT32]) <= 0 { |
| i = u.Int64() |
| } |
| v.U = string(i) |
| } |
| |
| return v |
| } |
| |
| func consttype(n *Node) Ctype { |
| if n == nil || n.Op != OLITERAL { |
| return 0 |
| } |
| 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 { |
| if nl.Type.IsInterface() != nr.Type.IsInterface() { |
| // Mixed interface/non-interface |
| // constant comparison means comparing |
| // nil interface with some typed |
| // constant, which is always unequal. |
| // E.g., interface{}(nil) == (*int)(nil). |
| setboolconst(n, op == ONE) |
| } else { |
| 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: |
| if okforconst[n.Type.Etype] && nl.Op == OLITERAL { |
| // TODO(mdempsky): There should be a convval function. |
| setconst(n, convlit1(nl, n.Type, true, false).Val()) |
| } |
| |
| 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].Val().U.(string)) |
| 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.Val().U.(string)))) |
| } |
| 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 == nil || nr == nil { |
| // TODO(mdempsky): Remove after early OAS2FUNC rewrite CL lands. |
| break |
| } |
| 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 CTNIL: |
| _, _ = x.U.(*NilVal), y.U.(*NilVal) // assert dynamic types match |
| switch op { |
| case OEQ: |
| return true |
| case ONE: |
| return false |
| } |
| |
| 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) |
| case OMOD, OOR, OAND, OANDNOT, OXOR: |
| // TODO(mdempsky): Move to typecheck; see #31060. |
| yyerror("invalid operation: operator %v not defined on untyped float", op) |
| return Val{} |
| 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{} |
| } |
| case OMOD, OOR, OAND, OANDNOT, OXOR: |
| // TODO(mdempsky): Move to typecheck; see #31060. |
| yyerror("invalid operation: operator %v not defined on untyped complex", op) |
| 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 CTFLT: |
| // TODO(mdempsky): Move to typecheck; see #31060. |
| yyerror("invalid operation: operator %v not defined on untyped float", op) |
| return Val{} |
| case CTCPLX: |
| // TODO(mdempsky): Move to typecheck; see #31060. |
| yyerror("invalid operation: operator %v not defined on untyped complex", op) |
| return Val{} |
| } |
| |
| 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) |
| |
| // Check range. |
| lno := setlineno(n) |
| overflow(v, n.Type) |
| lineno = lno |
| |
| // Truncate precision for non-ideal float. |
| if v.Ctype() == CTFLT && n.Type.Etype != TIDEAL { |
| n.SetVal(Val{truncfltlit(v.U.(*Mpflt), 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) |
| switch v.Ctype() { |
| default: |
| Fatalf("nodlit ctype %d", v.Ctype()) |
| |
| case CTSTR: |
| n.Type = types.Idealstring |
| |
| case CTBOOL: |
| n.Type = types.Idealbool |
| |
| case CTINT, CTRUNE, CTFLT, CTCPLX: |
| n.Type = types.Types[TIDEAL] |
| |
| case CTNIL: |
| n.Type = types.Types[TNIL] |
| } |
| |
| return n |
| } |
| |
| // idealkind returns a constant kind like consttype |
| // but for an arbitrary "ideal" (untyped constant) expression. |
| func idealkind(n *Node) Ctype { |
| if n == nil || !n.Type.IsUntyped() { |
| return CTxxx |
| } |
| |
| switch n.Op { |
| default: |
| return CTxxx |
| |
| case OLITERAL: |
| return n.Val().Ctype() |
| |
| // numeric kinds. |
| case OADD, |
| OAND, |
| OANDNOT, |
| OBITNOT, |
| ODIV, |
| ONEG, |
| OMOD, |
| OMUL, |
| OSUB, |
| OXOR, |
| OOR, |
| OPLUS: |
| k1 := idealkind(n.Left) |
| k2 := idealkind(n.Right) |
| if k1 > k2 { |
| return k1 |
| } else { |
| return k2 |
| } |
| |
| case OREAL, OIMAG: |
| return CTFLT |
| |
| case OCOMPLEX: |
| return CTCPLX |
| |
| case OADDSTR: |
| return CTSTR |
| |
| case OANDAND, |
| OEQ, |
| OGE, |
| OGT, |
| OLE, |
| OLT, |
| ONE, |
| ONOT, |
| OOROR: |
| return CTBOOL |
| |
| // shifts (beware!). |
| case OLSH, ORSH: |
| return idealkind(n.Left) |
| } |
| } |
| |
| // The result of defaultlit MUST be assigned back to n, e.g. |
| // n.Left = defaultlit(n.Left, t) |
| func defaultlit(n *Node, t *types.Type) *Node { |
| return defaultlitreuse(n, t, noReuse) |
| } |
| |
| // The result of defaultlitreuse MUST be assigned back to n, e.g. |
| // n.Left = defaultlitreuse(n.Left, t, reuse) |
| func defaultlitreuse(n *Node, t *types.Type, reuse canReuseNode) *Node { |
| if n == nil || !n.Type.IsUntyped() { |
| return n |
| } |
| |
| if n.Op == OLITERAL && !reuse { |
| n = n.rawcopy() |
| reuse = true |
| } |
| |
| lno := setlineno(n) |
| ctype := idealkind(n) |
| var t1 *types.Type |
| switch ctype { |
| default: |
| if t != nil { |
| return convlit(n, t) |
| } |
| |
| switch n.Val().Ctype() { |
| case CTNIL: |
| lineno = lno |
| if !n.Diag() { |
| yyerror("use of untyped nil") |
| n.SetDiag(true) |
| } |
| |
| n.Type = nil |
| case CTSTR: |
| t1 := types.Types[TSTRING] |
| n = convlit1(n, t1, false, reuse) |
| default: |
| yyerror("defaultlit: unknown literal: %v", n) |
| } |
| lineno = lno |
| return n |
| |
| case CTxxx: |
| Fatalf("defaultlit: idealkind is CTxxx: %+v", n) |
| |
| case CTBOOL: |
| t1 := types.Types[TBOOL] |
| if t != nil && t.IsBoolean() { |
| t1 = t |
| } |
| n = convlit1(n, t1, false, reuse) |
| lineno = lno |
| return n |
| |
| case CTINT: |
| t1 = types.Types[TINT] |
| case CTRUNE: |
| t1 = types.Runetype |
| case CTFLT: |
| t1 = types.Types[TFLOAT64] |
| case CTCPLX: |
| t1 = types.Types[TCOMPLEX128] |
| } |
| |
| // Note: n.Val().Ctype() can be CTxxx (not a constant) here |
| // in the case of an untyped non-constant value, like 1<<i. |
| v1 := n.Val() |
| if t != nil { |
| if t.IsInteger() { |
| t1 = t |
| v1 = toint(n.Val()) |
| } else if t.IsFloat() { |
| t1 = t |
| v1 = toflt(n.Val()) |
| } else if t.IsComplex() { |
| t1 = t |
| v1 = tocplx(n.Val()) |
| } |
| if n.Val().Ctype() != CTxxx { |
| n.SetVal(v1) |
| } |
| } |
| |
| if n.Val().Ctype() != CTxxx { |
| overflow(n.Val(), t1) |
| } |
| n = convlit1(n, t1, false, reuse) |
| lineno = lno |
| return n |
| } |
| |
| // 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 |
| } |
| |
| if l.Type.IsBoolean() { |
| l = convlit(l, types.Types[TBOOL]) |
| r = convlit(r, types.Types[TBOOL]) |
| } |
| |
| lkind := idealkind(l) |
| rkind := idealkind(r) |
| if lkind == CTCPLX || rkind == CTCPLX { |
| l = convlit(l, types.Types[TCOMPLEX128]) |
| r = convlit(r, types.Types[TCOMPLEX128]) |
| return l, r |
| } |
| |
| if lkind == CTFLT || rkind == CTFLT { |
| l = convlit(l, types.Types[TFLOAT64]) |
| r = convlit(r, types.Types[TFLOAT64]) |
| return l, r |
| } |
| |
| if lkind == CTRUNE || rkind == CTRUNE { |
| l = convlit(l, types.Runetype) |
| r = convlit(r, types.Runetype) |
| return l, r |
| } |
| |
| l = convlit(l, types.Types[TINT]) |
| r = convlit(r, types.Types[TINT]) |
| |
| return l, r |
| } |
| |
| // strlit returns the value of a literal string Node as a string. |
| func strlit(n *Node) string { |
| return n.Val().U.(string) |
| } |
| |
| // TODO(gri) smallintconst is only used in one place - can we used indexconst? |
| 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]*Node |
| } |
| |
| type constSetKey struct { |
| typ *types.Type |
| val interface{} |
| } |
| |
| // add adds constant expressions to s. If a constant expression of |
| // equal value and identical type has already been added, then that |
| // type expression is returned. Otherwise, add returns nil. |
| // |
| // add also returns nil if n is not a Go constant expression. |
| // |
| // n must not be an untyped constant. |
| func (s *constSet) add(n *Node) *Node { |
| if n.Op == OCONVIFACE && n.Implicit() { |
| n = n.Left |
| } |
| |
| if !n.isGoConst() { |
| return nil |
| } |
| 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 s.m == nil { |
| s.m = make(map[constSetKey]*Node) |
| } |
| old, dup := s.m[k] |
| if !dup { |
| s.m[k] = n |
| } |
| return old |
| } |