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 int32 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 case *SelectorExpr:
139 pkgName, ok := e.X.(*Name)
140 if !ok {
141 return nil
142 }
143 _, obj := c.lookup(pkgName.Value, c.pkg.scope)
144 if obj == nil {
145 return nil
146 }
147 pkgObj, ok := obj.(*PkgName)
148 if !ok {
149 return nil
150 }
151 if pkgObj.imported == nil {
152 return nil
153 }
154 member := pkgObj.imported.scope.Lookup(e.Sel.Value)
155 if member == nil {
156 return nil
157 }
158 if k, ok := member.(*TCConst); ok {
159 if cv, ok := k.val.(constant.Value); ok {
160 return cv
161 }
162 }
163 return nil
164 }
165 return nil
166 }
167
168 func evalBasicLit(e *BasicLit) constant.Value {
169 switch e.Kind {
170 case IntLit:
171 return constant.MakeFromLiteral(e.Value, token.INT, 0)
172 case FloatLit:
173 return constant.MakeFromLiteral(e.Value, token.FLOAT, 0)
174 case StringLit:
175 return constant.MakeFromLiteral(e.Value, token.STRING, 0)
176 case RuneLit:
177 return constant.MakeFromLiteral(e.Value, token.CHAR, 0)
178 }
179 return nil
180 }
181
182 func (c *Checker) evalConstOp(e *Operation, iotaVal int64) constant.Value {
183 if e.Y == nil {
184 // unary
185 x := c.evalConst(e.X, iotaVal)
186 if x == nil {
187 return nil
188 }
189 op := syntaxOpToToken(e.Op)
190 if op == token.ILLEGAL {
191 return nil
192 }
193 return constant.UnaryOp(op, x, 0)
194 }
195 // binary
196 x := c.evalConst(e.X, iotaVal)
197 y := c.evalConst(e.Y, iotaVal)
198 if x == nil || y == nil {
199 return nil
200 }
201 op := syntaxOpToToken(e.Op)
202 if op == token.ILLEGAL {
203 return nil
204 }
205 if op == token.SHL || op == token.SHR {
206 shift, ok := constant.Uint64Val(y)
207 if !ok {
208 return nil
209 }
210 return constant.Shift(x, op, uint32(shift))
211 }
212 // Moxie: | on string constants is concat. The syntax AST has Or (|) because
213 // rewriteConstPipes only runs on the go/ast side, not on syntax.File.
214 if op == token.OR && x.Kind() == constant.String && y.Kind() == constant.String {
215 xs := constant.StringVal(x)
216 ys := constant.StringVal(y)
217 return constant.MakeString(xs | ys)
218 }
219 // Comparison operators must use constant.Compare, not BinaryOp.
220 switch op {
221 case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
222 return constant.MakeBool(constant.Compare(x, op, y))
223 }
224 return constant.BinaryOp(x, op, y)
225 }
226
227 func syntaxOpToToken(op Operator) token.Token {
228 switch op {
229 case Add:
230 return token.ADD
231 case Sub:
232 return token.SUB
233 case Mul:
234 return token.MUL
235 case Div:
236 return token.QUO
237 case Rem:
238 return token.REM
239 case And:
240 return token.AND
241 case Or:
242 return token.OR
243 case Xor:
244 return token.XOR
245 case Shl:
246 return token.SHL
247 case Shr:
248 return token.SHR
249 case AndNot:
250 return token.AND_NOT
251 case Not:
252 return token.NOT
253 case Eql:
254 return token.EQL
255 case Neq:
256 return token.NEQ
257 case Lss:
258 return token.LSS
259 case Leq:
260 return token.LEQ
261 case Gtr:
262 return token.GTR
263 case Geq:
264 return token.GEQ
265 }
266 return token.ILLEGAL
267 }
268
269 // convertConst converts a constant value to the representation expected by
270 // a given target type. Handles explicit type conversions in const exprs.
271 func convertConst(v constant.Value, target Type) constant.Value {
272 b, ok := target.Underlying().(*Basic)
273 if !ok {
274 return v
275 }
276 switch {
277 case b.info&IsInteger != 0:
278 i, _ := constant.Int64Val(v)
279 return constant.MakeInt64(i)
280 case b.info&IsFloat != 0:
281 f, _ := constant.Float64Val(v)
282 return constant.MakeFloat64(f)
283 case b.info&IsString != 0:
284 if v.Kind() == constant.Int {
285 n, _ := constant.Uint64Val(v)
286 r := rune(n)
287 return constant.MakeString(string(r))
288 }
289 return v
290 }
291 return v
292 }
293
294 // untypedTypeOf returns the untyped type for a constant value.
295 func untypedTypeOf(v constant.Value) Type {
296 switch v.Kind() {
297 case constant.Bool:
298 return Typ[UntypedBool]
299 case constant.Int:
300 return Typ[UntypedInt]
301 case constant.Float:
302 return Typ[UntypedFloat]
303 case constant.String:
304 return Typ[UntypedString]
305 }
306 return nil
307 }
308
309 // constInt64 extracts the int64 value of a constant, used for array lengths etc.
310 // Returns 0 and false if not a constant integer.
311 func constInt64(v constant.Value) (int64, bool) {
312 if v == nil || v.Kind() != constant.Int {
313 return 0, false
314 }
315 n, exact := constant.Int64Val(v)
316 return n, exact
317 }
318
319 // IsConstExpr reports whether e is a constant expression that can be evaluated
320 // at compile time (used to gate array length resolution).
321 func (c *Checker) IsConstExpr(e Expr) bool {
322 return c.evalConst(e, 0) != nil
323 }
324
325 // evalArrayLen evaluates a constant array length expression.
326 func (c *Checker) evalArrayLen(e Expr) int64 {
327 v := c.evalConst(e, 0)
328 if v == nil {
329 return 0
330 }
331 n, _ := constInt64(v)
332 return n
333 }
334
335 // interpString converts a Go string literal value to a Go string.
336 // Handles quoted forms: "...", `...`, '...'.
337 func interpString(s string) string {
338 if len(s) < 2 {
339 return s
340 }
341 if s[0] == '`' {
342 return s[1 : len(s)-1]
343 }
344 unquoted, err := strconv.Unquote(s)
345 if err != nil {
346 return bytes.Trim(s, `"`)
347 }
348 return unquoted
349 }
350
351 // bigIntVal extracts a *big.Int from a constant for use in overflow checks.
352 func bigIntVal(v constant.Value) *big.Int {
353 if v.Kind() != constant.Int {
354 return nil
355 }
356 b := &big.Int{}
357 s := v.ExactString()
358 b.SetString(s, 10)
359 return b
360 }
361