tc_stmt.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 // checkBlock type-checks a block statement. It always opens a fresh child
9 // scope so that local declarations inside the block don't leak into the
10 // parent scope and don't conflict with sibling declarations (e.g. var ok
11 // in an if-body vs ok from the if-init).
12 func (c *Checker) checkBlock(b *syntax.BlockStmt, parent *Scope) {
13 if b == nil {
14 return
15 }
16 scope := c.openScope(b, parent)
17 saved := c.localScope
18 c.localScope = scope
19 defer func() { c.localScope = saved }()
20 for _, s := range b.List {
21 c.checkStmt(s, scope)
22 }
23 }
24
25 func (c *Checker) checkStmt(s syntax.Stmt, scope *Scope) {
26 if s == nil {
27 return
28 }
29 switch ss := s.(type) {
30 case *syntax.EmptyStmt:
31 // nothing
32 case *syntax.ExprStmt:
33 c.checkExpr(ss.X, scope)
34 case *syntax.SendStmt:
35 c.checkExpr(ss.Chan, scope)
36 c.checkExpr(ss.Value, scope)
37 case *syntax.AssignStmt:
38 c.checkAssign(ss, scope)
39 case *syntax.BlockStmt:
40 inner := c.openScope(ss, scope)
41 c.checkBlock(ss, inner)
42 case *syntax.DeclStmt:
43 for _, d := range ss.DeclList {
44 c.checkLocalDecl(d, scope)
45 }
46 case *syntax.IfStmt:
47 inner := c.openScope(ss, scope)
48 if ss.Init != nil {
49 c.checkStmt(ss.Init, inner)
50 }
51 c.checkExpr(ss.Cond, inner)
52 c.checkBlock(ss.Then, inner)
53 if ss.Else != nil {
54 c.checkStmt(ss.Else, inner)
55 }
56 case *syntax.ForStmt:
57 inner := c.openScope(ss, scope)
58 if rc, ok := ss.Init.(*syntax.RangeClause); ok {
59 c.checkRange(rc, inner)
60 } else {
61 if ss.Init != nil {
62 c.checkStmt(ss.Init, inner)
63 }
64 }
65 if ss.Cond != nil {
66 c.checkExpr(ss.Cond, inner)
67 }
68 if ss.Post != nil {
69 c.checkStmt(ss.Post, inner)
70 }
71 c.checkBlock(ss.Body, inner)
72 case *syntax.SwitchStmt:
73 c.checkSwitch(ss, scope)
74 case *syntax.SelectStmt:
75 c.checkSelect(ss, scope)
76 case *syntax.ReturnStmt:
77 if ss.Results != nil {
78 c.checkExpr(ss.Results, scope)
79 }
80 case *syntax.BranchStmt:
81 // break/continue/goto/fallthrough - label checking deferred
82 case *syntax.LabeledStmt:
83 c.checkStmt(ss.Stmt, scope)
84 case *syntax.CallStmt:
85 // go/defer
86 c.checkExpr(ss.Call, scope)
87 }
88 }
89
90 func (c *Checker) checkAssign(s *syntax.AssignStmt, scope *Scope) {
91 if s.Rhs == nil {
92 // x++ or x--
93 c.checkExpr(s.Lhs, scope)
94 return
95 }
96 if s.Op == token.Def {
97 // short variable declaration: x, y := ...
98 c.checkShortVarDecl(s, scope)
99 return
100 }
101 c.checkExpr(s.Lhs, scope)
102 c.checkExpr(s.Rhs, scope)
103 }
104
105 func (c *Checker) checkShortVarDecl(s *syntax.AssignStmt, scope *Scope) {
106 // Evaluate rhs first.
107 rhsType := c.checkExpr(s.Rhs, scope)
108
109 lhsNames := exprNames(s.Lhs)
110 // Determine which names are new in the current scope (not parent scopes).
111 // Go spec: at least one name must be new; existing names are re-assigned.
112 newCount := 0
113 for _, name := range lhsNames {
114 if name.Value != "_" && scope.Lookup(name.Value) == nil {
115 newCount++
116 }
117 }
118 if newCount == 0 && len(lhsNames) > 0 {
119 // All names already declared in this scope: error.
120 c.errorf(s.Pos(), "no new variables on left side of :=")
121 return
122 }
123
124 // Assign types. Single-value: use rhsType directly.
125 // Multi-value: we don't yet unpack tuples, so leave type nil for existing names.
126 for i, name := range lhsNames {
127 if name.Value == "_" {
128 continue
129 }
130 if scope.Lookup(name.Value) != nil {
131 // Already in scope: re-assignment, not redeclaration. No action needed.
132 continue
133 }
134 var typ Type
135 if i == 0 && rhsType != nil {
136 typ = rhsType
137 }
138 obj := NewTCVar(c.pkg, name.Value, typ)
139 c.declare(scope, name, obj)
140 }
141 }
142
143 func (c *Checker) checkLocalDecl(d syntax.Decl, scope *Scope) {
144 switch dd := d.(type) {
145 case *syntax.VarDecl:
146 var typ Type
147 if dd.Type != nil {
148 typ = c.resolveTypeExpr(dd.Type)
149 }
150 if dd.Values != nil && typ == nil {
151 typ = c.checkExpr(dd.Values, scope)
152 }
153 for _, name := range dd.NameList {
154 obj := NewTCVar(c.pkg, name.Value, typ)
155 c.declare(scope, name, obj)
156 }
157 case *syntax.TypeDecl:
158 underlying := c.resolveTypeExpr(dd.Type)
159 obj := NewTypeName(c.pkg, dd.Name.Value, nil)
160 named := NewNamed(obj, underlying)
161 obj.Typ = named
162 c.declare(scope, dd.Name, obj)
163 case *syntax.ConstDecl:
164 var vals []ConstVal
165 if dd.Values != nil {
166 vals = c.evalConstExprList(dd.Values, 0)
167 }
168 var typ Type
169 if dd.Type != nil {
170 typ = c.resolveTypeExpr(dd.Type)
171 }
172 for i, name := range dd.NameList {
173 var cv ConstVal
174 if i < len(vals) {
175 cv = vals[i]
176 }
177 var ct Type
178 if typ != nil {
179 ct = typ
180 } else if cv != nil {
181 ct = UntypedTypeOfCV(cv)
182 }
183 obj := NewTCConst(c.pkg, name.Value, ct, cv)
184 c.declare(scope, name, obj)
185 }
186 }
187 }
188
189 func (c *Checker) checkRange(rc *syntax.RangeClause, scope *Scope) {
190 rangeType := c.checkExpr(rc.X, scope)
191 if rc.Lhs != nil && rc.Def {
192 names := exprNames(rc.Lhs)
193 keyType, valType := rangeKeyVal(rangeType)
194 types := []Type{keyType, valType}
195 for i, name := range names {
196 if name.Value == "_" {
197 continue
198 }
199 var t Type
200 if i < len(types) {
201 t = types[i]
202 }
203 obj := NewTCVar(c.pkg, name.Value, t)
204 c.declare(scope, name, obj)
205 }
206 }
207 }
208
209 func (c *Checker) checkSwitch(s *syntax.SwitchStmt, scope *Scope) {
210 inner := c.openScope(s, scope)
211 if s.Init != nil {
212 c.checkStmt(s.Init, inner)
213 }
214
215 // Type switch: switch v := x.(type) { case T: ... }
216 if s.Tag != nil {
217 if tsg, ok := s.Tag.(*syntax.TypeSwitchGuard); ok {
218 xTyp := c.checkExpr(tsg.X, inner)
219 for _, clause := range s.Body {
220 clauseScope := c.openScope(clause, inner)
221 // Bind the guard variable to the case type in each clause.
222 if tsg.Lhs != nil {
223 caseTyp := xTyp // default / multi-type: keep interface type
224 if clause.Cases != nil {
225 if _, multi := clause.Cases.(*syntax.ListExpr); !multi {
226 // nil case or single type: nil keeps interface type.
227 if n, isNil := clause.Cases.(*syntax.Name); !isNil || n.Value != "nil" {
228 if t := c.resolveTypeExpr(clause.Cases); t != nil {
229 caseTyp = t
230 }
231 }
232 }
233 }
234 obj := NewTCVar(c.pkg, tsg.Lhs.Value, caseTyp)
235 clauseScope.Insert(obj)
236 if c.info != nil {
237 c.info.Implicits[clause] = obj
238 }
239 }
240 for _, stmt := range clause.Body {
241 c.checkStmt(stmt, clauseScope)
242 }
243 }
244 return
245 }
246 }
247
248 // Expression switch.
249 if s.Tag != nil {
250 c.checkExpr(s.Tag, inner)
251 }
252 for _, clause := range s.Body {
253 clauseScope := c.openScope(clause, inner)
254 if clause.Cases != nil {
255 c.checkExpr(clause.Cases, clauseScope)
256 }
257 for _, stmt := range clause.Body {
258 c.checkStmt(stmt, clauseScope)
259 }
260 }
261 }
262
263 func (c *Checker) checkSelect(s *syntax.SelectStmt, scope *Scope) {
264 for _, clause := range s.Body {
265 clauseScope := c.openScope(clause, scope)
266 if clause.Comm != nil {
267 c.checkStmt(clause.Comm, clauseScope)
268 }
269 for _, stmt := range clause.Body {
270 c.checkStmt(stmt, clauseScope)
271 }
272 }
273 }
274
275 // exprNames extracts *syntax.Name nodes from an expression.
276 // Used for the LHS of := and range clauses.
277 func exprNames(e syntax.Expr) (ns []*syntax.Name) {
278 if e == nil {
279 return nil
280 }
281 if n, ok := e.(*syntax.Name); ok {
282 return []*syntax.Name{n}
283 }
284 if l, ok := e.(*syntax.ListExpr); ok {
285 var names []*syntax.Name
286 for _, el := range l.ElemList {
287 if n2, ok2 := el.(*syntax.Name); ok2 {
288 push(names, n2)
289 }
290 }
291 return names
292 }
293 return nil
294 }
295
296 // rangeKeyVal returns the key and value types for ranging over t.
297 func rangeKeyVal(t Type) (key, val Type) {
298 if t == nil {
299 return nil, nil
300 }
301 switch ut := SafeUnderlying(t).(type) {
302 case *Array:
303 return Typ[Int32], ut.Elem
304 case *Slice:
305 return Typ[Int32], ut.Elem
306 case *TCMap:
307 return ut.Key, ut.Elem
308 case *TCChan:
309 return ut.Elem, nil
310 case *Basic:
311 if ut.Info&IsString != 0 {
312 return Typ[Int32], Typ[Uint8]
313 }
314 }
315 return nil, nil
316 }
317