| 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. |
| 3 | module checker |
| 4 | |
| 5 | import v.ast |
| 6 | import v.token |
| 7 | |
| 8 | fn 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 | |
| 23 | fn (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 | |
| 46 | fn (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 | |
| 339 | fn (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 | |