aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-01-17 00:14:24 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-01-17 00:15:25 -0700
commit8deb21c58a4e5f9f8805f6b1a2c9a1774c4a4df5 (patch)
treefdf5e11589187137b7189e5fda128c725259e659 /src
parent629d3bea1b6aa7660364448cf4e0c045d931be52 (diff)
downloadzig-8deb21c58a4e5f9f8805f6b1a2c9a1774c4a4df5.tar.gz
zig-8deb21c58a4e5f9f8805f6b1a2c9a1774c4a4df5.zip
stage2: add compile error for label redefinition
Also fix incorrectly destroying notes. This work is based on Vexu's patch in #7555.
Diffstat (limited to 'src')
-rw-r--r--src/astgen.zig145
1 files changed, 115 insertions, 30 deletions
diff --git a/src/astgen.zig b/src/astgen.zig
index 4631e46b5d..6c697897aa 100644
--- a/src/astgen.zig
+++ b/src/astgen.zig
@@ -442,6 +442,49 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block
try blockExprStmts(mod, parent_scope, &block_node.base, block_node.statements());
}
+fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void {
+ // Look for the label in the scope.
+ var scope = parent_scope;
+ while (true) {
+ switch (scope.tag) {
+ .gen_zir => {
+ const gen_zir = scope.cast(Scope.GenZIR).?;
+ if (gen_zir.label) |prev_label| {
+ if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) {
+ const tree = parent_scope.tree();
+ const label_src = tree.token_locs[label].start;
+ const prev_label_src = tree.token_locs[prev_label.token].start;
+
+ const label_name = try mod.identifierTokenString(parent_scope, label);
+ const msg = msg: {
+ const msg = try mod.errMsg(
+ parent_scope,
+ label_src,
+ "redefinition of label '{s}'",
+ .{label_name},
+ );
+ errdefer msg.destroy(mod.gpa);
+ try mod.errNote(
+ parent_scope,
+ prev_label_src,
+ msg,
+ "previous definition is here",
+ .{},
+ );
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(parent_scope, msg);
+ }
+ }
+ scope = gen_zir.parent;
+ },
+ .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
+ .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
+ else => return,
+ }
+ }
+}
+
fn labeledBlockExpr(
mod: *Module,
parent_scope: *Scope,
@@ -457,6 +500,8 @@ fn labeledBlockExpr(
const tree = parent_scope.tree();
const src = tree.token_locs[block_node.lbrace].start;
+ try checkLabelRedefinition(mod, parent_scope, block_node.label);
+
// Create the Block ZIR instruction so that we can put it into the GenZIR struct
// so that break statements can reference it.
const gen_zir = parent_scope.getGenZIR();
@@ -560,14 +605,30 @@ fn varDecl(
.local_val => {
const local_val = s.cast(Scope.LocalVal).?;
if (mem.eql(u8, local_val.name, ident_name)) {
- return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name});
+ const msg = msg: {
+ const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{
+ ident_name,
+ });
+ errdefer msg.destroy(mod.gpa);
+ try mod.errNote(scope, local_val.inst.src, msg, "previous definition is here", .{});
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(scope, msg);
}
s = local_val.parent;
},
.local_ptr => {
const local_ptr = s.cast(Scope.LocalPtr).?;
if (mem.eql(u8, local_ptr.name, ident_name)) {
- return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name});
+ const msg = msg: {
+ const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{
+ ident_name,
+ });
+ errdefer msg.destroy(mod.gpa);
+ try mod.errNote(scope, local_ptr.ptr.src, msg, "previous definition is here", .{});
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(scope, msg);
}
s = local_ptr.parent;
},
@@ -1166,8 +1227,10 @@ fn orelseCatchExpr(
return rlWrapPtr(mod, scope, rl, &block.base);
}
-/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.
-/// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used.
+/// Return whether the identifier names of two tokens are equal. Resolves @""
+/// tokens without allocating.
+/// OK in theory it could do it without allocating. This implementation
+/// allocates when the @"" form is used.
fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
const ident_name_1 = try mod.identifierTokenString(scope, token1);
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
}
}
+ if (while_node.label) |label| {
+ try checkLabelRedefinition(mod, scope, label);
+ }
+
if (while_node.inline_token) |tok|
return mod.failTok(scope, tok, "TODO inline while", .{});
@@ -1649,7 +1716,16 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
return &while_block.base;
}
-fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) InnerError!*zir.Inst {
+fn forExpr(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ for_node: *ast.Node.For,
+) InnerError!*zir.Inst {
+ if (for_node.label) |label| {
+ try checkLabelRedefinition(mod, scope, label);
+ }
+
if (for_node.inline_token) |tok|
return mod.failTok(scope, tok, "TODO inline for", .{});
@@ -1928,14 +2004,17 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
// Check for else/_ prong, those are handled last.
if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) {
if (else_src) |src| {
- const msg = try mod.errMsg(
- scope,
- case_src,
- "multiple else prongs in switch expression",
- .{},
- );
- errdefer msg.destroy(mod.gpa);
- try mod.errNote(scope, src, msg, "previous else prong is here", .{});
+ const msg = msg: {
+ const msg = try mod.errMsg(
+ scope,
+ case_src,
+ "multiple else prongs in switch expression",
+ .{},
+ );
+ errdefer msg.destroy(mod.gpa);
+ try mod.errNote(scope, src, msg, "previous else prong is here", .{});
+ break :msg msg;
+ };
return mod.failWithOwnedErrorMsg(scope, msg);
}
else_src = case_src;
@@ -1945,14 +2024,17 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))
{
if (underscore_src) |src| {
- const msg = try mod.errMsg(
- scope,
- case_src,
- "multiple '_' prongs in switch expression",
- .{},
- );
- errdefer msg.destroy(mod.gpa);
- try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});
+ const msg = msg: {
+ const msg = try mod.errMsg(
+ scope,
+ case_src,
+ "multiple '_' prongs in switch expression",
+ .{},
+ );
+ errdefer msg.destroy(mod.gpa);
+ try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});
+ break :msg msg;
+ };
return mod.failWithOwnedErrorMsg(scope, msg);
}
underscore_src = case_src;
@@ -1962,15 +2044,18 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
if (else_src) |some_else| {
if (underscore_src) |some_underscore| {
- const msg = try mod.errMsg(
- scope,
- switch_src,
- "else and '_' prong in switch expression",
- .{},
- );
- errdefer msg.destroy(mod.gpa);
- try mod.errNote(scope, some_else, msg, "else prong is here", .{});
- try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{});
+ const msg = msg: {
+ const msg = try mod.errMsg(
+ scope,
+ switch_src,
+ "else and '_' prong in switch expression",
+ .{},
+ );
+ errdefer msg.destroy(mod.gpa);
+ try mod.errNote(scope, some_else, msg, "else prong is here", .{});
+ try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{});
+ break :msg msg;
+ };
return mod.failWithOwnedErrorMsg(scope, msg);
}
}