translate-c-2 character literals and more test fixes

This commit is contained in:
Vexu 2019-12-18 01:04:01 +02:00
parent 6d7025d0c5
commit 21bc3353b8
No known key found for this signature in database
GPG Key ID: 59AEB8936E16A6AC
4 changed files with 365 additions and 261 deletions

View File

@ -582,7 +582,9 @@ pub fn formatAsciiChar(
comptime Errors: type,
output: fn (@TypeOf(context), []const u8) Errors!void,
) Errors!void {
return output(context, @as(*const [1]u8, &c)[0..]);
if (std.ascii.isPrint(c))
return output(context, @as(*const [1]u8, &c)[0..]);
return format(context, Errors, output, "\\x{x:0<2}", .{c});
}
pub fn formatBuf(

View File

@ -78,6 +78,7 @@ pub const struct_ZigClangInitListExpr = @OpaqueType();
pub const ZigClangPreprocessingRecord = @OpaqueType();
pub const ZigClangFloatingLiteral = @OpaqueType();
pub const ZigClangConstantExpr = @OpaqueType();
pub const ZigClangCharacterLiteral = @OpaqueType();
pub const ZigClangBO = extern enum {
PtrMemD,
@ -712,6 +713,14 @@ pub const ZigClangStringLiteral_StringKind = extern enum {
UTF32,
};
pub const ZigClangCharacterLiteral_CharacterKind = extern enum {
Ascii,
Wide,
UTF8,
UTF16,
UTF32,
};
pub const ZigClangRecordDecl_field_iterator = extern struct {
opaque: *c_void,
};
@ -1077,3 +1086,7 @@ pub extern fn ZigClangExpr_EvaluateAsConstantExpr(*const ZigClangExpr, *ZigClang
pub extern fn ZigClangPredefinedExpr_getFunctionName(*const ZigClangPredefinedExpr) *const ZigClangStringLiteral;
pub extern fn ZigClangCharacterLiteral_getBeginLoc(*const ZigClangCharacterLiteral) ZigClangSourceLocation;
pub extern fn ZigClangCharacterLiteral_getKind(*const ZigClangCharacterLiteral) ZigClangCharacterLiteral_CharacterKind;
pub extern fn ZigClangCharacterLiteral_getValue(*const ZigClangCharacterLiteral) c_uint;

View File

@ -49,7 +49,10 @@ const Scope = struct {
Root,
Condition,
FnDef,
/// used when getting a member `a.b`
Ref,
Loop,
};
const Switch = struct {
@ -59,11 +62,6 @@ const Scope = struct {
has_default: bool = false,
};
/// used when getting a member `a.b`
const Ref = struct {
base: Scope,
};
const Block = struct {
base: Scope,
block_node: *ast.Node.Block,
@ -125,10 +123,6 @@ const Scope = struct {
}
};
const Condition = struct {
base: Scope,
};
const FnDef = struct {
base: Scope,
params: AliasList,
@ -169,7 +163,6 @@ const Scope = struct {
.Root => unreachable,
.Block => return @fieldParentPtr(Block, "base", scope),
.Condition => {
const cond = @fieldParentPtr(Condition, "base", scope);
// comma operator used
return try Block.init(c, scope, "blk");
},
@ -192,6 +185,7 @@ const Scope = struct {
.FnDef => @fieldParentPtr(FnDef, "base", scope).getAlias(name),
.Block => @fieldParentPtr(Block, "base", scope).getAlias(name),
.Switch,
.Loop,
.Condition => scope.parent.?.getAlias(name),
};
}
@ -203,6 +197,7 @@ const Scope = struct {
.FnDef => @fieldParentPtr(FnDef, "base", scope).contains(name),
.Block => @fieldParentPtr(Block, "base", scope).contains(name),
.Switch,
.Loop,
.Condition => scope.parent.?.contains(name),
};
}
@ -213,6 +208,7 @@ const Scope = struct {
switch (scope.id) {
.FnDef => unreachable,
.Switch => return scope,
.Loop => return scope,
else => scope = scope.parent.?,
}
}
@ -434,7 +430,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
error.OutOfMemory => |e| return e,
};
},
else => unreachable,
else => return failDecl(c, fn_decl_loc, fn_name, "unable to resolve function type {}", .{ZigClangType_getTypeClass(fn_type)}),
};
if (!decl_ctx.has_body) {
@ -862,6 +858,7 @@ fn transStmt(
.DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const ZigClangDefaultStmt, stmt)),
.ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used),
.PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const ZigClangPredefinedExpr, stmt), result_used),
.CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, stmt), result_used),
else => {
return revertAndWarn(
rp,
@ -1202,7 +1199,7 @@ fn transStringLiteral(
const token = try appendToken(rp.c, .StringLiteral, buf);
const node = try rp.c.a().create(ast.Node.StringLiteral);
node.* = ast.Node.StringLiteral{
node.* = .{
.token = token,
};
return maybeSuppressResult(rp, scope, result_used, &node.base);
@ -1237,18 +1234,15 @@ fn writeEscapedString(buf: []u8, s: []const u8) void {
// Returns either a string literal or a slice of `buf`.
fn escapeChar(c: u8, char_buf: *[4]u8) []const u8 {
// TODO: https://github.com/ziglang/zig/issues/2749
const escaped = switch (c) {
// Printable ASCII except for ' " \
' ', '!', '#'...'&', '('...'[', ']'...'~' => ([_]u8{c})[0..],
'\'', '\"', '\\' => ([_]u8{ '\\', c })[0..],
'\n' => return "\\n"[0..],
'\r' => return "\\r"[0..],
'\t' => return "\\t"[0..],
else => return std.fmt.bufPrint(char_buf[0..], "\\x{x:2}", .{c}) catch unreachable,
return switch (c) {
'\"' => "\\\""[0..],
'\'' => "\\'"[0..],
'\\' => "\\\\"[0..],
'\n' => "\\n"[0..],
'\r' => "\\r"[0..],
'\t' => "\\t"[0..],
else => std.fmt.bufPrint(char_buf[0..], "{c}", .{c}) catch unreachable,
};
std.mem.copy(u8, char_buf, escaped);
return char_buf[0..escaped.len];
}
fn transCCast(
@ -1459,13 +1453,11 @@ fn transIfStmt(
// if (c) t else e
const if_node = try transCreateNodeIf(rp.c);
var cond_scope = Scope.Condition{
.base = .{
.parent = scope,
.id = .Condition,
},
var cond_scope = Scope{
.parent = scope,
.id = .Condition,
};
if_node.condition = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt)), .used, .r_value, false);
if_node.condition = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt)), .used, .r_value, false);
_ = try appendToken(rp.c, .RParen, ")");
if_node.body = try transStmt(rp, scope, ZigClangIfStmt_getThen(stmt), .unused, .r_value);
@ -1485,16 +1477,18 @@ fn transWhileLoop(
) TransError!*ast.Node {
const while_node = try transCreateNodeWhile(rp.c);
var cond_scope = Scope.Condition{
.base = .{
.parent = scope,
.id = .Condition,
},
var cond_scope = Scope{
.parent = scope,
.id = .Condition,
};
while_node.condition = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangWhileStmt_getCond(stmt)), .used, .r_value, false);
while_node.condition = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangWhileStmt_getCond(stmt)), .used, .r_value, false);
_ = try appendToken(rp.c, .RParen, ")");
while_node.body = try transStmt(rp, scope, ZigClangWhileStmt_getBody(stmt), .unused, .r_value);
var loop_scope = Scope{
.parent = scope,
.id = .Loop,
};
while_node.body = try transStmt(rp, &loop_scope, ZigClangWhileStmt_getBody(stmt), .unused, .r_value);
return &while_node.base;
}
@ -1508,6 +1502,10 @@ fn transDoWhileLoop(
while_node.condition = try transCreateNodeBoolLiteral(rp.c, true);
_ = try appendToken(rp.c, .RParen, ")");
var new = false;
var loop_scope = Scope{
.parent = scope,
.id = .Loop,
};
const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) blk: {
// there's already a block in C, so we'll append our condition to it.
@ -1520,7 +1518,7 @@ fn transDoWhileLoop(
// zig: b;
// zig: if (!cond) break;
// zig: }
break :blk (try transStmt(rp, scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).?;
break :blk (try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).?;
} else blk: {
// the C statement is without a block, so we need to create a block to contain it.
// c: do
@ -1532,20 +1530,18 @@ fn transDoWhileLoop(
// zig: }
new = true;
const block = try transCreateNodeBlock(rp.c, null);
try block.statements.push(try transStmt(rp, scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value));
try block.statements.push(try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value));
break :blk block;
};
// if (!cond) break;
const if_node = try transCreateNodeIf(rp.c);
var cond_scope = Scope.Condition{
.base = .{
.parent = scope,
.id = .Condition,
},
var cond_scope = Scope{
.parent = scope,
.id = .Condition,
};
const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");
prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, false);
prefix_op.rhs = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true);
_ = try appendToken(rp.c, .RParen, ")");
if_node.condition = &prefix_op.base;
if_node.body = &(try transCreateNodeBreak(rp.c, null)).base;
@ -1563,25 +1559,26 @@ fn transForLoop(
scope: *Scope,
stmt: *const ZigClangForStmt,
) TransError!*ast.Node {
var inner = scope;
var loop_scope = Scope{
.parent = scope,
.id = .Loop,
};
var block = false;
var block_scope: ?*Scope.Block = null;
if (ZigClangForStmt_getInit(stmt)) |init| {
block_scope = try Scope.Block.init(rp.c, scope, null);
block_scope.?.block_node = try transCreateNodeBlock(rp.c, null);
inner = &block_scope.?.base;
_ = try transStmt(rp, inner, init, .unused, .r_value);
loop_scope.parent = &block_scope.?.base;
_ = try transStmt(rp, &loop_scope, init, .unused, .r_value);
}
var cond_scope = Scope.Condition{
.base = .{
.parent = inner,
.id = .Condition,
},
var cond_scope = Scope{
.parent = scope,
.id = .Condition,
};
const while_node = try transCreateNodeWhile(rp.c);
while_node.condition = if (ZigClangForStmt_getCond(stmt)) |cond|
try transBoolExpr(rp, &cond_scope.base, cond, .used, .r_value, false)
try transBoolExpr(rp, &cond_scope, cond, .used, .r_value, false)
else
try transCreateNodeBoolLiteral(rp.c, true);
_ = try appendToken(rp.c, .RParen, ")");
@ -1589,11 +1586,11 @@ fn transForLoop(
if (ZigClangForStmt_getInc(stmt)) |incr| {
_ = try appendToken(rp.c, .Colon, ":");
_ = try appendToken(rp.c, .LParen, "(");
while_node.continue_expr = try transExpr(rp, &cond_scope.base, incr, .unused, .r_value);
while_node.continue_expr = try transExpr(rp, &cond_scope, incr, .unused, .r_value);
_ = try appendToken(rp.c, .RParen, ")");
}
while_node.body = try transStmt(rp, inner, ZigClangForStmt_getBody(stmt), .unused, .r_value);
while_node.body = try transStmt(rp, &loop_scope, ZigClangForStmt_getBody(stmt), .unused, .r_value);
if (block_scope != null) {
try block_scope.?.block_node.statements.push(&while_node.base);
block_scope.?.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
@ -1617,13 +1614,11 @@ fn transSwitch(
.pending_block = undefined,
};
var cond_scope = Scope.Condition{
.base = .{
.parent = scope,
.id = .Condition,
},
var cond_scope = Scope{
.parent = scope,
.id = .Condition,
};
switch_node.expr = try transExpr(rp, &cond_scope.base, ZigClangSwitchStmt_getCond(stmt), .used, .r_value);
switch_node.expr = try transExpr(rp, &cond_scope, ZigClangSwitchStmt_getCond(stmt), .used, .r_value);
_ = try appendToken(rp.c, .RParen, ")");
_ = try appendToken(rp.c, .LBrace, "{");
switch_node.rbrace = try appendToken(rp.c, .RBrace, "}");
@ -1753,6 +1748,41 @@ fn transPredefinedExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangPre
return transStringLiteral(rp, scope, ZigClangPredefinedExpr_getFunctionName(expr), used);
}
fn transCharLiteral(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangCharacterLiteral,
result_used: ResultUsed,
) TransError!*ast.Node {
const kind = ZigClangCharacterLiteral_getKind(stmt);
switch (kind) {
.Ascii, .UTF8 => {
const val = ZigClangCharacterLiteral_getValue(stmt);
if (kind == .Ascii) {
// C has a somewhat obscure feature called multi-character character
// constant
if (val > 255)
return transCreateNodeInt(rp.c, val);
}
var char_buf: [4]u8 = undefined;
const token = try appendTokenFmt(rp.c, .CharLiteral, "'{}'", .{escapeChar(@intCast(u8, val), &char_buf)});
const node = try rp.c.a().create(ast.Node.CharLiteral);
node.* = .{
.token = token,
};
return maybeSuppressResult(rp, scope, result_used, &node.base);
},
.UTF16, .UTF32, .Wide => return revertAndWarn(
rp,
error.UnsupportedTranslation,
ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)),
"TODO: support character literal kind {}",
.{kind},
),
else => unreachable,
}
}
fn transCPtrCast(
rp: RestorePoint,
loc: ZigClangSourceLocation,
@ -1796,6 +1826,7 @@ fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node {
"__switch"
else
null);
_ = try appendToken(rp.c, .Semicolon, ";");
return &br.base;
}
@ -1813,18 +1844,16 @@ fn transConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigCla
const gropued = scope.id == .Condition;
const lparen = if (gropued) try appendToken(rp.c, .LParen, "(") else undefined;
const if_node = try transCreateNodeIf(rp.c);
var cond_scope = Scope.Condition{
.base = .{
.parent = scope,
.id = .Condition,
},
var cond_scope = Scope{
.parent = scope,
.id = .Condition,
};
const cond_expr = ZigClangConditionalOperator_getCond(stmt);
const true_expr = ZigClangConditionalOperator_getTrueExpr(stmt);
const false_expr = ZigClangConditionalOperator_getFalseExpr(stmt);
if_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false);
if_node.condition = try transBoolExpr(rp, &cond_scope, cond_expr, .used, .r_value, false);
_ = try appendToken(rp.c, .RParen, ")");
if_node.body = try transExpr(rp, scope, true_expr, .used, .r_value);
@ -3104,7 +3133,7 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
var tok_it = tok_list.iterator(0);
const first_tok = tok_it.next().?;
assert(first_tok.id == .Identifier and std.mem.eql(u8, first_tok.bytes, checked_name));
assert(first_tok.id == .Identifier and std.mem.eql(u8, first_tok.bytes, name));
const next = tok_it.peek().?;
switch (next.id) {
.Identifier => {

View File

@ -600,6 +600,135 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
cases.add_both("simple union",
\\union Foo {
\\ int x;
\\ double y;
\\};
, &[_][]const u8{
\\pub const union_Foo = extern union {
\\ x: c_int,
\\ y: f64,
\\};
,
\\pub const Foo = union_Foo;
});
cases.addC_both("string literal",
\\const char *foo(void) {
\\ return "bar";
\\}
, &[_][]const u8{
\\pub export fn foo() [*c]const u8 {
\\ return "bar";
\\}
});
cases.addC_both("return void",
\\void foo(void) {
\\ return;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ return;
\\}
});
cases.addC_both("for loop",
\\void foo(void) {
\\ for (int i = 0; i; i = i + 1) { }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ {
\\ var i: c_int = 0;
\\ while (i != 0) : (i = (i + 1)) {}
\\ }
\\}
});
cases.addC_both("empty for loop",
\\void foo(void) {
\\ for (;;) { }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ while (true) {}
\\}
});
cases.addC_both("break statement",
\\void foo(void) {
\\ for (;;) {
\\ break;
\\ }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ while (true) {
\\ break;
\\ }
\\}
});
cases.addC_both("continue statement",
\\void foo(void) {
\\ for (;;) {
\\ continue;
\\ }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ while (true) {
\\ continue;
\\ }
\\}
});
cases.addC_both("pointer casting",
\\float *ptrcast(int *a) {
\\ return (float *)a;
\\}
, &[_][]const u8{
\\pub export fn ptrcast(a: [*c]c_int) [*c]f32 {
\\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a));
\\}
});
cases.addC_both("pointer conversion with different alignment",
\\void test_ptr_cast() {
\\ void *p;
\\ {
\\ char *to_char = (char *)p;
\\ short *to_short = (short *)p;
\\ int *to_int = (int *)p;
\\ long long *to_longlong = (long long *)p;
\\ }
\\ {
\\ char *to_char = p;
\\ short *to_short = p;
\\ int *to_int = p;
\\ long long *to_longlong = p;
\\ }
\\}
, &[_][]const u8{
\\pub export fn test_ptr_cast() void {
\\ var p: ?*c_void = undefined;
\\ {
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
\\ }
\\ {
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
\\ }
\\}
});
/////////////// Cases that pass for only stage2 ////////////////
cases.add_2("Parameterless function prototypes",
@ -957,11 +1086,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ while (true) {
\\ var a: c_int = 2;
\\ a = 12;
\\ if (!4 != 0) break;
\\ if (!(4 != 0)) break;
\\ }
\\ while (true) {
\\ a = 7;
\\ if (!4 != 0) break;
\\ if (!(4 != 0)) break;
\\ }
\\}
});
@ -1145,6 +1274,66 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
cases.add_2("escape sequences",
\\const char *escapes() {
\\char a = '\'',
\\ b = '\\',
\\ c = '\a',
\\ d = '\b',
\\ e = '\f',
\\ f = '\n',
\\ g = '\r',
\\ h = '\t',
\\ i = '\v',
\\ j = '\0',
\\ k = '\"';
\\ return "\'\\\a\b\f\n\r\t\v\0\"";
\\}
\\
, &[_][]const u8{
\\pub export fn escapes() [*c]const u8 {
\\ var a: u8 = @as(u8, '\'');
\\ var b: u8 = @as(u8, '\\');
\\ var c: u8 = @as(u8, '\x07');
\\ var d: u8 = @as(u8, '\x08');
\\ var e: u8 = @as(u8, '\x0c');
\\ var f: u8 = @as(u8, '\n');
\\ var g: u8 = @as(u8, '\r');
\\ var h: u8 = @as(u8, '\t');
\\ var i: u8 = @as(u8, '\x0b');
\\ var j: u8 = @as(u8, '\x00');
\\ var k: u8 = @as(u8, '\"');
\\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\"";
\\}
});
cases.add_2("do loop",
\\void foo(void) {
\\ int a = 2;
\\ do {
\\ a = a - 1;
\\ } while (a);
\\
\\ int b = 2;
\\ do
\\ b = b -1;
\\ while (b);
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: c_int = 2;
\\ while (true) {
\\ a = (a - 1);
\\ if (!(a != 0)) break;
\\ }
\\ var b: c_int = 2;
\\ while (true) {
\\ b = (b - 1);
\\ if (!(b != 0)) break;
\\ }
\\}
});
/////////////// Cases for only stage1 which are TODO items for stage2 ////////////////
cases.addAllowWarnings("simple data types",
@ -1641,33 +1830,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
cases.addC("do loop",
\\void foo(void) {
\\ int a = 2;
\\ do {
\\ a--;
\\ } while (a != 0);
\\
\\ int b = 2;
\\ do
\\ b--;
\\ while (b != 0);
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: c_int = 2;
\\ while (true) {
\\ a -= 1;
\\ if (!(a != 0)) break;
\\ }
\\ var b: c_int = 2;
\\ while (true) {
\\ b -= 1;
\\ if (!(b != 0)) break;
\\ }
\\}
});
cases.addC("deref function pointer",
\\void foo(void) {}
\\int baz(void) { return 0; }
@ -1708,20 +1870,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
cases.add("simple union",
\\union Foo {
\\ int x;
\\ double y;
\\};
, &[_][]const u8{
\\pub const union_Foo = extern union {
\\ x: c_int,
\\ y: f64,
\\};
,
\\pub const Foo = union_Foo;
});
cases.add("address of operator",
\\int foo(void) {
\\ int x = 1234;
@ -1736,77 +1884,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
cases.add("string literal",
\\const char *foo(void) {
\\ return "bar";
\\}
, &[_][]const u8{
\\pub fn foo() [*c]const u8 {
\\ return "bar";
\\}
});
cases.add("return void",
\\void foo(void) {
\\ return;
\\}
, &[_][]const u8{
\\pub fn foo() void {
\\ return;
\\}
});
cases.add("for loop",
\\void foo(void) {
\\ for (int i = 0; i < 10; i += 1) { }
\\}
, &[_][]const u8{
\\pub fn foo() void {
\\ {
\\ var i: c_int = 0;
\\ while (i < 10) : (i += 1) {}
\\ }
\\}
});
cases.add("empty for loop",
\\void foo(void) {
\\ for (;;) { }
\\}
, &[_][]const u8{
\\pub fn foo() void {
\\ while (true) {}
\\}
});
cases.add("break statement",
\\void foo(void) {
\\ for (;;) {
\\ break;
\\ }
\\}
, &[_][]const u8{
\\pub fn foo() void {
\\ while (true) {
\\ break;
\\ }
\\}
});
cases.add("continue statement",
\\void foo(void) {
\\ for (;;) {
\\ continue;
\\ }
\\}
, &[_][]const u8{
\\pub fn foo() void {
\\ while (true) {
\\ continue;
\\ }
\\}
});
cases.add("variable name shadowing",
\\int foo(void) {
\\ int x = 1;
@ -1827,16 +1904,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
cases.add("pointer casting",
\\float *ptrcast(int *a) {
\\ return (float *)a;
\\}
, &[_][]const u8{
\\fn ptrcast(a: [*c]c_int) [*c]f32 {
\\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a));
\\}
});
cases.add("bin not",
\\int foo(int x) {
\\ return ~x;
@ -1987,74 +2054,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
cases.addC("pointer conversion with different alignment",
\\void test_ptr_cast() {
\\ void *p;
\\ {
\\ char *to_char = (char *)p;
\\ short *to_short = (short *)p;
\\ int *to_int = (int *)p;
\\ long long *to_longlong = (long long *)p;
\\ }
\\ {
\\ char *to_char = p;
\\ short *to_short = p;
\\ int *to_int = p;
\\ long long *to_longlong = p;
\\ }
\\}
, &[_][]const u8{
\\pub export fn test_ptr_cast() void {
\\ var p: ?*c_void = undefined;
\\ {
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
\\ }
\\ {
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
\\ }
\\}
});
cases.addC("escape sequences",
\\const char *escapes() {
\\char a = '\'',
\\ b = '\\',
\\ c = '\a',
\\ d = '\b',
\\ e = '\f',
\\ f = '\n',
\\ g = '\r',
\\ h = '\t',
\\ i = '\v',
\\ j = '\0',
\\ k = '\"';
\\ return "\'\\\a\b\f\n\r\t\v\0\"";
\\}
\\
, &[_][]const u8{
\\pub export fn escapes() [*c]const u8 {
\\ var a: u8 = @as(u8, '\'');
\\ var b: u8 = @as(u8, '\\');
\\ var c: u8 = @as(u8, '\x07');
\\ var d: u8 = @as(u8, '\x08');
\\ var e: u8 = @as(u8, '\x0c');
\\ var f: u8 = @as(u8, '\n');
\\ var g: u8 = @as(u8, '\r');
\\ var h: u8 = @as(u8, '\t');
\\ var i: u8 = @as(u8, '\x0b');
\\ var j: u8 = @as(u8, '\x00');
\\ var k: u8 = @as(u8, '\"');
\\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\"";
\\}
\\
});
if (builtin.os != builtin.Os.windows) {
// sysv_abi not currently supported on windows
cases.add("Macro qualified functions",
@ -2386,4 +2385,65 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ };
\\}
});
cases.addC("escape sequences",
\\const char *escapes() {
\\char a = '\'',
\\ b = '\\',
\\ c = '\a',
\\ d = '\b',
\\ e = '\f',
\\ f = '\n',
\\ g = '\r',
\\ h = '\t',
\\ i = '\v',
\\ j = '\0',
\\ k = '\"';
\\ return "\'\\\a\b\f\n\r\t\v\0\"";
\\}
\\
, &[_][]const u8{
\\pub export fn escapes() [*c]const u8 {
\\ var a: u8 = @as(u8, '\'');
\\ var b: u8 = @as(u8, '\\');
\\ var c: u8 = @as(u8, '\x07');
\\ var d: u8 = @as(u8, '\x08');
\\ var e: u8 = @as(u8, '\x0c');
\\ var f: u8 = @as(u8, '\n');
\\ var g: u8 = @as(u8, '\r');
\\ var h: u8 = @as(u8, '\t');
\\ var i: u8 = @as(u8, '\x0b');
\\ var j: u8 = @as(u8, '\x00');
\\ var k: u8 = @as(u8, '\"');
\\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\"";
\\}
\\
});
cases.addC("do loop",
\\void foo(void) {
\\ int a = 2;
\\ do {
\\ a--;
\\ } while (a != 0);
\\
\\ int b = 2;
\\ do
\\ b--;
\\ while (b != 0);
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: c_int = 2;
\\ while (true) {
\\ a -= 1;
\\ if (!(a != 0)) break;
\\ }
\\ var b: c_int = 2;
\\ while (true) {
\\ b -= 1;
\\ if (!(b != 0)) break;
\\ }
\\}
});
}