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