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...@@ -442,6 +442,49 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block
442 try blockExprStmts(mod, parent_scope, &block_node.base, block_node.statements());442 try blockExprStmts(mod, parent_scope, &block_node.base, block_node.statements());
443}443}
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
445fn labeledBlockExpr(488fn labeledBlockExpr(
446 mod: *Module,489 mod: *Module,
447 parent_scope: *Scope,490 parent_scope: *Scope,
...@@ -457,6 +500,8 @@ fn labeledBlockExpr(...@@ -457,6 +500,8 @@ fn labeledBlockExpr(
457 const tree = parent_scope.tree();500 const tree = parent_scope.tree();
458 const src = tree.token_locs[block_node.lbrace].start;501 const src = tree.token_locs[block_node.lbrace].start;
459502
503 try checkLabelRedefinition(mod, parent_scope, block_node.label);
504
460 // Create the Block ZIR instruction so that we can put it into the GenZIR struct505 // Create the Block ZIR instruction so that we can put it into the GenZIR struct
461 // so that break statements can reference it.506 // so that break statements can reference it.
462 const gen_zir = parent_scope.getGenZIR();507 const gen_zir = parent_scope.getGenZIR();
...@@ -560,14 +605,30 @@ fn varDecl(...@@ -560,14 +605,30 @@ fn varDecl(
560 .local_val => {605 .local_val => {
561 const local_val = s.cast(Scope.LocalVal).?;606 const local_val = s.cast(Scope.LocalVal).?;
562 if (mem.eql(u8, local_val.name, ident_name)) {607 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);
564 }617 }
565 s = local_val.parent;618 s = local_val.parent;
566 },619 },
567 .local_ptr => {620 .local_ptr => {
568 const local_ptr = s.cast(Scope.LocalPtr).?;621 const local_ptr = s.cast(Scope.LocalPtr).?;
569 if (mem.eql(u8, local_ptr.name, ident_name)) {622 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);
571 }632 }
572 s = local_ptr.parent;633 s = local_ptr.parent;
573 },634 },
...@@ -1166,8 +1227,10 @@ fn orelseCatchExpr(...@@ -1166,8 +1227,10 @@ fn orelseCatchExpr(
1166 return rlWrapPtr(mod, scope, rl, &block.base);1227 return rlWrapPtr(mod, scope, rl, &block.base);
1167}1228}
11681229
1169/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.1230/// Return whether the identifier names of two tokens are equal. Resolves @""
1170/// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used.1231/// tokens without allocating.
1232/// OK in theory it could do it without allocating. This implementation
1233/// allocates when the @"" form is used.
1171fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {1234fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
1172 const ident_name_1 = try mod.identifierTokenString(scope, token1);1235 const ident_name_1 = try mod.identifierTokenString(scope, token1);
1173 const ident_name_2 = try mod.identifierTokenString(scope, token2);1236 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...@@ -1514,6 +1577,10 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
1514 }1577 }
1515 }1578 }
15161579
1580 if (while_node.label) |label| {
1581 try checkLabelRedefinition(mod, scope, label);
1582 }
1583
1517 if (while_node.inline_token) |tok|1584 if (while_node.inline_token) |tok|
1518 return mod.failTok(scope, tok, "TODO inline while", .{});1585 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...@@ -1649,7 +1716,16 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
1649 return &while_block.base;1716 return &while_block.base;
1650}1717}
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
1653 if (for_node.inline_token) |tok|1729 if (for_node.inline_token) |tok|
1654 return mod.failTok(scope, tok, "TODO inline for", .{});1730 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...@@ -1928,14 +2004,17 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1928 // Check for else/_ prong, those are handled last.2004 // Check for else/_ prong, those are handled last.
1929 if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) {2005 if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) {
1930 if (else_src) |src| {2006 if (else_src) |src| {
1931 const msg = try mod.errMsg(2007 const msg = msg: {
1932 scope,2008 const msg = try mod.errMsg(
1933 case_src,2009 scope,
1934 "multiple else prongs in switch expression",2010 case_src,
1935 .{},2011 "multiple else prongs in switch expression",
1936 );2012 .{},
1937 errdefer msg.destroy(mod.gpa);2013 );
1938 try mod.errNote(scope, src, msg, "previous else prong is here", .{});2014 errdefer msg.destroy(mod.gpa);
2015 try mod.errNote(scope, src, msg, "previous else prong is here", .{});
2016 break :msg msg;
2017 };
1939 return mod.failWithOwnedErrorMsg(scope, msg);2018 return mod.failWithOwnedErrorMsg(scope, msg);
1940 }2019 }
1941 else_src = case_src;2020 else_src = case_src;
...@@ -1945,14 +2024,17 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -1945,14 +2024,17 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1945 mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))2024 mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))
1946 {2025 {
1947 if (underscore_src) |src| {2026 if (underscore_src) |src| {
1948 const msg = try mod.errMsg(2027 const msg = msg: {
1949 scope,2028 const msg = try mod.errMsg(
1950 case_src,2029 scope,
1951 "multiple '_' prongs in switch expression",2030 case_src,
1952 .{},2031 "multiple '_' prongs in switch expression",
1953 );2032 .{},
1954 errdefer msg.destroy(mod.gpa);2033 );
1955 try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});2034 errdefer msg.destroy(mod.gpa);
2035 try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});
2036 break :msg msg;
2037 };
1956 return mod.failWithOwnedErrorMsg(scope, msg);2038 return mod.failWithOwnedErrorMsg(scope, msg);
1957 }2039 }
1958 underscore_src = case_src;2040 underscore_src = case_src;
...@@ -1962,15 +2044,18 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -1962,15 +2044,18 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
19622044
1963 if (else_src) |some_else| {2045 if (else_src) |some_else| {
1964 if (underscore_src) |some_underscore| {2046 if (underscore_src) |some_underscore| {
1965 const msg = try mod.errMsg(2047 const msg = msg: {
1966 scope,2048 const msg = try mod.errMsg(
1967 switch_src,2049 scope,
1968 "else and '_' prong in switch expression",2050 switch_src,
1969 .{},2051 "else and '_' prong in switch expression",
1970 );2052 .{},
1971 errdefer msg.destroy(mod.gpa);2053 );
1972 try mod.errNote(scope, some_else, msg, "else prong is here", .{});2054 errdefer msg.destroy(mod.gpa);
1973 try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{});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 };
1974 return mod.failWithOwnedErrorMsg(scope, msg);2059 return mod.failWithOwnedErrorMsg(scope, msg);
1975 }2060 }
1976 }2061 }
test/stage2/test.zig+12-1
...@@ -1234,7 +1234,10 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1234,7 +1234,10 @@ pub fn addCases(ctx: *TestContext) !void {
1234 \\ var i: u32 = 10;1234 \\ var i: u32 = 10;
1235 \\ unreachable;1235 \\ unreachable;
1236 \\}1236 \\}
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 });
1238 case.addError(1241 case.addError(
1239 \\var testing: i64 = 10;1242 \\var testing: i64 = 10;
1240 \\export fn _start() noreturn {1243 \\export fn _start() noreturn {
...@@ -1409,6 +1412,14 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1409,6 +1412,14 @@ pub fn addCases(ctx: *TestContext) !void {
1409 \\ foo: for ("foo") |_| {}1412 \\ foo: for ("foo") |_| {}
1410 \\}1413 \\}
1411 , &[_][]const u8{":2:5: error: unused for label"});1414 , &[_][]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 });
1412 }1423 }
14131424
1414 {1425 {