authorgravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2025-12-17 20:01:36+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-11 11:37:16+00:00
logd840bb511839464c852699acce471efccdd67f3e
treec7eebab23484f8835231e35845273a834202e17c
parente0108dec54deaf044e869bb76aa4a40813913277
signaturelock-open Commit is signed but in an unrecognized format.

AstGen: improve ergonomics of `Scope`

Adds `Scope.Unwrapped`, a simple union of pointers to already-casted scopes with `Scope.Tag` as its tag enum. This pairs very nicely with labeled switch and gets rid of almost every `scope.cast(...).?`, improving developer QOL :)

1 files changed, 122 insertions(+), 155 deletions(-)

lib/std/zig/AstGen.zig+122-155
...@@ -2161,10 +2161,9 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -2161,10 +2161,9 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
2161 const opt_break_label, const opt_rhs = tree.nodeData(node).opt_token_and_opt_node;2161 const opt_break_label, const opt_rhs = tree.nodeData(node).opt_token_and_opt_node;
21622162
2163 // Look for the label in the scope.2163 // Look for the label in the scope.
2164 var scope = parent_scope;2164 find_scope: switch (parent_scope.unwrap()) {
2165 find_scope: switch (scope.tag) {2165 .gen_zir => |gen_zir| {
2166 .gen_zir => {2166 const scope = &gen_zir.base;
2167 const gen_zir = scope.cast(GenZir).?;
21682167
2169 if (gen_zir.cur_defer_node.unwrap()) |cur_defer_node| {2168 if (gen_zir.cur_defer_node.unwrap()) |cur_defer_node| {
2170 // We are breaking out of a `defer` block.2169 // We are breaking out of a `defer` block.
...@@ -2185,13 +2184,11 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -2185,13 +2184,11 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
2185 }2184 }
2186 }2185 }
2187 // gz without or with different label, continue to parent scopes.2186 // gz without or with different label, continue to parent scopes.
2188 scope = gen_zir.parent;2187 continue :find_scope gen_zir.parent.unwrap();
2189 continue :find_scope scope.tag;
2190 } else if (!gen_zir.allow_unlabeled_control_flow) {2188 } else if (!gen_zir.allow_unlabeled_control_flow) {
2191 // This `break` is unlabeled and the gz we've found doesn't allow2189 // This `break` is unlabeled and the gz we've found doesn't allow
2192 // unlabeled control flow. Continue to parent scopes.2190 // unlabeled control flow. Continue to parent scopes.
2193 scope = gen_zir.parent;2191 continue :find_scope gen_zir.parent.unwrap();
2194 continue :find_scope scope.tag;
2195 }2192 }
21962193
2197 const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline)2194 const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline)
...@@ -2236,18 +2233,9 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -2236,18 +2233,9 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
2236 return .unreachable_value;2233 return .unreachable_value;
2237 }2234 }
2238 },2235 },
2239 .local_val => {2236 .local_val => |local_val| continue :find_scope local_val.parent.unwrap(),
2240 scope = scope.cast(Scope.LocalVal).?.parent;2237 .local_ptr => |local_ptr| continue :find_scope local_ptr.parent.unwrap(),
2241 continue :find_scope scope.tag;2238 .defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
2242 },
2243 .local_ptr => {
2244 scope = scope.cast(Scope.LocalPtr).?.parent;
2245 continue :find_scope scope.tag;
2246 },
2247 .defer_normal, .defer_error => {
2248 scope = scope.cast(Scope.Defer).?.parent;
2249 continue :find_scope scope.tag;
2250 },
2251 .namespace => {2239 .namespace => {
2252 if (opt_break_label.unwrap()) |break_label| {2240 if (opt_break_label.unwrap()) |break_label| {
2253 const label_name = try astgen.identifierTokenString(break_label);2241 const label_name = try astgen.identifierTokenString(break_label);
...@@ -2270,10 +2258,9 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)...@@ -2270,10 +2258,9 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
2270 }2258 }
22712259
2272 // Look for the label in the scope.2260 // Look for the label in the scope.
2273 var scope = parent_scope;2261 find_scope: switch (parent_scope.unwrap()) {
2274 find_scope: switch (scope.tag) {2262 .gen_zir => |gen_zir| {
2275 .gen_zir => {2263 const scope = &gen_zir.base;
2276 const gen_zir = scope.cast(GenZir).?;
22772264
2278 if (gen_zir.cur_defer_node.unwrap()) |cur_defer_node| {2265 if (gen_zir.cur_defer_node.unwrap()) |cur_defer_node| {
2279 return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{2266 return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{
...@@ -2305,24 +2292,21 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)...@@ -2305,24 +2292,21 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
2305 }2292 }
2306 }2293 }
2307 // gz without or with different label, continue to parent scopes.2294 // gz without or with different label, continue to parent scopes.
2308 scope = gen_zir.parent;2295 continue :find_scope gen_zir.parent.unwrap();
2309 continue :find_scope scope.tag;
2310 } else if (gen_zir.allow_unlabeled_control_flow) {2296 } else if (gen_zir.allow_unlabeled_control_flow) {
2311 // This `continue` is unlabeled. If the gz we've found doesn't2297 // This `continue` is unlabeled. If the gz we've found doesn't
2312 // provide a `continue` target or corresponds to a labeled2298 // provide a `continue` target or corresponds to a labeled
2313 // `switch`, ignore it and continue to parent scopes.2299 // `switch`, ignore it and continue to parent scopes.
2314 switch (gen_zir.continue_target) {2300 switch (gen_zir.continue_target) {
2315 .none, .switch_continue => {2301 .none, .switch_continue => {
2316 scope = gen_zir.parent;2302 continue :find_scope gen_zir.parent.unwrap();
2317 continue :find_scope scope.tag;
2318 },2303 },
2319 .@"break" => {},2304 .@"break" => {},
2320 }2305 }
2321 } else {2306 } else {
2322 // We don't have a break label and the gz we found doesn't allow2307 // We don't have a break label and the gz we found doesn't allow
2323 // unlabeled control flow, so we continue to its parent scopes.2308 // unlabeled control flow, so we continue to its parent scopes.
2324 scope = gen_zir.parent;2309 continue :find_scope gen_zir.parent.unwrap();
2325 continue :find_scope scope.tag;
2326 }2310 }
23272311
2328 switch (gen_zir.continue_target) {2312 switch (gen_zir.continue_target) {
...@@ -2360,18 +2344,9 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)...@@ -2360,18 +2344,9 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
2360 },2344 },
2361 }2345 }
2362 },2346 },
2363 .local_val => {2347 .local_val => |local_val| continue :find_scope local_val.parent.unwrap(),
2364 scope = scope.cast(Scope.LocalVal).?.parent;2348 .local_ptr => |local_ptr| continue :find_scope local_ptr.parent.unwrap(),
2365 continue :find_scope scope.tag;2349 .defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
2366 },
2367 .local_ptr => {
2368 scope = scope.cast(Scope.LocalPtr).?.parent;
2369 continue :find_scope scope.tag;
2370 },
2371 .defer_normal, .defer_error => {
2372 scope = scope.cast(Scope.Defer).?.parent;
2373 continue :find_scope scope.tag;
2374 },
2375 .namespace => {2350 .namespace => {
2376 if (opt_break_label.unwrap()) |break_label| {2351 if (opt_break_label.unwrap()) |break_label| {
2377 const label_name = try astgen.identifierTokenString(break_label);2352 const label_name = try astgen.identifierTokenString(break_label);
...@@ -2466,33 +2441,29 @@ fn blockExpr(...@@ -2466,33 +2441,29 @@ fn blockExpr(
24662441
2467fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.TokenIndex) !void {2442fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.TokenIndex) !void {
2468 // Look for the label in the scope.2443 // Look for the label in the scope.
2469 var scope = parent_scope;2444 find_scope: switch (parent_scope.unwrap()) {
2470 while (true) {2445 .gen_zir => |gen_zir| {
2471 switch (scope.tag) {2446 if (gen_zir.label) |prev_label| {
2472 .gen_zir => {2447 if (try astgen.tokenIdentEql(label, prev_label.token)) {
2473 const gen_zir = scope.cast(GenZir).?;2448 const label_name = try astgen.identifierTokenString(label);
2474 if (gen_zir.label) |prev_label| {2449 return astgen.failTokNotes(label, "redefinition of label '{s}'", .{
2475 if (try astgen.tokenIdentEql(label, prev_label.token)) {2450 label_name,
2476 const label_name = try astgen.identifierTokenString(label);2451 }, &[_]u32{
2477 return astgen.failTokNotes(label, "redefinition of label '{s}'", .{2452 try astgen.errNoteTok(
2478 label_name,2453 prev_label.token,
2479 }, &[_]u32{2454 "previous definition here",
2480 try astgen.errNoteTok(2455 .{},
2481 prev_label.token,2456 ),
2482 "previous definition here",2457 });
2483 .{},
2484 ),
2485 });
2486 }
2487 }2458 }
2488 scope = gen_zir.parent;2459 }
2489 },2460 continue :find_scope gen_zir.parent.unwrap();
2490 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,2461 },
2491 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,2462 .local_val => |local_val| continue :find_scope local_val.parent.unwrap(),
2492 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,2463 .local_ptr => |local_ptr| continue :find_scope local_ptr.parent.unwrap(),
2493 .namespace => break,2464 .defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
2494 .top => unreachable,2465 .namespace => break :find_scope,
2495 }2466 .top => unreachable,
2496 }2467 }
2497}2468}
24982469
...@@ -3007,18 +2978,16 @@ fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct {...@@ -3007,18 +2978,16 @@ fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct {
3007 var need_err_code = false;2978 var need_err_code = false;
3008 var scope = inner_scope;2979 var scope = inner_scope;
3009 while (scope != outer_scope) {2980 while (scope != outer_scope) {
3010 switch (scope.tag) {2981 switch (scope.unwrap()) {
3011 .gen_zir => scope = scope.cast(GenZir).?.parent,2982 .gen_zir => |gen_zir| scope = gen_zir.parent,
3012 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,2983 .local_val => |local_val| scope = local_val.parent,
3013 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,2984 .local_ptr => |local_ptr| scope = local_ptr.parent,
3014 .defer_normal => {2985 .defer_normal => |defer_scope| {
3015 const defer_scope = scope.cast(Scope.Defer).?;
3016 scope = defer_scope.parent;2986 scope = defer_scope.parent;
30172987
3018 have_normal = true;2988 have_normal = true;
3019 },2989 },
3020 .defer_error => {2990 .defer_error => |defer_scope| {
3021 const defer_scope = scope.cast(Scope.Defer).?;
3022 scope = defer_scope.parent;2991 scope = defer_scope.parent;
30232992
3024 have_err = true;2993 have_err = true;
...@@ -3054,17 +3023,15 @@ fn genDefers(...@@ -3054,17 +3023,15 @@ fn genDefers(
30543023
3055 var scope = inner_scope;3024 var scope = inner_scope;
3056 while (scope != outer_scope) {3025 while (scope != outer_scope) {
3057 switch (scope.tag) {3026 switch (scope.unwrap()) {
3058 .gen_zir => scope = scope.cast(GenZir).?.parent,3027 .gen_zir => |gen_zir| scope = gen_zir.parent,
3059 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,3028 .local_val => |local_val| scope = local_val.parent,
3060 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,3029 .local_ptr => |local_ptr| scope = local_ptr.parent,
3061 .defer_normal => {3030 .defer_normal => |defer_scope| {
3062 const defer_scope = scope.cast(Scope.Defer).?;
3063 scope = defer_scope.parent;3031 scope = defer_scope.parent;
3064 try gz.addDefer(defer_scope.index, defer_scope.len);3032 try gz.addDefer(defer_scope.index, defer_scope.len);
3065 },3033 },
3066 .defer_error => {3034 .defer_error => |defer_scope| {
3067 const defer_scope = scope.cast(Scope.Defer).?;
3068 scope = defer_scope.parent;3035 scope = defer_scope.parent;
3069 switch (which_ones) {3036 switch (which_ones) {
3070 .both_sans_err => {3037 .both_sans_err => {
...@@ -3107,10 +3074,9 @@ fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!v...@@ -3107,10 +3074,9 @@ fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!v
31073074
3108 var scope = inner_scope;3075 var scope = inner_scope;
3109 while (scope != outer_scope) {3076 while (scope != outer_scope) {
3110 switch (scope.tag) {3077 switch (scope.unwrap()) {
3111 .gen_zir => scope = scope.cast(GenZir).?.parent,3078 .gen_zir => |gen_zir| scope = gen_zir.parent,
3112 .local_val => {3079 .local_val => |s| {
3113 const s = scope.cast(Scope.LocalVal).?;
3114 if (s.used == .none and s.discarded == .none) {3080 if (s.used == .none and s.discarded == .none) {
3115 try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});3081 try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
3116 } else if (s.used != .none and s.discarded != .none) {3082 } else if (s.used != .none and s.discarded != .none) {
...@@ -3120,8 +3086,7 @@ fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!v...@@ -3120,8 +3086,7 @@ fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!v
3120 }3086 }
3121 scope = s.parent;3087 scope = s.parent;
3122 },3088 },
3123 .local_ptr => {3089 .local_ptr => |s| {
3124 const s = scope.cast(Scope.LocalPtr).?;
3125 if (s.used == .none and s.discarded == .none) {3090 if (s.used == .none and s.discarded == .none) {
3126 try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});3091 try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
3127 } else {3092 } else {
...@@ -3136,10 +3101,9 @@ fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!v...@@ -3136,10 +3101,9 @@ fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!v
3136 });3101 });
3137 }3102 }
3138 }3103 }
3139
3140 scope = s.parent;3104 scope = s.parent;
3141 },3105 },
3142 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,3106 .defer_normal, .defer_error => |defer_scope| scope = defer_scope.parent,
3143 .namespace => unreachable,3107 .namespace => unreachable,
3144 .top => unreachable,3108 .top => unreachable,
3145 }3109 }
...@@ -4805,13 +4769,11 @@ fn testDecl(...@@ -4805,13 +4769,11 @@ fn testDecl(
48054769
4806 // Local variables, including function parameters.4770 // Local variables, including function parameters.
4807 const name_str_index = try astgen.identAsString(test_name_token);4771 const name_str_index = try astgen.identAsString(test_name_token);
4808 var s = scope;
4809 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already4772 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
4810 var num_namespaces_out: u32 = 0;4773 var num_namespaces_out: u32 = 0;
4811 var capturing_namespace: ?*Scope.Namespace = null;4774 var capturing_namespace: ?*Scope.Namespace = null;
4812 while (true) switch (s.tag) {4775 find_scope: switch (scope.unwrap()) {
4813 .local_val => {4776 .local_val => |local_val| {
4814 const local_val = s.cast(Scope.LocalVal).?;
4815 if (local_val.name == name_str_index) {4777 if (local_val.name == name_str_index) {
4816 local_val.used = .fromToken(test_name_token);4778 local_val.used = .fromToken(test_name_token);
4817 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{4779 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
...@@ -4822,10 +4784,9 @@ fn testDecl(...@@ -4822,10 +4784,9 @@ fn testDecl(
4822 }),4784 }),
4823 });4785 });
4824 }4786 }
4825 s = local_val.parent;4787 continue :find_scope local_val.parent.unwrap();
4826 },4788 },
4827 .local_ptr => {4789 .local_ptr => |local_ptr| {
4828 const local_ptr = s.cast(Scope.LocalPtr).?;
4829 if (local_ptr.name == name_str_index) {4790 if (local_ptr.name == name_str_index) {
4830 local_ptr.used = .fromToken(test_name_token);4791 local_ptr.used = .fromToken(test_name_token);
4831 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{4792 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
...@@ -4836,12 +4797,11 @@ fn testDecl(...@@ -4836,12 +4797,11 @@ fn testDecl(
4836 }),4797 }),
4837 });4798 });
4838 }4799 }
4839 s = local_ptr.parent;4800 continue :find_scope local_ptr.parent.unwrap();
4840 },4801 },
4841 .gen_zir => s = s.cast(GenZir).?.parent,4802 .gen_zir => |gen_zir| continue :find_scope gen_zir.parent.unwrap(),
4842 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,4803 .defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
4843 .namespace => {4804 .namespace => |ns| {
4844 const ns = s.cast(Scope.Namespace).?;
4845 if (ns.decls.get(name_str_index)) |i| {4805 if (ns.decls.get(name_str_index)) |i| {
4846 if (found_already) |f| {4806 if (found_already) |f| {
4847 return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{4807 return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{
...@@ -4854,10 +4814,10 @@ fn testDecl(...@@ -4854,10 +4814,10 @@ fn testDecl(
4854 }4814 }
4855 num_namespaces_out += 1;4815 num_namespaces_out += 1;
4856 capturing_namespace = ns;4816 capturing_namespace = ns;
4857 s = ns.parent;4817 continue :find_scope ns.parent.unwrap();
4858 },4818 },
4859 .top => break,4819 .top => break :find_scope,
4860 };4820 }
4861 if (found_already == null) {4821 if (found_already == null) {
4862 const ident_name = try astgen.identifierTokenString(test_name_token);4822 const ident_name = try astgen.identifierTokenString(test_name_token);
4863 return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});4823 return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});
...@@ -8409,7 +8369,6 @@ fn localVarRef(...@@ -8409,7 +8369,6 @@ fn localVarRef(
8409) InnerError!Zir.Inst.Ref {8369) InnerError!Zir.Inst.Ref {
8410 const astgen = gz.astgen;8370 const astgen = gz.astgen;
8411 const name_str_index = try astgen.identAsString(ident_token);8371 const name_str_index = try astgen.identAsString(ident_token);
8412 var s = scope;
8413 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already8372 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
8414 var found_needs_tunnel: bool = undefined; // defined when `found_already != null`8373 var found_needs_tunnel: bool = undefined; // defined when `found_already != null`
8415 var found_namespaces_out: u32 = undefined; // defined when `found_already != null`8374 var found_namespaces_out: u32 = undefined; // defined when `found_already != null`
...@@ -8419,10 +8378,8 @@ fn localVarRef(...@@ -8419,10 +8378,8 @@ fn localVarRef(
8419 // defined by `num_namespaces_out != 0`8378 // defined by `num_namespaces_out != 0`
8420 var capturing_namespace: *Scope.Namespace = undefined;8379 var capturing_namespace: *Scope.Namespace = undefined;
84218380
8422 while (true) switch (s.tag) {8381 find_scope: switch (scope.unwrap()) {
8423 .local_val => {8382 .local_val => |local_val| {
8424 const local_val = s.cast(Scope.LocalVal).?;
8425
8426 if (local_val.name == name_str_index) {8383 if (local_val.name == name_str_index) {
8427 // Locals cannot shadow anything, so we do not need to look for ambiguous8384 // Locals cannot shadow anything, so we do not need to look for ambiguous
8428 // references in this case.8385 // references in this case.
...@@ -8445,10 +8402,9 @@ fn localVarRef(...@@ -8445,10 +8402,9 @@ fn localVarRef(
84458402
8446 return rvalueNoCoercePreRef(gz, ri, value_inst, ident);8403 return rvalueNoCoercePreRef(gz, ri, value_inst, ident);
8447 }8404 }
8448 s = local_val.parent;8405 continue :find_scope local_val.parent.unwrap();
8449 },8406 },
8450 .local_ptr => {8407 .local_ptr => |local_ptr| {
8451 const local_ptr = s.cast(Scope.LocalPtr).?;
8452 if (local_ptr.name == name_str_index) {8408 if (local_ptr.name == name_str_index) {
8453 if (ri.rl == .discard and ri.ctx == .assignment) {8409 if (ri.rl == .discard and ri.ctx == .assignment) {
8454 local_ptr.discarded = .fromToken(ident_token);8410 local_ptr.discarded = .fromToken(ident_token);
...@@ -8497,12 +8453,11 @@ fn localVarRef(...@@ -8497,12 +8453,11 @@ fn localVarRef(
8497 },8453 },
8498 }8454 }
8499 }8455 }
8500 s = local_ptr.parent;8456 continue :find_scope local_ptr.parent.unwrap();
8501 },8457 },
8502 .gen_zir => s = s.cast(GenZir).?.parent,8458 .gen_zir => |gen_zir| continue :find_scope gen_zir.parent.unwrap(),
8503 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,8459 .defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
8504 .namespace => {8460 .namespace => |ns| {
8505 const ns = s.cast(Scope.Namespace).?;
8506 if (ns.decls.get(name_str_index)) |i| {8461 if (ns.decls.get(name_str_index)) |i| {
8507 if (found_already) |f| {8462 if (found_already) |f| {
8508 return astgen.failNodeNotes(ident, "ambiguous reference", .{}, &.{8463 return astgen.failNodeNotes(ident, "ambiguous reference", .{}, &.{
...@@ -8517,10 +8472,10 @@ fn localVarRef(...@@ -8517,10 +8472,10 @@ fn localVarRef(
8517 }8472 }
8518 num_namespaces_out += 1;8473 num_namespaces_out += 1;
8519 capturing_namespace = ns;8474 capturing_namespace = ns;
8520 s = ns.parent;8475 continue :find_scope ns.parent.unwrap();
8521 },8476 },
8522 .top => break,8477 .top => break :find_scope,
8523 };8478 }
8524 if (found_already == null) {8479 if (found_already == null) {
8525 const ident_name = try astgen.identifierTokenString(ident_token);8480 const ident_name = try astgen.identifierTokenString(ident_token);
8526 return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name});8481 return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name});
...@@ -11812,6 +11767,26 @@ const Scope = struct {...@@ -11812,6 +11767,26 @@ const Scope = struct {
11812 };11767 };
11813 }11768 }
1181411769
11770 fn unwrap(base: *Scope) Unwrapped {
11771 return switch (base.tag) {
11772 inline else => |tag| @unionInit(
11773 Unwrapped,
11774 @tagName(tag),
11775 @alignCast(@fieldParentPtr("base", base)),
11776 ),
11777 };
11778 }
11779
11780 const Unwrapped = union(Tag) {
11781 gen_zir: *GenZir,
11782 local_val: *LocalVal,
11783 local_ptr: *LocalPtr,
11784 defer_normal: *Defer,
11785 defer_error: *Defer,
11786 namespace: *Namespace,
11787 top: *Top,
11788 };
11789
11815 const Tag = enum {11790 const Tag = enum {
11816 gen_zir,11791 gen_zir,
11817 local_val,11792 local_val,
...@@ -13408,11 +13383,9 @@ fn detectLocalShadowing(...@@ -13408,11 +13383,9 @@ fn detectLocalShadowing(
13408 });13383 });
13409 }13384 }
1341013385
13411 var s = scope;
13412 var outer_scope = false;13386 var outer_scope = false;
13413 while (true) switch (s.tag) {13387 find_scope: switch (scope.unwrap()) {
13414 .local_val => {13388 .local_val => |local_val| {
13415 const local_val = s.cast(Scope.LocalVal).?;
13416 if (local_val.name == ident_name) {13389 if (local_val.name == ident_name) {
13417 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));13390 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
13418 const name = try gpa.dupe(u8, name_slice);13391 const name = try gpa.dupe(u8, name_slice);
...@@ -13438,10 +13411,9 @@ fn detectLocalShadowing(...@@ -13438,10 +13411,9 @@ fn detectLocalShadowing(
13438 ),13411 ),
13439 });13412 });
13440 }13413 }
13441 s = local_val.parent;13414 continue :find_scope local_val.parent.unwrap();
13442 },13415 },
13443 .local_ptr => {13416 .local_ptr => |local_ptr| {
13444 const local_ptr = s.cast(Scope.LocalPtr).?;
13445 if (local_ptr.name == ident_name) {13417 if (local_ptr.name == ident_name) {
13446 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));13418 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
13447 const name = try gpa.dupe(u8, name_slice);13419 const name = try gpa.dupe(u8, name_slice);
...@@ -13467,14 +13439,12 @@ fn detectLocalShadowing(...@@ -13467,14 +13439,12 @@ fn detectLocalShadowing(
13467 ),13439 ),
13468 });13440 });
13469 }13441 }
13470 s = local_ptr.parent;13442 continue :find_scope local_ptr.parent.unwrap();
13471 },13443 },
13472 .namespace => {13444 .namespace => |ns| {
13473 outer_scope = true;13445 outer_scope = true;
13474 const ns = s.cast(Scope.Namespace).?;
13475 const decl_node = ns.decls.get(ident_name) orelse {13446 const decl_node = ns.decls.get(ident_name) orelse {
13476 s = ns.parent;13447 continue :find_scope ns.parent.unwrap();
13477 continue;
13478 };13448 };
13479 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));13449 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
13480 const name = try gpa.dupe(u8, name_slice);13450 const name = try gpa.dupe(u8, name_slice);
...@@ -13485,13 +13455,13 @@ fn detectLocalShadowing(...@@ -13485,13 +13455,13 @@ fn detectLocalShadowing(
13485 try astgen.errNoteNode(decl_node, "declared here", .{}),13455 try astgen.errNoteNode(decl_node, "declared here", .{}),
13486 });13456 });
13487 },13457 },
13488 .gen_zir => {13458 .gen_zir => |gen_zir| {
13489 s = s.cast(GenZir).?.parent;
13490 outer_scope = true;13459 outer_scope = true;
13460 continue :find_scope gen_zir.parent.unwrap();
13491 },13461 },
13492 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,13462 .defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
13493 .top => break,13463 .top => break :find_scope,
13494 };13464 }
13495}13465}
1349613466
13497const LineColumn = struct { u32, u32 };13467const LineColumn = struct { u32, u32 };
...@@ -13728,10 +13698,8 @@ fn scanContainer(...@@ -13728,10 +13698,8 @@ fn scanContainer(
13728 continue;13698 continue;
13729 }13699 }
1373013700
13731 var s = namespace.parent;13701 find_scope: switch (namespace.parent.unwrap()) {
13732 while (true) switch (s.tag) {13702 .local_val => |local_val| {
13733 .local_val => {
13734 const local_val = s.cast(Scope.LocalVal).?;
13735 if (local_val.name == name_str_index) {13703 if (local_val.name == name_str_index) {
13736 try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{13704 try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
13737 token_bytes, @tagName(local_val.id_cat),13705 token_bytes, @tagName(local_val.id_cat),
...@@ -13743,12 +13711,11 @@ fn scanContainer(...@@ -13743,12 +13711,11 @@ fn scanContainer(
13743 ),13711 ),
13744 });13712 });
13745 any_invalid_declarations = true;13713 any_invalid_declarations = true;
13746 break;13714 break :find_scope;
13747 }13715 }
13748 s = local_val.parent;13716 continue :find_scope local_val.parent.unwrap();
13749 },13717 },
13750 .local_ptr => {13718 .local_ptr => |local_ptr| {
13751 const local_ptr = s.cast(Scope.LocalPtr).?;
13752 if (local_ptr.name == name_str_index) {13719 if (local_ptr.name == name_str_index) {
13753 try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{13720 try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
13754 token_bytes, @tagName(local_ptr.id_cat),13721 token_bytes, @tagName(local_ptr.id_cat),
...@@ -13760,15 +13727,15 @@ fn scanContainer(...@@ -13760,15 +13727,15 @@ fn scanContainer(
13760 ),13727 ),
13761 });13728 });
13762 any_invalid_declarations = true;13729 any_invalid_declarations = true;
13763 break;13730 break :find_scope;
13764 }13731 }
13765 s = local_ptr.parent;13732 continue :find_scope local_ptr.parent.unwrap();
13766 },13733 },
13767 .namespace => s = s.cast(Scope.Namespace).?.parent,13734 .namespace => |ns| continue :find_scope ns.parent.unwrap(),
13768 .gen_zir => s = s.cast(GenZir).?.parent,13735 .gen_zir => |gen_zir| continue :find_scope gen_zir.parent.unwrap(),
13769 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,13736 .defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
13770 .top => break,13737 .top => break :find_scope,
13771 };13738 }
13772 }13739 }
1377313740
13774 if (!any_duplicates) {13741 if (!any_duplicates) {