Skip to content

Commit

Permalink
stage2: add compile error for label redefinition
Browse files Browse the repository at this point in the history
Also fix incorrectly destroying notes.

This work is based on Vexu's patch in #7555.
  • Loading branch information
andrewrk committed Jan 17, 2021
1 parent 629d3be commit 8deb21c
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 31 deletions.
145 changes: 115 additions & 30 deletions src/astgen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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();
Expand Down Expand Up @@ -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;
},
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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", .{});

Expand Down Expand Up @@ -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", .{});

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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);
}
}
Expand Down
13 changes: 12 additions & 1 deletion test/stage2/test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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",
});
}

{
Expand Down

0 comments on commit 8deb21c

Please sign in to comment.