tc_expr.mx raw
1 package main
2
3 // checkExpr type-checks an expression and returns its type.
4 // It also records the type info in c.info if non-nil.
5 func (c *Checker) checkExpr(e Expr, scope *Scope) Type {
6 if e == nil {
7 return nil
8 }
9 tv := c.typeExpr(e, scope)
10 c.record(e, tv)
11 return tv.Type
12 }
13
14 // typeExpr computes the TCTypeAndValue for expression e.
15 func (c *Checker) typeExpr(e Expr, scope *Scope) TCTypeAndValue {
16 switch e := e.(type) {
17 case *Name:
18 return c.typeIdent(e, scope)
19 case *BasicLit:
20 return c.typeBasicLit(e)
21 case *Operation:
22 return c.typeOperation(e, scope)
23 case *CallExpr:
24 return c.typeCall(e, scope)
25 case *SelectorExpr:
26 return c.typeSelector(e, scope)
27 case *IndexExpr:
28 return c.typeIndex(e, scope)
29 case *SliceExpr:
30 return c.typeSlice(e, scope)
31 case *AssertExpr:
32 return c.typeAssert(e, scope)
33 case *TypeSwitchGuard:
34 return TCTypeAndValue{Type: c.checkExpr(e.X, scope), mode: modeValue}
35 case *CompositeLit:
36 return c.typeCompositeLit(e, scope)
37 case *FuncLit:
38 return c.typeFuncLit(e, scope)
39 case *KeyValueExpr:
40 // key:value - type is the value's type
41 c.checkExpr(e.Key, scope)
42 return c.typeExpr(e.Value, scope)
43 case *ParenExpr:
44 return c.typeExpr(e.X, scope)
45 case *ListExpr:
46 // multi-value list (tuple unpacking context)
47 var last TCTypeAndValue
48 for _, el := range e.ElemList {
49 last = c.typeExpr(el, scope)
50 }
51 return last
52 // type expressions used where a value is expected
53 case *SliceType, *ArrayType, *MapType,
54 *ChanType, *StructType, *InterfaceType,
55 *FuncType, *DotsType:
56 typ := c.resolveTypeExpr(e)
57 return TCTypeAndValue{Type: typ, mode: modeType}
58 }
59 return TCTypeAndValue{}
60 }
61
62 func (c *Checker) typeIdent(e *Name, scope *Scope) TCTypeAndValue {
63 if e.Value == "_" {
64 return TCTypeAndValue{mode: modeValue}
65 }
66 _, obj := c.lookup(e.Value, scope)
67 if obj == nil {
68 c.errorf(e.Pos(), "undefined: %s", e.Value)
69 return TCTypeAndValue{}
70 }
71 if c.info != nil {
72 c.info.Uses[e] = obj
73 }
74 switch obj := obj.(type) {
75 case *TCVar:
76 return TCTypeAndValue{Type: obj.typ, mode: modeVar}
77 case *TCConst:
78 return TCTypeAndValue{Type: obj.typ, Value: obj.val, mode: modeConst}
79 case *TypeName:
80 return TCTypeAndValue{Type: obj.typ, mode: modeType}
81 case *TCFunc:
82 return TCTypeAndValue{Type: obj.typ, mode: modeValue}
83 case *Builtin:
84 return TCTypeAndValue{mode: modeBuiltin}
85 case *PkgName:
86 return TCTypeAndValue{mode: modePkg}
87 }
88 return TCTypeAndValue{}
89 }
90
91 func (c *Checker) typeBasicLit(e *BasicLit) TCTypeAndValue {
92 switch e.Kind {
93 case IntLit:
94 return TCTypeAndValue{Type: Typ[UntypedInt], mode: modeConst}
95 case FloatLit:
96 return TCTypeAndValue{Type: Typ[UntypedFloat], mode: modeConst}
97 case StringLit:
98 return TCTypeAndValue{Type: Typ[UntypedString], mode: modeConst}
99 case RuneLit:
100 return TCTypeAndValue{Type: Typ[UntypedRune], mode: modeConst}
101 }
102 return TCTypeAndValue{}
103 }
104
105 func (c *Checker) typeOperation(e *Operation, scope *Scope) TCTypeAndValue {
106 if e.Y == nil {
107 // unary
108 xt := c.checkExpr(e.X, scope)
109 if xt == nil {
110 return TCTypeAndValue{mode: modeValue}
111 }
112 switch e.Op {
113 case Recv: // <-ch
114 if ch, ok := xt.Underlying().(*TCChan); ok {
115 return TCTypeAndValue{Type: ch.elem, mode: modeValue}
116 }
117 case And: // &x
118 return TCTypeAndValue{Type: NewPointer(xt), mode: modeValue}
119 case Mul: // *x (dereference)
120 if pt, ok := xt.Underlying().(*Pointer); ok {
121 return TCTypeAndValue{Type: pt.base, mode: modeVar}
122 }
123 default:
124 return TCTypeAndValue{Type: xt, mode: modeValue}
125 }
126 return TCTypeAndValue{mode: modeValue}
127 }
128 // binary
129 xt := c.checkExpr(e.X, scope)
130 yt := c.checkExpr(e.Y, scope)
131 switch e.Op {
132 case Eql, Neq, Lss, Leq, Gtr, Geq,
133 AndAnd, OrOr:
134 return TCTypeAndValue{Type: Typ[Bool], mode: modeValue}
135 case Or, Add:
136 // In Moxie, | on strings is concat. Use dominant side's type.
137 if xt != nil {
138 return TCTypeAndValue{Type: xt, mode: modeValue}
139 }
140 return TCTypeAndValue{Type: yt, mode: modeValue}
141 default:
142 if xt != nil {
143 return TCTypeAndValue{Type: xt, mode: modeValue}
144 }
145 return TCTypeAndValue{Type: yt, mode: modeValue}
146 }
147 }
148
149 func (c *Checker) typeCall(e *CallExpr, scope *Scope) TCTypeAndValue {
150 funTV := c.typeExpr(e.Fun, scope)
151 if c.info != nil {
152 c.info.Types[e.Fun] = funTV
153 }
154
155 for _, arg := range e.ArgList {
156 c.checkExpr(arg, scope)
157 }
158
159 if funTV.mode == modeBuiltin {
160 return c.typeBuiltinCall(e, scope)
161 }
162 if funTV.mode == modeType {
163 // conversion: T(x)
164 if len(e.ArgList) == 1 {
165 c.checkExpr(e.ArgList[0], scope)
166 }
167 return TCTypeAndValue{Type: funTV.Type, mode: modeValue}
168 }
169
170 if funTV.Type == nil {
171 return TCTypeAndValue{}
172 }
173 sig, ok := funTV.Type.Underlying().(*Signature)
174 if !ok {
175 return TCTypeAndValue{}
176 }
177 if sig.results == nil || sig.results.Len() == 0 {
178 return TCTypeAndValue{mode: modeVoid}
179 }
180 if sig.results.Len() == 1 {
181 return TCTypeAndValue{Type: sig.results.At(0).typ, mode: modeValue}
182 }
183 // multi-return: return a Tuple type
184 return TCTypeAndValue{Type: sig.results, mode: modeValue}
185 }
186
187 func (c *Checker) typeBuiltinCall(e *CallExpr, scope *Scope) TCTypeAndValue {
188 // Determine which builtin from the ident.
189 name, ok := e.Fun.(*Name)
190 if !ok {
191 return TCTypeAndValue{}
192 }
193 _, obj := c.lookup(name.Value, scope)
194 b, ok := obj.(*Builtin)
195 if !ok {
196 return TCTypeAndValue{}
197 }
198 switch b.id {
199 case BuiltinLen, BuiltinCap:
200 return TCTypeAndValue{Type: Typ[Int32], mode: modeValue}
201 case BuiltinAppend:
202 if len(e.ArgList) > 0 {
203 return TCTypeAndValue{Type: c.checkExpr(e.ArgList[0], scope), mode: modeValue}
204 }
205 case BuiltinMake:
206 if len(e.ArgList) > 0 {
207 return TCTypeAndValue{Type: c.resolveTypeExpr(e.ArgList[0]), mode: modeValue}
208 }
209 case BuiltinNew:
210 if len(e.ArgList) > 0 {
211 return TCTypeAndValue{Type: NewPointer(c.resolveTypeExpr(e.ArgList[0])), mode: modeValue}
212 }
213 case BuiltinPanic, BuiltinPrint, BuiltinPrintln:
214 return TCTypeAndValue{mode: modeVoid}
215 case BuiltinRecover:
216 return TCTypeAndValue{Type: universeError.typ, mode: modeValue}
217 case BuiltinClose, BuiltinDelete, BuiltinClear:
218 return TCTypeAndValue{mode: modeVoid}
219 case BuiltinCopy:
220 return TCTypeAndValue{Type: Typ[Int32], mode: modeValue}
221 case BuiltinSpawn:
222 return TCTypeAndValue{mode: modeVoid}
223 }
224 return TCTypeAndValue{}
225 }
226
227 func (c *Checker) typeSelector(e *SelectorExpr, scope *Scope) TCTypeAndValue {
228 xt := c.typeExpr(e.X, scope)
229 if c.info != nil {
230 c.info.Types[e.X] = xt
231 }
232 if xt.mode == modePkg {
233 // package.Name
234 if pkgName, ok := e.X.(*Name); ok {
235 _, pkgObj := c.lookup(pkgName.Value, scope)
236 if pn, ok := pkgObj.(*PkgName); ok && pn.imported != nil {
237 obj := pn.imported.scope.Lookup(e.Sel.Value)
238 if obj != nil {
239 if c.info != nil {
240 c.info.Uses[e.Sel] = obj
241 }
242 return TCTypeAndValue{Type: obj.Type(), mode: modeValue}
243 }
244 }
245 }
246 return TCTypeAndValue{}
247 }
248
249 // field or method lookup
250 typ := xt.Type
251 if typ == nil {
252 return TCTypeAndValue{}
253 }
254 sel := c.lookupFieldOrMethod(typ, e.Sel.Value)
255 if sel != nil {
256 if c.info != nil {
257 c.info.Selections[e] = sel
258 if sel.obj != nil {
259 c.info.Uses[e.Sel] = sel.obj
260 }
261 }
262 return TCTypeAndValue{Type: sel.Type(), mode: modeValue}
263 }
264 return TCTypeAndValue{}
265 }
266
267 func (c *Checker) typeIndex(e *IndexExpr, scope *Scope) TCTypeAndValue {
268 xt := c.checkExpr(e.X, scope)
269 c.checkExpr(e.Index, scope)
270 if xt == nil {
271 return TCTypeAndValue{}
272 }
273 switch t := xt.Underlying().(type) {
274 case *Array:
275 return TCTypeAndValue{Type: t.elem, mode: modeVar}
276 case *Slice:
277 return TCTypeAndValue{Type: t.elem, mode: modeVar}
278 case *TCMap:
279 return TCTypeAndValue{Type: t.elem, mode: modeValue}
280 }
281 return TCTypeAndValue{}
282 }
283
284 func (c *Checker) typeSlice(e *SliceExpr, scope *Scope) TCTypeAndValue {
285 xt := c.checkExpr(e.X, scope)
286 for _, idx := range e.Index {
287 if idx != nil {
288 c.checkExpr(idx, scope)
289 }
290 }
291 if xt == nil {
292 return TCTypeAndValue{}
293 }
294 switch t := xt.Underlying().(type) {
295 case *Array:
296 return TCTypeAndValue{Type: NewSlice(t.elem), mode: modeValue}
297 case *Slice:
298 return TCTypeAndValue{Type: xt, mode: modeValue}
299 }
300 // string[:] -> string
301 if b, ok := xt.Underlying().(*Basic); ok && b.info&IsString != 0 {
302 return TCTypeAndValue{Type: xt, mode: modeValue}
303 }
304 return TCTypeAndValue{}
305 }
306
307 func (c *Checker) typeAssert(e *AssertExpr, scope *Scope) TCTypeAndValue {
308 c.checkExpr(e.X, scope)
309 assertedType := c.resolveTypeExpr(e.Type)
310 return TCTypeAndValue{Type: assertedType, mode: modeValue}
311 }
312
313 func (c *Checker) typeCompositeLit(e *CompositeLit, scope *Scope) TCTypeAndValue {
314 var typ Type
315 if e.Type != nil {
316 typ = c.resolveTypeExpr(e.Type)
317 }
318 // Determine whether keys in key:value pairs are field names (struct) or
319 // expressions (map/slice). Struct field names are not in scope.
320 // Also treat typeless composite literals (e.Type == nil) as structs -
321 // in Go they appear as slice/array element literals where type is inferred.
322 isStruct := e.Type == nil || (typ != nil && isStructType(typ))
323 // Fallback: if we have key:value elements but couldn't determine the type,
324 // assume struct (struct field names don't exist as scope variables).
325 if !isStruct && len(e.ElemList) > 0 {
326 if _, ok := e.ElemList[0].(*KeyValueExpr); ok {
327 isStruct = true
328 }
329 }
330 for _, el := range e.ElemList {
331 if kv, ok := el.(*KeyValueExpr); ok {
332 if isStruct {
333 // Struct field: only check value, skip key lookup.
334 c.checkExpr(kv.Value, scope)
335 } else {
336 // Map/unknown: check both key and value.
337 c.checkExpr(kv.Key, scope)
338 c.checkExpr(kv.Value, scope)
339 }
340 } else {
341 c.checkExpr(el, scope)
342 }
343 }
344 return TCTypeAndValue{Type: typ, mode: modeValue}
345 }
346
347 // isStructType returns true if t is (or is a Named wrapping) a struct.
348 // Named types with nil underlying (unresolved) are treated as structs
349 // because composite literals with key:value syntax on a Named type are
350 // always struct literals in Moxie code - map literals use an explicit
351 // map[K]V type, never a Named alias at this level.
352 func isStructType(t Type) bool {
353 if t == nil {
354 return false
355 }
356 switch t := t.(type) {
357 case *TCStruct:
358 return true
359 case *Named:
360 if t.underlying == nil {
361 return true // optimistic: Named with key:value syntax = struct
362 }
363 return isStructType(t.underlying)
364 }
365 return false
366 }
367
368 func (c *Checker) typeFuncLit(e *FuncLit, scope *Scope) TCTypeAndValue {
369 sig := c.resolveFuncType(e.Type, nil)
370 inner := c.openScope(e.Body, scope)
371 if sig != nil {
372 if sig.params != nil {
373 for i := 0; i < sig.params.Len(); i++ {
374 p := sig.params.At(i)
375 if p.name != "" {
376 inner.Insert(p)
377 }
378 }
379 }
380 if sig.results != nil {
381 for i := 0; i < sig.results.Len(); i++ {
382 r := sig.results.At(i)
383 if r.name != "" {
384 inner.Insert(r)
385 }
386 }
387 }
388 }
389 c.checkBlock(e.Body, inner)
390 return TCTypeAndValue{Type: sig, mode: modeValue}
391 }
392
393 // lookupFieldOrMethod finds a field or method named name on type t.
394 func (c *Checker) lookupFieldOrMethod(t Type, name string) *Selection {
395 if t == nil {
396 return nil
397 }
398 // dereference pointer
399 indirect := false
400 if pt, ok := safeUnderlying(t).(*Pointer); ok {
401 t = pt.base
402 indirect = true
403 }
404
405 switch t := safeUnderlying(t).(type) {
406 case *TCStruct:
407 for i, f := range t.fields {
408 if f.name == name {
409 return &Selection{
410 kind: FieldVal,
411 recv: t,
412 obj: f,
413 index: []int{i},
414 indir: indirect,
415 }
416 }
417 }
418 case *TCInterface:
419 for _, m := range t.allMethods {
420 if m.name == name {
421 fn := NewTCFunc(nil, name, m.sig)
422 return &Selection{kind: MethodVal, recv: t, obj: fn, indir: indirect}
423 }
424 }
425 case *Named:
426 for _, m := range t.methods {
427 if m.name == name {
428 return &Selection{kind: MethodVal, recv: t, obj: m, indir: indirect}
429 }
430 }
431 if t.underlying != nil {
432 return c.lookupFieldOrMethod(t.underlying, name)
433 }
434 }
435 return nil
436 }
437