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