authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-17 00:14:24-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-17 00:15:25-07:00
log8deb21c58a4e5f9f8805f6b1a2c9a1774c4a4df5
treefdf5e11589187137b7189e5fda128c725259e659
parent629d3bea1b6aa7660364448cf4e0c045d931be52

stage2: add compile error for label redefinition

Also fix incorrectly destroying notes. This work is based on Vexu's patch in #7555.

2 files changed, 127 insertions(+), 31 deletions(-)

src/astgen.zig+115-30
......@@ -442,6 +442,49 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block
442442 try blockExprStmts(mod, parent_scope, &block_node.base, block_node.statements());
443443}
444444
445fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void {
446 // Look for the label in the scope.
447 var scope = parent_scope;
448 while (true) {
449 switch (scope.tag) {
450 .gen_zir => {
451 const gen_zir = scope.cast(Scope.GenZIR).?;
452 if (gen_zir.label) |prev_label| {
453 if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) {
454 const tree = parent_scope.tree();
455 const label_src = tree.token_locs[label].start;
456 const prev_label_src = tree.token_locs[prev_label.token].start;
457
458 const label_name = try mod.identifierTokenString(parent_scope, label);
459 const msg = msg: {
460 const msg = try mod.errMsg(
461 parent_scope,
462 label_src,
463 "redefinition of label '{s}'",
464 .{label_name},
465 );
466 errdefer msg.destroy(mod.gpa);
467 try mod.errNote(
468 parent_scope,
469 prev_label_src,
470 msg,
471 "previous definition is here",
472 .{},
473 );
474 break :msg msg;
475 };
476 return mod.failWithOwnedErrorMsg(parent_scope, msg);
477 }
478 }
479 scope = gen_zir.parent;
480 },
481 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
482 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
483 else => return,
484 }
485 }
486}
487
445488fn labeledBlockExpr(
446489 mod: *Module,
447490 parent_scope: *Scope,
......@@ -457,6 +500,8 @@ fn labeledBlockExpr(
457500 const tree = parent_scope.tree();
458501 const src = tree.token_locs[block_node.lbrace].start;
459502
503 try checkLabelRedefinition(mod, parent_scope, block_node.label);
504
460505 // Create the Block ZIR instruction so that we can put it into the GenZIR struct
461506 // so that break statements can reference it.
462507 const gen_zir = parent_scope.getGenZIR();
......@@ -560,14 +605,30 @@ fn varDecl(
560605 .local_val => {
561606 const local_val = s.cast(Scope.LocalVal).?;
562607 if (mem.eql(u8, local_val.name, ident_name)) {
563 return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name});
608 const msg = msg: {
609 const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{
610 ident_name,
611 });
612 errdefer msg.destroy(mod.gpa);
613 try mod.errNote(scope, local_val.inst.src, msg, "previous definition is here", .{});
614 break :msg msg;
615 };
616 return mod.failWithOwnedErrorMsg(scope, msg);
564617 }
565618 s = local_val.parent;
566619 },
567620 .local_ptr => {
568621 const local_ptr = s.cast(Scope.LocalPtr).?;
569622 if (mem.eql(u8, local_ptr.name, ident_name)) {
570 return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name});
623 const msg = msg: {
624 const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{
625 ident_name,
626 });
627 errdefer msg.destroy(mod.gpa);
628 try mod.errNote(scope, local_ptr.ptr.src, msg, "previous definition is here", .{});
629 break :msg msg;
630 };
631 return mod.failWithOwnedErrorMsg(scope, msg);
571632 }
572633 s = local_ptr.parent;
573634 },
......@@ -1166,8 +1227,10 @@ fn orelseCatchExpr(
11661227 return rlWrapPtr(mod, scope, rl, &block.base);
11671228}
11681229
1169/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.
1170/// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used.
1230/// Return whether the identifier names of two tokens are equal. Resolves @""
1231/// tokens without allocating.
1232/// OK in theory it could do it without allocating. This implementation
1233/// allocates when the @"" form is used.
11711234fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
11721235 const ident_name_1 = try mod.identifierTokenString(scope, token1);
11731236 const ident_name_2 = try mod.identifierTokenString(scope, token2);
......@@ -1514,6 +1577,10 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
15141577 }
15151578 }
15161579
1580 if (while_node.label) |label| {
1581 try checkLabelRedefinition(mod, scope, label);
1582 }
1583
15171584 if (while_node.inline_token) |tok|
15181585 return mod.failTok(scope, tok, "TODO inline while", .{});
15191586
......@@ -1649,7 +1716,16 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
16491716 return &while_block.base;
16501717}
16511718
1652fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) InnerError!*zir.Inst {
1719fn forExpr(
1720 mod: *Module,
1721 scope: *Scope,
1722 rl: ResultLoc,
1723 for_node: *ast.Node.For,
1724) InnerError!*zir.Inst {
1725 if (for_node.label) |label| {
1726 try checkLabelRedefinition(mod, scope, label);
1727 }
1728
16531729 if (for_node.inline_token) |tok|
16541730 return mod.failTok(scope, tok, "TODO inline for", .{});
16551731
......@@ -1928,14 +2004,17 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
19282004 // Check for else/_ prong, those are handled last.
19292005 if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) {
19302006 if (else_src) |src| {
1931 const msg = try mod.errMsg(
1932 scope,
1933 case_src,
1934 "multiple else prongs in switch expression",
1935 .{},
1936 );
1937 errdefer msg.destroy(mod.gpa);
1938 try mod.errNote(scope, src, msg, "previous else prong is here", .{});
2007 const msg = msg: {
2008 const msg = try mod.errMsg(
2009 scope,
2010 case_src,
2011 "multiple else prongs in switch expression",
2012 .{},
2013 );
2014 errdefer msg.destroy(mod.gpa);
2015 try mod.errNote(scope, src, msg, "previous else prong is here", .{});
2016 break :msg msg;
2017 };
19392018 return mod.failWithOwnedErrorMsg(scope, msg);
19402019 }
19412020 else_src = case_src;
......@@ -1945,14 +2024,17 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
19452024 mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))
19462025 {
19472026 if (underscore_src) |src| {
1948 const msg = try mod.errMsg(
1949 scope,
1950 case_src,
1951 "multiple '_' prongs in switch expression",
1952 .{},
1953 );
1954 errdefer msg.destroy(mod.gpa);
1955 try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});
2027 const msg = msg: {
2028 const msg = try mod.errMsg(
2029 scope,
2030 case_src,
2031 "multiple '_' prongs in switch expression",
2032 .{},
2033 );
2034 errdefer msg.destroy(mod.gpa);
2035 try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});
2036 break :msg msg;
2037 };
19562038 return mod.failWithOwnedErrorMsg(scope, msg);
19572039 }
19582040 underscore_src = case_src;
......@@ -1962,15 +2044,18 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
19622044
19632045 if (else_src) |some_else| {
19642046 if (underscore_src) |some_underscore| {
1965 const msg = try mod.errMsg(
1966 scope,
1967 switch_src,
1968 "else and '_' prong in switch expression",
1969 .{},
1970 );
1971 errdefer msg.destroy(mod.gpa);
1972 try mod.errNote(scope, some_else, msg, "else prong is here", .{});
1973 try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{});
2047 const msg = msg: {
2048 const msg = try mod.errMsg(
2049 scope,
2050 switch_src,
2051 "else and '_' prong in switch expression",
2052 .{},
2053 );
2054 errdefer msg.destroy(mod.gpa);
2055 try mod.errNote(scope, some_else, msg, "else prong is here", .{});
2056 try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{});
2057 break :msg msg;
2058 };
19742059 return mod.failWithOwnedErrorMsg(scope, msg);
19752060 }
19762061 }
test/stage2/test.zig+12-1
......@@ -1234,7 +1234,10 @@ pub fn addCases(ctx: *TestContext) !void {
12341234 \\ var i: u32 = 10;
12351235 \\ unreachable;
12361236 \\}
1237 , &[_][]const u8{":3:9: error: redefinition of 'i'"});
1237 , &[_][]const u8{
1238 ":3:9: error: redefinition of 'i'",
1239 ":2:9: note: previous definition is here",
1240 });
12381241 case.addError(
12391242 \\var testing: i64 = 10;
12401243 \\export fn _start() noreturn {
......@@ -1409,6 +1412,14 @@ pub fn addCases(ctx: *TestContext) !void {
14091412 \\ foo: for ("foo") |_| {}
14101413 \\}
14111414 , &[_][]const u8{":2:5: error: unused for label"});
1415 case.addError(
1416 \\comptime {
1417 \\ blk: {blk: {}}
1418 \\}
1419 , &[_][]const u8{
1420 ":2:11: error: redefinition of label 'blk'",
1421 ":2:5: note: previous definition is here",
1422 });
14121423 }
14131424
14141425 {