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