translate-c-2 switch

This commit is contained in:
Vexu 2019-12-17 17:19:28 +02:00
parent 0283ab8a1a
commit 65531c73a9
No known key found for this signature in database
GPG Key ID: 59AEB8936E16A6AC
3 changed files with 321 additions and 50 deletions

View File

@ -77,6 +77,7 @@ pub const struct_ZigClangPredefinedExpr = @OpaqueType();
pub const struct_ZigClangInitListExpr = @OpaqueType();
pub const ZigClangPreprocessingRecord = @OpaqueType();
pub const ZigClangFloatingLiteral = @OpaqueType();
pub const ZigClangConstantExpr = @OpaqueType();
pub const ZigClangBO = extern enum {
PtrMemD,
@ -1059,3 +1060,17 @@ pub extern fn ZigClangAPFloat_getValueAsApproximateDouble(*const ZigClangFloatin
pub extern fn ZigClangConditionalOperator_getCond(*const ZigClangConditionalOperator) *const ZigClangExpr;
pub extern fn ZigClangConditionalOperator_getTrueExpr(*const ZigClangConditionalOperator) *const ZigClangExpr;
pub extern fn ZigClangConditionalOperator_getFalseExpr(*const ZigClangConditionalOperator) *const ZigClangExpr;
pub extern fn ZigClangSwitchStmt_getConditionVariableDeclStmt(*const ZigClangSwitchStmt) ?*const ZigClangDeclStmt;
pub extern fn ZigClangSwitchStmt_getCond(*const ZigClangSwitchStmt) *const ZigClangExpr;
pub extern fn ZigClangSwitchStmt_getBody(*const ZigClangSwitchStmt) *const ZigClangStmt;
pub extern fn ZigClangSwitchStmt_isAllEnumCasesCovered(*const ZigClangSwitchStmt) bool;
pub extern fn ZigClangCaseStmt_getLHS(*const ZigClangCaseStmt) *const ZigClangExpr;
pub extern fn ZigClangCaseStmt_getRHS(*const ZigClangCaseStmt) ?*const ZigClangExpr;
pub extern fn ZigClangCaseStmt_getBeginLoc(*const ZigClangCaseStmt) ZigClangSourceLocation;
pub extern fn ZigClangCaseStmt_getSubStmt(*const ZigClangCaseStmt) *const ZigClangStmt;
pub extern fn ZigClangDefaultStmt_getSubStmt(*const ZigClangDefaultStmt) *const ZigClangStmt;
pub extern fn ZigClangExpr_EvaluateAsConstantExpr(*const ZigClangExpr, *ZigClangExprEvalResult, ZigClangExpr_ConstExprUsage, *const ZigClangASTContext) bool;

View File

@ -54,7 +54,9 @@ const Scope = struct {
const Switch = struct {
base: Scope,
label: []const u8,
pending_block: *ast.Node.Block,
cases: *ast.Node.Switch.CaseList,
has_default: bool = false,
};
/// used when getting a member `a.b`
@ -189,8 +191,8 @@ const Scope = struct {
.Ref => null,
.FnDef => @fieldParentPtr(FnDef, "base", scope).getAlias(name),
.Block => @fieldParentPtr(Block, "base", scope).getAlias(name),
.Switch,
.Condition => scope.parent.?.getAlias(name),
else => @panic("TODO Scope.getAlias"),
};
}
@ -200,15 +202,31 @@ const Scope = struct {
.Root => @fieldParentPtr(Root, "base", scope).contains(name),
.FnDef => @fieldParentPtr(FnDef, "base", scope).contains(name),
.Block => @fieldParentPtr(Block, "base", scope).contains(name),
.Switch,
.Condition => scope.parent.?.contains(name),
else => @panic("TODO Scope.contains"),
};
}
fn getBreakableScope(inner: *Scope) *Scope {
var scope = inner;
while (scope.id != .Switch and scope.id != .Root) : (scope = scope.parent.?) {}
return scope;
while (true) {
switch (scope.id) {
.FnDef => unreachable,
.Switch => return scope,
else => scope = scope.parent.?,
}
}
}
fn getSwitch(inner: *Scope) *Scope.Switch {
var scope = inner;
while (true) {
switch (scope.id) {
.FnDef => unreachable,
.Switch => return @fieldParentPtr(Switch, "base", scope),
else => scope = scope.parent.?,
}
}
}
};
@ -634,6 +652,10 @@ fn transStmt(
.ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const ZigClangForStmt, stmt)),
.FloatingLiteralClass => return transFloatingLiteral(rp, scope, @ptrCast(*const ZigClangFloatingLiteral, stmt), result_used),
.ConditionalOperatorClass => return transConditionalOperator(rp, scope, @ptrCast(*const ZigClangConditionalOperator, stmt), result_used),
.SwitchStmtClass => return transSwitch(rp, scope, @ptrCast(*const ZigClangSwitchStmt, stmt)),
.CaseStmtClass => return transCase(rp, scope, @ptrCast(*const ZigClangCaseStmt, stmt)),
.DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const ZigClangDefaultStmt, stmt)),
.ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used),
else => {
return revertAndWarn(
rp,
@ -1374,6 +1396,154 @@ fn transForLoop(
return &while_node.base;
}
fn transSwitch(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangSwitchStmt,
) TransError!*ast.Node {
const switch_node = try transCreateNodeSwitch(rp.c);
var switch_scope = Scope.Switch{
.base = .{
.id = .Switch,
.parent = scope,
},
.cases = &switch_node.cases,
.pending_block = undefined,
};
var cond_scope = Scope.Condition{
.base = .{
.parent = scope,
.id = .Condition,
},
};
switch_node.expr = try transExpr(rp, &cond_scope.base, ZigClangSwitchStmt_getCond(stmt), .used, .r_value);
_ = try appendToken(rp.c, .RParen, ")");
_ = try appendToken(rp.c, .LBrace, "{");
switch_node.rbrace = try appendToken(rp.c, .RBrace, "}");
const block_scope = try Scope.Block.init(rp.c, &switch_scope.base, null);
// tmp block that all statements will go before being picked up by a case or default
const block = try transCreateNodeBlock(rp.c, null);
block_scope.block_node = block;
const switch_block = try transCreateNodeBlock(rp.c, null);
try switch_block.statements.push(&switch_node.base);
switch_scope.pending_block = switch_block;
const last = try transStmt(rp, &block_scope.base, ZigClangSwitchStmt_getBody(stmt), .unused, .r_value);
_ = try appendToken(rp.c, .Semicolon, ";");
// take all pending statements
var it = last.cast(ast.Node.Block).?.statements.iterator(0);
while (it.next()) |n| {
try switch_scope.pending_block.statements.push(n.*);
}
switch_scope.pending_block.label = try appendIdentifier(rp.c, "__switch");
_ = try appendToken(rp.c, .Colon, ":");
if (!switch_scope.has_default) {
const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
else_prong.expr = &(try transCreateNodeBreak(rp.c, "__switch")).base;
_ = try appendToken(rp.c, .Comma, ",");
try switch_node.cases.push(&else_prong.base);
}
switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}");
return &switch_scope.pending_block.base;
}
fn transCase(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangCaseStmt,
) 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.a(), "__case_{}", .{switch_scope.cases.len - @boolToInt(switch_scope.has_default)});
_ = try appendToken(rp.c, .Semicolon, ";");
const expr = if (ZigClangCaseStmt_getRHS(stmt)) |rhs| blk: {
const lhs_node = try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value);
const ellips = try appendToken(rp.c, .Ellipsis3, "...");
const rhs_node = try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value);
const node = try rp.c.a().create(ast.Node.InfixOp);
node.* = .{
.op_token = ellips,
.lhs = lhs_node,
.op = .Range,
.rhs = rhs_node,
};
break :blk &node.base;
} else
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)).base;
_ = try appendToken(rp.c, .Comma, ",");
try switch_scope.cases.push(&switch_prong.base);
const block = try transCreateNodeBlock(rp.c, null);
switch_scope.pending_block.label = try appendIdentifier(rp.c, label);
_ = try appendToken(rp.c, .Colon, ":");
switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}");
try block.statements.push(&switch_scope.pending_block.base);
// take all pending statements
var it = block_scope.block_node.statements.iterator(0);
while (it.next()) |n| {
try switch_scope.pending_block.statements.push(n.*);
}
block_scope.block_node.statements.shrink(0);
switch_scope.pending_block = block;
return transStmt(rp, scope, ZigClangCaseStmt_getSubStmt(stmt), .unused, .r_value);
}
fn transDefault(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangDefaultStmt,
) 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;
_ = try appendToken(rp.c, .Semicolon, ";");
const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
else_prong.expr = &(try transCreateNodeBreak(rp.c, label)).base;
_ = try appendToken(rp.c, .Comma, ",");
try switch_scope.cases.push(&else_prong.base);
const block = try transCreateNodeBlock(rp.c, null);
switch_scope.pending_block.label = try appendIdentifier(rp.c, label);
_ = try appendToken(rp.c, .Colon, ":");
switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}");
try block.statements.push(&switch_scope.pending_block.base);
// take all pending statements
var it = block_scope.block_node.statements.iterator(0);
while (it.next()) |n| {
try switch_scope.pending_block.statements.push(n.*);
}
block_scope.block_node.statements.shrink(0);
switch_scope.pending_block = block;
return transStmt(rp, scope, ZigClangDefaultStmt_getSubStmt(stmt), .unused, .r_value);
}
fn transConstantExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangExpr, used: ResultUsed) TransError!*ast.Node {
var result: ZigClangExprEvalResult = undefined;
if (!ZigClangExpr_EvaluateAsConstantExpr(expr, &result, .EvaluateForCodeGen, rp.c.clang_context))
return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangExpr_getBeginLoc(expr), "invalid constant expression", .{});
return maybeSuppressResult(rp, scope, used, try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&result.Val)));
}
fn transCPtrCast(
rp: RestorePoint,
loc: ZigClangSourceLocation,
@ -1414,7 +1584,7 @@ fn transCPtrCast(
fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node {
const break_scope = scope.getBreakableScope();
const br = try transCreateNodeBreak(rp.c, if (break_scope.id == .Switch)
@fieldParentPtr(Scope.Switch, "base", break_scope).label
"__switch"
else
null);
return &br.base;
@ -2339,6 +2509,42 @@ fn transCreateNodeContinue(c: *Context) !*ast.Node {
return &node.base;
}
fn transCreateNodeSwitch(c: *Context) !*ast.Node.Switch {
const switch_tok = try appendToken(c, .Keyword_switch, "switch");
_ = try appendToken(c, .LParen, "(");
const node = try c.a().create(ast.Node.Switch);
node.* = .{
.switch_token = switch_tok,
.expr = undefined,
.cases = ast.Node.Switch.CaseList.init(c.a()),
.rbrace = undefined,
};
return node;
}
fn transCreateNodeSwitchCase(c: *Context, lhs: *ast.Node) !*ast.Node.SwitchCase {
const arrow_tok = try appendToken(c, .EqualAngleBracketRight, "=>");
const node = try c.a().create(ast.Node.SwitchCase);
node.* = .{
.items = ast.Node.SwitchCase.ItemList.init(c.a()),
.arrow_token = arrow_tok,
.payload = null,
.expr = undefined,
};
try node.items.push(lhs);
return node;
}
fn transCreateNodeSwitchElse(c: *Context) !*ast.Node {
const node = try c.a().create(ast.Node.SwitchElse);
node.* = .{
.token = try appendToken(c, .Keyword_else, "else"),
};
return &node.base;
}
const RestorePoint = struct {
c: *Context,
token_index: ast.TokenIndex,
@ -2818,7 +3024,7 @@ fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8,
// TODO hook up with codegen
fn isZigPrimitiveType(name: []const u8) bool {
if (name.len > 1 and std.mem.startsWith(u8, name, "u") or std.mem.startsWith(u8, name, "u")) {
if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) {
for (name[1..]) |c| {
switch (c) {
'0'...'9' => {},
@ -2840,7 +3046,15 @@ fn isZigPrimitiveType(name: []const u8) bool {
std.mem.eql(u8, name, "c_longdouble") or
std.mem.eql(u8, name, "noreturn") or
std.mem.eql(u8, name, "type") or
std.mem.eql(u8, name, "anyerror");
std.mem.eql(u8, name, "anyerror") or
std.mem.eql(u8, name, "c_short") or
std.mem.eql(u8, name, "c_ushort") or
std.mem.eql(u8, name, "c_int") or
std.mem.eql(u8, name, "c_uint") or
std.mem.eql(u8, name, "c_long") or
std.mem.eql(u8, name, "c_ulong") or
std.mem.eql(u8, name, "c_longlong") or
std.mem.eql(u8, name, "c_ulonglong");
}
fn isValidZigIdentifier(name: []const u8) bool {

View File

@ -849,6 +849,48 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
cases.add_2("switch on int",
\\int switch_fn(int i) {
\\ int res = 0;
\\ switch (i) {
\\ case 0:
\\ res = 1;
\\ case 1:
\\ res = 2;
\\ default:
\\ res = 3 * i;
\\ break;
\\ case 2:
\\ res = 5;
\\ }
\\}
, &[_][]const u8{
\\pub export fn switch_fn(i: c_int) c_int {
\\ var res: c_int = 0;
\\ __switch: {
\\ __case_2: {
\\ __default: {
\\ __case_1: {
\\ __case_0: {
\\ switch (i) {
\\ 0 => break :__case_0,
\\ 1 => break :__case_1,
\\ else => break :__default,
\\ 2 => break :__case_2,
\\ }
\\ }
\\ res = 1;
\\ }
\\ res = 2;
\\ }
\\ res = (3 * i);
\\ break :__switch;
\\ }
\\ res = 5;
\\ }
\\}
});
/////////////// Cases for only stage1 which are TODO items for stage2 ////////////////
if (builtin.os != builtin.Os.windows) {
@ -1938,48 +1980,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
cases.add("switch on int",
\\int switch_fn(int i) {
\\ int res = 0;
\\ switch (i) {
\\ case 0:
\\ res = 1;
\\ case 1:
\\ res = 2;
\\ default:
\\ res = 3 * i;
\\ break;
\\ case 2:
\\ res = 5;
\\ }
\\}
, &[_][]const u8{
\\pub fn switch_fn(i: c_int) c_int {
\\ var res: c_int = 0;
\\ __switch: {
\\ __case_2: {
\\ __default: {
\\ __case_1: {
\\ __case_0: {
\\ switch (i) {
\\ 0 => break :__case_0,
\\ 1 => break :__case_1,
\\ else => break :__default,
\\ 2 => break :__case_2,
\\ }
\\ }
\\ res = 1;
\\ }
\\ res = 2;
\\ }
\\ res = (3 * i);
\\ break :__switch;
\\ }
\\ res = 5;
\\ }
\\}
});
cases.addC("implicit casts",
\\#include <stdbool.h>
\\
@ -2237,4 +2237,46 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
, &[_][]const u8{
\\pub const NRF_GPIO = if (@typeId(@TypeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@TypeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else @as([*c]NRF_GPIO_Type, NRF_GPIO_BASE);
});
cases.add("switch on int",
\\int switch_fn(int i) {
\\ int res = 0;
\\ switch (i) {
\\ case 0:
\\ res = 1;
\\ case 1:
\\ res = 2;
\\ default:
\\ res = 3 * i;
\\ break;
\\ case 2:
\\ res = 5;
\\ }
\\}
, &[_][]const u8{
\\pub fn switch_fn(i: c_int) c_int {
\\ var res: c_int = 0;
\\ __switch: {
\\ __case_2: {
\\ __default: {
\\ __case_1: {
\\ __case_0: {
\\ switch (i) {
\\ 0 => break :__case_0,
\\ 1 => break :__case_1,
\\ else => break :__default,
\\ 2 => break :__case_2,
\\ }
\\ }
\\ res = 1;
\\ }
\\ res = 2;
\\ }
\\ res = (3 * i);
\\ break :__switch;
\\ }
\\ res = 5;
\\ }
\\}
});
}