stage2: add compile error for label redefinition
Also fix incorrectly destroying notes. This work is based on Vexu's patch in #7555.
This commit is contained in:
145
src/astgen.zig
145
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1234,7 +1234,10 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
\\ var i: u32 = 10;
|
||||
\\ unreachable;
|
||||
\\}
|
||||
, &[_][]const u8{":3:9: error: redefinition of 'i'"});
|
||||
, &[_][]const u8{
|
||||
":3:9: error: redefinition of 'i'",
|
||||
":2:9: note: previous definition is here",
|
||||
});
|
||||
case.addError(
|
||||
\\var testing: i64 = 10;
|
||||
\\export fn _start() noreturn {
|
||||
@@ -1409,6 +1412,14 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
\\ foo: for ("foo") |_| {}
|
||||
\\}
|
||||
, &[_][]const u8{":2:5: error: unused for label"});
|
||||
case.addError(
|
||||
\\comptime {
|
||||
\\ blk: {blk: {}}
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
":2:11: error: redefinition of label 'blk'",
|
||||
":2:5: note: previous definition is here",
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user