Commit c5368ba20c

Vexu <git@vexu.eu>
2020-08-13 14:27:29
translate-c: ensure generated labels are unique
1 parent 3734881
Changed files (2)
src-self-hosted
test
src-self-hosted/translate_c.zig
@@ -61,7 +61,8 @@ const Scope = struct {
         pending_block: Block,
         cases: []*ast.Node,
         case_index: usize,
-        has_default: bool = false,
+        switch_label: ?[]const u8,
+        default_label: ?[]const u8,
     };
 
     /// Used for the scope of condition expressions, for example `if (cond)`.
@@ -73,7 +74,7 @@ const Scope = struct {
 
         fn getBlockScope(self: *Condition, c: *Context) !*Block {
             if (self.block) |*b| return b;
-            self.block = try Block.init(c, &self.base, "blk");
+            self.block = try Block.init(c, &self.base, true);
             return &self.block.?;
         }
 
@@ -93,21 +94,22 @@ const Scope = struct {
         mangle_count: u32 = 0,
         lbrace: ast.TokenIndex,
 
-        fn init(c: *Context, parent: *Scope, label: ?[]const u8) !Block {
-            return Block{
+        fn init(c: *Context, parent: *Scope, labeled: bool) !Block {
+            var blk = Block{
                 .base = .{
                     .id = .Block,
                     .parent = parent,
                 },
                 .statements = std.ArrayList(*ast.Node).init(c.gpa),
                 .variables = AliasList.init(c.gpa),
-                .label = if (label) |l| blk: {
-                    const ll = try appendIdentifier(c, l);
-                    _ = try appendToken(c, .Colon, ":");
-                    break :blk ll;
-                } else null,
+                .label = null,
                 .lbrace = try appendToken(c, .LBrace, "{"),
             };
+            if (labeled) {
+                blk.label = try appendIdentifier(c, try blk.makeMangledName(c, "blk"));
+                _ = try appendToken(c, .Colon, ":");
+            }
+            return blk;
         }
 
         fn deinit(self: *Block) void {
@@ -577,7 +579,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
 
     // actual function definition with body
     const body_stmt = ZigClangFunctionDecl_getBody(fn_decl);
-    var block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, null);
+    var block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, false);
     defer block_scope.deinit();
     var scope = &block_scope.base;
 
@@ -1307,7 +1309,7 @@ fn transBinaryOperator(
             const rhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
             if (expr) {
                 _ = try appendToken(rp.c, .Semicolon, ";");
-                const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label, rhs);
+                const break_node = try transCreateNodeBreak(rp.c, block_scope.label, rhs);
                 try block_scope.statements.append(&break_node.base);
                 const block_node = try block_scope.complete(rp.c);
                 const rparen = try appendToken(rp.c, .RParen, ")");
@@ -1476,7 +1478,7 @@ fn transCompoundStmtInline(
 }
 
 fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) TransError!*ast.Node {
-    var block_scope = try Scope.Block.init(rp.c, scope, null);
+    var block_scope = try Scope.Block.init(rp.c, scope, false);
     defer block_scope.deinit();
     try transCompoundStmtInline(rp, &block_scope.base, stmt, &block_scope);
     const node = try block_scope.complete(rp.c);
@@ -2587,7 +2589,7 @@ fn transForLoop(
     defer if (block_scope) |*bs| bs.deinit();
 
     if (ZigClangForStmt_getInit(stmt)) |init| {
-        block_scope = try Scope.Block.init(rp.c, scope, null);
+        block_scope = try Scope.Block.init(rp.c, scope, false);
         loop_scope.parent = &block_scope.?.base;
         const init_node = try transStmt(rp, &block_scope.?.base, init, .unused, .r_value);
         try block_scope.?.statements.append(init_node);
@@ -2673,17 +2675,19 @@ fn transSwitch(
         .cases = switch_node.cases(),
         .case_index = 0,
         .pending_block = undefined,
+        .default_label = null,
+        .switch_label = null,
     };
 
     // tmp block that all statements will go before being picked up by a case or default
-    var block_scope = try Scope.Block.init(rp.c, &switch_scope.base, null);
+    var block_scope = try Scope.Block.init(rp.c, &switch_scope.base, false);
     defer block_scope.deinit();
 
     // Note that we do not defer a deinit here; the switch_scope.pending_block field
     // has its own memory management. This resource is freed inside `transCase` and
     // then the final pending_block is freed at the bottom of this function with
     // pending_block.deinit().
-    switch_scope.pending_block = try Scope.Block.init(rp.c, scope, null);
+    switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);
     try switch_scope.pending_block.statements.append(&switch_node.base);
 
     const last = try transStmt(rp, &block_scope.base, ZigClangSwitchStmt_getBody(stmt), .unused, .r_value);
@@ -2698,11 +2702,19 @@ fn transSwitch(
         switch_scope.pending_block.statements.appendAssumeCapacity(n);
     }
 
-    switch_scope.pending_block.label = try appendIdentifier(rp.c, "__switch");
-    _ = try appendToken(rp.c, .Colon, ":");
-    if (!switch_scope.has_default) {
+    if (switch_scope.default_label == null) {
+        switch_scope.switch_label = try block_scope.makeMangledName(rp.c, "switch");
+    }
+    if (switch_scope.switch_label) |l| {
+        switch_scope.pending_block.label = try appendIdentifier(rp.c, l);
+        _ = try appendToken(rp.c, .Colon, ":");
+    }
+    if (switch_scope.default_label == null) {
         const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
-        else_prong.expr = &(try transCreateNodeBreak(rp.c, "__switch", null)).base;
+        else_prong.expr = blk: {
+            var br = try CtrlFlow.init(rp.c, .Break, switch_scope.switch_label.?);
+            break :blk &(try br.finish(null)).base;
+        };
         _ = try appendToken(rp.c, .Comma, ",");
 
         if (switch_scope.case_index >= switch_scope.cases.len)
@@ -2726,7 +2738,7 @@ fn transCase(
 ) TransError!*ast.Node {
     const block_scope = scope.findBlockScope(rp.c) catch unreachable;
     const switch_scope = scope.getSwitch();
-    const label = try std.fmt.allocPrint(rp.c.arena, "__case_{}", .{switch_scope.case_index - @boolToInt(switch_scope.has_default)});
+    const label = try block_scope.makeMangledName(rp.c, "case");
     _ = try appendToken(rp.c, .Semicolon, ";");
 
     const expr = if (ZigClangCaseStmt_getRHS(stmt)) |rhs| blk: {
@@ -2746,7 +2758,10 @@ fn transCase(
         try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value);
 
     const switch_prong = try transCreateNodeSwitchCase(rp.c, expr);
-    switch_prong.expr = &(try transCreateNodeBreak(rp.c, label, null)).base;
+    switch_prong.expr = blk: {
+        var br = try CtrlFlow.init(rp.c, .Break, label);
+        break :blk &(try br.finish(null)).base;
+    };
     _ = try appendToken(rp.c, .Comma, ",");
 
     if (switch_scope.case_index >= switch_scope.cases.len)
@@ -2763,7 +2778,7 @@ fn transCase(
 
     const pending_node = try switch_scope.pending_block.complete(rp.c);
     switch_scope.pending_block.deinit();
-    switch_scope.pending_block = try Scope.Block.init(rp.c, scope, null);
+    switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);
 
     try switch_scope.pending_block.statements.append(&pending_node.base);
 
@@ -2777,12 +2792,14 @@ fn transDefault(
 ) TransError!*ast.Node {
     const block_scope = scope.findBlockScope(rp.c) catch unreachable;
     const switch_scope = scope.getSwitch();
-    const label = "__default";
-    switch_scope.has_default = true;
+    switch_scope.default_label = try block_scope.makeMangledName(rp.c, "default");
     _ = try appendToken(rp.c, .Semicolon, ";");
 
     const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
-    else_prong.expr = &(try transCreateNodeBreak(rp.c, label, null)).base;
+    else_prong.expr = blk: {
+        var br = try CtrlFlow.init(rp.c, .Break, switch_scope.default_label.?);
+        break :blk &(try br.finish(null)).base;
+    };
     _ = try appendToken(rp.c, .Comma, ",");
 
     if (switch_scope.case_index >= switch_scope.cases.len)
@@ -2790,7 +2807,7 @@ fn transDefault(
     switch_scope.cases[switch_scope.case_index] = &else_prong.base;
     switch_scope.case_index += 1;
 
-    switch_scope.pending_block.label = try appendIdentifier(rp.c, label);
+    switch_scope.pending_block.label = try appendIdentifier(rp.c, switch_scope.default_label.?);
     _ = try appendToken(rp.c, .Colon, ":");
 
     // take all pending statements
@@ -2799,7 +2816,7 @@ fn transDefault(
 
     const pending_node = try switch_scope.pending_block.complete(rp.c);
     switch_scope.pending_block.deinit();
-    switch_scope.pending_block = try Scope.Block.init(rp.c, scope, null);
+    switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);
     try switch_scope.pending_block.statements.append(&pending_node.base);
 
     return transStmt(rp, scope, ZigClangDefaultStmt_getSubStmt(stmt), .unused, .r_value);
@@ -2894,7 +2911,7 @@ fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr,
         return transCompoundStmt(rp, scope, comp);
     }
     const lparen = try appendToken(rp.c, .LParen, "(");
-    var block_scope = try Scope.Block.init(rp.c, scope, "blk");
+    var block_scope = try Scope.Block.init(rp.c, scope, true);
     defer block_scope.deinit();
 
     var it = ZigClangCompoundStmt_body_begin(comp);
@@ -3209,7 +3226,7 @@ fn transCreatePreCrement(
     // zig:     _ref.* += 1;
     // zig:     break :blk _ref.*
     // zig: })
-    var block_scope = try Scope.Block.init(rp.c, scope, "blk");
+    var block_scope = try Scope.Block.init(rp.c, scope, true);
     defer block_scope.deinit();
     const ref = try block_scope.makeMangledName(rp.c, "ref");
 
@@ -3239,7 +3256,7 @@ fn transCreatePreCrement(
     const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
     try block_scope.statements.append(assign);
 
-    const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label, ref_node);
+    const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node);
     try block_scope.statements.append(&break_node.base);
     const block_node = try block_scope.complete(rp.c);
     // semicolon must immediately follow rbrace because it is the last token in a block
@@ -3283,7 +3300,7 @@ fn transCreatePostCrement(
     // zig:     _ref.* += 1;
     // zig:     break :blk _tmp
     // zig: })
-    var block_scope = try Scope.Block.init(rp.c, scope, "blk");
+    var block_scope = try Scope.Block.init(rp.c, scope, true);
     defer block_scope.deinit();
     const ref = try block_scope.makeMangledName(rp.c, "ref");
 
@@ -3458,7 +3475,7 @@ fn transCreateCompoundAssign(
     // zig:     _ref.* = _ref.* + rhs;
     // zig:     break :blk _ref.*
     // zig: })
-    var block_scope = try Scope.Block.init(rp.c, scope, "blk");
+    var block_scope = try Scope.Block.init(rp.c, scope, true);
     defer block_scope.deinit();
     const ref = try block_scope.makeMangledName(rp.c, "ref");
 
@@ -3526,7 +3543,7 @@ fn transCreateCompoundAssign(
         try block_scope.statements.append(assign);
     }
 
-    const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label, ref_node);
+    const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node);
     try block_scope.statements.append(&break_node.base);
     const block_node = try block_scope.complete(rp.c);
     const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
@@ -3602,8 +3619,16 @@ fn transCPtrCast(
 
 fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node {
     const break_scope = scope.getBreakableScope();
-    const label_text: ?[]const u8 = if (break_scope.id == .Switch) "__switch" else null;
-    const br = try transCreateNodeBreak(rp.c, label_text, null);
+    const label_text: ?[]const u8 = if (break_scope.id == .Switch) blk: {
+        const swtch = @fieldParentPtr(Scope.Switch, "base", break_scope);
+        const block_scope = try scope.findBlockScope(rp.c);
+        swtch.switch_label = try block_scope.makeMangledName(rp.c, "switch");
+        break :blk swtch.switch_label;
+    } else
+        null;
+
+    var cf = try CtrlFlow.init(rp.c, .Break, label_text);
+    const br = try cf.finish(null);
     _ = try appendToken(rp.c, .Semicolon, ";");
     return &br.base;
 }
@@ -3634,7 +3659,7 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const
     //      })
     const lparen = try appendToken(rp.c, .LParen, "(");
 
-    var block_scope = try Scope.Block.init(rp.c, scope, "blk");
+    var block_scope = try Scope.Block.init(rp.c, scope, true);
     defer block_scope.deinit();
 
     const mangled_name = try block_scope.makeMangledName(rp.c, "cond_temp");
@@ -4082,8 +4107,7 @@ fn transCreateNodeAssign(
     // zig:     lhs = _tmp;
     // zig:     break :blk _tmp
     // zig: })
-    const label_name = "blk";
-    var block_scope = try Scope.Block.init(rp.c, scope, label_name);
+    var block_scope = try Scope.Block.init(rp.c, scope, true);
     defer block_scope.deinit();
 
     const tmp = try block_scope.makeMangledName(rp.c, "tmp");
@@ -4118,7 +4142,7 @@ fn transCreateNodeAssign(
     try block_scope.statements.append(assign);
 
     const break_node = blk: {
-        var tmp_ctrl_flow = try CtrlFlow.init(rp.c, .Break, label_name);
+        var tmp_ctrl_flow = try CtrlFlow.init(rp.c, .Break, tokenSlice(rp.c, block_scope.label.?));
         const rhs_expr = try transCreateNodeIdentifier(rp.c, tmp);
         break :blk try tmp_ctrl_flow.finish(rhs_expr);
     };
@@ -4495,23 +4519,12 @@ fn transCreateNodeElse(c: *Context) !*ast.Node.Else {
     return node;
 }
 
-fn transCreateNodeBreakToken(
-    c: *Context,
-    label: ?ast.TokenIndex,
-    rhs: ?*ast.Node,
-) !*ast.Node.ControlFlowExpression {
-    const other_token = label orelse return transCreateNodeBreak(c, null, rhs);
-    const loc = c.token_locs.items[other_token];
-    const label_name = c.source_buffer.items[loc.start..loc.end];
-    return transCreateNodeBreak(c, label_name, rhs);
-}
-
 fn transCreateNodeBreak(
     c: *Context,
-    label: ?[]const u8,
+    label: ?ast.TokenIndex,
     rhs: ?*ast.Node,
 ) !*ast.Node.ControlFlowExpression {
-    var ctrl_flow = try CtrlFlow.init(c, .Break, label);
+    var ctrl_flow = try CtrlFlow.init(c, .Break, if (label) |l| tokenSlice(c, l) else null);
     return ctrl_flow.finish(rhs);
 }
 
@@ -5362,7 +5375,7 @@ fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
 }
 
 fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
-    var block_scope = try Scope.Block.init(c, &c.global_scope.base, null);
+    var block_scope = try Scope.Block.init(c, &c.global_scope.base, false);
     defer block_scope.deinit();
     const scope = &block_scope.base;
 
@@ -5475,8 +5488,7 @@ fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
         },
         .Comma => {
             _ = try appendToken(c, .Semicolon, ";");
-            const label_name = "blk";
-            var block_scope = try Scope.Block.init(c, scope, label_name);
+            var block_scope = try Scope.Block.init(c, scope, true);
             defer block_scope.deinit();
 
             var last = node;
@@ -5501,7 +5513,7 @@ fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
                 }
             }
 
-            const break_node = try transCreateNodeBreak(c, label_name, last);
+            const break_node = try transCreateNodeBreak(c, block_scope.label, last);
             try block_scope.statements.append(&break_node.base);
             const block_node = try block_scope.complete(c);
             return &block_node.base;
test/translate_c.zig
@@ -1730,16 +1730,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\pub export fn switch_fn(arg_i: c_int) c_int {
         \\    var i = arg_i;
         \\    var res: c_int = 0;
-        \\    __switch: {
-        \\        __case_2: {
-        \\            __default: {
-        \\                __case_1: {
-        \\                    __case_0: {
+        \\    @"switch": {
+        \\        case_2: {
+        \\            default: {
+        \\                case_1: {
+        \\                    case: {
         \\                        switch (i) {
-        \\                            @as(c_int, 0) => break :__case_0,
-        \\                            @as(c_int, 1)...@as(c_int, 3) => break :__case_1,
-        \\                            else => break :__default,
-        \\                            @as(c_int, 4) => break :__case_2,
+        \\                            @as(c_int, 0) => break :case,
+        \\                            @as(c_int, 1)...@as(c_int, 3) => break :case_1,
+        \\                            else => break :default,
+        \\                            @as(c_int, 4) => break :case_2,
         \\                        }
         \\                    }
         \\                    res = 1;
@@ -1747,7 +1747,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\                res = 2;
         \\            }
         \\            res = (@as(c_int, 3) * i);
-        \\            break :__switch;
+        \\            break :@"switch";
         \\        }
         \\        res = 5;
         \\    }
@@ -2782,11 +2782,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\    var x = arg_x;
         \\    return blk: {
         \\        const tmp = x;
-        \\        (blk: {
+        \\        (blk_1: {
         \\            const ref = &p;
-        \\            const tmp_1 = ref.*;
+        \\            const tmp_2 = ref.*;
         \\            ref.* += 1;
-        \\            break :blk tmp_1;
+        \\            break :blk_1 tmp_2;
         \\        }).?.* = tmp;
         \\        break :blk tmp;
         \\    };