tc_const.mx raw
1 package main
2
3 import (
4 "go/constant"
5 "go/token"
6 "math/big"
7 "bytes"
8 "strconv"
9 )
10
11 // checkConstGroup processes a sequence of ConstDecls that share a Group.
12 // iota advances per ConstDecl position. Inherited type/value expressions
13 // carry forward when a ConstDecl has no explicit values (standard Go rule).
14 func (c *Checker) checkConstGroup(decls []*ConstDecl) {
15 var prevType Expr
16 var prevValues Expr
17 for i, d := range decls {
18 typ := d.Type
19 vals := d.Values
20 if vals == nil {
21 // inherit from previous spec
22 typ = prevType
23 vals = prevValues
24 } else {
25 prevType = typ
26 prevValues = vals
27 }
28 c.checkConstDeclAt(d, typ, vals, int64(i))
29 }
30 }
31
32 func (c *Checker) checkConstDeclAt(d *ConstDecl, typExpr Expr, valExpr Expr, iotaVal int64) {
33 var typ Type
34 if typExpr != nil {
35 typ = c.resolveTypeExpr(typExpr)
36 }
37
38 var vals []constant.Value
39 if valExpr != nil {
40 vals = c.evalConstExprList(valExpr, iotaVal)
41 }
42
43 for i, name := range d.NameList {
44 if name.Value == "_" {
45 continue
46 }
47 obj, ok := c.pkg.scope.Lookup(name.Value).(*TCConst)
48 if !ok {
49 continue
50 }
51 var cv constant.Value
52 if i < len(vals) {
53 cv = vals[i]
54 }
55 obj.val = cv
56 if typ != nil {
57 obj.typ = typ
58 } else if cv != nil {
59 obj.typ = untypedTypeOf(cv)
60 }
61 if c.info != nil {
62 c.info.Defs[name] = obj
63 }
64 }
65 }
66
67 // evalConstExprList evaluates a (possibly multi-value) const expression.
68 func (c *Checker) evalConstExprList(e Expr, iotaVal int64) []constant.Value {
69 if l, ok := e.(*ListExpr); ok {
70 var out []constant.Value
71 for _, el := range l.ElemList {
72 v := c.evalConst(el, iotaVal)
73 out = append(out, v)
74 }
75 return out
76 }
77 v := c.evalConst(e, iotaVal)
78 return []constant.Value{v}
79 }
80
81 // evalConst evaluates a constant expression, returning a constant.Value.
82 // Returns nil if the expression is not a constant.
83 func (c *Checker) evalConst(e Expr, iotaVal int64) constant.Value {
84 if e == nil {
85 return nil
86 }
87 switch e := e.(type) {
88 case *BasicLit:
89 return evalBasicLit(e)
90 case *Name:
91 if e.Value == "iota" {
92 return constant.MakeInt64(iotaVal)
93 }
94 _, obj := c.lookup(e.Value, c.pkg.scope)
95 if obj == nil {
96 return nil
97 }
98 if k, ok := obj.(*TCConst); ok {
99 if cv, ok := k.val.(constant.Value); ok {
100 return cv
101 }
102 }
103 return nil
104 case *Operation:
105 return c.evalConstOp(e, iotaVal)
106 case *ParenExpr:
107 return c.evalConst(e.X, iotaVal)
108 case *CallExpr:
109 // len("string literal") is a constant in Go.
110 if id, ok := e.Fun.(*Name); ok && id.Value == "len" && len(e.ArgList) == 1 {
111 if lit, ok := e.ArgList[0].(*BasicLit); ok && lit.Kind == StringLit {
112 s := constant.StringVal(evalBasicLit(lit))
113 return constant.MakeInt64(int64(len(s)))
114 }
115 }
116 // unsafe.Sizeof/Alignof/Offsetof - return a placeholder int constant.
117 if sel, ok := e.Fun.(*SelectorExpr); ok {
118 if pkg, ok := sel.X.(*Name); ok && pkg.Value == "unsafe" {
119 switch sel.Sel.Value {
120 case "Sizeof", "Alignof", "Offsetof":
121 return constant.MakeInt64(8) // conservative placeholder (pointer size)
122 }
123 }
124 }
125 // Type conversion of constant: T(x).
126 if len(e.ArgList) != 1 {
127 return nil
128 }
129 inner := c.evalConst(e.ArgList[0], iotaVal)
130 if inner == nil {
131 return nil
132 }
133 targetType := c.resolveTypeExpr(e.Fun)
134 if targetType == nil {
135 return nil
136 }
137 return convertConst(inner, targetType)
138 }
139 return nil
140 }
141
142 func evalBasicLit(e *BasicLit) constant.Value {
143 switch e.Kind {
144 case IntLit:
145 return constant.MakeFromLiteral(e.Value, token.INT, 0)
146 case FloatLit:
147 return constant.MakeFromLiteral(e.Value, token.FLOAT, 0)
148 case StringLit:
149 return constant.MakeFromLiteral(e.Value, token.STRING, 0)
150 case RuneLit:
151 return constant.MakeFromLiteral(e.Value, token.CHAR, 0)
152 }
153 return nil
154 }
155
156 func (c *Checker) evalConstOp(e *Operation, iotaVal int64) constant.Value {
157 if e.Y == nil {
158 // unary
159 x := c.evalConst(e.X, iotaVal)
160 if x == nil {
161 return nil
162 }
163 op := syntaxOpToToken(e.Op)
164 if op == token.ILLEGAL {
165 return nil
166 }
167 return constant.UnaryOp(op, x, 0)
168 }
169 // binary
170 x := c.evalConst(e.X, iotaVal)
171 y := c.evalConst(e.Y, iotaVal)
172 if x == nil || y == nil {
173 return nil
174 }
175 op := syntaxOpToToken(e.Op)
176 if op == token.ILLEGAL {
177 return nil
178 }
179 if op == token.SHL || op == token.SHR {
180 shift, ok := constant.Uint64Val(y)
181 if !ok {
182 return nil
183 }
184 return constant.Shift(x, op, uint(shift))
185 }
186 // Moxie: | on string constants is concat. The syntax AST has Or (|) because
187 // rewriteConstPipes only runs on the go/ast side, not on syntax.File.
188 if op == token.OR && x.Kind() == constant.String && y.Kind() == constant.String {
189 xs := constant.StringVal(x)
190 ys := constant.StringVal(y)
191 return constant.MakeString(xs | ys)
192 }
193 // Comparison operators must use constant.Compare, not BinaryOp.
194 switch op {
195 case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
196 return constant.MakeBool(constant.Compare(x, op, y))
197 }
198 return constant.BinaryOp(x, op, y)
199 }
200
201 func syntaxOpToToken(op Operator) token.Token {
202 switch op {
203 case Add:
204 return token.ADD
205 case Sub:
206 return token.SUB
207 case Mul:
208 return token.MUL
209 case Div:
210 return token.QUO
211 case Rem:
212 return token.REM
213 case And:
214 return token.AND
215 case Or:
216 return token.OR
217 case Xor:
218 return token.XOR
219 case Shl:
220 return token.SHL
221 case Shr:
222 return token.SHR
223 case AndNot:
224 return token.AND_NOT
225 case Not:
226 return token.NOT
227 case Eql:
228 return token.EQL
229 case Neq:
230 return token.NEQ
231 case Lss:
232 return token.LSS
233 case Leq:
234 return token.LEQ
235 case Gtr:
236 return token.GTR
237 case Geq:
238 return token.GEQ
239 }
240 return token.ILLEGAL
241 }
242
243 // convertConst converts a constant value to the representation expected by
244 // a given target type. Handles explicit type conversions in const exprs.
245 func convertConst(v constant.Value, target Type) constant.Value {
246 b, ok := safeUnderlying(target).(*Basic)
247 if !ok {
248 return v
249 }
250 switch {
251 case b.info&IsInteger != 0:
252 i, _ := constant.Int64Val(v)
253 return constant.MakeInt64(i)
254 case b.info&IsFloat != 0:
255 f, _ := constant.Float64Val(v)
256 return constant.MakeFloat64(f)
257 case b.info&IsString != 0:
258 if v.Kind() == constant.Int {
259 n, _ := constant.Uint64Val(v)
260 r := rune(n)
261 return constant.MakeString(string(r))
262 }
263 return v
264 }
265 return v
266 }
267
268 // untypedTypeOf returns the untyped type for a constant value.
269 func untypedTypeOf(v constant.Value) Type {
270 switch v.Kind() {
271 case constant.Bool:
272 return Typ[UntypedBool]
273 case constant.Int:
274 return Typ[UntypedInt]
275 case constant.Float:
276 return Typ[UntypedFloat]
277 case constant.String:
278 return Typ[UntypedString]
279 }
280 return nil
281 }
282
283 // constInt64 extracts the int64 value of a constant, used for array lengths etc.
284 // Returns 0 and false if not a constant integer.
285 func constInt64(v constant.Value) (int64, bool) {
286 if v == nil || v.Kind() != constant.Int {
287 return 0, false
288 }
289 n, exact := constant.Int64Val(v)
290 return n, exact
291 }
292
293 // IsConstExpr reports whether e is a constant expression that can be evaluated
294 // at compile time (used to gate array length resolution).
295 func (c *Checker) IsConstExpr(e Expr) bool {
296 return c.evalConst(e, 0) != nil
297 }
298
299 // evalArrayLen evaluates a constant array length expression.
300 func (c *Checker) evalArrayLen(e Expr) int64 {
301 v := c.evalConst(e, 0)
302 if v == nil {
303 return 0
304 }
305 n, _ := constInt64(v)
306 return n
307 }
308
309 // interpString converts a Go string literal value to a Go string.
310 // Handles quoted forms: "...", `...`, '...'.
311 func interpString(s string) string {
312 if len(s) < 2 {
313 return s
314 }
315 if s[0] == '`' {
316 return s[1 : len(s)-1]
317 }
318 unquoted, err := strconv.Unquote(s)
319 if err != nil {
320 return bytes.Trim(s, `"`)
321 }
322 return unquoted
323 }
324
325 // bigIntVal extracts a *big.Int from a constant for use in overflow checks.
326 func bigIntVal(v constant.Value) *big.Int {
327 if v.Kind() != constant.Int {
328 return nil
329 }
330 b := &big.Int{}
331 s := v.ExactString()
332 b.SetString(s, 10)
333 return b
334 }
335