| ... | ... | @@ -2216,25 +2216,15 @@ fn checkUsed( |
| 2216 | 2216 | .gen_zir => scope = scope.cast(GenZir).?.parent, |
| 2217 | 2217 | .local_val => { |
| 2218 | 2218 | const s = scope.cast(Scope.LocalVal).?; |
| 2219 | | switch (s.used) { |
| 2220 | | .used => {}, |
| 2221 | | .fn_param => return astgen.failTok(s.token_src, "unused function parameter", .{}), |
| 2222 | | .constant => return astgen.failTok(s.token_src, "unused local constant", .{}), |
| 2223 | | .variable => unreachable, |
| 2224 | | .loop_index => unreachable, |
| 2225 | | .capture => return astgen.failTok(s.token_src, "unused capture", .{}), |
| 2219 | if (!s.used) { |
| 2220 | return astgen.failTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)}); |
| 2226 | 2221 | } |
| 2227 | 2222 | scope = s.parent; |
| 2228 | 2223 | }, |
| 2229 | 2224 | .local_ptr => { |
| 2230 | 2225 | const s = scope.cast(Scope.LocalPtr).?; |
| 2231 | | switch (s.used) { |
| 2232 | | .used => {}, |
| 2233 | | .fn_param => unreachable, |
| 2234 | | .constant => return astgen.failTok(s.token_src, "unused local constant", .{}), |
| 2235 | | .variable => return astgen.failTok(s.token_src, "unused local variable", .{}), |
| 2236 | | .loop_index => return astgen.failTok(s.token_src, "unused loop index capture", .{}), |
| 2237 | | .capture => unreachable, |
| 2226 | if (!s.used) { |
| 2227 | return astgen.failTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)}); |
| 2238 | 2228 | } |
| 2239 | 2229 | scope = s.parent; |
| 2240 | 2230 | }, |
| ... | ... | @@ -2280,63 +2270,7 @@ fn varDecl( |
| 2280 | 2270 | } |
| 2281 | 2271 | const ident_name = try astgen.identAsString(name_token); |
| 2282 | 2272 | |
| 2283 | | // Local variables shadowing detection, including function parameters. |
| 2284 | | { |
| 2285 | | var s = scope; |
| 2286 | | while (true) switch (s.tag) { |
| 2287 | | .local_val => { |
| 2288 | | const local_val = s.cast(Scope.LocalVal).?; |
| 2289 | | if (local_val.name == ident_name) { |
| 2290 | | const name = try gpa.dupe(u8, mem.spanZ(astgen.nullTerminatedString(ident_name))); |
| 2291 | | defer gpa.free(name); |
| 2292 | | return astgen.failTokNotes(name_token, "redeclaration of '{s}'", .{ |
| 2293 | | name, |
| 2294 | | }, &[_]u32{ |
| 2295 | | try astgen.errNoteTok( |
| 2296 | | local_val.token_src, |
| 2297 | | "previously declared here", |
| 2298 | | .{}, |
| 2299 | | ), |
| 2300 | | }); |
| 2301 | | } |
| 2302 | | s = local_val.parent; |
| 2303 | | }, |
| 2304 | | .local_ptr => { |
| 2305 | | const local_ptr = s.cast(Scope.LocalPtr).?; |
| 2306 | | if (local_ptr.name == ident_name) { |
| 2307 | | const name = try gpa.dupe(u8, mem.spanZ(astgen.nullTerminatedString(ident_name))); |
| 2308 | | defer gpa.free(name); |
| 2309 | | return astgen.failTokNotes(name_token, "redeclaration of '{s}'", .{ |
| 2310 | | name, |
| 2311 | | }, &[_]u32{ |
| 2312 | | try astgen.errNoteTok( |
| 2313 | | local_ptr.token_src, |
| 2314 | | "previously declared here", |
| 2315 | | .{}, |
| 2316 | | ), |
| 2317 | | }); |
| 2318 | | } |
| 2319 | | s = local_ptr.parent; |
| 2320 | | }, |
| 2321 | | .namespace => { |
| 2322 | | const ns = s.cast(Scope.Namespace).?; |
| 2323 | | const decl_node = ns.decls.get(ident_name) orelse { |
| 2324 | | s = ns.parent; |
| 2325 | | continue; |
| 2326 | | }; |
| 2327 | | const name = try gpa.dupe(u8, mem.spanZ(astgen.nullTerminatedString(ident_name))); |
| 2328 | | defer gpa.free(name); |
| 2329 | | return astgen.failTokNotes(name_token, "local shadows declaration of '{s}'", .{ |
| 2330 | | name, |
| 2331 | | }, &[_]u32{ |
| 2332 | | try astgen.errNoteNode(decl_node, "declared here", .{}), |
| 2333 | | }); |
| 2334 | | }, |
| 2335 | | .gen_zir => s = s.cast(GenZir).?.parent, |
| 2336 | | .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, |
| 2337 | | .top => break, |
| 2338 | | }; |
| 2339 | | } |
| 2273 | try astgen.detectLocalShadowing(scope, ident_name, name_token); |
| 2340 | 2274 | |
| 2341 | 2275 | if (var_decl.ast.init_node == 0) { |
| 2342 | 2276 | return astgen.failNode(node, "variables must be initialized", .{}); |
| ... | ... | @@ -2369,7 +2303,7 @@ fn varDecl( |
| 2369 | 2303 | .name = ident_name, |
| 2370 | 2304 | .inst = init_inst, |
| 2371 | 2305 | .token_src = name_token, |
| 2372 | | .used = .constant, |
| 2306 | .id_cat = .@"local constant", |
| 2373 | 2307 | }; |
| 2374 | 2308 | return &sub_scope.base; |
| 2375 | 2309 | } |
| ... | ... | @@ -2438,7 +2372,7 @@ fn varDecl( |
| 2438 | 2372 | .name = ident_name, |
| 2439 | 2373 | .inst = init_inst, |
| 2440 | 2374 | .token_src = name_token, |
| 2441 | | .used = .constant, |
| 2375 | .id_cat = .@"local constant", |
| 2442 | 2376 | }; |
| 2443 | 2377 | return &sub_scope.base; |
| 2444 | 2378 | } |
| ... | ... | @@ -2468,7 +2402,7 @@ fn varDecl( |
| 2468 | 2402 | .ptr = init_scope.rl_ptr, |
| 2469 | 2403 | .token_src = name_token, |
| 2470 | 2404 | .maybe_comptime = true, |
| 2471 | | .used = .constant, |
| 2405 | .id_cat = .@"local constant", |
| 2472 | 2406 | }; |
| 2473 | 2407 | return &sub_scope.base; |
| 2474 | 2408 | }, |
| ... | ... | @@ -2525,7 +2459,7 @@ fn varDecl( |
| 2525 | 2459 | .ptr = var_data.alloc, |
| 2526 | 2460 | .token_src = name_token, |
| 2527 | 2461 | .maybe_comptime = is_comptime, |
| 2528 | | .used = .variable, |
| 2462 | .id_cat = .@"local variable", |
| 2529 | 2463 | }; |
| 2530 | 2464 | return &sub_scope.base; |
| 2531 | 2465 | }, |
| ... | ... | @@ -3028,7 +2962,7 @@ fn fnDecl( |
| 3028 | 2962 | const param_name = try astgen.identAsString(name_token); |
| 3029 | 2963 | // Create an arg instruction. This is needed to emit a semantic analysis |
| 3030 | 2964 | // error for shadowing decls. |
| 3031 | | // TODO emit a compile error here for shadowing locals. |
| 2965 | try astgen.detectLocalShadowing(params_scope, param_name, name_token); |
| 3032 | 2966 | const arg_inst = try fn_gz.addStrTok(.arg, param_name, name_token); |
| 3033 | 2967 | const sub_scope = try astgen.arena.create(Scope.LocalVal); |
| 3034 | 2968 | sub_scope.* = .{ |
| ... | ... | @@ -3037,7 +2971,7 @@ fn fnDecl( |
| 3037 | 2971 | .name = param_name, |
| 3038 | 2972 | .inst = arg_inst, |
| 3039 | 2973 | .token_src = name_token, |
| 3040 | | .used = .fn_param, |
| 2974 | .id_cat = .@"function parameter", |
| 3041 | 2975 | }; |
| 3042 | 2976 | params_scope = &sub_scope.base; |
| 3043 | 2977 | |
| ... | ... | @@ -4701,7 +4635,7 @@ fn orelseCatchExpr( |
| 4701 | 4635 | .name = err_name, |
| 4702 | 4636 | .inst = try then_scope.addUnNode(unwrap_code_op, operand, node), |
| 4703 | 4637 | .token_src = payload, |
| 4704 | | .used = .capture, |
| 4638 | .id_cat = .@"capture", |
| 4705 | 4639 | }; |
| 4706 | 4640 | break :blk &err_val_scope.base; |
| 4707 | 4641 | }; |
| ... | ... | @@ -4993,7 +4927,7 @@ fn ifExpr( |
| 4993 | 4927 | .name = ident_name, |
| 4994 | 4928 | .inst = payload_inst, |
| 4995 | 4929 | .token_src = payload_token, |
| 4996 | | .used = .capture, |
| 4930 | .id_cat = .@"capture", |
| 4997 | 4931 | }; |
| 4998 | 4932 | break :s &payload_val_scope.base; |
| 4999 | 4933 | } else { |
| ... | ... | @@ -5015,7 +4949,7 @@ fn ifExpr( |
| 5015 | 4949 | .name = ident_name, |
| 5016 | 4950 | .inst = payload_inst, |
| 5017 | 4951 | .token_src = ident_token, |
| 5018 | | .used = .capture, |
| 4952 | .id_cat = .@"capture", |
| 5019 | 4953 | }; |
| 5020 | 4954 | break :s &payload_val_scope.base; |
| 5021 | 4955 | } else { |
| ... | ... | @@ -5056,7 +4990,7 @@ fn ifExpr( |
| 5056 | 4990 | .name = ident_name, |
| 5057 | 4991 | .inst = payload_inst, |
| 5058 | 4992 | .token_src = error_token, |
| 5059 | | .used = .capture, |
| 4993 | .id_cat = .@"capture", |
| 5060 | 4994 | }; |
| 5061 | 4995 | break :s &payload_val_scope.base; |
| 5062 | 4996 | } else { |
| ... | ... | @@ -5250,7 +5184,7 @@ fn whileExpr( |
| 5250 | 5184 | .name = ident_name, |
| 5251 | 5185 | .inst = payload_inst, |
| 5252 | 5186 | .token_src = payload_token, |
| 5253 | | .used = .capture, |
| 5187 | .id_cat = .@"capture", |
| 5254 | 5188 | }; |
| 5255 | 5189 | break :s &payload_val_scope.base; |
| 5256 | 5190 | } else { |
| ... | ... | @@ -5272,7 +5206,7 @@ fn whileExpr( |
| 5272 | 5206 | .name = ident_name, |
| 5273 | 5207 | .inst = payload_inst, |
| 5274 | 5208 | .token_src = ident_token, |
| 5275 | | .used = .capture, |
| 5209 | .id_cat = .@"capture", |
| 5276 | 5210 | }; |
| 5277 | 5211 | break :s &payload_val_scope.base; |
| 5278 | 5212 | } else { |
| ... | ... | @@ -5329,7 +5263,7 @@ fn whileExpr( |
| 5329 | 5263 | .name = ident_name, |
| 5330 | 5264 | .inst = payload_inst, |
| 5331 | 5265 | .token_src = error_token, |
| 5332 | | .used = .capture, |
| 5266 | .id_cat = .@"capture", |
| 5333 | 5267 | }; |
| 5334 | 5268 | break :s &payload_val_scope.base; |
| 5335 | 5269 | } else { |
| ... | ... | @@ -5468,7 +5402,7 @@ fn forExpr( |
| 5468 | 5402 | .name = name_str_index, |
| 5469 | 5403 | .inst = payload_inst, |
| 5470 | 5404 | .token_src = ident, |
| 5471 | | .used = .capture, |
| 5405 | .id_cat = .@"capture", |
| 5472 | 5406 | }; |
| 5473 | 5407 | payload_sub_scope = &payload_val_scope.base; |
| 5474 | 5408 | } else if (is_ptr) { |
| ... | ... | @@ -5492,7 +5426,7 @@ fn forExpr( |
| 5492 | 5426 | .ptr = index_ptr, |
| 5493 | 5427 | .token_src = index_token, |
| 5494 | 5428 | .maybe_comptime = is_inline, |
| 5495 | | .used = .loop_index, |
| 5429 | .id_cat = .@"loop index capture", |
| 5496 | 5430 | }; |
| 5497 | 5431 | break :blk &index_scope.base; |
| 5498 | 5432 | }; |
| ... | ... | @@ -5737,7 +5671,7 @@ fn switchExpr( |
| 5737 | 5671 | .name = capture_name, |
| 5738 | 5672 | .inst = capture, |
| 5739 | 5673 | .token_src = payload_token, |
| 5740 | | .used = .capture, |
| 5674 | .id_cat = .@"capture", |
| 5741 | 5675 | }; |
| 5742 | 5676 | break :blk &capture_val_scope.base; |
| 5743 | 5677 | }; |
| ... | ... | @@ -5831,7 +5765,7 @@ fn switchExpr( |
| 5831 | 5765 | .name = capture_name, |
| 5832 | 5766 | .inst = capture, |
| 5833 | 5767 | .token_src = payload_token, |
| 5834 | | .used = .capture, |
| 5768 | .id_cat = .@"capture", |
| 5835 | 5769 | }; |
| 5836 | 5770 | break :blk &capture_val_scope.base; |
| 5837 | 5771 | }; |
| ... | ... | @@ -6261,7 +6195,7 @@ fn identifier( |
| 6261 | 6195 | const local_val = s.cast(Scope.LocalVal).?; |
| 6262 | 6196 | |
| 6263 | 6197 | if (local_val.name == name_str_index) { |
| 6264 | | local_val.used = .used; |
| 6198 | local_val.used = true; |
| 6265 | 6199 | // Captures of non-locals need to be emitted as decl_val or decl_ref. |
| 6266 | 6200 | // This *might* be capturable depending on if it is comptime known. |
| 6267 | 6201 | if (!hit_namespace) { |
| ... | ... | @@ -6273,7 +6207,7 @@ fn identifier( |
| 6273 | 6207 | .local_ptr => { |
| 6274 | 6208 | const local_ptr = s.cast(Scope.LocalPtr).?; |
| 6275 | 6209 | if (local_ptr.name == name_str_index) { |
| 6276 | | local_ptr.used = .used; |
| 6210 | local_ptr.used = true; |
| 6277 | 6211 | if (hit_namespace) { |
| 6278 | 6212 | if (local_ptr.maybe_comptime) |
| 6279 | 6213 | break |
| ... | ... | @@ -6609,7 +6543,7 @@ fn asmExpr( |
| 6609 | 6543 | .local_val => { |
| 6610 | 6544 | const local_val = s.cast(Scope.LocalVal).?; |
| 6611 | 6545 | if (local_val.name == str_index) { |
| 6612 | | local_val.used = .used; |
| 6546 | local_val.used = true; |
| 6613 | 6547 | break; |
| 6614 | 6548 | } |
| 6615 | 6549 | s = local_val.parent; |
| ... | ... | @@ -6617,7 +6551,7 @@ fn asmExpr( |
| 6617 | 6551 | .local_ptr => { |
| 6618 | 6552 | const local_ptr = s.cast(Scope.LocalPtr).?; |
| 6619 | 6553 | if (local_ptr.name == str_index) { |
| 6620 | | local_ptr.used = .used; |
| 6554 | local_ptr.used = true; |
| 6621 | 6555 | break; |
| 6622 | 6556 | } |
| 6623 | 6557 | s = local_ptr.parent; |
| ... | ... | @@ -6968,7 +6902,7 @@ fn builtinCall( |
| 6968 | 6902 | .local_val => { |
| 6969 | 6903 | const local_val = s.cast(Scope.LocalVal).?; |
| 6970 | 6904 | if (local_val.name == decl_name) { |
| 6971 | | local_val.used = .used; |
| 6905 | local_val.used = true; |
| 6972 | 6906 | break; |
| 6973 | 6907 | } |
| 6974 | 6908 | s = local_val.parent; |
| ... | ... | @@ -6978,7 +6912,7 @@ fn builtinCall( |
| 6978 | 6912 | if (local_ptr.name == decl_name) { |
| 6979 | 6913 | if (!local_ptr.maybe_comptime) |
| 6980 | 6914 | return astgen.failNode(params[0], "unable to export runtime-known value", .{}); |
| 6981 | | local_ptr.used = .used; |
| 6915 | local_ptr.used = true; |
| 6982 | 6916 | break; |
| 6983 | 6917 | } |
| 6984 | 6918 | s = local_ptr.parent; |
| ... | ... | @@ -8545,15 +8479,15 @@ const Scope = struct { |
| 8545 | 8479 | top, |
| 8546 | 8480 | }; |
| 8547 | 8481 | |
| 8548 | | // either .used or the type of the var/constant |
| 8549 | | const Used = enum { |
| 8550 | | fn_param, |
| 8551 | | constant, |
| 8552 | | variable, |
| 8553 | | loop_index, |
| 8554 | | capture, |
| 8555 | | used, |
| 8482 | /// The category of identifier. These tag names are user-visible in compile errors. |
| 8483 | const IdCat = enum { |
| 8484 | @"function parameter", |
| 8485 | @"local constant", |
| 8486 | @"local variable", |
| 8487 | @"loop index capture", |
| 8488 | @"capture", |
| 8556 | 8489 | }; |
| 8490 | |
| 8557 | 8491 | /// This is always a `const` local and importantly the `inst` is a value type, not a pointer. |
| 8558 | 8492 | /// This structure lives as long as the AST generation of the Block |
| 8559 | 8493 | /// node that contains the variable. |
| ... | ... | @@ -8568,8 +8502,9 @@ const Scope = struct { |
| 8568 | 8502 | token_src: ast.TokenIndex, |
| 8569 | 8503 | /// String table index. |
| 8570 | 8504 | name: u32, |
| 8571 | | /// has this variable been referenced? |
| 8572 | | used: Used, |
| 8505 | id_cat: IdCat, |
| 8506 | /// Track whether the name has been referenced. |
| 8507 | used: bool = false, |
| 8573 | 8508 | }; |
| 8574 | 8509 | |
| 8575 | 8510 | /// This could be a `const` or `var` local. It has a pointer instead of a value. |
| ... | ... | @@ -8586,10 +8521,12 @@ const Scope = struct { |
| 8586 | 8521 | token_src: ast.TokenIndex, |
| 8587 | 8522 | /// String table index. |
| 8588 | 8523 | name: u32, |
| 8589 | | /// true means we find out during Sema whether the value is comptime. false means it is already known at AstGen the value is runtime-known. |
| 8524 | id_cat: IdCat, |
| 8525 | /// true means we find out during Sema whether the value is comptime. |
| 8526 | /// false means it is already known at AstGen the value is runtime-known. |
| 8590 | 8527 | maybe_comptime: bool, |
| 8591 | | /// has this variable been referenced? |
| 8592 | | used: Used, |
| 8528 | /// Track whether the name has been referenced. |
| 8529 | used: bool = false, |
| 8593 | 8530 | }; |
| 8594 | 8531 | |
| 8595 | 8532 | const Defer = struct { |
| ... | ... | @@ -9680,6 +9617,71 @@ fn declareNewName( |
| 9680 | 9617 | } |
| 9681 | 9618 | } |
| 9682 | 9619 | |
| 9620 | /// Local variables shadowing detection, including function parameters. |
| 9621 | fn detectLocalShadowing( |
| 9622 | astgen: *AstGen, |
| 9623 | scope: *Scope, |
| 9624 | ident_name: u32, |
| 9625 | name_token: ast.TokenIndex, |
| 9626 | ) !void { |
| 9627 | const gpa = astgen.gpa; |
| 9628 | |
| 9629 | var s = scope; |
| 9630 | while (true) switch (s.tag) { |
| 9631 | .local_val => { |
| 9632 | const local_val = s.cast(Scope.LocalVal).?; |
| 9633 | if (local_val.name == ident_name) { |
| 9634 | const name = try gpa.dupe(u8, mem.spanZ(astgen.nullTerminatedString(ident_name))); |
| 9635 | defer gpa.free(name); |
| 9636 | return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{ |
| 9637 | @tagName(local_val.id_cat), name, |
| 9638 | }, &[_]u32{ |
| 9639 | try astgen.errNoteTok( |
| 9640 | local_val.token_src, |
| 9641 | "previously declared here", |
| 9642 | .{}, |
| 9643 | ), |
| 9644 | }); |
| 9645 | } |
| 9646 | s = local_val.parent; |
| 9647 | }, |
| 9648 | .local_ptr => { |
| 9649 | const local_ptr = s.cast(Scope.LocalPtr).?; |
| 9650 | if (local_ptr.name == ident_name) { |
| 9651 | const name = try gpa.dupe(u8, mem.spanZ(astgen.nullTerminatedString(ident_name))); |
| 9652 | defer gpa.free(name); |
| 9653 | return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{ |
| 9654 | @tagName(local_ptr.id_cat), name, |
| 9655 | }, &[_]u32{ |
| 9656 | try astgen.errNoteTok( |
| 9657 | local_ptr.token_src, |
| 9658 | "previously declared here", |
| 9659 | .{}, |
| 9660 | ), |
| 9661 | }); |
| 9662 | } |
| 9663 | s = local_ptr.parent; |
| 9664 | }, |
| 9665 | .namespace => { |
| 9666 | const ns = s.cast(Scope.Namespace).?; |
| 9667 | const decl_node = ns.decls.get(ident_name) orelse { |
| 9668 | s = ns.parent; |
| 9669 | continue; |
| 9670 | }; |
| 9671 | const name = try gpa.dupe(u8, mem.spanZ(astgen.nullTerminatedString(ident_name))); |
| 9672 | defer gpa.free(name); |
| 9673 | return astgen.failTokNotes(name_token, "local shadows declaration of '{s}'", .{ |
| 9674 | name, |
| 9675 | }, &[_]u32{ |
| 9676 | try astgen.errNoteNode(decl_node, "declared here", .{}), |
| 9677 | }); |
| 9678 | }, |
| 9679 | .gen_zir => s = s.cast(GenZir).?.parent, |
| 9680 | .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, |
| 9681 | .top => break, |
| 9682 | }; |
| 9683 | } |
| 9684 | |
| 9683 | 9685 | fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void { |
| 9684 | 9686 | var i = astgen.source_offset; |
| 9685 | 9687 | var line = astgen.source_line; |