| 1 | // Copyright (c) 2019-2024 Felipe Pena. 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 | |
| 7 | @[inline] |
| 8 | fn (mut c Checker) markused_comptime_call(check bool, key string) { |
| 9 | if check { |
| 10 | c.table.used_features.comptime_calls[key] = true |
| 11 | } |
| 12 | } |
| 13 | |
| 14 | fn comptime_call_last_arg_type(arg ast.CallArg) ast.Type { |
| 15 | return if arg.expr is ast.ArrayDecompose { |
| 16 | arg.expr.expr_type |
| 17 | } else { |
| 18 | arg.typ |
| 19 | } |
| 20 | } |
| 21 | |
| 22 | fn (mut c Checker) markused_assertstmt_auto_str(mut node ast.AssertStmt) { |
| 23 | if !c.is_builtin_mod && mut node.expr is ast.InfixExpr { |
| 24 | left_type := c.unwrap_generic(node.expr.left_type) |
| 25 | right_type := c.unwrap_generic(node.expr.right_type) |
| 26 | c.markused_auto_str_dependencies(left_type) |
| 27 | c.markused_auto_str_dependencies(right_type) |
| 28 | if !c.table.used_features.auto_str && !c.table.sym(left_type).has_method('str') { |
| 29 | c.table.used_features.auto_str = true |
| 30 | return |
| 31 | } |
| 32 | if !c.table.used_features.auto_str && !c.table.sym(right_type).has_method('str') { |
| 33 | c.table.used_features.auto_str = true |
| 34 | } |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | fn (mut c Checker) markused_dumpexpr(mut node ast.DumpExpr) { |
| 39 | if c.is_builtin_mod { |
| 40 | return |
| 41 | } |
| 42 | unwrapped_type := c.unwrap_generic(node.expr_type) |
| 43 | if node.expr_type.has_flag(.generic) { |
| 44 | c.table.used_features.comptime_syms[unwrapped_type] = true |
| 45 | } |
| 46 | if !c.table.sym(unwrapped_type).has_method('str') { |
| 47 | c.table.used_features.auto_str = true |
| 48 | c.markused_auto_str_dependencies(unwrapped_type) |
| 49 | if node.expr_type.is_ptr() { |
| 50 | c.table.used_features.auto_str_ptr = true |
| 51 | } |
| 52 | if !c.table.used_features.auto_str_arr { |
| 53 | c.table.used_features.auto_str_arr = c.table.final_sym(unwrapped_type).kind == .array |
| 54 | } |
| 55 | } else { |
| 56 | c.markused_generic_str_method(unwrapped_type, c.table.sym(unwrapped_type)) |
| 57 | c.table.used_features.print_types[node.expr_type.idx()] = true |
| 58 | } |
| 59 | c.table.used_features.print_types[ast.int_type_idx] = true |
| 60 | } |
| 61 | |
| 62 | @[inline] |
| 63 | fn (mut c Checker) markused_used_maps(check bool) { |
| 64 | if check { |
| 65 | c.table.used_features.used_maps++ |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | fn (mut c Checker) markused_castexpr(mut _ ast.CastExpr, _ ast.Type, mut final_to_sym ast.TypeSymbol) { |
| 70 | if c.is_builtin_mod { |
| 71 | return |
| 72 | } |
| 73 | if c.table.used_features.used_maps == 0 && mut final_to_sym.info is ast.SumType { |
| 74 | if final_to_sym.info.variants.any(c.table.final_sym(it).kind == .map) { |
| 75 | c.table.used_features.used_maps++ |
| 76 | } |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | fn (mut c Checker) markused_comptimecall(mut node ast.ComptimeCall) { |
| 81 | c.markused_comptime_call(true, |
| 82 | '${int(c.unwrap_generic(c.comptime.comptime_for_method.receiver_type))}.${c.comptime.comptime_for_method.name}') |
| 83 | if c.inside_anon_fn { |
| 84 | // $method passed to anon fn, mark all methods as used |
| 85 | sym := c.table.sym(c.unwrap_generic(node.left_type)) |
| 86 | for m in sym.get_methods() { |
| 87 | c.table.used_features.comptime_calls['${int(c.unwrap_generic(m.receiver_type))}.${m.name}'] = true |
| 88 | if node.args.len > 0 && m.params.len > 0 { |
| 89 | last_param := m.params.last().typ |
| 90 | last_arg_type := comptime_call_last_arg_type(node.args.last()) |
| 91 | if (last_param.is_int() || last_param.is_float() |
| 92 | || last_param.is_bool()) && c.table.final_sym(last_arg_type).kind == .array { |
| 93 | c.table.used_features.comptime_calls['${ast.string_type_idx}.${c.table.type_to_str(m.params.last().typ)}'] = true |
| 94 | } |
| 95 | } |
| 96 | } |
| 97 | } else { |
| 98 | m := c.comptime.comptime_for_method |
| 99 | if node.args.len > 0 && m.params.len > 0 { |
| 100 | last_param := m.params.last().typ |
| 101 | last_arg_type := comptime_call_last_arg_type(node.args.last()) |
| 102 | if (last_param.is_int() || last_param.is_float() || last_param.is_bool()) |
| 103 | && c.table.final_sym(last_arg_type).kind == .array { |
| 104 | c.table.used_features.comptime_calls['${ast.string_type_idx}.${c.table.type_to_str(m.params.last().typ)}'] = true |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | fn (mut c Checker) markused_comptimefor(mut _ ast.ComptimeFor, unwrapped_expr_type ast.Type) { |
| 111 | if c.table.used_features.used_maps == 0 { |
| 112 | final_sym := c.table.final_sym(unwrapped_expr_type) |
| 113 | if final_sym.info is ast.Map { |
| 114 | c.table.used_features.used_maps++ |
| 115 | } else if final_sym.info is ast.SumType { |
| 116 | if final_sym.info.variants.any(c.table.final_sym(it).kind == .map) { |
| 117 | c.table.used_features.used_maps++ |
| 118 | } |
| 119 | } |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | fn (mut c Checker) markused_call_expr(left_type ast.Type, mut node ast.CallExpr) { |
| 124 | if left_type != 0 && left_type.is_ptr() && !c.table.used_features.auto_str_ptr |
| 125 | && node.name == 'str' { |
| 126 | c.table.used_features.auto_str_ptr = true |
| 127 | if !c.table.used_features.auto_str_arr { |
| 128 | c.table.used_features.auto_str_arr = c.table.final_sym(left_type).kind == .array |
| 129 | } |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | fn (mut c Checker) markused_print_call(mut node ast.CallExpr) { |
| 134 | if !c.is_builtin_mod && c.mod != 'math.bits' && node.args[0].expr !is ast.StringLiteral { |
| 135 | arg_typ := c.unwrap_generic(node.args[0].typ) |
| 136 | if arg_typ == 0 { |
| 137 | return |
| 138 | } |
| 139 | c.markused_auto_str_dependencies(arg_typ) |
| 140 | if (node.args[0].expr is ast.CallExpr && node.args[0].expr.is_method |
| 141 | && node.args[0].expr.name == 'str') |
| 142 | || !c.table.sym(arg_typ).has_method('str') { |
| 143 | c.table.used_features.auto_str = true |
| 144 | } else { |
| 145 | c.mark_type_str_method_as_referenced(arg_typ) |
| 146 | if arg_typ.has_option_or_result() { |
| 147 | c.table.used_features.print_options = true |
| 148 | } |
| 149 | c.table.used_features.print_types[arg_typ.idx()] = true |
| 150 | if !c.table.used_features.auto_str_ptr && node.args[0].expr is ast.Ident { |
| 151 | var_obj := node.args[0].expr.obj |
| 152 | if var_obj is ast.Var { |
| 153 | if var_obj.orig_type != 0 { |
| 154 | fsym := c.table.final_sym(var_obj.orig_type) |
| 155 | if fsym.kind == .interface { |
| 156 | c.table.used_features.auto_str_ptr = true |
| 157 | } else if fsym.kind == .array { |
| 158 | c.table.used_features.auto_str_arr = true |
| 159 | } |
| 160 | return |
| 161 | } |
| 162 | } |
| 163 | } |
| 164 | } |
| 165 | if arg_typ.is_ptr() { |
| 166 | c.table.used_features.auto_str_ptr = true |
| 167 | } |
| 168 | if !c.table.used_features.auto_str_arr || !c.table.used_features.auto_str_ptr { |
| 169 | sym := c.table.final_sym(arg_typ) |
| 170 | if sym.kind == .array { |
| 171 | c.table.used_features.auto_str_arr = true |
| 172 | } else if sym.info is ast.Struct { |
| 173 | if !c.table.used_features.auto_str_ptr { |
| 174 | c.table.used_features.auto_str_ptr = sym.info.fields.any(it.typ.is_ptr() |
| 175 | || it.typ.is_pointer()) |
| 176 | } |
| 177 | c.table.used_features.auto_str_arr = |
| 178 | sym.info.fields.any(c.table.final_sym(it.typ).kind == .array) |
| 179 | } |
| 180 | } |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | fn (mut c Checker) markused_method_call(mut node ast.CallExpr, mut left_expr ast.Expr, left_type ast.Type) { |
| 185 | if !left_type.has_flag(.generic) && mut left_expr is ast.Ident { |
| 186 | if left_expr.obj is ast.Var && left_expr.obj.ct_type_var == .smartcast { |
| 187 | c.table.used_features.comptime_calls['${int(left_type)}.${node.name}'] = true |
| 188 | } |
| 189 | if c.table.sym(left_type).kind == .alias { |
| 190 | c.table.used_features.comptime_calls['${int(left_type)}.${node.name}'] = true |
| 191 | // Alias method calls can also auto-resolve to pointer receivers in cgen. |
| 192 | if !left_type.is_ptr() { |
| 193 | c.table.used_features.comptime_calls['${int(left_type.ref())}.${node.name}'] = true |
| 194 | } |
| 195 | } |
| 196 | } else if !left_type.has_flag(.generic) && left_expr is ast.ComptimeSelector { |
| 197 | // `x.$(field.name).method()` — the concrete receiver type varies per |
| 198 | // outer `$for f in T.fields` iteration, and skip-unused has no way to |
| 199 | // see the runtime dispatch. Mark the per-iteration receiver type so |
| 200 | // the method survives -skip-unused. |
| 201 | c.table.used_features.comptime_calls['${int(left_type)}.${node.name}'] = true |
| 202 | if !left_type.is_ptr() { |
| 203 | c.table.used_features.comptime_calls['${int(left_type.ref())}.${node.name}'] = true |
| 204 | } |
| 205 | } else if left_type.has_flag(.generic) { |
| 206 | unwrapped_left := c.unwrap_generic(left_type) |
| 207 | c.table.used_features.comptime_calls['${int(unwrapped_left)}.${node.name}'] = true |
| 208 | // Generic method calls can resolve to pointer receivers during cgen (`x.name()` -> `(&x).name()`). |
| 209 | // Mark both forms so skip-unused does not drop pointer receiver methods. |
| 210 | if !unwrapped_left.is_ptr() { |
| 211 | c.table.used_features.comptime_calls['${int(unwrapped_left.ref())}.${node.name}'] = true |
| 212 | } |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | fn string_inter_lit_format_uses_str(fmt u8) bool { |
| 217 | return fmt == `s` |
| 218 | } |
| 219 | |
| 220 | fn (mut c Checker) markused_string_inter_lit(mut _ ast.StringInterLiteral, ftyp ast.Type, fmt u8) { |
| 221 | if c.is_builtin_mod { |
| 222 | return |
| 223 | } |
| 224 | uses_str_format := string_inter_lit_format_uses_str(fmt) |
| 225 | if !c.table.sym(ftyp).has_method('str') { |
| 226 | if uses_str_format { |
| 227 | c.table.used_features.auto_str = true |
| 228 | c.markused_auto_str_dependencies(ftyp) |
| 229 | } |
| 230 | } else { |
| 231 | if uses_str_format { |
| 232 | c.markused_generic_str_method(ftyp, c.table.sym(ftyp)) |
| 233 | c.mark_type_str_method_as_referenced(ftyp) |
| 234 | c.table.used_features.print_types[ftyp.idx()] = true |
| 235 | } |
| 236 | } |
| 237 | if ftyp.is_ptr() { |
| 238 | c.table.used_features.auto_str_ptr = true |
| 239 | } |
| 240 | if !c.table.used_features.auto_str_arr { |
| 241 | c.table.used_features.auto_str_arr = c.table.final_sym(ftyp).kind == .array |
| 242 | } |
| 243 | if ftyp.has_option_or_result() { |
| 244 | c.table.used_features.print_options = true |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | fn (mut c Checker) markused_auto_str_dependencies(typ ast.Type) { |
| 249 | mut visited := map[ast.Type]bool{} |
| 250 | c.markused_auto_str_dependencies_for_type(typ, mut visited) |
| 251 | } |
| 252 | |
| 253 | fn (mut c Checker) markused_auto_str_dependencies_for_type(typ ast.Type, mut visited map[ast.Type]bool) { |
| 254 | base_typ := c.unwrap_generic(typ.clear_option_and_result().clear_flags(.variadic, .shared_f, |
| 255 | .atomic_f).clear_ref()) |
| 256 | if base_typ == 0 || visited[base_typ] { |
| 257 | return |
| 258 | } |
| 259 | visited[base_typ] = true |
| 260 | mut sym := c.table.sym(base_typ) |
| 261 | has_parent_str := sym.has_method_with_generic_parent('str') |
| 262 | has_exact_str := sym.has_method('str') |
| 263 | if has_parent_str || has_exact_str { |
| 264 | c.markused_generic_str_method(base_typ, sym) |
| 265 | return |
| 266 | } |
| 267 | if sym.info is ast.Alias { |
| 268 | c.markused_auto_str_dependencies_for_type(sym.info.parent_type.derive(base_typ), mut |
| 269 | visited) |
| 270 | return |
| 271 | } |
| 272 | sym = c.table.final_sym(base_typ) |
| 273 | if sym.has_method_with_generic_parent('str') || sym.has_method('str') { |
| 274 | c.markused_generic_str_method(ast.idx_to_type(sym.idx), sym) |
| 275 | return |
| 276 | } |
| 277 | match sym.info { |
| 278 | ast.Array { |
| 279 | c.markused_auto_str_dependencies_for_type(sym.info.elem_type, mut visited) |
| 280 | } |
| 281 | ast.ArrayFixed { |
| 282 | c.markused_auto_str_dependencies_for_type(sym.info.elem_type, mut visited) |
| 283 | } |
| 284 | ast.Chan { |
| 285 | c.markused_auto_str_dependencies_for_type(sym.info.elem_type, mut visited) |
| 286 | } |
| 287 | ast.Interface { |
| 288 | for concrete_type in sym.info.types { |
| 289 | c.markused_auto_str_dependencies_for_type(concrete_type, mut visited) |
| 290 | } |
| 291 | } |
| 292 | ast.Map { |
| 293 | c.markused_auto_str_dependencies_for_type(sym.info.key_type, mut visited) |
| 294 | c.markused_auto_str_dependencies_for_type(sym.info.value_type, mut visited) |
| 295 | } |
| 296 | ast.MultiReturn { |
| 297 | for ret_type in sym.info.types { |
| 298 | c.markused_auto_str_dependencies_for_type(ret_type, mut visited) |
| 299 | } |
| 300 | } |
| 301 | ast.Struct { |
| 302 | for field in sym.info.fields { |
| 303 | if attr := field.attrs.find_first('str') { |
| 304 | if attr.arg == 'skip' { |
| 305 | continue |
| 306 | } |
| 307 | } |
| 308 | c.markused_auto_str_dependencies_for_type(field.typ, mut visited) |
| 309 | } |
| 310 | } |
| 311 | ast.SumType { |
| 312 | for variant in sym.info.variants { |
| 313 | c.markused_auto_str_dependencies_for_type(variant, mut visited) |
| 314 | } |
| 315 | } |
| 316 | ast.Thread { |
| 317 | c.markused_auto_str_dependencies_for_type(sym.info.return_type, mut visited) |
| 318 | } |
| 319 | else {} |
| 320 | } |
| 321 | } |
| 322 | |
| 323 | fn (mut c Checker) markused_generic_str_method(typ ast.Type, sym &ast.TypeSymbol) { |
| 324 | mut method := ast.Fn{} |
| 325 | mut concrete_types := []ast.Type{} |
| 326 | if structured_method := c.table.find_structured_receiver_method_with_types(typ, 'str') { |
| 327 | if exact_method := sym.find_method('str') { |
| 328 | method = exact_method |
| 329 | } else if exact_method := c.table.find_alias_parent_exact_method(typ, 'str') { |
| 330 | method = exact_method |
| 331 | } else { |
| 332 | method = structured_method.method |
| 333 | concrete_types = structured_method.concrete_types.map(c.unwrap_generic(it)) |
| 334 | } |
| 335 | } else { |
| 336 | method = sym.find_method_with_generic_parent('str') or { |
| 337 | sym.find_method('str') or { return } |
| 338 | } |
| 339 | } |
| 340 | if concrete_types.len == 0 { |
| 341 | concrete_types = c.concrete_types_for_generic_str_receiver(typ, sym, method) |
| 342 | } |
| 343 | if method.generic_names.len != concrete_types.len || concrete_types.len == 0 |
| 344 | || concrete_types.any(it.has_flag(.generic) || c.type_has_unresolved_generic_parts(it)) { |
| 345 | return |
| 346 | } |
| 347 | mut registered := c.register_auto_str_fn_concrete_types(method.fkey(), concrete_types) |
| 348 | decl_fkey := method_decl_fkey(method) |
| 349 | if decl_fkey != method.fkey() { |
| 350 | registered = c.register_auto_str_fn_concrete_types(decl_fkey, concrete_types) || registered |
| 351 | } |
| 352 | for resolved_decl_fkey in c.str_method_decl_fkeys_for_type(typ, method) { |
| 353 | if resolved_decl_fkey != method.fkey() && resolved_decl_fkey != decl_fkey { |
| 354 | registered = c.register_auto_str_fn_concrete_types(resolved_decl_fkey, concrete_types) |
| 355 | || registered |
| 356 | } |
| 357 | } |
| 358 | if registered { |
| 359 | c.need_recheck_generic_fns = true |
| 360 | } |
| 361 | } |
| 362 | |
| 363 | fn (mut c Checker) register_auto_str_fn_concrete_types(fkey string, concrete_types []ast.Type) bool { |
| 364 | if fkey == '' { |
| 365 | return false |
| 366 | } |
| 367 | if _ := c.table.fn_generic_types[fkey] { |
| 368 | // The generic function key is already registered. |
| 369 | } else { |
| 370 | c.table.register_fn_generic_types(fkey) |
| 371 | } |
| 372 | return c.table.register_fn_concrete_types(fkey, concrete_types) |
| 373 | } |
| 374 | |
| 375 | fn method_decl_fkey(method ast.Fn) string { |
| 376 | if method.source_fn != unsafe { nil } { |
| 377 | fndecl := unsafe { &ast.FnDecl(method.source_fn) } |
| 378 | return fndecl.fkey() |
| 379 | } |
| 380 | return method.fkey() |
| 381 | } |
| 382 | |
| 383 | fn (c &Checker) generic_parent_str_method_fkey(sym &ast.TypeSymbol) string { |
| 384 | match sym.info { |
| 385 | ast.Struct, ast.Interface, ast.SumType { |
| 386 | if sym.info.parent_type.has_flag(.generic) { |
| 387 | parent_sym := c.table.sym(sym.info.parent_type) |
| 388 | if method := parent_sym.find_method('str') { |
| 389 | return method_decl_fkey(method) |
| 390 | } |
| 391 | } |
| 392 | } |
| 393 | ast.GenericInst { |
| 394 | if sym.info.parent_idx > 0 { |
| 395 | parent_sym := c.table.sym(ast.idx_to_type(sym.info.parent_idx)) |
| 396 | if method := parent_sym.find_method('str') { |
| 397 | return method_decl_fkey(method) |
| 398 | } |
| 399 | } |
| 400 | } |
| 401 | else {} |
| 402 | } |
| 403 | |
| 404 | return '' |
| 405 | } |
| 406 | |
| 407 | fn add_str_method_decl_fkey(mut fkeys []string, fkey string) { |
| 408 | if fkey != '' && fkey !in fkeys { |
| 409 | fkeys << fkey |
| 410 | } |
| 411 | } |
| 412 | |
| 413 | fn add_matching_str_method_decl_fkeys(mut fkeys []string, candidate_fkeys []string, resolved_decl_fkey string) { |
| 414 | if resolved_decl_fkey in candidate_fkeys { |
| 415 | for fkey in candidate_fkeys { |
| 416 | add_str_method_decl_fkey(mut fkeys, fkey) |
| 417 | } |
| 418 | } |
| 419 | } |
| 420 | |
| 421 | fn (c &Checker) str_method_decl_fkeys_for_type(typ ast.Type, method ast.Fn) []string { |
| 422 | mut fkeys := []string{} |
| 423 | if typ == 0 { |
| 424 | return fkeys |
| 425 | } |
| 426 | resolved_decl_fkey := method_decl_fkey(method) |
| 427 | for fkey in c.str_method_decl_fkeys_for_candidate(typ) { |
| 428 | add_str_method_decl_fkey(mut fkeys, fkey) |
| 429 | } |
| 430 | unaliased := c.table.unaliased_type(typ) |
| 431 | if unaliased != 0 && unaliased != typ { |
| 432 | add_matching_str_method_decl_fkeys(mut fkeys, |
| 433 | c.str_method_decl_fkeys_for_candidate(unaliased), resolved_decl_fkey) |
| 434 | } |
| 435 | if !typ.is_ptr() { |
| 436 | ref_typ := typ.ref() |
| 437 | if ref_typ != 0 { |
| 438 | add_matching_str_method_decl_fkeys(mut fkeys, |
| 439 | c.str_method_decl_fkeys_for_candidate(ref_typ), resolved_decl_fkey) |
| 440 | } |
| 441 | } else { |
| 442 | deref_typ := typ.deref() |
| 443 | if deref_typ != 0 { |
| 444 | add_matching_str_method_decl_fkeys(mut fkeys, |
| 445 | c.str_method_decl_fkeys_for_candidate(deref_typ), resolved_decl_fkey) |
| 446 | } |
| 447 | } |
| 448 | return fkeys |
| 449 | } |
| 450 | |
| 451 | fn (c &Checker) str_method_decl_fkeys_for_candidate(candidate ast.Type) []string { |
| 452 | mut fkeys := []string{} |
| 453 | sym := c.table.sym(candidate) |
| 454 | parent_fkey := c.generic_parent_str_method_fkey(sym) |
| 455 | if parent_fkey != '' { |
| 456 | add_str_method_decl_fkey(mut fkeys, parent_fkey) |
| 457 | } |
| 458 | if method := sym.find_method_with_generic_parent('str') { |
| 459 | add_str_method_decl_fkey(mut fkeys, method_decl_fkey(method)) |
| 460 | } |
| 461 | if method := c.table.find_method(sym, 'str') { |
| 462 | add_str_method_decl_fkey(mut fkeys, method_decl_fkey(method)) |
| 463 | } |
| 464 | method, embed_types := c.table.find_method_from_embeds(c.table.final_sym(candidate), 'str') or { |
| 465 | ast.Fn{}, []ast.Type{} |
| 466 | } |
| 467 | if embed_types.len != 0 { |
| 468 | add_str_method_decl_fkey(mut fkeys, method_decl_fkey(method)) |
| 469 | } |
| 470 | return fkeys |
| 471 | } |
| 472 | |
| 473 | fn (mut c Checker) concrete_types_for_generic_str_receiver(typ ast.Type, sym &ast.TypeSymbol, method ast.Fn) []ast.Type { |
| 474 | mut concrete_types := c.concrete_types_for_type_symbol(sym) |
| 475 | if concrete_types.len == 0 && sym.info is ast.Alias { |
| 476 | concrete_types = c.alias_parent_concrete_types(sym.info) |
| 477 | } |
| 478 | if method.generic_names.len == 0 |
| 479 | || (concrete_types.len > 0 && concrete_types.len == method.generic_names.len) { |
| 480 | return concrete_types |
| 481 | } |
| 482 | return c.infer_method_receiver_concrete_types(method, typ) |
| 483 | } |
| 484 | |
| 485 | fn (c &Checker) alias_parent_concrete_types(info ast.Alias) []ast.Type { |
| 486 | parent_sym := c.table.sym(info.parent_type) |
| 487 | match parent_sym.info { |
| 488 | ast.Struct, ast.SumType, ast.Interface { |
| 489 | mut concrete_types := parent_sym.info.concrete_types.clone() |
| 490 | if concrete_types.len == 0 |
| 491 | && parent_sym.generic_types.len == parent_sym.info.generic_types.len |
| 492 | && parent_sym.generic_types != parent_sym.info.generic_types { |
| 493 | concrete_types = parent_sym.generic_types.clone() |
| 494 | } |
| 495 | return concrete_types |
| 496 | } |
| 497 | ast.GenericInst { |
| 498 | return parent_sym.info.concrete_types.clone() |
| 499 | } |
| 500 | ast.Array { |
| 501 | return [parent_sym.info.elem_type] |
| 502 | } |
| 503 | ast.ArrayFixed { |
| 504 | return [parent_sym.info.elem_type] |
| 505 | } |
| 506 | ast.Chan { |
| 507 | return [parent_sym.info.elem_type] |
| 508 | } |
| 509 | ast.Map { |
| 510 | return [parent_sym.info.key_type, parent_sym.info.value_type] |
| 511 | } |
| 512 | ast.Alias { |
| 513 | return c.alias_parent_concrete_types(parent_sym.info) |
| 514 | } |
| 515 | else {} |
| 516 | } |
| 517 | |
| 518 | return []ast.Type{} |
| 519 | } |
| 520 | |
| 521 | fn (mut c Checker) infer_method_receiver_concrete_types(method ast.Fn, typ ast.Type) []ast.Type { |
| 522 | mut concrete_types := []ast.Type{cap: method.generic_names.len} |
| 523 | for generic_name in method.generic_names { |
| 524 | mut concrete_type := c.infer_composite_generic_type(generic_name, method.receiver_type, typ) |
| 525 | if concrete_type == ast.void_type { |
| 526 | unaliased_receiver_type := c.table.fully_unaliased_type(method.receiver_type) |
| 527 | unaliased_typ := c.table.fully_unaliased_type(typ) |
| 528 | if unaliased_receiver_type != method.receiver_type || unaliased_typ != typ { |
| 529 | concrete_type = c.infer_composite_generic_type(generic_name, |
| 530 | unaliased_receiver_type, unaliased_typ) |
| 531 | } |
| 532 | } |
| 533 | if concrete_type == ast.void_type { |
| 534 | return []ast.Type{} |
| 535 | } |
| 536 | concrete_types << c.unwrap_generic(concrete_type) |
| 537 | } |
| 538 | return concrete_types |
| 539 | } |
| 540 | |
| 541 | fn (mut c Checker) markused_array_method(check bool, method_name string) { |
| 542 | if !check { |
| 543 | return |
| 544 | } |
| 545 | match method_name { |
| 546 | '' { // array init |
| 547 | } |
| 548 | 'first' { |
| 549 | c.table.used_features.arr_first = true |
| 550 | } |
| 551 | 'last' { |
| 552 | c.table.used_features.arr_last = true |
| 553 | } |
| 554 | 'pop_left' { |
| 555 | c.table.used_features.arr_pop_left = true |
| 556 | } |
| 557 | 'pop' { |
| 558 | c.table.used_features.arr_pop = true |
| 559 | } |
| 560 | 'delete' { |
| 561 | c.table.used_features.arr_delete = true |
| 562 | } |
| 563 | 'map' { |
| 564 | c.table.used_features.arr_map = true |
| 565 | } |
| 566 | else {} |
| 567 | } |
| 568 | } |
| 569 | |