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