diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-01-17 00:14:24 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-01-17 00:15:25 -0700 |
| commit | 8deb21c58a4e5f9f8805f6b1a2c9a1774c4a4df5 (patch) | |
| tree | fdf5e11589187137b7189e5fda128c725259e659 /src | |
| parent | 629d3bea1b6aa7660364448cf4e0c045d931be52 (diff) | |
| download | zig-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.zig | 145 |
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); } } |
