v2 / vlib / v / checker / for.v
371 lines · 345 sloc · 12.09 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
3module checker
4
5import v.ast
6import v.token
7
8fn for_in_val_type(base_type ast.Type, is_mut bool, is_ref bool) ast.Type {
9 if base_type == 0 {
10 return base_type
11 }
12 if is_mut || is_ref {
13 if base_type.has_flag(.option) {
14 return base_type.set_flag(.option_mut_param_t)
15 }
16 if !base_type.is_any_kind_of_pointer() {
17 return base_type.ref()
18 }
19 }
20 return base_type
21}
22
23fn (mut c Checker) for_c_stmt(mut node ast.ForCStmt) {
24 c.in_for_count++
25 prev_loop_labels := c.loop_labels
26 if node.has_init {
27 c.stmt(mut node.init)
28 }
29 c.expr(mut node.cond)
30 if node.has_inc {
31 if mut node.inc is ast.AssignStmt {
32 assign := node.inc
33
34 if assign.op == .decl_assign {
35 c.error('for loop post statement cannot be a variable declaration', assign.pos)
36 }
37 }
38 c.stmt(mut node.inc)
39 }
40 c.check_loop_labels(node.label, node.pos)
41 c.stmts(mut node.stmts)
42 c.loop_labels = prev_loop_labels
43 c.in_for_count--
44}
45
46fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
47 c.in_for_count++
48 prev_loop_labels := c.loop_labels
49 cond_pos := node.cond.pos()
50 high_pos := node.high.pos()
51 mut typ := c.expr(mut node.cond)
52 if node.key_var.len > 0 && node.key_var != '_' {
53 c.check_valid_snake_case(node.key_var, 'variable name', node.pos)
54 if reserved_type_names_chk.matches(node.key_var) {
55 c.error('invalid use of reserved type `${node.key_var}` as key name', node.pos)
56 }
57 }
58 if node.val_var.len > 0 && node.val_var != '_' {
59 c.check_valid_snake_case(node.val_var, 'variable name', node.pos)
60 if reserved_type_names_chk.matches(node.val_var) {
61 c.error('invalid use of reserved type `${node.val_var}` as value name', node.pos)
62 }
63 }
64 if _ := c.file.global_scope.find_const('${c.mod}.${node.key_var}') {
65 c.error('duplicate of a const name `${c.mod}.${node.key_var}`', node.kv_pos)
66 }
67
68 if _ := c.file.global_scope.find_const('${c.mod}.${node.val_var}') {
69 c.error('duplicate of a const name `${c.mod}.${node.val_var}`', node.vv_pos)
70 }
71
72 if node.is_range {
73 typ_idx := typ.idx()
74 high_type := c.expr(mut node.high)
75 high_type_idx := high_type.idx()
76 if typ_idx in ast.integer_type_idxs && high_type_idx !in ast.integer_type_idxs
77 && high_type_idx != ast.void_type_idx {
78 c.error('range types do not match', node.cond.pos())
79 } else if c.table.final_sym(typ).kind == .multi_return
80 && c.table.final_sym(high_type).kind == .multi_return {
81 c.error('multi-returns cannot be used in ranges. A range is from a single value to a single higher value.',
82 node.cond.pos().extend(node.high.pos()))
83 } else if typ_idx !in ast.integer_type_idxs {
84 c.error('range type can only be an integer type',
85 node.cond.pos().extend(node.high.pos()))
86 } else if high_type.has_option_or_result() {
87 c.error('the `high` value in a `for x in low..high {` loop, cannot be Result or Option',
88 high_pos)
89 } else if node.cond is ast.Ident && node.val_var == node.cond.name {
90 if node.is_range {
91 c.error('in a `for x in <range>` loop, the key or value iteration variable `${node.val_var}` can not be the same as the low variable',
92 cond_pos)
93 } else {
94 c.error('in a `for x in array` loop, the key or value iteration variable `${node.val_var}` can not be the same as the low variable',
95 cond_pos)
96 }
97 } else if node.high is ast.Ident && node.val_var == node.high.name {
98 c.error('in a `for x in <range>` loop, the key or value iteration variable `${node.val_var}` can not be the same as the high variable',
99 high_pos)
100 }
101
102 if high_type in [ast.int_type, ast.int_literal_type] {
103 node.val_type = typ
104 } else {
105 node.val_type = high_type
106 }
107 node.high_type = high_type
108 node.scope.update_var_type(node.val_var, node.val_type)
109 } else {
110 if node.cond is ast.Ident && node.cond.name in [node.key_var, node.val_var] {
111 c.error('in a `for x in array` loop, the key or value iteration variable `${node.val_var}` can not be the same as the low variable',
112 cond_pos)
113 }
114 mut is_comptime := false
115 if (node.cond is ast.Ident && node.cond.ct_expr) || node.cond is ast.ComptimeSelector {
116 ctyp := c.type_resolver.get_type(node.cond)
117 if ctyp != ast.void_type {
118 is_comptime = true
119 typ = ctyp
120 }
121 }
122
123 mut sym := c.table.final_sym(typ)
124 if sym.kind != .string {
125 match mut node.cond {
126 ast.PrefixExpr {
127 node.val_is_ref = node.cond.op == .amp
128 }
129 ast.ComptimeSelector {
130 comptime_typ := c.type_resolver.get_comptime_selector_type(node.cond,
131 ast.void_type)
132 if comptime_typ != ast.void_type {
133 sym = c.table.final_sym(comptime_typ)
134 typ = comptime_typ
135 }
136 }
137 ast.Ident {
138 match mut node.cond.info {
139 ast.IdentVar {
140 node.val_is_ref = !node.cond.is_mut() && node.cond.info.typ.is_ptr()
141 }
142 else {}
143 }
144 }
145 else {}
146 }
147
148 if node.cond is ast.Ident {
149 cond_ident := node.cond as ast.Ident
150 if cond_ident.obj is ast.Var && c.table.is_interface_smartcast(cond_ident.obj)
151 && cond_ident.obj.smartcasts.len > 0
152 && cond_ident.obj.smartcasts.last().is_ptr()
153 && sym.kind in [.array, .array_fixed, .map] {
154 node.val_is_ref = true
155 }
156 }
157 } else if node.val_is_mut {
158 c.error('string type is immutable, it cannot be changed', node.pos)
159 return
160 }
161 if sym.kind in [.struct, .interface] {
162 // iterators
163 next_fn := sym.find_method_with_generic_parent('next') or {
164 kind_str := if sym.kind == .struct { 'struct' } else { 'interface' }
165 c.error('a ${kind_str} must have a `next()` method to be an iterator',
166 node.cond.pos())
167 return
168 }
169 if !next_fn.return_type.has_flag(.option) {
170 c.error('iterator method `next()` must return an Option', node.cond.pos())
171 }
172 return_sym := c.table.sym(next_fn.return_type)
173 if return_sym.kind == .multi_return {
174 c.error('iterator method `next()` must not return multiple values', node.cond.pos())
175 }
176 // the receiver
177 if next_fn.params.len != 1 {
178 c.error('iterator method `next()` must have 0 parameters', node.cond.pos())
179 }
180 mut val_type := for_in_val_type(next_fn.return_type.clear_option_and_result(),
181 node.val_is_mut, node.val_is_ref)
182 node.cond_type = typ
183 node.kind = sym.kind
184 node.val_type = val_type
185 if node.val_type.has_flag(.generic) {
186 if c.table.sym(c.unwrap_generic(node.val_type)).kind == .any {
187 c.add_error_detail('type parameters defined by `next()` method should be bounded by method owner type')
188 c.error('cannot infer from generic type `${c.table.get_type_name(c.unwrap_generic(node.val_type))}`',
189 node.vv_pos)
190 }
191 }
192 node.scope.update_var_type(node.val_var, val_type)
193
194 if is_comptime {
195 c.type_resolver.update_ct_type(node.val_var, val_type)
196 node.scope.update_ct_var_kind(node.val_var, .value_var)
197
198 defer(fn) {
199 c.type_resolver.type_map.delete(node.val_var)
200 }
201 }
202 } else if sym.kind == .any {
203 node.cond_type = typ
204 node.kind = sym.kind
205
206 unwrapped_typ := c.unwrap_generic(typ)
207 unwrapped_sym := c.table.sym(unwrapped_typ)
208
209 c.table.used_features.comptime_calls['${int(unwrapped_typ)}.next'] = true
210
211 if node.key_var.len > 0 {
212 key_type := match unwrapped_sym.kind {
213 .map { unwrapped_sym.map_info().key_type }
214 else { ast.int_type }
215 }
216
217 node.key_type = key_type
218 node.scope.update_var_type(node.key_var, key_type)
219
220 if is_comptime {
221 c.type_resolver.update_ct_type(node.key_var, key_type)
222 node.scope.update_ct_var_kind(node.key_var, .key_var)
223
224 defer(fn) {
225 c.type_resolver.type_map.delete(node.key_var)
226 }
227 }
228 }
229
230 mut value_type := for_in_val_type(c.table.value_type(unwrapped_typ), node.val_is_mut,
231 node.val_is_ref)
232 node.scope.update_var_type(node.val_var, value_type)
233 node.val_type = value_type
234
235 if is_comptime {
236 c.type_resolver.update_ct_type(node.val_var, value_type)
237 node.scope.update_ct_var_kind(node.val_var, .value_var)
238
239 defer(fn) {
240 c.type_resolver.type_map.delete(node.val_var)
241 }
242 }
243 } else {
244 if sym.kind == .map && !(node.key_var.len > 0 && node.val_var.len > 0) {
245 c.error(
246 'declare a key and a value variable when ranging a map: `for key, val in map {`\n' +
247 'use `_` if you do not need the variable', node.pos)
248 }
249 if !c.is_builtin_mod && c.mod != 'strings' {
250 c.table.used_features.used_maps++
251 }
252 if node.key_var.len > 0 {
253 key_type := match sym.kind {
254 .map { sym.map_info().key_type }
255 else { ast.int_type }
256 }
257
258 node.key_type = key_type
259 node.scope.update_var_type(node.key_var, key_type)
260
261 if is_comptime {
262 c.type_resolver.update_ct_type(node.key_var, key_type)
263 node.scope.update_ct_var_kind(node.key_var, .key_var)
264
265 defer(fn) {
266 c.type_resolver.type_map.delete(node.key_var)
267 }
268 }
269 }
270 mut value_type := c.table.value_type(typ)
271 if sym.kind == .string {
272 value_type = ast.u8_type
273 } else if sym.kind == .aggregate&& (sym.info as ast.Aggregate).types.all(c.table.type_kind(it) in [.array, .array_fixed, .string, .map]) {
274 value_type = c.table.value_type((sym.info as ast.Aggregate).types[0])
275 }
276 if value_type == ast.void_type || typ.has_flag(.result) {
277 if typ != ast.void_type {
278 c.error('for in: cannot index `${c.table.type_to_str(typ)}`', node.cond.pos())
279 }
280 }
281 if node.val_is_mut {
282 value_type = for_in_val_type(value_type, true, false)
283 match mut node.cond {
284 ast.Ident {
285 if mut node.cond.obj is ast.Var {
286 if !node.cond.obj.is_mut {
287 c.error('`${node.cond.obj.name}` is immutable, it cannot be changed',
288 node.cond.pos)
289 }
290 }
291 }
292 ast.ArrayInit {
293 c.error('array literal is immutable, it cannot be changed', node.cond.pos)
294 }
295 ast.MapInit {
296 c.error('map literal is immutable, it cannot be changed', node.cond.pos)
297 }
298 ast.SelectorExpr {
299 if root_ident := node.cond.root_ident() {
300 if root_ident.kind != .unresolved {
301 if var := node.scope.find_var(root_ident.name) {
302 if !var.is_mut {
303 sym2 := c.table.sym(root_ident.obj.typ)
304 c.error('field `${sym2.name}.${node.cond.field_name}` is immutable, it cannot be changed',
305 node.cond.pos)
306 }
307 }
308 }
309 }
310 }
311 else {}
312 }
313 } else if node.val_is_ref {
314 value_type = for_in_val_type(value_type, false, true)
315 }
316 node.cond_type = typ
317 node.kind = sym.kind
318 node.val_type = value_type
319 node.scope.update_var_type(node.val_var, value_type)
320 // Clear any smartcasts from a previous generic recheck pass,
321 // so they don't interfere with this iteration's type resolution.
322 node.scope.reset_smartcasts(node.val_var)
323 if is_comptime {
324 c.type_resolver.update_ct_type(node.val_var, value_type)
325 node.scope.update_ct_var_kind(node.val_var, .value_var)
326
327 defer(fn) {
328 c.type_resolver.type_map.delete(node.val_var)
329 }
330 }
331 }
332 }
333 c.check_loop_labels(node.label, node.pos)
334 c.stmts(mut node.stmts)
335 c.loop_labels = prev_loop_labels
336 c.in_for_count--
337}
338
339fn (mut c Checker) for_stmt(mut node ast.ForStmt) {
340 c.in_for_count++
341 prev_loop_labels := c.loop_labels
342 c.expected_type = ast.bool_type
343 if node.cond !is ast.EmptyExpr {
344 typ := c.expr(mut node.cond)
345 if !node.is_inf && typ.idx() != ast.bool_type_idx && !c.pref.translated
346 && !c.file.is_translated {
347 c.error('non-bool used as for condition', node.pos)
348 }
349 }
350 if mut node.cond is ast.InfixExpr && node.cond.op == .key_is {
351 if node.cond.right is ast.TypeNode && node.cond.left in [ast.Ident, ast.SelectorExpr] {
352 left_kind := c.table.type_kind(node.cond.left_type)
353 is_receiver_smartcast := left_kind == .placeholder && node.cond.left.is_auto_deref_var()
354 && node.cond.left_type.is_ptr()
355 && c.table.final_sym(node.cond.left_type).kind in [.sum_type, .interface]
356 if left_kind in [.sum_type, .interface] || is_receiver_smartcast {
357 c.smartcast(mut node.cond.left, node.cond.left_type, node.cond.right_type, mut
358 node.scope, false, false, false, false)
359 }
360 }
361 }
362 // TODO: update loop var type
363 // how does this work currently?
364 c.check_loop_labels(node.label, node.pos)
365 c.stmts(mut node.stmts)
366 c.loop_labels = prev_loop_labels
367 c.in_for_count--
368 if c.smartcast_mut_pos != token.Pos{} {
369 c.smartcast_mut_pos = token.Pos{}
370 }
371}
372