commit 28dbdba37eed0cfd875cfc337446da3048743dd9 (tree)
parent ba1d213f480eb0cb7c8641a8d8cb1d6763e82438
Author: Andrew Kelley <andrew@ziglang.org>
Date: Sun, 22 Dec 2019 16:12:56 -0500
Merge pull request #3935 from Vexu/translate-c-2
Translate-c-2 the rest
Diffstat:
10 files changed, 5146 insertions(+), 2288 deletions(-)
diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig
@@ -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(
diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig
@@ -1431,13 +1431,13 @@ pub const Node = struct {
AssignBitShiftRight,
AssignBitXor,
AssignDiv,
- AssignMinus,
- AssignMinusWrap,
+ AssignSub,
+ AssignSubWrap,
AssignMod,
- AssignPlus,
- AssignPlusWrap,
- AssignTimes,
- AssignTimesWarp,
+ AssignAdd,
+ AssignAddWrap,
+ AssignMul,
+ AssignMulWrap,
BangEqual,
BitAnd,
BitOr,
@@ -1456,8 +1456,8 @@ pub const Node = struct {
LessThan,
MergeErrorSets,
Mod,
- Mult,
- MultWrap,
+ Mul,
+ MulWrap,
Period,
Range,
Sub,
@@ -1490,13 +1490,13 @@ pub const Node = struct {
Op.AssignBitShiftRight,
Op.AssignBitXor,
Op.AssignDiv,
- Op.AssignMinus,
- Op.AssignMinusWrap,
+ Op.AssignSub,
+ Op.AssignSubWrap,
Op.AssignMod,
- Op.AssignPlus,
- Op.AssignPlusWrap,
- Op.AssignTimes,
- Op.AssignTimesWarp,
+ Op.AssignAdd,
+ Op.AssignAddWrap,
+ Op.AssignMul,
+ Op.AssignMulWrap,
Op.BangEqual,
Op.BitAnd,
Op.BitOr,
@@ -1514,8 +1514,8 @@ pub const Node = struct {
Op.LessThan,
Op.MergeErrorSets,
Op.Mod,
- Op.Mult,
- Op.MultWrap,
+ Op.Mul,
+ Op.MulWrap,
Op.Period,
Op.Range,
Op.Sub,
diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig
@@ -1981,19 +1981,19 @@ fn parseAssignOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
const token = nextToken(it);
const op = switch (token.ptr.id) {
- .AsteriskEqual => Op{ .AssignTimes = {} },
+ .AsteriskEqual => Op{ .AssignMul = {} },
.SlashEqual => Op{ .AssignDiv = {} },
.PercentEqual => Op{ .AssignMod = {} },
- .PlusEqual => Op{ .AssignPlus = {} },
- .MinusEqual => Op{ .AssignMinus = {} },
+ .PlusEqual => Op{ .AssignAdd = {} },
+ .MinusEqual => Op{ .AssignSub = {} },
.AngleBracketAngleBracketLeftEqual => Op{ .AssignBitShiftLeft = {} },
.AngleBracketAngleBracketRightEqual => Op{ .AssignBitShiftRight = {} },
.AmpersandEqual => Op{ .AssignBitAnd = {} },
.CaretEqual => Op{ .AssignBitXor = {} },
.PipeEqual => Op{ .AssignBitOr = {} },
- .AsteriskPercentEqual => Op{ .AssignTimesWarp = {} },
- .PlusPercentEqual => Op{ .AssignPlusWrap = {} },
- .MinusPercentEqual => Op{ .AssignMinusWrap = {} },
+ .AsteriskPercentEqual => Op{ .AssignMulWrap = {} },
+ .PlusPercentEqual => Op{ .AssignAddWrap = {} },
+ .MinusPercentEqual => Op{ .AssignSubWrap = {} },
.Equal => Op{ .Assign = {} },
else => {
putBackToken(it, token.index);
@@ -2120,11 +2120,11 @@ fn parseMultiplyOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
const token = nextToken(it);
const op = switch (token.ptr.id) {
.PipePipe => ops{ .BoolOr = {} },
- .Asterisk => ops{ .Mult = {} },
+ .Asterisk => ops{ .Mul = {} },
.Slash => ops{ .Div = {} },
.Percent => ops{ .Mod = {} },
.AsteriskAsterisk => ops{ .ArrayMult = {} },
- .AsteriskPercent => ops{ .MultWrap = {} },
+ .AsteriskPercent => ops{ .MulWrap = {} },
else => {
putBackToken(it, token.index);
return null;
diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig
@@ -1635,7 +1635,7 @@ fn renderExpression(
.If => {
const if_node = @fieldParentPtr(ast.Node.If, "base", base);
- const lparen = tree.prevToken(if_node.condition.firstToken());
+ const lparen = tree.nextToken(if_node.if_token);
const rparen = tree.nextToken(if_node.condition.lastToken());
try renderToken(tree, stream, if_node.if_token, indent, start_col, Space.Space); // if
diff --git a/src-self-hosted/c_tokenizer.zig b/src-self-hosted/c_tokenizer.zig
@@ -27,6 +27,10 @@ pub const CToken = struct {
Lt,
Comma,
Fn,
+ Arrow,
+ LBrace,
+ RBrace,
+ Pipe,
};
pub const NumLitSuffix = enum {
@@ -71,69 +75,130 @@ fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken {
}
} else return tok;
var bytes = try allocator.alloc(u8, tok.bytes.len * 2);
- var escape = false;
+ var state: enum {
+ Start,
+ Escape,
+ Hex,
+ Octal,
+ } = .Start;
var i: usize = 0;
+ var count: u8 = 0;
+ var num: u8 = 0;
for (tok.bytes) |c| {
- if (escape) {
- switch (c) {
- 'n', 'r', 't', '\\', '\'', '\"', 'x' => {
- bytes[i] = c;
- },
- 'a' => {
- bytes[i] = 'x';
- i += 1;
- bytes[i] = '0';
- i += 1;
- bytes[i] = '7';
- },
- 'b' => {
- bytes[i] = 'x';
- i += 1;
- bytes[i] = '0';
- i += 1;
- bytes[i] = '8';
- },
- 'f' => {
- bytes[i] = 'x';
- i += 1;
- bytes[i] = '0';
- i += 1;
- bytes[i] = 'C';
- },
- 'v' => {
- bytes[i] = 'x';
- i += 1;
- bytes[i] = '0';
- i += 1;
- bytes[i] = 'B';
- },
- '?' => {
- i -= 1;
- bytes[i] = '?';
- },
- 'u', 'U' => {
- // TODO unicode escape sequences
- return error.TokenizingFailed;
- },
- '0'...'7' => {
- // TODO octal escape sequences
- return error.TokenizingFailed;
- },
- else => {
- // unknown escape sequence
- return error.TokenizingFailed;
- },
- }
- i += 1;
- escape = false;
- } else {
- if (c == '\\') {
- escape = true;
- }
- bytes[i] = c;
- i += 1;
+ switch (state) {
+ .Escape => {
+ switch (c) {
+ 'n', 'r', 't', '\\', '\'', '\"' => {
+ bytes[i] = c;
+ },
+ '0'...'7' => {
+ count += 1;
+ num += c - '0';
+ state = .Octal;
+ bytes[i] = 'x';
+ },
+ 'x' => {
+ state = .Hex;
+ bytes[i] = 'x';
+ },
+ 'a' => {
+ bytes[i] = 'x';
+ i += 1;
+ bytes[i] = '0';
+ i += 1;
+ bytes[i] = '7';
+ },
+ 'b' => {
+ bytes[i] = 'x';
+ i += 1;
+ bytes[i] = '0';
+ i += 1;
+ bytes[i] = '8';
+ },
+ 'f' => {
+ bytes[i] = 'x';
+ i += 1;
+ bytes[i] = '0';
+ i += 1;
+ bytes[i] = 'C';
+ },
+ 'v' => {
+ bytes[i] = 'x';
+ i += 1;
+ bytes[i] = '0';
+ i += 1;
+ bytes[i] = 'B';
+ },
+ '?' => {
+ i -= 1;
+ bytes[i] = '?';
+ },
+ 'u', 'U' => {
+ // TODO unicode escape sequences
+ return error.TokenizingFailed;
+ },
+ else => {
+ // unknown escape sequence
+ return error.TokenizingFailed;
+ },
+ }
+ i += 1;
+ if (state == .Escape)
+ state = .Start;
+ },
+ .Start => {
+ if (c == '\\') {
+ state = .Escape;
+ }
+ bytes[i] = c;
+ i += 1;
+ },
+ .Hex => {
+ switch (c) {
+ '0'...'9' => {
+ num = std.math.mul(u8, num, 16) catch return error.TokenizingFailed;
+ num += c - '0';
+ },
+ 'a'...'f' => {
+ num = std.math.mul(u8, num, 16) catch return error.TokenizingFailed;
+ num += c - 'a' + 10;
+ },
+ 'A'...'F' => {
+ num = std.math.mul(u8, num, 16) catch return error.TokenizingFailed;
+ num += c - 'A' + 10;
+ },
+ else => {
+ i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2});
+ num = 0;
+ if (c == '\\')
+ state = .Escape
+ else
+ state = .Start;
+ bytes[i] = c;
+ i += 1;
+ },
+ }
+ },
+ .Octal => {
+ switch (c) {
+ '0'...'7' => {
+ count += 1;
+ num = std.math.mul(u8, num, 8) catch return error.TokenizingFailed;
+ num += c - '0';
+ if (count < 3)
+ continue;
+ },
+ else => {},
+ }
+ i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2});
+ state = .Start;
+ count = 0;
+ num = 0;
+ },
}
}
+ if (state == .Hex or state == .Octal)
+ i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2});
return CToken{
.id = tok.id,
.bytes = bytes[0..i],
@@ -164,6 +229,8 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
NumLitIntSuffixL,
NumLitIntSuffixLL,
NumLitIntSuffixUL,
+ Minus,
+ Done,
} = .Start;
var result = CToken{
@@ -178,9 +245,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
const c = chars[i.*];
if (c == 0) {
switch (state) {
- .Start => {
- return result;
- },
.Identifier,
.Decimal,
.Hex,
@@ -193,6 +257,9 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
result.bytes = chars[begin_index..i.*];
return result;
},
+ .Start,
+ .Minus,
+ .Done,
.NumLitIntSuffixU,
.NumLitIntSuffixL,
.NumLitIntSuffixUL,
@@ -212,7 +279,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
=> return error.TokenizingFailed,
}
}
- i.* += 1;
switch (state) {
.Start => {
switch (c) {
@@ -220,12 +286,12 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
'\'' => {
state = .CharLit;
result.id = .CharLit;
- begin_index = i.* - 1;
+ begin_index = i.*;
},
'\"' => {
state = .String;
result.id = .StrLit;
- begin_index = i.* - 1;
+ begin_index = i.*;
},
'/' => {
state = .OpenComment;
@@ -239,21 +305,21 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
'a'...'z', 'A'...'Z', '_' => {
state = .Identifier;
result.id = .Identifier;
- begin_index = i.* - 1;
+ begin_index = i.*;
},
'1'...'9' => {
state = .Decimal;
result.id = .NumLitInt;
- begin_index = i.* - 1;
+ begin_index = i.*;
},
'0' => {
state = .GotZero;
result.id = .NumLitInt;
- begin_index = i.* - 1;
+ begin_index = i.*;
},
'.' => {
result.id = .Dot;
- return result;
+ state = .Done;
},
'<' => {
result.id = .Lt;
@@ -261,40 +327,64 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
},
'(' => {
result.id = .LParen;
- return result;
+ state = .Done;
},
')' => {
result.id = .RParen;
- return result;
+ state = .Done;
},
'*' => {
result.id = .Asterisk;
- return result;
+ state = .Done;
},
'-' => {
+ state = .Minus;
result.id = .Minus;
- return result;
},
'!' => {
result.id = .Bang;
- return result;
+ state = .Done;
},
'~' => {
result.id = .Tilde;
- return result;
+ state = .Done;
},
',' => {
result.id = .Comma;
- return result;
+ state = .Done;
+ },
+ '[' => {
+ result.id = .LBrace;
+ state = .Done;
+ },
+ ']' => {
+ result.id = .RBrace;
+ state = .Done;
+ },
+ '|' => {
+ result.id = .Pipe;
+ state = .Done;
},
else => return error.TokenizingFailed,
}
},
+ .Done => return result,
+ .Minus => {
+ switch (c) {
+ '>' => {
+ result.id = .Arrow;
+ state = .Done;
+ },
+ else => {
+ return result;
+ },
+ }
+ },
.GotLt => {
switch (c) {
'<' => {
result.id = .Shl;
- return result;
+ state = .Done;
},
else => {
return result;
@@ -310,19 +400,16 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
'f',
'F',
=> {
- i.* -= 1;
result.num_lit_suffix = .F;
result.bytes = chars[begin_index..i.*];
- return result;
+ state = .Done;
},
'l', 'L' => {
- i.* -= 1;
result.num_lit_suffix = .L;
result.bytes = chars[begin_index..i.*];
- return result;
+ state = .Done;
},
else => {
- i.* -= 1;
result.bytes = chars[begin_index..i.*];
return result;
},
@@ -352,16 +439,15 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
'0'...'9' => {},
'f', 'F' => {
result.num_lit_suffix = .F;
- result.bytes = chars[begin_index .. i.* - 1];
- return result;
+ result.bytes = chars[begin_index..i.*];
+ state = .Done;
},
'l', 'L' => {
result.num_lit_suffix = .L;
- result.bytes = chars[begin_index .. i.* - 1];
- return result;
+ result.bytes = chars[begin_index..i.*];
+ state = .Done;
},
else => {
- i.* -= 1;
result.bytes = chars[begin_index..i.*];
return result;
},
@@ -374,19 +460,18 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
'u', 'U' => {
state = .NumLitIntSuffixU;
result.num_lit_suffix = .U;
- result.bytes = chars[begin_index .. i.* - 1];
+ result.bytes = chars[begin_index..i.*];
},
'l', 'L' => {
state = .NumLitIntSuffixL;
result.num_lit_suffix = .L;
- result.bytes = chars[begin_index .. i.* - 1];
+ result.bytes = chars[begin_index..i.*];
},
'.' => {
result.id = .NumLitFloat;
state = .Float;
},
else => {
- i.* -= 1;
result.bytes = chars[begin_index..i.*];
return result;
},
@@ -407,12 +492,12 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
'u', 'U' => {
state = .NumLitIntSuffixU;
result.num_lit_suffix = .U;
- result.bytes = chars[begin_index .. i.* - 1];
+ result.bytes = chars[begin_index..i.*];
},
'l', 'L' => {
state = .NumLitIntSuffixL;
result.num_lit_suffix = .L;
- result.bytes = chars[begin_index .. i.* - 1];
+ result.bytes = chars[begin_index..i.*];
},
else => {
i.* -= 1;
@@ -425,7 +510,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
'0'...'7' => {},
'8', '9' => return error.TokenizingFailed,
else => {
- i.* -= 1;
result.bytes = chars[begin_index..i.*];
return result;
},
@@ -438,16 +522,15 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
// marks the number literal as unsigned
state = .NumLitIntSuffixU;
result.num_lit_suffix = .U;
- result.bytes = chars[begin_index .. i.* - 1];
+ result.bytes = chars[begin_index..i.*];
},
'l', 'L' => {
// marks the number literal as long
state = .NumLitIntSuffixL;
result.num_lit_suffix = .L;
- result.bytes = chars[begin_index .. i.* - 1];
+ result.bytes = chars[begin_index..i.*];
},
else => {
- i.* -= 1;
result.bytes = chars[begin_index..i.*];
return result;
},
@@ -461,16 +544,15 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
// marks the number literal as unsigned
state = .NumLitIntSuffixU;
result.num_lit_suffix = .U;
- result.bytes = chars[begin_index .. i.* - 1];
+ result.bytes = chars[begin_index..i.*];
},
'l', 'L' => {
// marks the number literal as long
state = .NumLitIntSuffixL;
result.num_lit_suffix = .L;
- result.bytes = chars[begin_index .. i.* - 1];
+ result.bytes = chars[begin_index..i.*];
},
else => {
- i.* -= 1;
result.bytes = chars[begin_index..i.*];
return result;
},
@@ -483,7 +565,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
state = .NumLitIntSuffixUL;
},
else => {
- i.* -= 1;
return result;
},
}
@@ -496,10 +577,9 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
},
'u', 'U' => {
result.num_lit_suffix = .LU;
- return result;
+ state = .Done;
},
else => {
- i.* -= 1;
return result;
},
}
@@ -508,10 +588,9 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
switch (c) {
'u', 'U' => {
result.num_lit_suffix = .LLU;
- return result;
+ state = .Done;
},
else => {
- i.* -= 1;
return result;
},
}
@@ -520,10 +599,9 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
switch (c) {
'l', 'L' => {
result.num_lit_suffix = .LLU;
- return result;
+ state = .Done;
},
else => {
- i.* -= 1;
return result;
},
}
@@ -532,17 +610,16 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
switch (c) {
'_', 'a'...'z', 'A'...'Z', '0'...'9' => {},
else => {
- i.* -= 1;
result.bytes = chars[begin_index..i.*];
return result;
},
}
},
- .String => { // TODO char escapes
+ .String => {
switch (c) {
'\"' => {
- result.bytes = chars[begin_index..i.*];
- return result;
+ result.bytes = chars[begin_index .. i.* + 1];
+ state = .Done;
},
else => {},
}
@@ -550,8 +627,8 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
.CharLit => {
switch (c) {
'\'' => {
- result.bytes = chars[begin_index..i.*];
- return result;
+ result.bytes = chars[begin_index .. i.* + 1];
+ state = .Done;
},
else => {},
}
@@ -566,7 +643,7 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
},
else => {
result.id = .Slash;
- return result;
+ state = .Done;
},
}
},
@@ -598,6 +675,7 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
}
},
}
+ i.* += 1;
}
unreachable;
}
@@ -645,7 +723,7 @@ test "tokenize macro" {
expect(it.next() == null);
tl.shrink(0);
- const src5 = "FOO 0l";
+ const src5 = "FOO 0ull";
try tokenizeCMacro(&tl, src5);
it = tl.iterator(0);
expect(it.next().?.id == .Identifier);
@@ -654,3 +732,25 @@ test "tokenize macro" {
expect(it.next() == null);
tl.shrink(0);
}
+
+test "escape sequences" {
+ var buf: [1024]u8 = undefined;
+ var alloc = std.heap.FixedBufferAllocator.init(buf[0..]);
+ const a = &alloc.allocator;
+ expect(std.mem.eql(u8, (try zigifyEscapeSequences(a, .{
+ .id = .StrLit,
+ .bytes = "\\x0077",
+ })).bytes, "\\x77"));
+ expect(std.mem.eql(u8, (try zigifyEscapeSequences(a, .{
+ .id = .StrLit,
+ .bytes = "\\24500",
+ })).bytes, "\\xa500"));
+ expect(std.mem.eql(u8, (try zigifyEscapeSequences(a, .{
+ .id = .StrLit,
+ .bytes = "\\x0077 abc",
+ })).bytes, "\\x77 abc"));
+ expect(std.mem.eql(u8, (try zigifyEscapeSequences(a, .{
+ .id = .StrLit,
+ .bytes = "\\045abc",
+ })).bytes, "\\x25abc"));
+}
diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig
@@ -76,6 +76,10 @@ pub const struct_ZigClangFunctionType = @OpaqueType();
pub const struct_ZigClangPredefinedExpr = @OpaqueType();
pub const struct_ZigClangInitListExpr = @OpaqueType();
pub const ZigClangPreprocessingRecord = @OpaqueType();
+pub const ZigClangFloatingLiteral = @OpaqueType();
+pub const ZigClangConstantExpr = @OpaqueType();
+pub const ZigClangCharacterLiteral = @OpaqueType();
+pub const ZigClangStmtExpr = @OpaqueType();
pub const ZigClangBO = extern enum {
PtrMemD,
@@ -710,6 +714,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,
};
@@ -730,6 +742,11 @@ pub const ZigClangPreprocessedEntity_EntityKind = extern enum {
InclusionDirectiveKind,
};
+pub const ZigClangExpr_ConstExprUsage = extern enum {
+ EvaluateForCodeGen,
+ EvaluateForMangling,
+};
+
pub extern fn ZigClangSourceManager_getSpellingLoc(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) struct_ZigClangSourceLocation;
pub extern fn ZigClangSourceManager_getFilename(self: *const struct_ZigClangSourceManager, SpellingLoc: struct_ZigClangSourceLocation) ?[*:0]const u8;
pub extern fn ZigClangSourceManager_getSpellingLineNumber(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint;
@@ -744,6 +761,8 @@ pub extern fn ZigClangEnumType_getDecl(record_ty: ?*const struct_ZigClangEnumTyp
pub extern fn ZigClangRecordDecl_getCanonicalDecl(record_decl: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangTagDecl;
pub extern fn ZigClangEnumDecl_getCanonicalDecl(self: ?*const struct_ZigClangEnumDecl) ?*const struct_ZigClangTagDecl;
pub extern fn ZigClangTypedefNameDecl_getCanonicalDecl(self: ?*const struct_ZigClangTypedefNameDecl) ?*const struct_ZigClangTypedefNameDecl;
+pub extern fn ZigClangFunctionDecl_getCanonicalDecl(self: ?*const struct_ZigClangFunctionDecl) ?*const struct_ZigClangFunctionDecl;
+pub extern fn ZigClangVarDecl_getCanonicalDecl(self: ?*const struct_ZigClangVarDecl) ?*const struct_ZigClangVarDecl;
pub extern fn ZigClangRecordDecl_getDefinition(self: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangRecordDecl;
pub extern fn ZigClangEnumDecl_getDefinition(self: ?*const struct_ZigClangEnumDecl) ?*const struct_ZigClangEnumDecl;
pub extern fn ZigClangRecordDecl_getLocation(self: ?*const struct_ZigClangRecordDecl) struct_ZigClangSourceLocation;
@@ -791,16 +810,16 @@ pub extern fn ZigClangInitListExpr_getInit(self: ?*const struct_ZigClangInitList
pub extern fn ZigClangInitListExpr_getArrayFiller(self: ?*const struct_ZigClangInitListExpr) *const ZigClangExpr;
pub extern fn ZigClangInitListExpr_getNumInits(self: ?*const struct_ZigClangInitListExpr) c_uint;
pub extern fn ZigClangAPValue_getKind(self: ?*const struct_ZigClangAPValue) ZigClangAPValueKind;
-pub extern fn ZigClangAPValue_getInt(self: ?*const struct_ZigClangAPValue) ?*const struct_ZigClangAPSInt;
+pub extern fn ZigClangAPValue_getInt(self: ?*const struct_ZigClangAPValue) *const struct_ZigClangAPSInt;
pub extern fn ZigClangAPValue_getArrayInitializedElts(self: ?*const struct_ZigClangAPValue) c_uint;
pub extern fn ZigClangAPValue_getArraySize(self: ?*const struct_ZigClangAPValue) c_uint;
pub extern fn ZigClangAPValue_getLValueBase(self: ?*const struct_ZigClangAPValue) struct_ZigClangAPValueLValueBase;
-pub extern fn ZigClangAPSInt_isSigned(self: ?*const struct_ZigClangAPSInt) bool;
-pub extern fn ZigClangAPSInt_isNegative(self: ?*const struct_ZigClangAPSInt) bool;
-pub extern fn ZigClangAPSInt_negate(self: ?*const struct_ZigClangAPSInt) ?*const struct_ZigClangAPSInt;
-pub extern fn ZigClangAPSInt_free(self: ?*const struct_ZigClangAPSInt) void;
-pub extern fn ZigClangAPSInt_getRawData(self: ?*const struct_ZigClangAPSInt) [*:0]const u64;
-pub extern fn ZigClangAPSInt_getNumWords(self: ?*const struct_ZigClangAPSInt) c_uint;
+pub extern fn ZigClangAPSInt_isSigned(self: *const struct_ZigClangAPSInt) bool;
+pub extern fn ZigClangAPSInt_isNegative(self: *const struct_ZigClangAPSInt) bool;
+pub extern fn ZigClangAPSInt_negate(self: *const struct_ZigClangAPSInt) *const struct_ZigClangAPSInt;
+pub extern fn ZigClangAPSInt_free(self: *const struct_ZigClangAPSInt) void;
+pub extern fn ZigClangAPSInt_getRawData(self: *const struct_ZigClangAPSInt) [*:0]const u64;
+pub extern fn ZigClangAPSInt_getNumWords(self: *const struct_ZigClangAPSInt) c_uint;
pub extern fn ZigClangAPInt_getLimitedValue(self: *const struct_ZigClangAPInt, limit: u64) u64;
pub extern fn ZigClangAPValueLValueBase_dyn_cast_Expr(self: struct_ZigClangAPValueLValueBase) ?*const struct_ZigClangExpr;
@@ -822,6 +841,7 @@ pub extern fn ZigClangFunctionType_getReturnType(self: *const ZigClangFunctionTy
pub extern fn ZigClangFunctionProtoType_isVariadic(self: *const struct_ZigClangFunctionProtoType) bool;
pub extern fn ZigClangFunctionProtoType_getNumParams(self: *const struct_ZigClangFunctionProtoType) c_uint;
pub extern fn ZigClangFunctionProtoType_getParamType(self: *const struct_ZigClangFunctionProtoType, i: c_uint) ZigClangQualType;
+pub extern fn ZigClangFunctionProtoType_getReturnType(self: *const ZigClangFunctionProtoType) ZigClangQualType;
pub const ZigClangSourceLocation = struct_ZigClangSourceLocation;
pub const ZigClangQualType = struct_ZigClangQualType;
@@ -976,6 +996,7 @@ pub extern fn ZigClangIncompleteArrayType_getElementType(*const ZigClangIncomple
pub extern fn ZigClangConstantArrayType_getElementType(self: *const struct_ZigClangConstantArrayType) ZigClangQualType;
pub extern fn ZigClangConstantArrayType_getSize(self: *const struct_ZigClangConstantArrayType) *const struct_ZigClangAPInt;
pub extern fn ZigClangDeclRefExpr_getDecl(*const ZigClangDeclRefExpr) *const ZigClangValueDecl;
+pub extern fn ZigClangDeclRefExpr_getFoundDecl(*const ZigClangDeclRefExpr) *const ZigClangNamedDecl;
pub extern fn ZigClangParenType_getInnerType(*const ZigClangParenType) ZigClangQualType;
@@ -1036,3 +1057,74 @@ pub extern fn ZigClangPreprocessedEntity_getKind(*const ZigClangPreprocessedEnti
pub extern fn ZigClangMacroDefinitionRecord_getName_getNameStart(*const ZigClangMacroDefinitionRecord) [*:0]const u8;
pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getBegin(*const ZigClangMacroDefinitionRecord) ZigClangSourceLocation;
pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getEnd(*const ZigClangMacroDefinitionRecord) ZigClangSourceLocation;
+
+pub extern fn ZigClangIfStmt_getThen(*const ZigClangIfStmt) *const ZigClangStmt;
+pub extern fn ZigClangIfStmt_getElse(*const ZigClangIfStmt) ?*const ZigClangStmt;
+pub extern fn ZigClangIfStmt_getCond(*const ZigClangIfStmt) *const ZigClangStmt;
+
+pub extern fn ZigClangWhileStmt_getCond(*const ZigClangWhileStmt) *const ZigClangExpr;
+pub extern fn ZigClangWhileStmt_getBody(*const ZigClangWhileStmt) *const ZigClangStmt;
+
+pub extern fn ZigClangDoStmt_getCond(*const ZigClangDoStmt) *const ZigClangExpr;
+pub extern fn ZigClangDoStmt_getBody(*const ZigClangDoStmt) *const ZigClangStmt;
+
+pub extern fn ZigClangForStmt_getInit(*const ZigClangForStmt) ?*const ZigClangStmt;
+pub extern fn ZigClangForStmt_getCond(*const ZigClangForStmt) ?*const ZigClangExpr;
+pub extern fn ZigClangForStmt_getInc(*const ZigClangForStmt) ?*const ZigClangExpr;
+pub extern fn ZigClangForStmt_getBody(*const ZigClangForStmt) *const ZigClangStmt;
+
+pub extern fn ZigClangAPFloat_toString(self: *const ZigClangAPFloat, precision: c_uint, maxPadding: c_uint, truncateZero: bool) [*:0]const u8;
+pub extern fn ZigClangAPFloat_getValueAsApproximateDouble(*const ZigClangFloatingLiteral) f64;
+
+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;
+
+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;
+
+pub extern fn ZigClangStmtExpr_getSubStmt(*const ZigClangStmtExpr) *const ZigClangCompoundStmt;
+
+pub extern fn ZigClangMemberExpr_getBase(*const ZigClangMemberExpr) *const ZigClangExpr;
+pub extern fn ZigClangMemberExpr_isArrow(*const ZigClangMemberExpr) bool;
+pub extern fn ZigClangMemberExpr_getMemberDecl(*const ZigClangMemberExpr) *const ZigClangValueDecl;
+
+pub extern fn ZigClangArraySubscriptExpr_getBase(*const ZigClangArraySubscriptExpr) *const ZigClangExpr;
+pub extern fn ZigClangArraySubscriptExpr_getIdx(*const ZigClangArraySubscriptExpr) *const ZigClangExpr;
+
+pub extern fn ZigClangCallExpr_getCallee(*const ZigClangCallExpr) *const ZigClangExpr;
+pub extern fn ZigClangCallExpr_getNumArgs(*const ZigClangCallExpr) c_uint;
+pub extern fn ZigClangCallExpr_getArgs(*const ZigClangCallExpr) [*]const *const ZigClangExpr;
+
+pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangQualType;
+pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangSourceLocation;
+
+pub extern fn ZigClangUnaryOperator_getOpcode(*const ZigClangUnaryOperator) ZigClangUO;
+pub extern fn ZigClangUnaryOperator_getType(*const ZigClangUnaryOperator) ZigClangQualType;
+pub extern fn ZigClangUnaryOperator_getSubExpr(*const ZigClangUnaryOperator) *const ZigClangExpr;
+pub extern fn ZigClangUnaryOperator_getBeginLoc(*const ZigClangUnaryOperator) ZigClangSourceLocation;
+
+pub extern fn ZigClangCompoundAssignOperator_getType(*const ZigClangCompoundAssignOperator) ZigClangQualType;
+pub extern fn ZigClangCompoundAssignOperator_getComputationLHSType(*const ZigClangCompoundAssignOperator) ZigClangQualType;
+pub extern fn ZigClangCompoundAssignOperator_getComputationResultType(*const ZigClangCompoundAssignOperator) ZigClangQualType;
+pub extern fn ZigClangCompoundAssignOperator_getBeginLoc(*const ZigClangCompoundAssignOperator) ZigClangSourceLocation;
+pub extern fn ZigClangCompoundAssignOperator_getOpcode(*const ZigClangCompoundAssignOperator) ZigClangBO;
+pub extern fn ZigClangCompoundAssignOperator_getLHS(*const ZigClangCompoundAssignOperator) *const ZigClangExpr;
+pub extern fn ZigClangCompoundAssignOperator_getRHS(*const ZigClangCompoundAssignOperator) *const ZigClangExpr;
diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig
@@ -8,6 +8,7 @@ const Token = std.zig.Token;
usingnamespace @import("clang.zig");
const ctok = @import("c_tokenizer.zig");
const CToken = ctok.CToken;
+const mem = std.mem;
const CallingConvention = std.builtin.TypeInfo.CallingConvention;
@@ -47,35 +48,34 @@ const Scope = struct {
Switch,
Block,
Root,
- While,
- FnDef,
- Ref,
+ Condition,
+ Loop,
};
const Switch = struct {
base: Scope,
- };
-
- /// used when getting a member `a.b`
- const Ref = struct {
- base: Scope,
+ pending_block: *ast.Node.Block,
+ cases: *ast.Node.Switch.CaseList,
+ has_default: bool = false,
};
const Block = struct {
base: Scope,
block_node: *ast.Node.Block,
variables: AliasList,
+ label: ?[]const u8,
- /// Don't forget to set rbrace token later
- fn init(c: *Context, parent: *Scope, block_node: *ast.Node.Block) !*Block {
+ /// Don't forget to set rbrace token and block_node later
+ fn init(c: *Context, parent: *Scope, label: ?[]const u8) !*Block {
const block = try c.a().create(Block);
block.* = .{
.base = .{
.id = .Block,
.parent = parent,
},
- .block_node = block_node,
+ .block_node = undefined,
.variables = AliasList.init(c.a()),
+ .label = label,
};
return block;
}
@@ -83,7 +83,7 @@ const Scope = struct {
fn getAlias(scope: *Block, name: []const u8) ?[]const u8 {
var it = scope.variables.iterator(0);
while (it.next()) |p| {
- if (std.mem.eql(u8, p.name, name))
+ if (mem.eql(u8, p.name, name))
return p.alias;
}
return scope.base.parent.?.getAlias(name);
@@ -92,7 +92,7 @@ const Scope = struct {
fn contains(scope: *Block, name: []const u8) bool {
var it = scope.variables.iterator(0);
while (it.next()) |p| {
- if (std.mem.eql(u8, p.name, name))
+ if (mem.eql(u8, p.name, name))
return true;
}
return scope.base.parent.?.contains(name);
@@ -120,52 +120,23 @@ const Scope = struct {
}
};
- const While = struct {
- base: Scope,
- };
-
- const FnDef = struct {
- base: Scope,
- params: AliasList,
-
- fn init(c: *Context) FnDef {
- return .{
- .base = .{
- .id = .FnDef,
- .parent = &c.global_scope.base,
+ fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block {
+ var scope = inner;
+ while (true) {
+ switch (scope.id) {
+ .Root => unreachable,
+ .Block => return @fieldParentPtr(Block, "base", scope),
+ .Condition => {
+ // comma operator used
+ return try Block.init(c, scope, "blk");
},
- .params = AliasList.init(c.a()),
- };
- }
-
- fn getAlias(scope: *FnDef, name: []const u8) ?[]const u8 {
- var it = scope.params.iterator(0);
- while (it.next()) |p| {
- if (std.mem.eql(u8, p.name, name))
- return p.alias;
+ else => scope = scope.parent.?,
}
- return scope.base.parent.?.getAlias(name);
- }
-
- fn contains(scope: *FnDef, name: []const u8) bool {
- var it = scope.params.iterator(0);
- while (it.next()) |p| {
- if (std.mem.eql(u8, p.name, name))
- return true;
- }
- return scope.base.parent.?.contains(name);
- }
- };
-
- fn findBlockScope(inner: *Scope) *Scope.Block {
- var scope = inner;
- while (true) : (scope = scope.parent orelse unreachable) {
- if (scope.id == .Block) return @fieldParentPtr(Scope.Block, "base", scope);
}
}
fn createAlias(scope: *Scope, c: *Context, name: []const u8) !?[]const u8 {
- if (scope.contains(name)) {
+ if (isZigPrimitiveType(name) or scope.contains(name)) {
return try std.fmt.allocPrint(c.a(), "{}_{}", .{ name, c.getMangle() });
}
return null;
@@ -174,22 +145,41 @@ const Scope = struct {
fn getAlias(scope: *Scope, name: []const u8) ?[]const u8 {
return switch (scope.id) {
.Root => null,
- .Ref => null,
- .FnDef => @fieldParentPtr(FnDef, "base", scope).getAlias(name),
.Block => @fieldParentPtr(Block, "base", scope).getAlias(name),
- else => @panic("TODO Scope.getAlias"),
+ .Switch, .Loop, .Condition => scope.parent.?.getAlias(name),
};
}
fn contains(scope: *Scope, name: []const u8) bool {
return switch (scope.id) {
- .Ref => false,
.Root => @fieldParentPtr(Root, "base", scope).contains(name),
- .FnDef => @fieldParentPtr(FnDef, "base", scope).contains(name),
.Block => @fieldParentPtr(Block, "base", scope).contains(name),
- else => @panic("TODO Scope.contains"),
+ .Switch, .Loop, .Condition => scope.parent.?.contains(name),
};
}
+
+ fn getBreakableScope(inner: *Scope) *Scope {
+ var scope = inner;
+ while (true) {
+ switch (scope.id) {
+ .Root => unreachable,
+ .Switch => return scope,
+ .Loop => return scope,
+ else => scope = scope.parent.?,
+ }
+ }
+ }
+
+ fn getSwitch(inner: *Scope) *Scope.Switch {
+ var scope = inner;
+ while (true) {
+ switch (scope.id) {
+ .Root => unreachable,
+ .Switch => return @fieldParentPtr(Switch, "base", scope),
+ else => scope = scope.parent.?,
+ }
+ }
+ }
};
const Context = struct {
@@ -200,7 +190,6 @@ const Context = struct {
decl_table: DeclTable,
alias_list: AliasList,
global_scope: *Scope.Root,
- ptr_params: std.BufSet,
clang_context: *ZigClangASTContext,
mangle_count: u64 = 0,
@@ -209,13 +198,13 @@ const Context = struct {
return c.mangle_count;
}
- fn a(c: *Context) *std.mem.Allocator {
+ fn a(c: *Context) *mem.Allocator {
return &c.tree.arena_allocator.allocator;
}
/// Convert a null-terminated C string to a slice allocated in the arena
fn str(c: *Context, s: [*:0]const u8) ![]u8 {
- return std.mem.dupe(c.a(), u8, std.mem.toSliceConst(u8, s));
+ return mem.dupe(c.a(), u8, mem.toSliceConst(u8, s));
}
/// Convert a clang source location to a file:line:column string
@@ -231,7 +220,7 @@ const Context = struct {
};
pub fn translate(
- backing_allocator: *std.mem.Allocator,
+ backing_allocator: *mem.Allocator,
args_begin: [*]?[*]const u8,
args_end: [*]?[*]const u8,
errors: *[]ClangErrMsg,
@@ -286,7 +275,6 @@ pub fn translate(
.decl_table = DeclTable.init(arena),
.alias_list = AliasList.init(arena),
.global_scope = try arena.create(Scope.Root),
- .ptr_params = std.BufSet.init(arena),
.clang_context = ZigClangASTUnit_getASTContext(ast_unit).?,
};
context.global_scope.* = Scope.Root.init(&context);
@@ -333,13 +321,13 @@ fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void {
return visitFnDecl(c, @ptrCast(*const ZigClangFunctionDecl, decl));
},
.Typedef => {
- return resolveTypeDef(c, @ptrCast(*const ZigClangTypedefNameDecl, decl));
+ _ = try transTypeDef(c, @ptrCast(*const ZigClangTypedefNameDecl, decl));
},
.Enum => {
_ = try transEnumDecl(c, @ptrCast(*const ZigClangEnumDecl, decl));
},
.Record => {
- return resolveRecordDecl(c, @ptrCast(*const ZigClangRecordDecl, decl));
+ _ = try transRecordDecl(c, @ptrCast(*const ZigClangRecordDecl, decl));
},
.Var => {
return visitVarDecl(c, @ptrCast(*const ZigClangVarDecl, decl));
@@ -352,22 +340,17 @@ fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void {
}
fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
- if (c.decl_table.contains(@ptrToInt(fn_decl))) return; // Avoid processing this decl twice
- const rp = makeRestorePoint(c);
const fn_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, fn_decl)));
- _ = try c.decl_table.put(@ptrToInt(fn_decl), fn_name);
+ if (c.global_scope.sym_table.contains(fn_name))
+ return; // Avoid processing this decl twice
+ const rp = makeRestorePoint(c);
const fn_decl_loc = ZigClangFunctionDecl_getLocation(fn_decl);
- const fn_qt = ZigClangFunctionDecl_getType(fn_decl);
- const fn_type = ZigClangQualType_getTypePtr(fn_qt);
- var fndef_scope = Scope.FnDef.init(c);
- var scope = &fndef_scope.base;
const has_body = ZigClangFunctionDecl_hasBody(fn_decl);
const storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl);
const decl_ctx = FnDeclContext{
.fn_name = fn_name,
.has_body = has_body,
.storage_class = storage_class,
- .scope = &scope,
.is_export = switch (storage_class) {
.None => has_body,
.Extern, .Static => false,
@@ -377,6 +360,15 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
else => unreachable,
},
};
+
+ var fn_qt = ZigClangFunctionDecl_getType(fn_decl);
+ var fn_type = ZigClangQualType_getTypePtr(fn_qt);
+ if (ZigClangType_getTypeClass(fn_type) == .Attributed) {
+ const attr_type = @ptrCast(*const ZigClangAttributedType, fn_type);
+ fn_qt = ZigClangAttributedType_getEquivalentType(attr_type);
+ fn_type = ZigClangQualType_getTypePtr(fn_qt);
+ }
+
const proto_node = switch (ZigClangType_getTypeClass(fn_type)) {
.FunctionProto => blk: {
const fn_proto_type = @ptrCast(*const ZigClangFunctionProtoType, fn_type);
@@ -396,7 +388,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) {
@@ -406,20 +398,47 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
// actual function definition with body
const body_stmt = ZigClangFunctionDecl_getBody(fn_decl);
- const body_node = transStmt(rp, scope, body_stmt, .unused, .r_value) catch |err| switch (err) {
+ const block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, null);
+ var scope = &block_scope.base;
+ const block_node = try transCreateNodeBlock(rp.c, null);
+ block_scope.block_node = block_node;
+
+ var it = proto_node.params.iterator(0);
+ while (it.next()) |p| {
+ const param = @fieldParentPtr(ast.Node.ParamDecl, "base", p.*);
+ const param_name = tokenSlice(c, param.name_token orelse
+ return failDecl(c, fn_decl_loc, fn_name, "function {} parameter has no name", .{fn_name}));
+
+ const checked_param_name = if (try scope.createAlias(rp.c, param_name)) |a| blk: {
+ try block_scope.variables.push(.{ .name = param_name, .alias = a });
+ break :blk a;
+ } else param_name;
+ const arg_name = try std.fmt.allocPrint(c.a(), "_arg_{}", .{checked_param_name});
+
+ const node = try transCreateNodeVarDecl(c, false, false, checked_param_name);
+ node.eq_token = try appendToken(c, .Equal, "=");
+ node.init_node = try transCreateNodeIdentifier(c, arg_name);
+ node.semicolon_token = try appendToken(c, .Semicolon, ";");
+ try block_node.statements.push(&node.base);
+ param.name_token = try appendIdentifier(c, arg_name);
+ _ = try appendToken(c, .Colon, ":");
+ }
+
+ transCompoundStmtInline(rp, &block_scope.base, @ptrCast(*const ZigClangCompoundStmt, body_stmt), block_node) catch |err| switch (err) {
error.OutOfMemory => |e| return e,
error.UnsupportedTranslation,
error.UnsupportedType,
=> return failDecl(c, fn_decl_loc, fn_name, "unable to translate function", .{}),
};
- assert(body_node.id == .Block);
- proto_node.body_node = body_node;
-
+ block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
+ proto_node.body_node = &block_node.base;
return addTopLevelDecl(c, fn_name, &proto_node.base);
}
fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void {
- if (c.decl_table.contains(@ptrToInt(var_decl))) return; // Avoid processing this decl twice
+ const var_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, var_decl)));
+ if (c.global_scope.sym_table.contains(var_name))
+ return; // Avoid processing this decl twice
const rp = makeRestorePoint(c);
const visib_tok = try appendToken(c, .Keyword_pub, "pub");
@@ -429,8 +448,10 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void {
try appendToken(c, .Keyword_threadlocal, "threadlocal");
const scope = &c.global_scope.base;
- const var_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, var_decl)));
- _ = try c.decl_table.put(@ptrToInt(var_decl), var_name);
+
+ // TODO https://github.com/ziglang/zig/issues/3756
+ // TODO https://github.com/ziglang/zig/issues/1802
+ const checked_name = if (isZigPrimitiveType(var_name)) try std.fmt.allocPrint(c.a(), "_{}", .{var_name}) else var_name;
const var_decl_loc = ZigClangVarDecl_getLocation(var_decl);
const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl);
@@ -449,12 +470,12 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void {
else
try appendToken(c, .Keyword_var, "var");
- const name_tok = try appendIdentifier(c, var_name);
+ const name_tok = try appendIdentifier(c, checked_name);
_ = try appendToken(c, .Colon, ":");
const type_node = transQualType(rp, qual_type, var_decl_loc) catch |err| switch (err) {
error.UnsupportedType => {
- return failDecl(c, var_decl_loc, var_name, "unable to resolve variable type", .{});
+ return failDecl(c, var_decl_loc, checked_name, "unable to resolve variable type", .{});
},
error.OutOfMemory => |e| return e,
};
@@ -469,14 +490,14 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void {
error.UnsupportedTranslation,
error.UnsupportedType,
=> {
- return failDecl(c, var_decl_loc, var_name, "unable to translate initializer", .{});
+ return failDecl(c, var_decl_loc, checked_name, "unable to translate initializer", .{});
},
error.OutOfMemory => |e| return e,
}
else
try transCreateNodeUndefinedLiteral(c);
} else if (storage_class != .Extern) {
- return failDecl(c, var_decl_loc, var_name, "non-extern variable has no initializer", .{});
+ return failDecl(c, var_decl_loc, checked_name, "non-extern variable has no initializer", .{});
}
const node = try c.a().create(ast.Node.VarDecl);
@@ -496,269 +517,538 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void {
.init_node = init_node,
.semicolon_token = try appendToken(c, .Semicolon, ";"),
};
- return addTopLevelDecl(c, var_name, &node.base);
+ return addTopLevelDecl(c, checked_name, &node.base);
}
-fn resolveTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl) Error!void {
- if (c.decl_table.contains(
- @ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)),
- )) return; // Avoid processing this decl twice
+fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, builtin_name: []const u8) !*ast.Node {
+ _ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), builtin_name);
+ return transCreateNodeIdentifier(c, builtin_name);
+}
+
+fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl) Error!?*ast.Node {
+ if (c.decl_table.get(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)))) |kv|
+ return try transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice
const rp = makeRestorePoint(c);
- const visib_tok = try appendToken(c, .Keyword_pub, "pub");
- const const_tok = try appendToken(c, .Keyword_const, "const");
const typedef_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_decl)));
+
+ if (mem.eql(u8, typedef_name, "uint8_t"))
+ return transTypeDefAsBuiltin(c, typedef_decl, "u8")
+ else if (mem.eql(u8, typedef_name, "int8_t"))
+ return transTypeDefAsBuiltin(c, typedef_decl, "i8")
+ else if (mem.eql(u8, typedef_name, "uint16_t"))
+ return transTypeDefAsBuiltin(c, typedef_decl, "u16")
+ else if (mem.eql(u8, typedef_name, "int16_t"))
+ return transTypeDefAsBuiltin(c, typedef_decl, "i16")
+ else if (mem.eql(u8, typedef_name, "uint32_t"))
+ return transTypeDefAsBuiltin(c, typedef_decl, "u32")
+ else if (mem.eql(u8, typedef_name, "int32_t"))
+ return transTypeDefAsBuiltin(c, typedef_decl, "i32")
+ else if (mem.eql(u8, typedef_name, "uint64_t"))
+ return transTypeDefAsBuiltin(c, typedef_decl, "u64")
+ else if (mem.eql(u8, typedef_name, "int64_t"))
+ return transTypeDefAsBuiltin(c, typedef_decl, "i64")
+ else if (mem.eql(u8, typedef_name, "intptr_t"))
+ return transTypeDefAsBuiltin(c, typedef_decl, "isize")
+ else if (mem.eql(u8, typedef_name, "uintptr_t"))
+ return transTypeDefAsBuiltin(c, typedef_decl, "usize")
+ else if (mem.eql(u8, typedef_name, "ssize_t"))
+ return transTypeDefAsBuiltin(c, typedef_decl, "isize")
+ else if (mem.eql(u8, typedef_name, "size_t"))
+ return transTypeDefAsBuiltin(c, typedef_decl, "usize");
+
_ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), typedef_name);
- const name_tok = try appendIdentifier(c, typedef_name);
- const eq_tok = try appendToken(c, .Equal, "=");
+ const visib_tok = try appendToken(c, .Keyword_pub, "pub");
+ const const_tok = try appendToken(c, .Keyword_const, "const");
+ const node = try transCreateNodeVarDecl(c, true, true, typedef_name);
+ node.eq_token = try appendToken(c, .Equal, "=");
const child_qt = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);
const typedef_loc = ZigClangTypedefNameDecl_getLocation(typedef_decl);
- const type_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) {
+ node.init_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) {
error.UnsupportedType => {
- return failDecl(c, typedef_loc, typedef_name, "unable to resolve typedef child type", .{});
+ try failDecl(c, typedef_loc, typedef_name, "unable to resolve typedef child type", .{});
+ return null;
},
error.OutOfMemory => |e| return e,
};
-
- const node = try c.a().create(ast.Node.VarDecl);
- node.* = ast.Node.VarDecl{
- .doc_comments = null,
- .visib_token = visib_tok,
- .thread_local_token = null,
- .name_token = name_tok,
- .eq_token = eq_tok,
- .mut_token = const_tok,
- .comptime_token = null,
- .extern_export_token = null,
- .lib_name = null,
- .type_node = null,
- .align_node = null,
- .section_node = null,
- .init_node = type_node,
- .semicolon_token = try appendToken(c, .Semicolon, ";"),
- };
+ node.semicolon_token = try appendToken(c, .Semicolon, ";");
try addTopLevelDecl(c, typedef_name, &node.base);
+ return transCreateNodeIdentifier(c, typedef_name);
}
-fn resolveRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!void {
- if (c.decl_table.contains(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) return; // Avoid processing this decl twice
- const rp = makeRestorePoint(c);
-
- const bare_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, record_decl)));
-
- const container_kind_name = if (ZigClangRecordDecl_isUnion(record_decl))
- "union"
- else if (ZigClangRecordDecl_isStruct(record_decl))
- "struct"
- else
- return emitWarning(c, ZigClangRecordDecl_getLocation(record_decl), "record {} is not a struct or union", .{bare_name});
+fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*ast.Node {
+ if (c.decl_table.get(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) |kv|
+ return try transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice
+ const record_loc = ZigClangRecordDecl_getLocation(record_decl);
- if (ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl) or bare_name.len == 0)
- return;
+ var bare_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, record_decl)));
+ var is_unnamed = false;
+ if (ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl) or bare_name.len == 0) {
+ bare_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()});
+ is_unnamed = true;
+ }
- const visib_tok = try appendToken(c, .Keyword_pub, "pub");
- const const_tok = try appendToken(c, .Keyword_const, "const");
+ var container_kind_name: []const u8 = undefined;
+ var container_kind: std.zig.Token.Id = undefined;
+ if (ZigClangRecordDecl_isUnion(record_decl)) {
+ container_kind_name = "union";
+ container_kind = .Keyword_union;
+ } else if (ZigClangRecordDecl_isStruct(record_decl)) {
+ container_kind_name = "struct";
+ container_kind = .Keyword_struct;
+ } else {
+ try emitWarning(c, record_loc, "record {} is not a struct or union", .{bare_name});
+ return null;
+ }
const name = try std.fmt.allocPrint(c.a(), "{}_{}", .{ container_kind_name, bare_name });
_ = try c.decl_table.put(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)), name);
- const name_tok = try appendIdentifier(c, name);
- const eq_tok = try appendToken(c, .Equal, "=");
- const init_node = transRecordDecl(c, record_decl) catch |err| switch (err) {
- error.UnsupportedType => {
- return failDecl(c, ZigClangRecordDecl_getLocation(record_decl), name, "unable to resolve record type", .{});
- },
- error.OutOfMemory => |e| return e,
- };
- const semicolon_token = try appendToken(c, .Semicolon, ";");
+ const node = try transCreateNodeVarDecl(c, !is_unnamed, true, name);
- const node = try c.a().create(ast.Node.VarDecl);
- node.* = ast.Node.VarDecl{
- .doc_comments = null,
- .visib_token = visib_tok,
- .thread_local_token = null,
- .name_token = name_tok,
- .eq_token = eq_tok,
- .mut_token = const_tok,
- .comptime_token = null,
- .extern_export_token = null,
- .lib_name = null,
- .type_node = null,
- .align_node = null,
- .section_node = null,
- .init_node = init_node,
- .semicolon_token = semicolon_token,
+ node.eq_token = try appendToken(c, .Equal, "=");
+
+ var semicolon: ast.TokenIndex = undefined;
+ node.init_node = blk: {
+ const rp = makeRestorePoint(c);
+ const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse {
+ const opaque = try transCreateNodeOpaqueType(c);
+ semicolon = try appendToken(c, .Semicolon, ";");
+ break :blk opaque;
+ };
+
+ const extern_tok = try appendToken(c, .Keyword_extern, "extern");
+ const container_tok = try appendToken(c, container_kind, container_kind_name);
+ const lbrace_token = try appendToken(c, .LBrace, "{");
+
+ const container_node = try c.a().create(ast.Node.ContainerDecl);
+ container_node.* = .{
+ .layout_token = extern_tok,
+ .kind_token = container_tok,
+ .init_arg_expr = .None,
+ .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(c.a()),
+ .lbrace_token = lbrace_token,
+ .rbrace_token = undefined,
+ };
+
+ var it = ZigClangRecordDecl_field_begin(record_def);
+ const end_it = ZigClangRecordDecl_field_end(record_def);
+ while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) {
+ const field_decl = ZigClangRecordDecl_field_iterator_deref(it);
+ const field_loc = ZigClangFieldDecl_getLocation(field_decl);
+
+ if (ZigClangFieldDecl_isBitField(field_decl)) {
+ const opaque = try transCreateNodeOpaqueType(c);
+ semicolon = try appendToken(c, .Semicolon, ";");
+ try emitWarning(c, field_loc, "{} demoted to opaque type - has bitfield", .{container_kind_name});
+ break :blk opaque;
+ }
+ const raw_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, field_decl)));
+ if (raw_name.len < 1) continue; // fix weird windows bug?
+ const field_name = try appendIdentifier(c, raw_name);
+ _ = try appendToken(c, .Colon, ":");
+ const field_type = transQualType(rp, ZigClangFieldDecl_getType(field_decl), field_loc) catch |err| switch (err) {
+ error.UnsupportedType => {
+ try failDecl(c, record_loc, name, "unable to translate {} member type", .{container_kind_name});
+ return null;
+ },
+ else => |e| return e,
+ };
+
+ const field_node = try c.a().create(ast.Node.ContainerField);
+ field_node.* = .{
+ .doc_comments = null,
+ .comptime_token = null,
+ .name_token = field_name,
+ .type_expr = field_type,
+ .value_expr = null,
+ .align_expr = null,
+ };
+
+ try container_node.fields_and_decls.push(&field_node.base);
+ _ = try appendToken(c, .Comma, ",");
+ }
+ container_node.rbrace_token = try appendToken(c, .RBrace, "}");
+ semicolon = try appendToken(c, .Semicolon, ";");
+ break :blk &container_node.base;
};
+ node.semicolon_token = semicolon;
try addTopLevelDecl(c, name, &node.base);
- try c.alias_list.push(.{ .alias = bare_name, .name = name });
+ if (!is_unnamed)
+ try c.alias_list.push(.{ .alias = bare_name, .name = name });
+ return transCreateNodeIdentifier(c, name);
}
-fn createAlias(c: *Context, alias: var) !void {
- const visib_tok = try appendToken(c, .Keyword_pub, "pub");
- const mut_tok = try appendToken(c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(c, alias.alias);
+fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.Node {
+ if (c.decl_table.get(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)))) |name|
+ return try transCreateNodeIdentifier(c, name.value); // Avoid processing this decl twice
+ const rp = makeRestorePoint(c);
+ const enum_loc = ZigClangEnumDecl_getLocation(enum_decl);
- const eq_tok = try appendToken(c, .Equal, "=");
- const init_node = try transCreateNodeIdentifier(c, alias.name);
+ var bare_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, enum_decl)));
+ var is_unnamed = false;
+ if (bare_name.len == 0) {
+ bare_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()});
+ is_unnamed = true;
+ }
- const node = try c.a().create(ast.Node.VarDecl);
- node.* = ast.Node.VarDecl{
- .doc_comments = null,
- .visib_token = visib_tok,
- .thread_local_token = null,
- .name_token = name_tok,
- .eq_token = eq_tok,
- .mut_token = mut_tok,
- .comptime_token = null,
- .extern_export_token = null,
- .lib_name = null,
- .type_node = null,
- .align_node = null,
- .section_node = null,
- .init_node = init_node,
- .semicolon_token = try appendToken(c, .Semicolon, ";"),
- };
- return addTopLevelDecl(c, alias.alias, &node.base);
-}
+ const name = try std.fmt.allocPrint(c.a(), "enum_{}", .{bare_name});
+ _ = try c.decl_table.put(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)), name);
+ const node = try transCreateNodeVarDecl(c, !is_unnamed, true, name);
+ node.eq_token = try appendToken(c, .Equal, "=");
-const ResultUsed = enum {
- used,
- unused,
-};
+ node.init_node = if (ZigClangEnumDecl_getDefinition(enum_decl)) |enum_def| blk: {
+ var pure_enum = true;
+ var it = ZigClangEnumDecl_enumerator_begin(enum_def);
+ var end_it = ZigClangEnumDecl_enumerator_end(enum_def);
+ while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) {
+ const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it);
+ if (ZigClangEnumConstantDecl_getInitExpr(enum_const)) |_| {
+ pure_enum = false;
+ break;
+ }
+ }
-const LRValue = enum {
- l_value,
- r_value,
-};
+ const extern_tok = try appendToken(c, .Keyword_extern, "extern");
+ const container_tok = try appendToken(c, .Keyword_enum, "enum");
-fn transStmt(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangStmt,
- result_used: ResultUsed,
- lrvalue: LRValue,
-) TransError!*ast.Node {
- const sc = ZigClangStmt_getStmtClass(stmt);
- switch (sc) {
- .BinaryOperatorClass => return transBinaryOperator(rp, scope, @ptrCast(*const ZigClangBinaryOperator, stmt), result_used),
- .CompoundStmtClass => return transCompoundStmt(rp, scope, @ptrCast(*const ZigClangCompoundStmt, stmt)),
- .CStyleCastExprClass => return transCStyleCastExprClass(rp, scope, @ptrCast(*const ZigClangCStyleCastExpr, stmt), result_used, lrvalue),
- .DeclStmtClass => return transDeclStmt(rp, scope, @ptrCast(*const ZigClangDeclStmt, stmt)),
- .DeclRefExprClass => return transDeclRefExpr(rp, scope, @ptrCast(*const ZigClangDeclRefExpr, stmt), lrvalue),
- .ImplicitCastExprClass => return transImplicitCastExpr(rp, scope, @ptrCast(*const ZigClangImplicitCastExpr, stmt), result_used),
- .IntegerLiteralClass => return transIntegerLiteral(rp, scope, @ptrCast(*const ZigClangIntegerLiteral, stmt), result_used),
- .ReturnStmtClass => return transReturnStmt(rp, scope, @ptrCast(*const ZigClangReturnStmt, stmt)),
- .StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const ZigClangStringLiteral, stmt), result_used),
- .ParenExprClass => return transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), result_used, lrvalue),
- .InitListExprClass => return transInitListExpr(rp, scope, @ptrCast(*const ZigClangInitListExpr, stmt), result_used),
- .ImplicitValueInitExprClass => return transImplicitValueInitExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used),
- else => {
- return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- ZigClangStmt_getBeginLoc(stmt),
- "TODO implement translation of stmt class {}",
- .{@tagName(sc)},
- );
- },
- }
-}
+ const container_node = try c.a().create(ast.Node.ContainerDecl);
+ container_node.* = .{
+ .layout_token = extern_tok,
+ .kind_token = container_tok,
+ .init_arg_expr = .None,
+ .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(c.a()),
+ .lbrace_token = undefined,
+ .rbrace_token = undefined,
+ };
-fn transBinaryOperator(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangBinaryOperator,
- result_used: ResultUsed,
-) TransError!*ast.Node {
- const op = ZigClangBinaryOperator_getOpcode(stmt);
- const qt = ZigClangBinaryOperator_getType(stmt);
- switch (op) {
- .PtrMemD, .PtrMemI, .Cmp => return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- ZigClangBinaryOperator_getBeginLoc(stmt),
- "TODO: handle more C binary operators: {}",
- .{op},
- ),
- .Assign => return &(try transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt))).base,
- .Add => {
- const node = if (cIsUnsignedInteger(qt))
- try transCreateNodeInfixOp(rp, scope, stmt, .AddWrap, .PlusPercent, "+%", true)
+ const int_type = ZigClangEnumDecl_getIntegerType(enum_decl);
+
+ // TODO only emit this tag type if the enum tag type is not the default.
+ // I don't know what the default is, need to figure out how clang is deciding.
+ // it appears to at least be different across gcc/msvc
+ if (!isCBuiltinType(int_type, .UInt) and
+ !isCBuiltinType(int_type, .Int))
+ {
+ _ = try appendToken(c, .LParen, "(");
+ container_node.init_arg_expr = .{
+ .Type = transQualType(rp, int_type, enum_loc) catch |err| switch (err) {
+ error.UnsupportedType => {
+ try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});
+ return null;
+ },
+ else => |e| return e,
+ },
+ };
+ _ = try appendToken(c, .RParen, ")");
+ }
+
+ container_node.lbrace_token = try appendToken(c, .LBrace, "{");
+
+ it = ZigClangEnumDecl_enumerator_begin(enum_def);
+ end_it = ZigClangEnumDecl_enumerator_end(enum_def);
+ while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) {
+ const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it);
+
+ const enum_val_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, enum_const)));
+
+ const field_name = if (!is_unnamed and mem.startsWith(u8, enum_val_name, bare_name))
+ enum_val_name[bare_name.len..]
else
- try transCreateNodeInfixOp(rp, scope, stmt, .Add, .Plus, "+", true);
- return maybeSuppressResult(rp, scope, result_used, node);
+ enum_val_name;
+
+ const field_name_tok = try appendIdentifier(c, field_name);
+
+ const int_node = if (!pure_enum) blk: {
+ _ = try appendToken(c, .Colon, "=");
+ break :blk try transCreateNodeAPInt(c, ZigClangEnumConstantDecl_getInitVal(enum_const));
+ } else
+ null;
+
+ const field_node = try c.a().create(ast.Node.ContainerField);
+ field_node.* = .{
+ .doc_comments = null,
+ .comptime_token = null,
+ .name_token = field_name_tok,
+ .type_expr = null,
+ .value_expr = int_node,
+ .align_expr = null,
+ };
+
+ try container_node.fields_and_decls.push(&field_node.base);
+ _ = try appendToken(c, .Comma, ",");
+ // In C each enum value is in the global namespace. So we put them there too.
+ // At this point we can rely on the enum emitting successfully.
+ const tld_node = try transCreateNodeVarDecl(c, true, true, enum_val_name);
+ tld_node.eq_token = try appendToken(c, .Equal, "=");
+ tld_node.init_node = try transCreateNodeAPInt(c, ZigClangEnumConstantDecl_getInitVal(enum_const));
+ tld_node.semicolon_token = try appendToken(c, .Semicolon, ";");
+ try addTopLevelDecl(c, field_name, &tld_node.base);
+ }
+ container_node.rbrace_token = try appendToken(c, .RBrace, "}");
+
+ break :blk &container_node.base;
+ } else
+ try transCreateNodeOpaqueType(c);
+
+ node.semicolon_token = try appendToken(c, .Semicolon, ";");
+
+ try addTopLevelDecl(c, name, &node.base);
+ if (!is_unnamed)
+ try c.alias_list.push(.{ .alias = bare_name, .name = name });
+ return transCreateNodeIdentifier(c, name);
+}
+
+fn createAlias(c: *Context, alias: var) !void {
+ const node = try transCreateNodeVarDecl(c, true, true, alias.alias);
+ node.eq_token = try appendToken(c, .Equal, "=");
+ node.init_node = try transCreateNodeIdentifier(c, alias.name);
+ node.semicolon_token = try appendToken(c, .Semicolon, ";");
+ return addTopLevelDecl(c, alias.alias, &node.base);
+}
+
+const ResultUsed = enum {
+ used,
+ unused,
+};
+
+const LRValue = enum {
+ l_value,
+ r_value,
+};
+
+fn transStmt(
+ rp: RestorePoint,
+ scope: *Scope,
+ stmt: *const ZigClangStmt,
+ result_used: ResultUsed,
+ lrvalue: LRValue,
+) TransError!*ast.Node {
+ const sc = ZigClangStmt_getStmtClass(stmt);
+ switch (sc) {
+ .BinaryOperatorClass => return transBinaryOperator(rp, scope, @ptrCast(*const ZigClangBinaryOperator, stmt), result_used),
+ .CompoundStmtClass => return transCompoundStmt(rp, scope, @ptrCast(*const ZigClangCompoundStmt, stmt)),
+ .CStyleCastExprClass => return transCStyleCastExprClass(rp, scope, @ptrCast(*const ZigClangCStyleCastExpr, stmt), result_used, lrvalue),
+ .DeclStmtClass => return transDeclStmt(rp, scope, @ptrCast(*const ZigClangDeclStmt, stmt)),
+ .DeclRefExprClass => return transDeclRefExpr(rp, scope, @ptrCast(*const ZigClangDeclRefExpr, stmt), lrvalue),
+ .ImplicitCastExprClass => return transImplicitCastExpr(rp, scope, @ptrCast(*const ZigClangImplicitCastExpr, stmt), result_used),
+ .IntegerLiteralClass => return transIntegerLiteral(rp, scope, @ptrCast(*const ZigClangIntegerLiteral, stmt), result_used),
+ .ReturnStmtClass => return transReturnStmt(rp, scope, @ptrCast(*const ZigClangReturnStmt, stmt)),
+ .StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const ZigClangStringLiteral, stmt), result_used),
+ .ParenExprClass => {
+ const expr = try transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), result_used, lrvalue);
+ if (expr.id == .GroupedExpression) return expr;
+ const node = try rp.c.a().create(ast.Node.GroupedExpression);
+ node.* = .{
+ .lparen = try appendToken(rp.c, .LParen, "("),
+ .expr = expr,
+ .rparen = try appendToken(rp.c, .RParen, ")"),
+ };
+ return &node.base;
},
- .Sub => {
- const node = if (cIsUnsignedInteger(qt))
- try transCreateNodeInfixOp(rp, scope, stmt, .SubWrap, .MinusPercent, "-%", true)
- else
- try transCreateNodeInfixOp(rp, scope, stmt, .Sub, .Minus, "-", true);
- return maybeSuppressResult(rp, scope, result_used, node);
+ .InitListExprClass => return transInitListExpr(rp, scope, @ptrCast(*const ZigClangInitListExpr, stmt), result_used),
+ .ImplicitValueInitExprClass => return transImplicitValueInitExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used),
+ .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const ZigClangIfStmt, stmt)),
+ .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const ZigClangWhileStmt, stmt)),
+ .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const ZigClangDoStmt, stmt)),
+ .NullStmtClass => {
+ const block = try transCreateNodeBlock(rp.c, null);
+ block.rbrace = try appendToken(rp.c, .RBrace, "}");
+ return &block.base;
},
- .Mul => {
- const node = if (cIsUnsignedInteger(qt))
- try transCreateNodeInfixOp(rp, scope, stmt, .MultWrap, .AsteriskPercent, "*%", true)
- else
- try transCreateNodeInfixOp(rp, scope, stmt, .Mult, .Asterisk, "*", true);
- return maybeSuppressResult(rp, scope, result_used, node);
+ .ContinueStmtClass => return try transCreateNodeContinue(rp.c),
+ .BreakStmtClass => return transBreak(rp, scope),
+ .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),
+ .PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const ZigClangPredefinedExpr, stmt), result_used),
+ .CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, stmt), result_used),
+ .StmtExprClass => return transStmtExpr(rp, scope, @ptrCast(*const ZigClangStmtExpr, stmt), result_used),
+ .MemberExprClass => return transMemberExpr(rp, scope, @ptrCast(*const ZigClangMemberExpr, stmt), result_used),
+ .ArraySubscriptExprClass => return transArrayAccess(rp, scope, @ptrCast(*const ZigClangArraySubscriptExpr, stmt), result_used),
+ .CallExprClass => return transCallExpr(rp, scope, @ptrCast(*const ZigClangCallExpr, stmt), result_used),
+ .UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(rp, scope, @ptrCast(*const ZigClangUnaryExprOrTypeTraitExpr, stmt), result_used),
+ .UnaryOperatorClass => return transUnaryOperator(rp, scope, @ptrCast(*const ZigClangUnaryOperator, stmt), result_used),
+ .CompoundAssignOperatorClass => return transCompoundAssignOperator(rp, scope, @ptrCast(*const ZigClangCompoundAssignOperator, stmt), result_used),
+ else => {
+ return revertAndWarn(
+ rp,
+ error.UnsupportedTranslation,
+ ZigClangStmt_getBeginLoc(stmt),
+ "TODO implement translation of stmt class {}",
+ .{@tagName(sc)},
+ );
+ },
+ }
+}
+
+fn transBinaryOperator(
+ rp: RestorePoint,
+ scope: *Scope,
+ stmt: *const ZigClangBinaryOperator,
+ result_used: ResultUsed,
+) TransError!*ast.Node {
+ const op = ZigClangBinaryOperator_getOpcode(stmt);
+ const qt = ZigClangBinaryOperator_getType(stmt);
+ var op_token: ast.TokenIndex = undefined;
+ var op_id: ast.Node.InfixOp.Op = undefined;
+ switch (op) {
+ .Assign => return transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt)),
+ .Comma => {
+ const block_scope = try scope.findBlockScope(rp.c);
+ const expr = block_scope.base.parent == scope;
+ const lparen = if (expr) blk: {
+ const l = try appendToken(rp.c, .LParen, "(");
+ block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label);
+ break :blk l;
+ } else undefined;
+
+ const lhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getLHS(stmt), .unused, .r_value);
+ try block_scope.block_node.statements.push(lhs);
+
+ 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 transCreateNodeBreak(rp.c, block_scope.label);
+ break_node.rhs = rhs;
+ try block_scope.block_node.statements.push(&break_node.base);
+ block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
+ const rparen = try appendToken(rp.c, .RParen, ")");
+ const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
+ grouped_expr.* = .{
+ .lparen = lparen,
+ .expr = &block_scope.block_node.base,
+ .rparen = rparen,
+ };
+ return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base);
+ } else {
+ return maybeSuppressResult(rp, scope, result_used, rhs);
+ }
},
.Div => {
if (!cIsUnsignedInteger(qt)) {
// signed integer division uses @divTrunc
const div_trunc_node = try transCreateNodeBuiltinFnCall(rp.c, "@divTrunc");
- const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
- try div_trunc_node.params.push(lhs);
+ try div_trunc_node.params.push(try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value));
_ = try appendToken(rp.c, .Comma, ",");
const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
try div_trunc_node.params.push(rhs);
div_trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")");
return maybeSuppressResult(rp, scope, result_used, &div_trunc_node.base);
- } else {
- // unsigned/float division uses the operator
- const node = try transCreateNodeInfixOp(rp, scope, stmt, .Div, .Slash, "/", true);
- return maybeSuppressResult(rp, scope, result_used, node);
}
},
.Rem => {
if (!cIsUnsignedInteger(qt)) {
// signed integer division uses @rem
const rem_node = try transCreateNodeBuiltinFnCall(rp.c, "@rem");
- const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
- try rem_node.params.push(lhs);
+ try rem_node.params.push(try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value));
_ = try appendToken(rp.c, .Comma, ",");
const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
try rem_node.params.push(rhs);
rem_node.rparen_token = try appendToken(rp.c, .RParen, ")");
return maybeSuppressResult(rp, scope, result_used, &rem_node.base);
+ }
+ },
+ .Shl => {
+ const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<");
+ return maybeSuppressResult(rp, scope, result_used, node);
+ },
+ .Shr => {
+ const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftRight, .AngleBracketAngleBracketRight, ">>");
+ return maybeSuppressResult(rp, scope, result_used, node);
+ },
+ .LAnd => {
+ const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolAnd, result_used, true);
+ return maybeSuppressResult(rp, scope, result_used, node);
+ },
+ .LOr => {
+ const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolOr, result_used, true);
+ return maybeSuppressResult(rp, scope, result_used, node);
+ },
+ else => {},
+ }
+ const lhs_node = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
+ switch (op) {
+ .Add => {
+ if (cIsUnsignedInteger(qt)) {
+ op_token = try appendToken(rp.c, .PlusPercent, "+%");
+ op_id = .AddWrap;
} else {
- // unsigned/float division uses the operator
- const node = try transCreateNodeInfixOp(rp, scope, stmt, .Mod, .Percent, "%", true);
- return maybeSuppressResult(rp, scope, result_used, node);
+ op_token = try appendToken(rp.c, .Plus, "+");
+ op_id = .Add;
}
},
- .Shl,
- .Shr,
- .LT,
- .GT,
- .LE,
- .GE,
- .EQ,
- .NE,
- .And,
- .Xor,
- .Or,
- .LAnd,
- .LOr,
- .Comma,
- => return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- ZigClangBinaryOperator_getBeginLoc(stmt),
- "TODO: handle more C binary operators: {}",
- .{op},
- ),
+ .Sub => {
+ if (cIsUnsignedInteger(qt)) {
+ op_token = try appendToken(rp.c, .MinusPercent, "-%");
+ op_id = .SubWrap;
+ } else {
+ op_token = try appendToken(rp.c, .Minus, "-");
+ op_id = .Sub;
+ }
+ },
+ .Mul => {
+ if (cIsUnsignedInteger(qt)) {
+ op_token = try appendToken(rp.c, .AsteriskPercent, "*%");
+ op_id = .MulWrap;
+ } else {
+ op_token = try appendToken(rp.c, .Asterisk, "*");
+ op_id = .Mul;
+ }
+ },
+ .Div => {
+ // unsigned/float division uses the operator
+ op_id = .Div;
+ op_token = try appendToken(rp.c, .Slash, "/");
+ },
+ .Rem => {
+ // unsigned/float division uses the operator
+ op_id = .Mod;
+ op_token = try appendToken(rp.c, .Percent, "%");
+ },
+ .LT => {
+ op_id = .LessThan;
+ op_token = try appendToken(rp.c, .AngleBracketLeft, "<");
+ },
+ .GT => {
+ op_id = .GreaterThan;
+ op_token = try appendToken(rp.c, .AngleBracketRight, ">");
+ },
+ .LE => {
+ op_id = .LessOrEqual;
+ op_token = try appendToken(rp.c, .AngleBracketLeftEqual, "<=");
+ },
+ .GE => {
+ op_id = .GreaterOrEqual;
+ op_token = try appendToken(rp.c, .AngleBracketRightEqual, ">=");
+ },
+ .EQ => {
+ op_id = .EqualEqual;
+ op_token = try appendToken(rp.c, .EqualEqual, "==");
+ },
+ .NE => {
+ op_id = .BangEqual;
+ op_token = try appendToken(rp.c, .BangEqual, "!=");
+ },
+ .And => {
+ op_id = .BitAnd;
+ op_token = try appendToken(rp.c, .Ampersand, "&");
+ },
+ .Xor => {
+ op_id = .BitXor;
+ op_token = try appendToken(rp.c, .Caret, "^");
+ },
+ .Or => {
+ op_id = .BitOr;
+ op_token = try appendToken(rp.c, .Pipe, "|");
+ },
+ .Assign,
.MulAssign,
.DivAssign,
.RemAssign,
@@ -772,6 +1062,9 @@ fn transBinaryOperator(
=> unreachable,
else => unreachable,
}
+
+ const rhs_node = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
+ return transCreateNodeInfixOp(rp, scope, lhs_node, op_id, op_token, rhs_node, result_used, true);
}
fn transCompoundStmtInline(
@@ -790,11 +1083,11 @@ fn transCompoundStmtInline(
}
fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) TransError!*ast.Node {
- const block_node = try transCreateNodeBlock(rp.c, null);
- const block_scope = try Scope.Block.init(rp.c, scope, block_node);
- try transCompoundStmtInline(rp, &block_scope.base, stmt, block_node);
- block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
- return &block_node.base;
+ const block_scope = try Scope.Block.init(rp.c, scope, null);
+ block_scope.block_node = try transCreateNodeBlock(rp.c, null);
+ try transCompoundStmtInline(rp, &block_scope.base, stmt, block_scope.block_node);
+ block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
+ return &block_scope.block_node.base;
}
fn transCStyleCastExprClass(
@@ -818,7 +1111,7 @@ fn transCStyleCastExprClass(
fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt) TransError!*ast.Node {
const c = rp.c;
- const block_scope = scope.findBlockScope();
+ const block_scope = scope.findBlockScope(c) catch unreachable;
var it = ZigClangDeclStmt_decl_begin(stmt);
const end_it = ZigClangDeclStmt_decl_end(stmt);
@@ -832,10 +1125,6 @@ fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt)
else
try appendToken(c, .Keyword_threadlocal, "threadlocal");
const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl);
- const mut_token = if (ZigClangQualType_isConstQualified(qual_type))
- try appendToken(c, .Keyword_const, "const")
- else
- try appendToken(c, .Keyword_var, "var");
const name = try c.str(ZigClangDecl_getName_bytes_begin(
@ptrCast(*const ZigClangDecl, var_decl),
));
@@ -843,36 +1132,25 @@ fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt)
try block_scope.variables.push(.{ .name = name, .alias = a });
break :blk a;
} else name;
- const name_token = try appendIdentifier(c, checked_name);
+ const node = try transCreateNodeVarDecl(c, false, ZigClangQualType_isConstQualified(qual_type), checked_name);
- const colon_token = try appendToken(c, .Colon, ":");
+ _ = try appendToken(c, .Colon, ":");
const loc = ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt));
- const type_node = try transQualType(rp, qual_type, loc);
+ node.type_node = try transQualType(rp, qual_type, loc);
- const eq_token = try appendToken(c, .Equal, "=");
- const init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr|
+ node.eq_token = try appendToken(c, .Equal, "=");
+ var init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr|
try transExpr(rp, scope, expr, .used, .r_value)
else
try transCreateNodeUndefinedLiteral(c);
- const semicolon_token = try appendToken(c, .Semicolon, ";");
-
- const node = try c.a().create(ast.Node.VarDecl);
- node.* = ast.Node.VarDecl{
- .doc_comments = null,
- .visib_token = null,
- .thread_local_token = thread_local_token,
- .name_token = name_token,
- .eq_token = eq_token,
- .mut_token = mut_token,
- .comptime_token = null,
- .extern_export_token = null,
- .lib_name = null,
- .type_node = type_node,
- .align_node = null, // TODO ?*Node,
- .section_node = null,
- .init_node = init_node,
- .semicolon_token = semicolon_token,
- };
+ if (isBoolRes(init_node)) {
+ const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt");
+ try builtin_node.params.push(init_node);
+ builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ init_node = &builtin_node.base;
+ }
+ node.init_node = init_node;
+ node.semicolon_token = try appendToken(c, .Semicolon, ";");
try block_scope.block_node.statements.push(&node.base);
},
else => |kind| return revertAndWarn(
@@ -896,7 +1174,6 @@ fn transDeclRefExpr(
const value_decl = ZigClangDeclRefExpr_getDecl(expr);
const name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, value_decl)));
const checked_name = if (scope.getAlias(name)) |a| a else name;
- if (lrvalue == .l_value) try rp.c.ptr_params.put(checked_name);
return transCreateNodeIdentifier(rp.c, checked_name);
}
@@ -909,25 +1186,58 @@ fn transImplicitCastExpr(
const c = rp.c;
const sub_expr = ZigClangImplicitCastExpr_getSubExpr(expr);
const sub_expr_node = try transExpr(rp, scope, @ptrCast(*const ZigClangExpr, sub_expr), .used, .r_value);
+ const dest_type = getExprQualType(c, @ptrCast(*const ZigClangExpr, expr));
+ const src_type = getExprQualType(c, sub_expr);
switch (ZigClangImplicitCastExpr_getCastKind(expr)) {
- .BitCast => {
- const dest_type = getExprQualType(c, @ptrCast(*const ZigClangExpr, expr));
- const src_type = getExprQualType(c, sub_expr);
+ .BitCast, .FloatingCast, .FloatingToIntegral, .IntegralToFloating, .IntegralCast => {
return transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node);
},
- .IntegralCast => {
- const dest_type = ZigClangExpr_getType(@ptrCast(*const ZigClangExpr, expr));
- const src_type = ZigClangExpr_getType(sub_expr);
- return transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node);
- },
- .FunctionToPointerDecay, .ArrayToPointerDecay => {
+ .LValueToRValue, .NoOp, .FunctionToPointerDecay, .ArrayToPointerDecay => {
return maybeSuppressResult(rp, scope, result_used, sub_expr_node);
},
- .LValueToRValue, .NoOp => {
- return transExpr(rp, scope, sub_expr, .used, .r_value);
- },
.NullToPointer => {
- return transCreateNodeNullLiteral(rp.c);
+ return try transCreateNodeNullLiteral(rp.c);
+ },
+ .PointerToBoolean => {
+ // @ptrToInt(val) != 0
+ const ptr_to_int = try transCreateNodeBuiltinFnCall(rp.c, "@ptrToInt");
+ try ptr_to_int.params.push(try transExpr(rp, scope, sub_expr, .used, .r_value));
+ ptr_to_int.rparen_token = try appendToken(rp.c, .RParen, ")");
+
+ const op_token = try appendToken(rp.c, .BangEqual, "!=");
+ const rhs_node = try transCreateNodeInt(rp.c, 0);
+ return transCreateNodeInfixOp(rp, scope, &ptr_to_int.base, .BangEqual, op_token, rhs_node, result_used, false);
+ },
+ .IntegralToBoolean => {
+ // val != 0
+ const node = try transExpr(rp, scope, sub_expr, .used, .r_value);
+
+ const op_token = try appendToken(rp.c, .BangEqual, "!=");
+ const rhs_node = try transCreateNodeInt(rp.c, 0);
+ return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, result_used, false);
+ },
+ .PointerToIntegral => {
+ // @intCast(dest_type, @ptrToInt(val))
+ const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@intCast");
+ try cast_node.params.push(try transQualType(rp, dest_type, ZigClangImplicitCastExpr_getBeginLoc(expr)));
+ _ = try appendToken(rp.c, .Comma, ",");
+
+ const ptr_to_int = try transCreateNodeBuiltinFnCall(rp.c, "@ptrToInt");
+ try ptr_to_int.params.push(try transExpr(rp, scope, sub_expr, .used, .r_value));
+ ptr_to_int.rparen_token = try appendToken(rp.c, .RParen, ")");
+ try cast_node.params.push(&ptr_to_int.base);
+ cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ return maybeSuppressResult(rp, scope, result_used, &cast_node.base);
+ },
+ .IntegralToPointer => {
+ // @intToPtr(dest_type, val)
+ const int_to_ptr = try transCreateNodeBuiltinFnCall(rp.c, "@intToPtr");
+ try int_to_ptr.params.push(try transQualType(rp, dest_type, ZigClangImplicitCastExpr_getBeginLoc(expr)));
+ _ = try appendToken(rp.c, .Comma, ",");
+
+ try int_to_ptr.params.push(try transExpr(rp, scope, sub_expr, .used, .r_value));
+ int_to_ptr.rparen_token = try appendToken(rp.c, .RParen, ")");
+ return maybeSuppressResult(rp, scope, result_used, &int_to_ptr.base);
},
else => |kind| return revertAndWarn(
rp,
@@ -939,50 +1249,195 @@ fn transImplicitCastExpr(
}
}
-fn transIntegerLiteral(
+fn transBoolExpr(
rp: RestorePoint,
scope: *Scope,
- expr: *const ZigClangIntegerLiteral,
- result_used: ResultUsed,
+ expr: *const ZigClangExpr,
+ used: ResultUsed,
+ lrvalue: LRValue,
+ grouped: bool,
) TransError!*ast.Node {
- var eval_result: ZigClangExprEvalResult = undefined;
- if (!ZigClangIntegerLiteral_EvaluateAsInt(expr, &eval_result, rp.c.clang_context)) {
- const loc = ZigClangIntegerLiteral_getBeginLoc(expr);
- return revertAndWarn(rp, error.UnsupportedTranslation, loc, "invalid integer literal", .{});
+ const lparen = if (grouped)
+ try appendToken(rp.c, .LParen, "(")
+ else
+ undefined;
+ var res = try transExpr(rp, scope, expr, used, lrvalue);
+
+ if (isBoolRes(res)) {
+ if (!grouped and res.id == .GroupedExpression) {
+ const group = @fieldParentPtr(ast.Node.GroupedExpression, "base", res);
+ res = group.expr;
+ // get zig fmt to work properly
+ tokenSlice(rp.c, group.lparen)[0] = ')';
+ }
+ return res;
+ }
+
+ const ty = ZigClangQualType_getTypePtr(getExprQualTypeBeforeImplicitCast(rp.c, expr));
+ const node = try finishBoolExpr(rp, scope, ZigClangExpr_getBeginLoc(expr), ty, res, used);
+
+ if (grouped) {
+ const rparen = try appendToken(rp.c, .RParen, ")");
+ const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
+ grouped_expr.* = .{
+ .lparen = lparen,
+ .expr = node,
+ .rparen = rparen,
+ };
+ return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
+ } else {
+ return maybeSuppressResult(rp, scope, used, node);
}
- const node = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&eval_result.Val));
- return maybeSuppressResult(rp, scope, result_used, node);
}
-fn transReturnStmt(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangReturnStmt,
-) TransError!*ast.Node {
- const node = try transCreateNodeReturnExpr(rp.c);
- if (ZigClangReturnStmt_getRetValue(expr)) |val_expr| {
- node.rhs = try transExpr(rp, scope, val_expr, .used, .r_value);
+fn isBoolRes(res: *ast.Node) bool {
+ switch (res.id) {
+ .InfixOp => switch (@fieldParentPtr(ast.Node.InfixOp, "base", res).op) {
+ .BoolOr,
+ .BoolAnd,
+ .EqualEqual,
+ .BangEqual,
+ .LessThan,
+ .GreaterThan,
+ .LessOrEqual,
+ .GreaterOrEqual,
+ => return true,
+
+ else => {},
+ },
+ .PrefixOp => switch (@fieldParentPtr(ast.Node.PrefixOp, "base", res).op) {
+ .BoolNot => return true,
+
+ else => {},
+ },
+ .BoolLiteral => return true,
+ .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr),
+ else => {},
}
- _ = try appendToken(rp.c, .Semicolon, ";");
- return &node.base;
+ return false;
}
-fn transStringLiteral(
+fn finishBoolExpr(
rp: RestorePoint,
scope: *Scope,
- stmt: *const ZigClangStringLiteral,
- result_used: ResultUsed,
+ loc: ZigClangSourceLocation,
+ ty: *const ZigClangType,
+ node: *ast.Node,
+ used: ResultUsed,
) TransError!*ast.Node {
- const kind = ZigClangStringLiteral_getKind(stmt);
- switch (kind) {
- .Ascii, .UTF8 => {
- var len: usize = undefined;
- const bytes_ptr = ZigClangStringLiteral_getString_bytes_begin_size(stmt, &len);
- const str = bytes_ptr[0..len];
-
- var char_buf: [4]u8 = undefined;
- len = 0;
- for (str) |c| len += escapeChar(c, &char_buf).len;
+ switch (ZigClangType_getTypeClass(ty)) {
+ .Builtin => {
+ const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
+
+ switch (ZigClangBuiltinType_getKind(builtin_ty)) {
+ .Bool => return node,
+ .Char_U,
+ .UChar,
+ .Char_S,
+ .SChar,
+ .UShort,
+ .UInt,
+ .ULong,
+ .ULongLong,
+ .Short,
+ .Int,
+ .Long,
+ .LongLong,
+ .UInt128,
+ .Int128,
+ .Float,
+ .Double,
+ .Float128,
+ .LongDouble,
+ .WChar_U,
+ .Char8,
+ .Char16,
+ .Char32,
+ .WChar_S,
+ .Float16,
+ => {
+ const op_token = try appendToken(rp.c, .BangEqual, "!=");
+ const rhs_node = try transCreateNodeInt(rp.c, 0);
+ return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
+ },
+ .NullPtr => {
+ const op_token = try appendToken(rp.c, .EqualEqual, "==");
+ const rhs_node = try transCreateNodeNullLiteral(rp.c);
+ return transCreateNodeInfixOp(rp, scope, node, .EqualEqual, op_token, rhs_node, used, false);
+ },
+ else => {},
+ }
+ },
+ .Pointer => {
+ const op_token = try appendToken(rp.c, .BangEqual, "!=");
+ const rhs_node = try transCreateNodeNullLiteral(rp.c);
+ return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
+ },
+ .Typedef => {
+ const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
+ const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
+ const underlying_type = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);
+ return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(underlying_type), node, used);
+ },
+ .Enum => {
+ const op_token = try appendToken(rp.c, .BangEqual, "!=");
+ const rhs_node = try transCreateNodeInt(rp.c, 0);
+ return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
+ },
+ .Elaborated => {
+ const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty);
+ const named_type = ZigClangElaboratedType_getNamedType(elaborated_ty);
+ return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(named_type), node, used);
+ },
+ else => {},
+ }
+ return revertAndWarn(rp, error.UnsupportedType, loc, "unsupported bool expression type", .{});
+}
+
+fn transIntegerLiteral(
+ rp: RestorePoint,
+ scope: *Scope,
+ expr: *const ZigClangIntegerLiteral,
+ result_used: ResultUsed,
+) TransError!*ast.Node {
+ var eval_result: ZigClangExprEvalResult = undefined;
+ if (!ZigClangIntegerLiteral_EvaluateAsInt(expr, &eval_result, rp.c.clang_context)) {
+ const loc = ZigClangIntegerLiteral_getBeginLoc(expr);
+ return revertAndWarn(rp, error.UnsupportedTranslation, loc, "invalid integer literal", .{});
+ }
+ const node = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&eval_result.Val));
+ return maybeSuppressResult(rp, scope, result_used, node);
+}
+
+fn transReturnStmt(
+ rp: RestorePoint,
+ scope: *Scope,
+ expr: *const ZigClangReturnStmt,
+) TransError!*ast.Node {
+ const node = try transCreateNodeReturnExpr(rp.c);
+ if (ZigClangReturnStmt_getRetValue(expr)) |val_expr| {
+ node.rhs = try transExpr(rp, scope, val_expr, .used, .r_value);
+ }
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ return &node.base;
+}
+
+fn transStringLiteral(
+ rp: RestorePoint,
+ scope: *Scope,
+ stmt: *const ZigClangStringLiteral,
+ result_used: ResultUsed,
+) TransError!*ast.Node {
+ const kind = ZigClangStringLiteral_getKind(stmt);
+ switch (kind) {
+ .Ascii, .UTF8 => {
+ var len: usize = undefined;
+ const bytes_ptr = ZigClangStringLiteral_getString_bytes_begin_size(stmt, &len);
+ const str = bytes_ptr[0..len];
+
+ var char_buf: [4]u8 = undefined;
+ len = 0;
+ for (str) |c| len += escapeChar(c, &char_buf).len;
const buf = try rp.c.a().alloc(u8, len + "\"\"".len);
buf[0] = '"';
@@ -991,7 +1446,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);
@@ -1019,25 +1474,22 @@ fn writeEscapedString(buf: []u8, s: []const u8) void {
var i: usize = 0;
for (s) |c| {
const escaped = escapeChar(c, &char_buf);
- std.mem.copy(u8, buf[i..], escaped);
+ mem.copy(u8, buf[i..], escaped);
i += escaped.len;
}
}
// 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(
@@ -1071,6 +1523,55 @@ fn transCCast(
builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
return &builtin_node.base;
}
+ if (cIsFloating(src_type) and cIsFloating(dst_type)) {
+ const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@floatCast");
+ try builtin_node.params.push(try transQualType(rp, dst_type, loc));
+ _ = try appendToken(rp.c, .Comma, ",");
+ try builtin_node.params.push(expr);
+ builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ return &builtin_node.base;
+ }
+ if (cIsFloating(src_type) and !cIsFloating(dst_type)) {
+ const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@floatToInt");
+ try builtin_node.params.push(try transQualType(rp, dst_type, loc));
+ _ = try appendToken(rp.c, .Comma, ",");
+ try builtin_node.params.push(expr);
+ builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ return &builtin_node.base;
+ }
+ if (!cIsFloating(src_type) and cIsFloating(dst_type)) {
+ const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@intToFloat");
+ try builtin_node.params.push(try transQualType(rp, dst_type, loc));
+ _ = try appendToken(rp.c, .Comma, ",");
+ try builtin_node.params.push(expr);
+ builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ return &builtin_node.base;
+ }
+ if (ZigClangQualType_getTypeClass(src_type) == .Elaborated) {
+ const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ZigClangQualType_getTypePtr(src_type));
+ return transCCast(rp, scope, loc, dst_type, ZigClangElaboratedType_getNamedType(elaborated_ty), expr);
+ }
+ if (ZigClangQualType_getTypeClass(dst_type) == .Elaborated) {
+ const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ZigClangQualType_getTypePtr(dst_type));
+ return transCCast(rp, scope, loc, ZigClangElaboratedType_getNamedType(elaborated_ty), src_type, expr);
+ }
+ if (ZigClangQualType_getTypeClass(dst_type) == .Enum)
+ {
+ const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@intToEnum");
+ try builtin_node.params.push(try transQualType(rp, dst_type, loc));
+ _ = try appendToken(rp.c, .Comma, ",");
+ try builtin_node.params.push(expr);
+ builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ return &builtin_node.base;
+ }
+ if (ZigClangQualType_getTypeClass(src_type) == .Enum and
+ ZigClangQualType_getTypeClass(dst_type) != .Enum)
+ {
+ const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@enumToInt");
+ try builtin_node.params.push(expr);
+ builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ return &builtin_node.base;
+ }
// TODO: maybe widen to increase size
// TODO: maybe bitcast to change sign
// TODO: maybe truncate to reduce size
@@ -1124,7 +1625,7 @@ fn transInitListExpr(
var cat_tok: ast.TokenIndex = undefined;
if (init_count != 0) {
const dot_tok = try appendToken(rp.c, .Period, ".");
- init_node = try transCreateNodeArrayInitializer(rp.c, dot_tok);
+ init_node = try transCreateNodeContainerInitializer(rp.c, dot_tok);
var i: c_uint = 0;
while (i < init_count) : (i += 1) {
const elem_expr = ZigClangInitListExpr_getInit(expr, i);
@@ -1139,7 +1640,7 @@ fn transInitListExpr(
}
const dot_tok = try appendToken(rp.c, .Period, ".");
- var filler_init_node = try transCreateNodeArrayInitializer(rp.c, dot_tok);
+ var filler_init_node = try transCreateNodeContainerInitializer(rp.c, dot_tok);
const filler_val_expr = ZigClangInitListExpr_getArrayFiller(expr);
try filler_init_node.op.ArrayInitializer.push(try transExpr(rp, scope, filler_val_expr, .used, .r_value));
filler_init_node.rtoken = try appendToken(rp.c, .RBrace, "}");
@@ -1185,7 +1686,7 @@ fn transImplicitValueInitExpr(
.Builtin => blk: {
const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Bool => return transCreateNodeBoolLiteral(rp.c, false),
+ .Bool => return try transCreateNodeBoolLiteral(rp.c, false),
.Char_U,
.UChar,
.Char_S,
@@ -1215,329 +1716,941 @@ fn transImplicitValueInitExpr(
};
}
-fn transCPtrCast(
+fn transIfStmt(
rp: RestorePoint,
- loc: ZigClangSourceLocation,
- dst_type: ZigClangQualType,
- src_type: ZigClangQualType,
- expr: *ast.Node,
-) !*ast.Node {
- const ty = ZigClangQualType_getTypePtr(dst_type);
- const child_type = ZigClangType_getPointeeType(ty);
+ scope: *Scope,
+ stmt: *const ZigClangIfStmt,
+) TransError!*ast.Node {
+ // if (c) t
+ // if (c) t else e
+ const if_node = try transCreateNodeIf(rp.c);
- // Implicit downcasting from higher to lower alignment values is forbidden,
- // use @alignCast to side-step this problem
- const ptrcast_node = try transCreateNodeBuiltinFnCall(rp.c, "@ptrCast");
- const dst_type_node = try transType(rp, ty, loc);
- try ptrcast_node.params.push(dst_type_node);
- _ = try appendToken(rp.c, .Comma, ",");
+ var cond_scope = Scope{
+ .parent = scope,
+ .id = .Condition,
+ };
+ if_node.condition = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt)), .used, .r_value, false);
+ _ = try appendToken(rp.c, .RParen, ")");
- if (ZigClangType_isVoidType(qualTypeCanon(child_type))) {
- // void has 1-byte alignment, so @alignCast is not needed
- try ptrcast_node.params.push(expr);
- } else {
- const aligncast_node = try transCreateNodeBuiltinFnCall(rp.c, "@alignCast");
- const alignof_node = try transCreateNodeBuiltinFnCall(rp.c, "@alignOf");
- const child_type_node = try transQualType(rp, child_type, loc);
- try alignof_node.params.push(child_type_node);
- alignof_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- try aligncast_node.params.push(&alignof_node.base);
- _ = try appendToken(rp.c, .Comma, ",");
- try aligncast_node.params.push(expr);
- aligncast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- try ptrcast_node.params.push(&aligncast_node.base);
- }
- ptrcast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ if_node.body = try transStmt(rp, scope, ZigClangIfStmt_getThen(stmt), .unused, .r_value);
- return &ptrcast_node.base;
+ if (ZigClangIfStmt_getElse(stmt)) |expr| {
+ if_node.@"else" = try transCreateNodeElse(rp.c);
+ if_node.@"else".?.body = try transStmt(rp, scope, expr, .unused, .r_value);
+ }
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ return &if_node.base;
}
-fn maybeSuppressResult(
+fn transWhileLoop(
rp: RestorePoint,
scope: *Scope,
- used: ResultUsed,
- result: *ast.Node,
+ stmt: *const ZigClangWhileStmt,
) TransError!*ast.Node {
- if (used == .used) return result;
- // NOTE: This is backwards, but the semicolon must immediately follow the node.
- _ = try appendToken(rp.c, .Semicolon, ";");
- const lhs = try transCreateNodeIdentifier(rp.c, "_");
- const op_token = try appendToken(rp.c, .Equal, "=");
- const op_node = try rp.c.a().create(ast.Node.InfixOp);
- op_node.* = ast.Node.InfixOp{
- .op_token = op_token,
- .lhs = lhs,
- .op = .Assign,
- .rhs = result,
+ const while_node = try transCreateNodeWhile(rp.c);
+
+ var cond_scope = Scope{
+ .parent = scope,
+ .id = .Condition,
};
- return &op_node.base;
-}
+ while_node.condition = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangWhileStmt_getCond(stmt)), .used, .r_value, false);
+ _ = try appendToken(rp.c, .RParen, ")");
-fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: *ast.Node) !void {
- try c.tree.root_node.decls.push(decl_node);
- _ = try c.global_scope.sym_table.put(name, decl_node);
+ 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;
}
-fn transQualType(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) TypeError!*ast.Node {
- return transType(rp, ZigClangQualType_getTypePtr(qt), source_loc);
-}
+fn transDoWhileLoop(
+ rp: RestorePoint,
+ scope: *Scope,
+ stmt: *const ZigClangDoStmt,
+) TransError!*ast.Node {
+ const while_node = try transCreateNodeWhile(rp.c);
+
+ while_node.condition = try transCreateNodeBoolLiteral(rp.c, true);
+ _ = try appendToken(rp.c, .RParen, ")");
+ var new = false;
+ var loop_scope = Scope{
+ .parent = scope,
+ .id = .Loop,
+ };
-fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) TypeError!*ast.Node {
- const rp = makeRestorePoint(c);
+ 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.
+ // c: do {
+ // c: a;
+ // c: b;
+ // c: } while(c);
+ // zig: while (true) {
+ // zig: a;
+ // zig: b;
+ // zig: if (!cond) break;
+ // zig: }
+ 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
+ // c: a;
+ // c: while(c);
+ // zig: while (true) {
+ // zig: a;
+ // zig: if (!cond) break;
+ // zig: }
+ new = true;
+ const block = try transCreateNodeBlock(rp.c, null);
+ try block.statements.push(try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value));
+ break :blk block;
+ };
- const record_loc = ZigClangRecordDecl_getLocation(record_decl);
+ // if (!cond) break;
+ const if_node = try transCreateNodeIf(rp.c);
+ 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, @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;
+ _ = try appendToken(rp.c, .Semicolon, ";");
- var container_kind_name: []const u8 = undefined;
- var container_kind: std.zig.Token.Id = undefined;
+ try body_node.statements.push(&if_node.base);
+ if (new)
+ body_node.rbrace = try appendToken(rp.c, .RBrace, "}");
+ while_node.body = &body_node.base;
+ return &while_node.base;
+}
- if (ZigClangRecordDecl_isUnion(record_decl)) {
- container_kind_name = "union";
- container_kind = .Keyword_union;
- } else if (ZigClangRecordDecl_isStruct(record_decl)) {
- container_kind_name = "struct";
- container_kind = .Keyword_struct;
- } else {
- return revertAndWarn(
- rp,
- error.UnsupportedType,
- record_loc,
- "unsupported record type",
- .{},
- );
+fn transForLoop(
+ rp: RestorePoint,
+ scope: *Scope,
+ stmt: *const ZigClangForStmt,
+) TransError!*ast.Node {
+ 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);
+ loop_scope.parent = &block_scope.?.base;
+ _ = try transStmt(rp, &loop_scope, init, .unused, .r_value);
}
-
- const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse {
- return transCreateNodeOpaqueType(c);
+ var cond_scope = Scope{
+ .parent = scope,
+ .id = .Condition,
};
- const extern_tok = try appendToken(c, .Keyword_extern, "extern");
- const container_tok = try appendToken(c, container_kind, container_kind_name);
- const lbrace_token = try appendToken(c, .LBrace, "{");
+ const while_node = try transCreateNodeWhile(rp.c);
+ while_node.condition = if (ZigClangForStmt_getCond(stmt)) |cond|
+ try transBoolExpr(rp, &cond_scope, cond, .used, .r_value, false)
+ else
+ try transCreateNodeBoolLiteral(rp.c, true);
+ _ = try appendToken(rp.c, .RParen, ")");
+
+ if (ZigClangForStmt_getInc(stmt)) |incr| {
+ _ = try appendToken(rp.c, .Colon, ":");
+ _ = try appendToken(rp.c, .LParen, "(");
+ while_node.continue_expr = try transExpr(rp, &cond_scope, incr, .unused, .r_value);
+ _ = try appendToken(rp.c, .RParen, ")");
+ }
+
+ 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, "}");
+ return &block_scope.?.block_node.base;
+ } else
+ return &while_node.base;
+}
- const container_node = try c.a().create(ast.Node.ContainerDecl);
- container_node.* = .{
- .layout_token = extern_tok,
- .kind_token = container_tok,
- .init_arg_expr = .None,
- .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(c.a()),
- .lbrace_token = lbrace_token,
- .rbrace_token = undefined,
+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 it = ZigClangRecordDecl_field_begin(record_def);
- const end_it = ZigClangRecordDecl_field_end(record_def);
- while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) {
- const field_decl = ZigClangRecordDecl_field_iterator_deref(it);
- const field_loc = ZigClangFieldDecl_getLocation(field_decl);
+ var cond_scope = Scope{
+ .parent = scope,
+ .id = .Condition,
+ };
+ 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, "}");
- if (ZigClangFieldDecl_isBitField(field_decl)) {
- rp.activate();
- const node = try transCreateNodeOpaqueType(c);
- try emitWarning(c, field_loc, "{} demoted to opaque type - has bitfield", .{container_kind_name});
- return node;
- }
+ 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 field_name = try appendIdentifier(c, try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, field_decl))));
- _ = try appendToken(c, .Colon, ":");
- const field_type = try transQualType(rp, ZigClangFieldDecl_getType(field_decl), field_loc);
+ const switch_block = try transCreateNodeBlock(rp.c, null);
+ try switch_block.statements.push(&switch_node.base);
+ switch_scope.pending_block = switch_block;
- const field_node = try c.a().create(ast.Node.ContainerField);
- field_node.* = .{
- .doc_comments = null,
- .comptime_token = null,
- .name_token = field_name,
- .type_expr = field_type,
- .value_expr = null,
- .align_expr = null,
- };
+ const last = try transStmt(rp, &block_scope.base, ZigClangSwitchStmt_getBody(stmt), .unused, .r_value);
+ _ = try appendToken(rp.c, .Semicolon, ";");
- try container_node.fields_and_decls.push(&field_node.base);
- _ = try appendToken(c, .Comma, ",");
+ // 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.*);
}
- container_node.rbrace_token = try appendToken(c, .RBrace, "}");
- return &container_node.base;
+ 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 transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.Node {
- if (c.decl_table.get(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)))) |name|
- return try transCreateNodeIdentifier(c, name.value); // Avoid processing this decl twice
- const rp = makeRestorePoint(c);
- const enum_loc = ZigClangEnumDecl_getLocation(enum_decl);
+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 visib_tok = try appendToken(c, .Keyword_pub, "pub");
- const const_tok = try appendToken(c, .Keyword_const, "const");
+ 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, rhs, .used, .r_value);
- var bare_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, enum_decl)));
- var is_unnamed = false;
- if (bare_name.len == 0) {
- bare_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()});
- is_unnamed = true;
+ 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);
- const name = try std.fmt.allocPrint(c.a(), "enum_{}", .{bare_name});
- _ = try c.decl_table.put(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)), name);
- const name_tok = try appendIdentifier(c, name);
- const eq_tok = try appendToken(c, .Equal, "=");
+ switch_scope.pending_block = block;
- const init_node = if (ZigClangEnumDecl_getDefinition(enum_decl)) |enum_def| blk: {
- var pure_enum = true;
- var it = ZigClangEnumDecl_enumerator_begin(enum_def);
- var end_it = ZigClangEnumDecl_enumerator_end(enum_def);
- while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) {
- const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it);
- if (ZigClangEnumConstantDecl_getInitExpr(enum_const)) |_| {
- pure_enum = false;
- break;
- }
- }
+ return transStmt(rp, scope, ZigClangCaseStmt_getSubStmt(stmt), .unused, .r_value);
+}
- const extern_tok = try appendToken(c, .Keyword_extern, "extern");
- const container_tok = try appendToken(c, .Keyword_enum, "enum");
+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 container_node = try c.a().create(ast.Node.ContainerDecl);
- container_node.* = .{
- .layout_token = extern_tok,
- .kind_token = container_tok,
- .init_arg_expr = .None,
- .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(c.a()),
- .lbrace_token = undefined,
- .rbrace_token = undefined,
- };
+ 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);
- const int_type = ZigClangEnumDecl_getIntegerType(enum_decl);
+ switch_scope.pending_block = block;
+ return transStmt(rp, scope, ZigClangDefaultStmt_getSubStmt(stmt), .unused, .r_value);
+}
- // TODO only emit this tag type if the enum tag type is not the default.
- // I don't know what the default is, need to figure out how clang is deciding.
- // it appears to at least be different across gcc/msvc
- if (!isCBuiltinType(int_type, .UInt) and
- !isCBuiltinType(int_type, .Int))
- {
- _ = try appendToken(c, .LParen, "(");
- container_node.init_arg_expr = .{
- .Type = transQualType(rp, int_type, enum_loc) catch |err| switch (err) {
- error.UnsupportedType => {
- if (is_unnamed) {
- try emitWarning(c, enum_loc, "unable to translate enum tag type", .{});
- } else {
- try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});
- }
- return null;
- },
- else => |e| return e,
- },
+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 transPredefinedExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangPredefinedExpr, used: ResultUsed) TransError!*ast.Node {
+ 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,
};
- _ = try appendToken(c, .RParen, ")");
+ 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 transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr, used: ResultUsed) TransError!*ast.Node {
+ const comp = ZigClangStmtExpr_getSubStmt(stmt);
+ if (used == .unused) {
+ return transCompoundStmt(rp, scope, comp);
+ }
+ const lparen = try appendToken(rp.c, .LParen, "(");
+ const block_scope = try Scope.Block.init(rp.c, scope, "blk");
+ const block = try transCreateNodeBlock(rp.c, "blk");
+ block_scope.block_node = block;
+
+ var it = ZigClangCompoundStmt_body_begin(comp);
+ const end_it = ZigClangCompoundStmt_body_end(comp);
+ while (it != end_it - 1) : (it += 1) {
+ const result = try transStmt(rp, &block_scope.base, it[0], .unused, .r_value);
+ if (result != &block.base)
+ try block.statements.push(result);
+ }
+ const break_node = try transCreateNodeBreak(rp.c, "blk");
+ break_node.rhs = try transStmt(rp, &block_scope.base, it[0], .used, .r_value);
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ try block.statements.push(&break_node.base);
+ block.rbrace = try appendToken(rp.c, .RBrace, "}");
+ const rparen = try appendToken(rp.c, .RParen, ")");
+ const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
+ grouped_expr.* = .{
+ .lparen = lparen,
+ .expr = &block.base,
+ .rparen = rparen,
+ };
+ return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
+}
+
+fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangMemberExpr, result_used: ResultUsed) TransError!*ast.Node {
+ var container_node = try transExpr(rp, scope, ZigClangMemberExpr_getBase(stmt), .used, .r_value);
+
+ if (ZigClangMemberExpr_isArrow(stmt)) {
+ container_node = try transCreateNodePtrDeref(rp.c, container_node);
+ }
+
+ const name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, ZigClangMemberExpr_getMemberDecl(stmt))));
+ const node = try transCreateNodeFieldAccess(rp.c, container_node, name);
+ return maybeSuppressResult(rp, scope, result_used, node);
+}
+
+fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangArraySubscriptExpr, result_used: ResultUsed) TransError!*ast.Node {
+ const container_node = try transExpr(rp, scope, ZigClangArraySubscriptExpr_getBase(stmt), .used, .r_value);
+ const node = try transCreateNodeArrayAccess(rp.c, container_node);
+ node.op.ArrayAccess = try transExpr(rp, scope, ZigClangArraySubscriptExpr_getIdx(stmt), .used, .r_value);
+ node.rtoken = try appendToken(rp.c, .RBrace, "]");
+ return maybeSuppressResult(rp, scope, result_used, &node.base);
+}
+
+fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCallExpr, result_used: ResultUsed) TransError!*ast.Node {
+ const callee = ZigClangCallExpr_getCallee(stmt);
+ var raw_fn_expr = try transExpr(rp, scope, callee, .used, .r_value);
+
+ var is_ptr = false;
+ const fn_ty = qualTypeGetFnProto(ZigClangExpr_getType(callee), &is_ptr);
+
+ const fn_expr = if (is_ptr and fn_ty != null) blk: {
+ if (ZigClangExpr_getStmtClass(callee) == .ImplicitCastExprClass) {
+ const implicit_cast = @ptrCast(*const ZigClangImplicitCastExpr, callee);
+
+ if (ZigClangImplicitCastExpr_getCastKind(implicit_cast) == .FunctionToPointerDecay) {
+ const subexpr = ZigClangImplicitCastExpr_getSubExpr(implicit_cast);
+ if (ZigClangExpr_getStmtClass(subexpr) == .DeclRefExprClass) {
+ const decl_ref = @ptrCast(*const ZigClangDeclRefExpr, subexpr);
+ const named_decl = ZigClangDeclRefExpr_getFoundDecl(decl_ref);
+ if (ZigClangDecl_getKind(@ptrCast(*const ZigClangDecl, named_decl)) == .Function) {
+ break :blk raw_fn_expr;
+ }
+ }
+ }
}
+ break :blk try transCreateNodeUnwrapNull(rp.c, raw_fn_expr);
+ } else
+ raw_fn_expr;
+ const node = try transCreateNodeFnCall(rp.c, fn_expr);
- container_node.lbrace_token = try appendToken(c, .LBrace, "{");
+ const num_args = ZigClangCallExpr_getNumArgs(stmt);
+ const args = ZigClangCallExpr_getArgs(stmt);
+ var i: usize = 0;
+ while (i < num_args) : (i += 1) {
+ if (i != 0) {
+ _ = try appendToken(rp.c, .Comma, ",");
+ }
+ const arg = try transExpr(rp, scope, args[i], .used, .r_value);
+ try node.op.Call.params.push(arg);
+ }
+ node.rtoken = try appendToken(rp.c, .RParen, ")");
- it = ZigClangEnumDecl_enumerator_begin(enum_def);
- end_it = ZigClangEnumDecl_enumerator_end(enum_def);
- while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) {
- const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it);
+ if (fn_ty) |ty| {
+ const canon = ZigClangQualType_getCanonicalType(ZigClangFunctionProtoType_getReturnType(ty));
+ const ret_ty = ZigClangQualType_getTypePtr(canon);
+ if (ZigClangType_isVoidType(ret_ty)) {
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ return &node.base;
+ }
+ }
- const enum_val_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, enum_const)));
+ return maybeSuppressResult(rp, scope, result_used, &node.base);
+}
- const field_name = if (!is_unnamed and std.mem.startsWith(u8, enum_val_name, bare_name))
- enum_val_name[bare_name.len..]
- else
- enum_val_name;
+fn qualTypeGetFnProto(qt: ZigClangQualType, is_ptr: *bool) ?*const ZigClangFunctionProtoType {
+ const canon = ZigClangQualType_getCanonicalType(qt);
+ var ty = ZigClangQualType_getTypePtr(canon);
+ is_ptr.* = false;
- const field_name_tok = try appendIdentifier(c, field_name);
+ if (ZigClangType_getTypeClass(ty) == .Pointer) {
+ is_ptr.* = true;
+ const child_qt = ZigClangType_getPointeeType(ty);
+ ty = ZigClangQualType_getTypePtr(child_qt);
+ }
+ if (ZigClangType_getTypeClass(ty) == .FunctionProto) {
+ return @ptrCast(*const ZigClangFunctionProtoType, ty);
+ }
+ return null;
+}
- const int_node = if (!pure_enum) blk: {
- _ = try appendToken(c, .Colon, "=");
- break :blk try transCreateNodeAPInt(c, ZigClangEnumConstantDecl_getInitVal(enum_const));
+fn transUnaryExprOrTypeTraitExpr(
+ rp: RestorePoint,
+ scope: *Scope,
+ stmt: *const ZigClangUnaryExprOrTypeTraitExpr,
+ result_used: ResultUsed,
+) TransError!*ast.Node {
+ const type_node = try transQualType(
+ rp,
+ ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(stmt),
+ ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(stmt),
+ );
+
+ const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@sizeOf");
+ try builtin_node.params.push(type_node);
+ builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ return maybeSuppressResult(rp, scope, result_used, &builtin_node.base);
+}
+
+fn qualTypeHaswrappingOverflow(qt: ZigClangQualType) bool {
+ if (cIsSignedInteger(qt) or cIsFloating(qt)) {
+ // float and signed integer overflow is undefined behavior.
+ return false;
+ } else {
+ // unsigned integer overflow wraps around.
+ return true;
+ }
+}
+
+fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnaryOperator, used: ResultUsed) TransError!*ast.Node {
+ const op_expr = ZigClangUnaryOperator_getSubExpr(stmt);
+ switch (ZigClangUnaryOperator_getOpcode(stmt)) {
+ .PostInc => if (qualTypeHaswrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
+ return transCreatePostCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used)
+ else
+ return transCreatePostCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used),
+ .PostDec => if (qualTypeHaswrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
+ return transCreatePostCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used)
+ else
+ return transCreatePostCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
+ .PreInc => if (qualTypeHaswrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
+ return transCreatePreCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used)
+ else
+ return transCreatePreCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used),
+ .PreDec => if (qualTypeHaswrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
+ return transCreatePreCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used)
+ else
+ return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
+ .AddrOf => {
+ const op_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
+ op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value);
+ return &op_node.base;
+ },
+ .Deref => {
+ const value_node = try transExpr(rp, scope, op_expr, used, .r_value);
+ var is_ptr = false;
+ const fn_ty = qualTypeGetFnProto(ZigClangExpr_getType(op_expr), &is_ptr);
+ if (fn_ty != null and is_ptr)
+ return value_node;
+ const unwrapped = try transCreateNodeUnwrapNull(rp.c, value_node);
+ return transCreateNodePtrDeref(rp.c, unwrapped);
+ },
+ .Plus => return transExpr(rp, scope, op_expr, used, .r_value),
+ .Minus => {
+ if (!qualTypeHaswrappingOverflow(ZigClangExpr_getType(op_expr))) {
+ const op_node = try transCreateNodePrefixOp(rp.c, .Negation, .Minus, "-");
+ op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
+ return &op_node.base;
+ } else if (cIsUnsignedInteger(ZigClangExpr_getType(op_expr))) {
+ // we gotta emit 0 -% x
+ const zero = try transCreateNodeInt(rp.c, 0);
+ const token = try appendToken(rp.c, .MinusPercent, "-%");
+ const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
+ return transCreateNodeInfixOp(rp, scope, zero, .SubWrap, token, expr, used, true);
} else
- null;
+ return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "C negation with non float non integer", .{});
+ },
+ .Not => {
+ const op_node = try transCreateNodePrefixOp(rp.c, .BitNot, .Tilde, "~");
+ op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
+ return &op_node.base;
+ },
+ .LNot => {
+ const op_node = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");
+ op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true);
+ return &op_node.base;
+ },
+ .Extension => {
+ return transExpr(rp, scope, ZigClangUnaryOperator_getSubExpr(stmt), used, .l_value);
+ },
+ else => return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "unsupported C translation {}", .{ZigClangUnaryOperator_getOpcode(stmt)}),
+ }
+}
- const field_node = try c.a().create(ast.Node.ContainerField);
- field_node.* = .{
- .doc_comments = null,
- .comptime_token = null,
- .name_token = field_name_tok,
- .type_expr = null,
- .value_expr = int_node,
- .align_expr = null,
- };
+fn transCreatePreCrement(
+ rp: RestorePoint,
+ scope: *Scope,
+ stmt: *const ZigClangUnaryOperator,
+ op: ast.Node.InfixOp.Op,
+ op_tok_id: std.zig.Token.Id,
+ bytes: []const u8,
+ used: ResultUsed,
+) TransError!*ast.Node {
+ const op_expr = ZigClangUnaryOperator_getSubExpr(stmt);
+
+ if (used == .unused) {
+ // common case
+ // c: ++expr
+ // zig: expr += 1
+ const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
+ const token = try appendToken(rp.c, op_tok_id, bytes);
+ const one = try transCreateNodeInt(rp.c, 1);
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false);
+ }
+ // worst case
+ // c: ++expr
+ // zig: (blk: {
+ // zig: const _ref = &expr;
+ // zig: _ref.* += 1;
+ // zig: break :blk _ref.*
+ // zig: })
+ const block_scope = try Scope.Block.init(rp.c, scope, "blk");
+ block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label);
+ const ref = try std.fmt.allocPrint(rp.c.a(), "_ref_{}", .{rp.c.getMangle()});
+
+ const node = try transCreateNodeVarDecl(rp.c, false, true, ref);
+ node.eq_token = try appendToken(rp.c, .Equal, "=");
+ const rhs_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
+ rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
+ node.init_node = &rhs_node.base;
+ node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
+ try block_scope.block_node.statements.push(&node.base);
+
+ const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
+ const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ const token = try appendToken(rp.c, op_tok_id, bytes);
+ const one = try transCreateNodeInt(rp.c, 1);
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
+ try block_scope.block_node.statements.push(assign);
+
+ const break_node = try transCreateNodeBreak(rp.c, block_scope.label);
+ break_node.rhs = ref_node;
+ try block_scope.block_node.statements.push(&break_node.base);
+ block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
+ // semicolon must immediately follow rbrace because it is the last token in a block
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
+ grouped_expr.* = .{
+ .lparen = try appendToken(rp.c, .LParen, "("),
+ .expr = &block_scope.block_node.base,
+ .rparen = try appendToken(rp.c, .RParen, ")"),
+ };
+ return &grouped_expr.base;
+}
- try container_node.fields_and_decls.push(&field_node.base);
- _ = try appendToken(c, .Comma, ",");
- // In C each enum value is in the global namespace. So we put them there too.
- // At this point we can rely on the enum emitting successfully.
- try addEnumTopLevel(c, name, field_name, enum_val_name);
+fn transCreatePostCrement(
+ rp: RestorePoint,
+ scope: *Scope,
+ stmt: *const ZigClangUnaryOperator,
+ op: ast.Node.InfixOp.Op,
+ op_tok_id: std.zig.Token.Id,
+ bytes: []const u8,
+ used: ResultUsed,
+) TransError!*ast.Node {
+ const op_expr = ZigClangUnaryOperator_getSubExpr(stmt);
+
+ if (used == .unused) {
+ // common case
+ // c: ++expr
+ // zig: expr += 1
+ const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
+ const token = try appendToken(rp.c, op_tok_id, bytes);
+ const one = try transCreateNodeInt(rp.c, 1);
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false);
+ }
+ // worst case
+ // c: expr++
+ // zig: (blk: {
+ // zig: const _ref = &expr;
+ // zig: const _tmp = _ref.*;
+ // zig: _ref.* += 1;
+ // zig: break :blk _tmp
+ // zig: })
+ const block_scope = try Scope.Block.init(rp.c, scope, "blk");
+ block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label);
+ const ref = try std.fmt.allocPrint(rp.c.a(), "_ref_{}", .{rp.c.getMangle()});
+
+ const node = try transCreateNodeVarDecl(rp.c, false, true, ref);
+ node.eq_token = try appendToken(rp.c, .Equal, "=");
+ const rhs_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
+ rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
+ node.init_node = &rhs_node.base;
+ node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
+ try block_scope.block_node.statements.push(&node.base);
+
+ const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
+ const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
+ _ = try appendToken(rp.c, .Semicolon, ";");
+
+ const tmp = try std.fmt.allocPrint(rp.c.a(), "_tmp_{}", .{rp.c.getMangle()});
+ const tmp_node = try transCreateNodeVarDecl(rp.c, false, true, tmp);
+ tmp_node.eq_token = try appendToken(rp.c, .Equal, "=");
+ tmp_node.init_node = ref_node;
+ tmp_node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
+ try block_scope.block_node.statements.push(&tmp_node.base);
+
+ const token = try appendToken(rp.c, op_tok_id, bytes);
+ const one = try transCreateNodeInt(rp.c, 1);
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
+ try block_scope.block_node.statements.push(assign);
+
+ const break_node = try transCreateNodeBreak(rp.c, block_scope.label);
+ break_node.rhs = try transCreateNodeIdentifier(rp.c, tmp);
+ try block_scope.block_node.statements.push(&break_node.base);
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
+ const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
+ grouped_expr.* = .{
+ .lparen = try appendToken(rp.c, .LParen, "("),
+ .expr = &block_scope.block_node.base,
+ .rparen = try appendToken(rp.c, .RParen, ")"),
+ };
+ return &grouped_expr.base;
+}
+
+fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundAssignOperator, used: ResultUsed) TransError!*ast.Node {
+ switch (ZigClangCompoundAssignOperator_getOpcode(stmt)) {
+ .MulAssign => if (qualTypeHaswrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt)))
+ return transCreateCompoundAssign(rp, scope, stmt, .AssignMulWrap, .AsteriskPercentEqual, "*%=", .MulWrap, .AsteriskPercent, "*%", used)
+ else
+ return transCreateCompoundAssign(rp, scope, stmt, .AssignMul, .AsteriskEqual, "*=", .Mul, .Asterisk, "*", used),
+ .AddAssign => if (qualTypeHaswrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt)))
+ return transCreateCompoundAssign(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", .AddWrap, .PlusPercent, "+%", used)
+ else
+ return transCreateCompoundAssign(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", .Add, .Plus, "+", used),
+ .SubAssign => if (qualTypeHaswrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt)))
+ return transCreateCompoundAssign(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", .SubWrap, .MinusPercent, "-%", used)
+ else
+ return transCreateCompoundAssign(rp, scope, stmt, .AssignSub, .MinusPercentEqual, "-=", .Sub, .Minus, "-", used),
+ .ShlAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftLeft, .AngleBracketAngleBracketLeftEqual, "<<=", .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<", used),
+ .ShrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftRight, .AngleBracketAngleBracketRightEqual, ">>=", .BitShiftRight, .AngleBracketAngleBracketRight, ">>", used),
+ .AndAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitAnd, .AmpersandEqual, "&=", .BitAnd, .Ampersand, "&", used),
+ .XorAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitXor, .CaretEqual, "^=", .BitXor, .Caret, "^", used),
+ .OrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitOr, .PipeEqual, "|=", .BitOr, .Pipe, "|", used),
+ else => return revertAndWarn(
+ rp,
+ error.UnsupportedTranslation,
+ ZigClangCompoundAssignOperator_getBeginLoc(stmt),
+ "unsupported C translation {}",
+ .{ZigClangCompoundAssignOperator_getOpcode(stmt)},
+ ),
+ }
+}
+
+fn transCreateCompoundAssign(
+ rp: RestorePoint,
+ scope: *Scope,
+ stmt: *const ZigClangCompoundAssignOperator,
+ assign_op: ast.Node.InfixOp.Op,
+ assign_tok_id: std.zig.Token.Id,
+ assign_bytes: []const u8,
+ bin_op: ast.Node.InfixOp.Op,
+ bin_tok_id: std.zig.Token.Id,
+ bin_bytes: []const u8,
+ used: ResultUsed,
+) TransError!*ast.Node {
+ const is_shift = bin_op == .BitShiftLeft or bin_op == .BitShiftRight;
+ const lhs = ZigClangCompoundAssignOperator_getLHS(stmt);
+ const rhs = ZigClangCompoundAssignOperator_getRHS(stmt);
+ const loc = ZigClangCompoundAssignOperator_getBeginLoc(stmt);
+ if (used == .unused) {
+ // common case
+ // c: lhs += rhs
+ // zig: lhs += rhs
+ const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
+ const eq_token = try appendToken(rp.c, assign_tok_id, assign_bytes);
+ var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
+
+ if (is_shift) {
+ const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
+ const rhs_type = try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc);
+ try as_node.params.push(rhs_type);
+ _ = try appendToken(rp.c, .Comma, ",");
+ try as_node.params.push(rhs_node);
+ as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ rhs_node = &as_node.base;
}
- container_node.rbrace_token = try appendToken(c, .RBrace, "}");
+ if (scope.id != .Condition)
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ return transCreateNodeInfixOp(rp, scope, lhs_node, assign_op, eq_token, rhs_node, .used, false);
+ }
+ // worst case
+ // c: lhs += rhs
+ // zig: (blk: {
+ // zig: const _ref = &lhs;
+ // zig: _ref.* = _ref.* + rhs;
+ // zig: break :blk _ref.*
+ // zig: })
+ const block_scope = try Scope.Block.init(rp.c, scope, "blk");
+ block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label);
+ const ref = try std.fmt.allocPrint(rp.c.a(), "_ref_{}", .{rp.c.getMangle()});
+
+ const node = try transCreateNodeVarDecl(rp.c, false, true, ref);
+ node.eq_token = try appendToken(rp.c, .Equal, "=");
+ const addr_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
+ addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value);
+ node.init_node = &addr_node.base;
+ node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
+ try block_scope.block_node.statements.push(&node.base);
+
+ const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
+ const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes);
+ var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
+ if (is_shift) {
+ const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
+ const rhs_type = try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc);
+ try as_node.params.push(rhs_type);
+ _ = try appendToken(rp.c, .Comma, ",");
+ try as_node.params.push(rhs_node);
+ as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ rhs_node = &as_node.base;
+ }
+ const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false);
- break :blk &container_node.base;
- } else
- try transCreateNodeOpaqueType(c);
+ _ = try appendToken(rp.c, .Semicolon, ";");
- const semicolon_token = try appendToken(c, .Semicolon, ";");
+ const eq_token = try appendToken(rp.c, .Equal, "=");
+ const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, eq_token, rhs_bin, .used, false);
+ try block_scope.block_node.statements.push(assign);
- const node = try c.a().create(ast.Node.VarDecl);
- node.* = ast.Node.VarDecl{
- .visib_token = visib_tok,
- .mut_token = const_tok,
- .name_token = name_tok,
- .eq_token = eq_tok,
- .init_node = init_node,
- .semicolon_token = semicolon_token,
- .doc_comments = null,
- .comptime_token = null,
- .extern_export_token = null,
- .thread_local_token = null,
- .lib_name = null,
- .type_node = null,
- .align_node = null,
- .section_node = null,
+ const break_node = try transCreateNodeBreak(rp.c, block_scope.label);
+ break_node.rhs = ref_node;
+ try block_scope.block_node.statements.push(&break_node.base);
+ block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
+ // semicolon must immediately follow rbrace because it is the last token in a block
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
+ grouped_expr.* = .{
+ .lparen = try appendToken(rp.c, .LParen, "("),
+ .expr = &block_scope.block_node.base,
+ .rparen = try appendToken(rp.c, .RParen, ")"),
};
+ return &grouped_expr.base;
+}
- try addTopLevelDecl(c, name, &node.base);
- if (!is_unnamed)
- try c.alias_list.push(.{ .alias = bare_name, .name = name });
- return transCreateNodeIdentifier(c, name);
+fn transCPtrCast(
+ rp: RestorePoint,
+ loc: ZigClangSourceLocation,
+ dst_type: ZigClangQualType,
+ src_type: ZigClangQualType,
+ expr: *ast.Node,
+) !*ast.Node {
+ const ty = ZigClangQualType_getTypePtr(dst_type);
+ const child_type = ZigClangType_getPointeeType(ty);
+
+ // Implicit downcasting from higher to lower alignment values is forbidden,
+ // use @alignCast to side-step this problem
+ const ptrcast_node = try transCreateNodeBuiltinFnCall(rp.c, "@ptrCast");
+ const dst_type_node = try transType(rp, ty, loc);
+ try ptrcast_node.params.push(dst_type_node);
+ _ = try appendToken(rp.c, .Comma, ",");
+
+ if (ZigClangType_isVoidType(qualTypeCanon(child_type))) {
+ // void has 1-byte alignment, so @alignCast is not needed
+ try ptrcast_node.params.push(expr);
+ } else {
+ const aligncast_node = try transCreateNodeBuiltinFnCall(rp.c, "@alignCast");
+ const alignof_node = try transCreateNodeBuiltinFnCall(rp.c, "@alignOf");
+ const child_type_node = try transQualType(rp, child_type, loc);
+ try alignof_node.params.push(child_type_node);
+ alignof_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ try aligncast_node.params.push(&alignof_node.base);
+ _ = try appendToken(rp.c, .Comma, ",");
+ try aligncast_node.params.push(expr);
+ aligncast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ try ptrcast_node.params.push(&aligncast_node.base);
+ }
+ ptrcast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+
+ return &ptrcast_node.base;
}
-fn addEnumTopLevel(c: *Context, enum_name: []const u8, field_name: []const u8, enum_val_name: []const u8) !void {
- const visib_tok = try appendToken(c, .Keyword_pub, "pub");
- const const_tok = try appendToken(c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(c, enum_val_name);
- const eq_tok = try appendToken(c, .Equal, "=");
+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)
+ "__switch"
+ else
+ null);
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ return &br.base;
+}
- const enum_ident = try transCreateNodeIdentifier(c, enum_name);
- const period_tok = try appendToken(c, .Period, ".");
- const field_ident = try transCreateNodeIdentifier(c, field_name);
+fn transFloatingLiteral(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangFloatingLiteral, used: ResultUsed) TransError!*ast.Node {
+ // TODO use something more accurate
+ const dbl = ZigClangAPFloat_getValueAsApproximateDouble(stmt);
+ const node = try rp.c.a().create(ast.Node.FloatLiteral);
+ node.* = .{
+ .token = try appendTokenFmt(rp.c, .FloatLiteral, "{d}", .{dbl}),
+ };
+ return maybeSuppressResult(rp, scope, used, &node.base);
+}
- const field_access_node = try c.a().create(ast.Node.InfixOp);
- field_access_node.* = .{
- .op_token = period_tok,
- .lhs = enum_ident,
- .op = .Period,
- .rhs = field_ident,
+fn transConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangConditionalOperator, used: ResultUsed) TransError!*ast.Node {
+ const grouped = scope.id == .Condition;
+ const lparen = if (grouped) try appendToken(rp.c, .LParen, "(") else undefined;
+ const if_node = try transCreateNodeIf(rp.c);
+ var cond_scope = Scope{
+ .parent = scope,
+ .id = .Condition,
};
- const semicolon_token = try appendToken(c, .Semicolon, ";");
- const node = try c.a().create(ast.Node.VarDecl);
- node.* = ast.Node.VarDecl{
- .visib_token = visib_tok,
- .mut_token = const_tok,
- .name_token = name_tok,
- .eq_token = eq_tok,
- .init_node = &field_access_node.base,
- .semicolon_token = semicolon_token,
- .thread_local_token = null,
- .doc_comments = null,
- .comptime_token = null,
- .extern_export_token = null,
- .lib_name = null,
- .type_node = null,
- .align_node = null,
- .section_node = null,
+ 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, cond_expr, .used, .r_value, false);
+ _ = try appendToken(rp.c, .RParen, ")");
+
+ if_node.body = try transExpr(rp, scope, true_expr, .used, .r_value);
+
+ if_node.@"else" = try transCreateNodeElse(rp.c);
+ if_node.@"else".?.body = try transExpr(rp, scope, false_expr, .used, .r_value);
+
+ if (grouped) {
+ const rparen = try appendToken(rp.c, .RParen, ")");
+ const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
+ grouped_expr.* = .{
+ .lparen = lparen,
+ .expr = &if_node.base,
+ .rparen = rparen,
+ };
+ return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
+ } else {
+ return maybeSuppressResult(rp, scope, used, &if_node.base);
+ }
+}
+
+fn maybeSuppressResult(
+ rp: RestorePoint,
+ scope: *Scope,
+ used: ResultUsed,
+ result: *ast.Node,
+) TransError!*ast.Node {
+ if (used == .used) return result;
+ if (scope.id != .Condition) {
+ // NOTE: This is backwards, but the semicolon must immediately follow the node.
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ } else { // TODO is there a way to avoid this hack?
+ // this parenthesis must come immediately following the node
+ _ = try appendToken(rp.c, .RParen, ")");
+ // these need to come before _
+ _ = try appendToken(rp.c, .Colon, ":");
+ _ = try appendToken(rp.c, .LParen, "(");
+ }
+ const lhs = try transCreateNodeIdentifier(rp.c, "_");
+ const op_token = try appendToken(rp.c, .Equal, "=");
+ const op_node = try rp.c.a().create(ast.Node.InfixOp);
+ op_node.* = .{
+ .op_token = op_token,
+ .lhs = lhs,
+ .op = .Assign,
+ .rhs = result,
};
+ return &op_node.base;
+}
- try addTopLevelDecl(c, field_name, &node.base);
+fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: *ast.Node) !void {
+ try c.tree.root_node.decls.push(decl_node);
+ _ = try c.global_scope.sym_table.put(name, decl_node);
+}
+
+fn transQualType(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) TypeError!*ast.Node {
+ return transType(rp, ZigClangQualType_getTypePtr(qt), source_loc);
}
fn isCBuiltinType(qt: ZigClangQualType, kind: ZigClangBuiltinTypeKind) bool {
@@ -1552,6 +2665,95 @@ fn qualTypeIsPtr(qt: ZigClangQualType) bool {
return ZigClangType_getTypeClass(qualTypeCanon(qt)) == .Pointer;
}
+fn qualTypeIntBitWidth(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) !u32 {
+ const ty = ZigClangQualType_getTypePtr(qt);
+
+ switch (ZigClangType_getTypeClass(ty)) {
+ .Builtin => {
+ const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
+
+ switch (ZigClangBuiltinType_getKind(builtin_ty)) {
+ .Char_U,
+ .UChar,
+ .Char_S,
+ .SChar,
+ => return 8,
+ .UInt128,
+ .Int128,
+ => return 128,
+ else => return 0,
+ }
+
+ unreachable;
+ },
+ .Typedef => {
+ const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
+ const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
+ const type_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_decl)));
+
+ if (mem.eql(u8, type_name, "uint8_t") or mem.eql(u8, type_name, "int8_t")) {
+ return 8;
+ } else if (mem.eql(u8, type_name, "uint16_t") or mem.eql(u8, type_name, "int16_t")) {
+ return 16;
+ } else if (mem.eql(u8, type_name, "uint32_t") or mem.eql(u8, type_name, "int32_t")) {
+ return 32;
+ } else if (mem.eql(u8, type_name, "uint64_t") or mem.eql(u8, type_name, "int64_t")) {
+ return 64;
+ } else {
+ return 0;
+ }
+ },
+ else => return 0,
+ }
+
+ unreachable;
+}
+
+fn qualTypeToLog2IntRef(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) !*ast.Node {
+ const int_bit_width = try qualTypeIntBitWidth(rp, qt, source_loc);
+
+ if (int_bit_width != 0) {
+ // we can perform the log2 now.
+ const cast_bit_width = std.math.log2_int(u64, int_bit_width);
+ const node = try rp.c.a().create(ast.Node.IntegerLiteral);
+ node.* = ast.Node.IntegerLiteral{
+ .token = try appendTokenFmt(rp.c, .Identifier, "u{}", .{cast_bit_width}),
+ };
+ return &node.base;
+ }
+
+ const zig_type_node = try transQualType(rp, qt, source_loc);
+
+ // @import("std").math.Log2Int(c_long);
+ //
+ // FnCall
+ // FieldAccess
+ // FieldAccess
+ // FnCall (.builtin = true)
+ // Symbol "import"
+ // StringLiteral "std"
+ // Symbol "math"
+ // Symbol "Log2Int"
+ // Symbol <zig_type_node> (var from above)
+
+ const import_fn_call = try transCreateNodeBuiltinFnCall(rp.c, "@import");
+ const std_token = try appendToken(rp.c, .StringLiteral, "\"std\"");
+ const std_node = try rp.c.a().create(ast.Node.StringLiteral);
+ std_node.* = ast.Node.StringLiteral{
+ .token = std_token,
+ };
+ try import_fn_call.params.push(&std_node.base);
+ import_fn_call.rparen_token = try appendToken(rp.c, .RParen, ")");
+
+ const inner_field_access = try transCreateNodeFieldAccess(rp.c, &import_fn_call.base, "math");
+ const outer_field_access = try transCreateNodeFieldAccess(rp.c, inner_field_access, "Log2Int");
+ const log2int_fn_call = try transCreateNodeFnCall(rp.c, outer_field_access);
+ try @fieldParentPtr(ast.Node.SuffixOp, "base", &log2int_fn_call.base).op.Call.params.push(zig_type_node);
+ log2int_fn_call.rtoken = try appendToken(rp.c, .RParen, ")");
+
+ return &log2int_fn_call.base;
+}
+
fn qualTypeChildIsFnProto(qt: ZigClangQualType) bool {
const ty = ZigClangQualType_getTypePtr(qt);
@@ -1601,6 +2803,14 @@ fn getExprQualType(c: *Context, expr: *const ZigClangExpr) ZigClangQualType {
return ZigClangExpr_getType(expr);
}
+fn getExprQualTypeBeforeImplicitCast(c: *Context, expr: *const ZigClangExpr) ZigClangQualType {
+ if (ZigClangExpr_getStmtClass(expr) == .ImplicitCastExprClass) {
+ const cast_expr = @ptrCast(*const ZigClangImplicitCastExpr, expr);
+ return getExprQualType(c, ZigClangImplicitCastExpr_getSubExpr(cast_expr));
+ }
+ return ZigClangExpr_getType(expr);
+}
+
fn typeIsOpaque(c: *Context, ty: *const ZigClangType, loc: ZigClangSourceLocation) bool {
switch (ZigClangType_getTypeClass(ty)) {
.Builtin => {
@@ -1657,53 +2867,109 @@ fn cIsUnsignedInteger(qt: ZigClangQualType) bool {
};
}
+fn cIsSignedInteger(qt: ZigClangQualType) bool {
+ const c_type = qualTypeCanon(qt);
+ if (ZigClangType_getTypeClass(c_type) != .Builtin) return false;
+ const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
+ return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
+ .SChar,
+ .Short,
+ .Int,
+ .Long,
+ .LongLong,
+ .Int128,
+ .WChar_S,
+ => true,
+ else => false,
+ };
+}
+
+fn cIsFloating(qt: ZigClangQualType) bool {
+ const c_type = qualTypeCanon(qt);
+ if (ZigClangType_getTypeClass(c_type) != .Builtin) return false;
+ const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
+ return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
+ .Float,
+ .Double,
+ .Float128,
+ .LongDouble,
+ => true,
+ else => false,
+ };
+}
+
fn transCreateNodeAssign(
rp: RestorePoint,
scope: *Scope,
result_used: ResultUsed,
lhs: *const ZigClangExpr,
rhs: *const ZigClangExpr,
-) !*ast.Node.InfixOp {
+) !*ast.Node {
// common case
// c: lhs = rhs
// zig: lhs = rhs
if (result_used == .unused) {
const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
const eq_token = try appendToken(rp.c, .Equal, "=");
- const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- const node = try rp.c.a().create(ast.Node.InfixOp);
- node.* = .{
- .op_token = eq_token,
- .lhs = lhs_node,
- .op = .Assign,
- .rhs = rhs_node,
- };
- return node;
+ var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
+ if (isBoolRes(rhs_node)) {
+ const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt");
+ try builtin_node.params.push(rhs_node);
+ builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ rhs_node = &builtin_node.base;
+ }
+ if (scope.id != .Condition)
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ return transCreateNodeInfixOp(rp, scope, lhs_node, .Assign, eq_token, rhs_node, .used, false);
}
// worst case
// c: lhs = rhs
- // zig: (x: {
+ // zig: (blk: {
// zig: const _tmp = rhs;
// zig: lhs = _tmp;
- // zig: break :x _tmp
+ // zig: break :blk _tmp
// zig: })
- return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- ZigClangExpr_getBeginLoc(lhs),
- "TODO: worst case assign op expr",
- .{},
- );
+ const block_scope = try Scope.Block.init(rp.c, scope, "blk");
+ block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label);
+ const tmp = try std.fmt.allocPrint(rp.c.a(), "_tmp_{}", .{rp.c.getMangle()});
+
+ const node = try transCreateNodeVarDecl(rp.c, false, true, tmp);
+ node.eq_token = try appendToken(rp.c, .Equal, "=");
+ var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
+ if (isBoolRes(rhs_node)) {
+ const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt");
+ try builtin_node.params.push(rhs_node);
+ builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ rhs_node = &builtin_node.base;
+ }
+ node.init_node = rhs_node;
+ node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
+ try block_scope.block_node.statements.push(&node.base);
+
+ const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
+ const eq_token = try appendToken(rp.c, .Equal, "=");
+ const ident = try transCreateNodeIdentifier(rp.c, tmp);
+ _ = try appendToken(rp.c, .Semicolon, ";");
+
+ const assign = try transCreateNodeInfixOp(rp, scope, lhs_node, .Assign, eq_token, ident, .used, false);
+ try block_scope.block_node.statements.push(assign);
+
+ const break_node = try transCreateNodeBreak(rp.c, block_scope.label);
+ break_node.rhs = try transCreateNodeIdentifier(rp.c, tmp);
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ try block_scope.block_node.statements.push(&break_node.base);
+ block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
+ // semicolon must immediately follow rbrace because it is the last token in a block
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ return &block_scope.block_node.base;
}
fn transCreateNodeBuiltinFnCall(c: *Context, name: []const u8) !*ast.Node.BuiltinCall {
const builtin_token = try appendToken(c, .Builtin, name);
_ = try appendToken(c, .LParen, "(");
const node = try c.a().create(ast.Node.BuiltinCall);
- node.* = ast.Node.BuiltinCall{
+ node.* = .{
.builtin_token = builtin_token,
.params = ast.Node.BuiltinCall.ParamList.init(c.a()),
.rparen_token = undefined, // set after appending args
@@ -1714,10 +2980,10 @@ fn transCreateNodeBuiltinFnCall(c: *Context, name: []const u8) !*ast.Node.Builti
fn transCreateNodeFnCall(c: *Context, fn_expr: *ast.Node) !*ast.Node.SuffixOp {
_ = try appendToken(c, .LParen, "(");
const node = try c.a().create(ast.Node.SuffixOp);
- node.* = ast.Node.SuffixOp{
+ node.* = .{
.lhs = .{ .node = fn_expr },
- .op = ast.Node.SuffixOp.Op{
- .Call = ast.Node.SuffixOp.Op.Call{
+ .op = .{
+ .Call = .{
.params = ast.Node.SuffixOp.Op.Call.ParamList.init(c.a()),
.async_token = null,
},
@@ -1727,6 +2993,17 @@ fn transCreateNodeFnCall(c: *Context, fn_expr: *ast.Node) !*ast.Node.SuffixOp {
return node;
}
+fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node {
+ const field_access_node = try c.a().create(ast.Node.InfixOp);
+ field_access_node.* = .{
+ .op_token = try appendToken(c, .Period, "."),
+ .lhs = container,
+ .op = .Period,
+ .rhs = try transCreateNodeIdentifier(c, field_name),
+ };
+ return &field_access_node.base;
+}
+
fn transCreateNodePrefixOp(
c: *Context,
op: ast.Node.PrefixOp.Op,
@@ -1745,32 +3022,62 @@ fn transCreateNodePrefixOp(
fn transCreateNodeInfixOp(
rp: RestorePoint,
scope: *Scope,
- stmt: *const ZigClangBinaryOperator,
+ lhs_node: *ast.Node,
op: ast.Node.InfixOp.Op,
- op_tok_id: std.zig.Token.Id,
- bytes: []const u8,
+ op_token: ast.TokenIndex,
+ rhs_node: *ast.Node,
+ used: ResultUsed,
grouped: bool,
) !*ast.Node {
- const lparen = if (grouped) try appendToken(rp.c, .LParen, "(") else undefined;
- const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
- const op_token = try appendToken(rp.c, op_tok_id, bytes);
- const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
+ var lparen = if (grouped)
+ try appendToken(rp.c, .LParen, "(")
+ else
+ null;
const node = try rp.c.a().create(ast.Node.InfixOp);
- node.* = ast.Node.InfixOp{
+ node.* = .{
.op_token = op_token,
- .lhs = lhs,
+ .lhs = lhs_node,
.op = op,
- .rhs = rhs,
+ .rhs = rhs_node,
};
- if (!grouped) return &node.base;
+ if (!grouped) return maybeSuppressResult(rp, scope, used, &node.base);
const rparen = try appendToken(rp.c, .RParen, ")");
const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
- grouped_expr.* = ast.Node.GroupedExpression{
- .lparen = lparen,
+ grouped_expr.* = .{
+ .lparen = lparen.?,
.expr = &node.base,
.rparen = rparen,
};
- return &grouped_expr.base;
+ return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
+}
+
+fn transCreateNodeBoolInfixOp(
+ rp: RestorePoint,
+ scope: *Scope,
+ stmt: *const ZigClangBinaryOperator,
+ op: ast.Node.InfixOp.Op,
+ used: ResultUsed,
+ grouped: bool,
+) !*ast.Node {
+ std.debug.assert(op == .BoolAnd or op == .BoolOr);
+
+ const lhs_hode = try transBoolExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value, true);
+ const op_token = if (op == .BoolAnd)
+ try appendToken(rp.c, .Keyword_and, "and")
+ else
+ try appendToken(rp.c, .Keyword_or, "or");
+ const rhs = try transBoolExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value, true);
+
+ return transCreateNodeInfixOp(
+ rp,
+ scope,
+ lhs_hode,
+ op,
+ op_token,
+ rhs,
+ used,
+ grouped,
+ );
}
fn transCreateNodePtrType(
@@ -1797,9 +3104,9 @@ fn transCreateNodePtrType(
.Asterisk => try appendToken(c, .Asterisk, "*"),
else => unreachable,
};
- node.* = ast.Node.PrefixOp{
+ node.* = .{
.op_token = op_token,
- .op = ast.Node.PrefixOp.Op{
+ .op = .{
.PtrType = .{
.const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null,
.volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null,
@@ -1810,11 +3117,17 @@ fn transCreateNodePtrType(
return node;
}
-fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*ast.Node {
- const num_limbs = ZigClangAPSInt_getNumWords(int.?);
+fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {
+ const num_limbs = ZigClangAPSInt_getNumWords(int);
+ var aps_int = int;
+ const is_negative = ZigClangAPSInt_isSigned(int) and ZigClangAPSInt_isNegative(int);
+ if (is_negative)
+ aps_int = ZigClangAPSInt_negate(aps_int);
var big = try std.math.big.Int.initCapacity(c.a(), num_limbs);
+ if (is_negative)
+ big.negate();
defer big.deinit();
- const data = ZigClangAPSInt_getRawData(int.?);
+ const data = ZigClangAPSInt_getRawData(aps_int);
var i: @TypeOf(num_limbs) = 0;
while (i < num_limbs) : (i += 1) big.limbs[i] = data[i];
const str = big.toString(c.a(), 10) catch |err| switch (err) {
@@ -1823,16 +3136,18 @@ fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*ast.Node {
};
const token = try appendToken(c, .IntegerLiteral, str);
const node = try c.a().create(ast.Node.IntegerLiteral);
- node.* = ast.Node.IntegerLiteral{
+ node.* = .{
.token = token,
};
+ if (is_negative)
+ ZigClangAPSInt_free(aps_int);
return &node.base;
}
fn transCreateNodeReturnExpr(c: *Context) !*ast.Node.ControlFlowExpression {
const ltoken = try appendToken(c, .Keyword_return, "return");
const node = try c.a().create(ast.Node.ControlFlowExpression);
- node.* = ast.Node.ControlFlowExpression{
+ node.* = .{
.ltoken = ltoken,
.kind = .Return,
.rhs = null,
@@ -1843,7 +3158,7 @@ fn transCreateNodeReturnExpr(c: *Context) !*ast.Node.ControlFlowExpression {
fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node {
const token = try appendToken(c, .Keyword_undefined, "undefined");
const node = try c.a().create(ast.Node.UndefinedLiteral);
- node.* = ast.Node.UndefinedLiteral{
+ node.* = .{
.token = token,
};
return &node.base;
@@ -1852,7 +3167,7 @@ fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node {
fn transCreateNodeNullLiteral(c: *Context) !*ast.Node {
const token = try appendToken(c, .Keyword_null, "null");
const node = try c.a().create(ast.Node.NullLiteral);
- node.* = ast.Node.NullLiteral{
+ node.* = .{
.token = token,
};
return &node.base;
@@ -1864,13 +3179,13 @@ fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node {
else
try appendToken(c, .Keyword_false, "false");
const node = try c.a().create(ast.Node.BoolLiteral);
- node.* = ast.Node.BoolLiteral{
+ node.* = .{
.token = token,
};
return &node.base;
}
-fn transCreateNodeArrayInitializer(c: *Context, dot_tok: ast.TokenIndex) !*ast.Node.SuffixOp {
+fn transCreateNodeContainerInitializer(c: *Context, dot_tok: ast.TokenIndex) !*ast.Node.SuffixOp {
_ = try appendToken(c, .LBrace, "{");
const node = try c.a().create(ast.Node.SuffixOp);
node.* = ast.Node.SuffixOp{
@@ -1886,7 +3201,7 @@ fn transCreateNodeArrayInitializer(c: *Context, dot_tok: ast.TokenIndex) !*ast.N
fn transCreateNodeInt(c: *Context, int: var) !*ast.Node {
const token = try appendTokenFmt(c, .IntegerLiteral, "{}", .{int});
const node = try c.a().create(ast.Node.IntegerLiteral);
- node.* = ast.Node.IntegerLiteral{
+ node.* = .{
.token = token,
};
return &node.base;
@@ -1907,7 +3222,7 @@ fn transCreateNodeOpaqueType(c: *Context) !*ast.Node {
return &call_node.base;
}
-fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias_node: *ast.Node) !*ast.Node {
+fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias: *ast.Node.FnProto) !*ast.Node {
const scope = &c.global_scope.base;
const pub_tok = try appendToken(c, .Keyword_pub, "pub");
@@ -1916,8 +3231,6 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a
const name_tok = try appendIdentifier(c, name);
_ = try appendToken(c, .LParen, "(");
- const proto_alias = proto_alias_node.cast(ast.Node.FnProto).?;
-
var fn_params = ast.Node.FnProto.ParamList.init(c.a());
var it = proto_alias.params.iterator(0);
while (it.next()) |pn| {
@@ -2046,6 +3359,172 @@ fn transCreateNodeBlock(c: *Context, label: ?[]const u8) !*ast.Node.Block {
return block_node;
}
+fn transCreateNodeBreak(c: *Context, label: ?[]const u8) !*ast.Node.ControlFlowExpression {
+ const ltoken = try appendToken(c, .Keyword_break, "break");
+ const label_node = if (label) |l| blk: {
+ _ = try appendToken(c, .Colon, ":");
+ break :blk try transCreateNodeIdentifier(c, l);
+ } else null;
+ const node = try c.a().create(ast.Node.ControlFlowExpression);
+ node.* = .{
+ .ltoken = ltoken,
+ .kind = .{ .Break = label_node },
+ .rhs = null,
+ };
+ return node;
+}
+
+fn transCreateNodeVarDecl(c: *Context, is_pub: bool, is_const: bool, name: []const u8) !*ast.Node.VarDecl {
+ const visib_tok = if (is_pub) try appendToken(c, .Keyword_pub, "pub") else null;
+ const mut_tok = if (is_const) try appendToken(c, .Keyword_const, "const") else try appendToken(c, .Keyword_var, "var");
+ const name_tok = try appendIdentifier(c, name);
+
+ const node = try c.a().create(ast.Node.VarDecl);
+ node.* = .{
+ .doc_comments = null,
+ .visib_token = visib_tok,
+ .thread_local_token = null,
+ .name_token = name_tok,
+ .eq_token = undefined,
+ .mut_token = mut_tok,
+ .comptime_token = null,
+ .extern_export_token = null,
+ .lib_name = null,
+ .type_node = null,
+ .align_node = null,
+ .section_node = null,
+ .init_node = null,
+ .semicolon_token = undefined,
+ };
+ return node;
+}
+
+fn transCreateNodeWhile(c: *Context) !*ast.Node.While {
+ const while_tok = try appendToken(c, .Keyword_while, "while");
+ _ = try appendToken(c, .LParen, "(");
+
+ const node = try c.a().create(ast.Node.While);
+ node.* = .{
+ .label = null,
+ .inline_token = null,
+ .while_token = while_tok,
+ .condition = undefined,
+ .payload = null,
+ .continue_expr = null,
+ .body = undefined,
+ .@"else" = null,
+ };
+ return node;
+}
+
+fn transCreateNodeContinue(c: *Context) !*ast.Node {
+ const ltoken = try appendToken(c, .Keyword_continue, "continue");
+ const node = try c.a().create(ast.Node.ControlFlowExpression);
+ node.* = .{
+ .ltoken = ltoken,
+ .kind = .{ .Continue = null },
+ .rhs = null,
+ };
+ _ = try appendToken(c, .Semicolon, ";");
+ 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;
+}
+
+fn transCreateNodeShiftOp(
+ rp: RestorePoint,
+ scope: *Scope,
+ stmt: *const ZigClangBinaryOperator,
+ op: ast.Node.InfixOp.Op,
+ op_tok_id: std.zig.Token.Id,
+ bytes: []const u8,
+) !*ast.Node {
+ std.debug.assert(op == .BitShiftLeft or op == .BitShiftRight);
+
+ const lhs_expr = ZigClangBinaryOperator_getLHS(stmt);
+ const rhs_expr = ZigClangBinaryOperator_getRHS(stmt);
+ const rhs_location = ZigClangExpr_getBeginLoc(rhs_expr);
+ // lhs >> @as(u5, rh)
+
+ const lhs = try transExpr(rp, scope, lhs_expr, .used, .l_value);
+ const op_token = try appendToken(rp.c, op_tok_id, bytes);
+
+ const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
+ const rhs_type = try qualTypeToLog2IntRef(rp, ZigClangBinaryOperator_getType(stmt), rhs_location);
+ try as_node.params.push(rhs_type);
+ _ = try appendToken(rp.c, .Comma, ",");
+ const rhs = try transExpr(rp, scope, rhs_expr, .used, .r_value);
+ try as_node.params.push(rhs);
+ as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+
+ const node = try rp.c.a().create(ast.Node.InfixOp);
+ node.* = ast.Node.InfixOp{
+ .op_token = op_token,
+ .lhs = lhs,
+ .op = op,
+ .rhs = &as_node.base,
+ };
+
+ return &node.base;
+}
+
+fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node {
+ const node = try c.a().create(ast.Node.SuffixOp);
+ node.* = .{
+ .lhs = .{ .node = lhs },
+ .op = .Deref,
+ .rtoken = try appendToken(c, .PeriodAsterisk, ".*"),
+ };
+ return &node.base;
+}
+
+fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.SuffixOp {
+ _ = try appendToken(c, .LBrace, "[");
+ const node = try c.a().create(ast.Node.SuffixOp);
+ node.* = .{
+ .lhs = .{ .node = lhs },
+ .op = .{
+ .ArrayAccess = undefined,
+ },
+ .rtoken = undefined,
+ };
+ return node;
+}
+
const RestorePoint = struct {
c: *Context,
token_index: ast.TokenIndex,
@@ -2092,7 +3571,7 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
});
},
- .FunctionProto => {
+ .FunctionProto, .FunctionNoProto => {
const fn_proto_ty = @ptrCast(*const ZigClangFunctionProtoType, ty);
const fn_proto = try transFnProto(rp, null, fn_proto_ty, source_loc, null, false);
return &fn_proto.base;
@@ -2167,24 +3646,15 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
- const typedef_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_decl)));
- return transCreateNodeIdentifier(rp.c, typedef_name);
+ return (try transTypeDef(rp.c, typedef_decl)) orelse
+ revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate typedef declaration", .{});
},
.Record => {
const record_ty = @ptrCast(*const ZigClangRecordType, ty);
- // TODO this sould get the name from decl_table
- // struct Foo {
- // struct Bar{
- // int b;
- // };
- // struct Bar c;
- // };
const record_decl = ZigClangRecordType_getDecl(record_ty);
- if (try getContainerName(rp, record_decl)) |name|
- return transCreateNodeIdentifier(rp.c, name)
- else
- return transRecordDecl(rp.c, record_decl);
+ return (try transRecordDecl(rp.c, record_decl)) orelse
+ revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to resolve record declaration", .{});
},
.Enum => {
const enum_ty = @ptrCast(*const ZigClangEnumType, ty);
@@ -2205,6 +3675,10 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
const attributed_ty = @ptrCast(*const ZigClangAttributedType, ty);
return transQualType(rp, ZigClangAttributedType_getEquivalentType(attributed_ty), source_loc);
},
+ .MacroQualified => {
+ const macroqualified_ty = @ptrCast(*const ZigClangMacroQualifiedType, ty);
+ return transQualType(rp, ZigClangMacroQualifiedType_getModifiedType(macroqualified_ty), source_loc);
+ },
else => {
const type_name = rp.c.str(ZigClangType_getTypeClassName(ty));
return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{}'", .{type_name});
@@ -2212,22 +3686,6 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
}
}
-fn getContainerName(rp: RestorePoint, record_decl: *const ZigClangRecordDecl) !?[]const u8 {
- const bare_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, record_decl)));
-
- const container_kind_name = if (ZigClangRecordDecl_isUnion(record_decl))
- "union"
- else if (ZigClangRecordDecl_isStruct(record_decl))
- "struct"
- else
- return revertAndWarn(rp, error.UnsupportedType, ZigClangRecordDecl_getLocation(record_decl), "record {} is not a struct or union", .{bare_name});
-
- if (ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl) or bare_name.len == 0)
- return null;
-
- return try std.fmt.allocPrint(rp.c.a(), "{}_{}", .{ container_kind_name, bare_name });
-}
-
fn isCVoid(qt: ZigClangQualType) bool {
const ty = ZigClangQualType_getTypePtr(qt);
if (ZigClangType_getTypeClass(ty) == .Builtin) {
@@ -2241,7 +3699,6 @@ const FnDeclContext = struct {
fn_name: []const u8,
has_body: bool,
storage_class: ZigClangStorageClass,
- scope: **Scope,
is_export: bool,
};
@@ -2307,9 +3764,6 @@ fn finishTransFnProto(
// TODO check for always_inline attribute
// TODO check for align attribute
- var fndef_scope = Scope.FnDef.init(rp.c);
- const scope = &fndef_scope.base;
-
// pub extern fn name(...) T
const pub_tok = if (is_pub) try appendToken(rp.c, .Keyword_pub, "pub") else null;
const cc_tok = if (cc == .Stdcall) try appendToken(rp.c, .Keyword_stdcallcc, "stdcallcc") else null;
@@ -2335,15 +3789,11 @@ fn finishTransFnProto(
const param_name_tok: ?ast.TokenIndex = blk: {
if (fn_decl != null) {
const param = ZigClangFunctionDecl_getParamDecl(fn_decl.?, @intCast(c_uint, i));
- var param_name: []const u8 = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, param)));
+ const param_name: []const u8 = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, param)));
if (param_name.len < 1)
- param_name = "arg"[0..];
- const checked_param_name = if (try scope.createAlias(rp.c, param_name)) |a| blk: {
- try fndef_scope.params.push(.{ .name = param_name, .alias = a });
- break :blk a;
- } else param_name;
+ break :blk null;
- const result = try appendIdentifier(rp.c, checked_param_name);
+ const result = try appendIdentifier(rp.c, param_name);
_ = try appendToken(rp.c, .Colon, ":");
break :blk result;
}
@@ -2410,14 +3860,13 @@ fn finishTransFnProto(
};
const fn_proto = try rp.c.a().create(ast.Node.FnProto);
- fn_proto.* = ast.Node.FnProto{
- .base = ast.Node{ .id = ast.Node.Id.FnProto },
+ fn_proto.* = .{
.doc_comments = null,
.visib_token = pub_tok,
.fn_token = fn_tok,
.name_token = name_tok,
.params = fn_params,
- .return_type = ast.Node.FnProto.ReturnType{ .Explicit = return_type_node },
+ .return_type = .{ .Explicit = return_type_node },
.var_args_token = null, // TODO this field is broken in the AST data model
.extern_export_inline_token = extern_export_inline_tok,
.cc_token = cc_tok,
@@ -2523,6 +3972,41 @@ fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8,
return token_index;
}
+// TODO hook up with codegen
+fn isZigPrimitiveType(name: []const u8) bool {
+ if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) {
+ for (name[1..]) |c| {
+ switch (c) {
+ '0'...'9' => {},
+ else => return false,
+ }
+ }
+ return true;
+ }
+ // void is invalid in c so it doesn't need to be checked.
+ return mem.eql(u8, name, "comptime_float") or
+ mem.eql(u8, name, "comptime_int") or
+ mem.eql(u8, name, "bool") or
+ mem.eql(u8, name, "isize") or
+ mem.eql(u8, name, "usize") or
+ mem.eql(u8, name, "f16") or
+ mem.eql(u8, name, "f32") or
+ mem.eql(u8, name, "f64") or
+ mem.eql(u8, name, "f128") or
+ mem.eql(u8, name, "c_longdouble") or
+ mem.eql(u8, name, "noreturn") or
+ mem.eql(u8, name, "type") or
+ mem.eql(u8, name, "anyerror") or
+ mem.eql(u8, name, "c_short") or
+ mem.eql(u8, name, "c_ushort") or
+ mem.eql(u8, name, "c_int") or
+ mem.eql(u8, name, "c_uint") or
+ mem.eql(u8, name, "c_long") or
+ mem.eql(u8, name, "c_ulong") or
+ mem.eql(u8, name, "c_longlong") or
+ mem.eql(u8, name, "c_ulonglong");
+}
+
fn isValidZigIdentifier(name: []const u8) bool {
for (name) |c, i| {
switch (c) {
@@ -2573,27 +4057,31 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
const begin_loc = ZigClangMacroDefinitionRecord_getSourceRange_getBegin(macro);
const name = try c.str(raw_name);
- if (scope.contains(name)) {
+
+ // TODO https://github.com/ziglang/zig/issues/3756
+ // TODO https://github.com/ziglang/zig/issues/1802
+ const checked_name = if (isZigPrimitiveType(name)) try std.fmt.allocPrint(c.a(), "_{}", .{name}) else name;
+ if (scope.contains(checked_name)) {
continue;
}
const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc);
ctok.tokenizeCMacro(&tok_list, begin_c) catch |err| switch (err) {
error.OutOfMemory => |e| return e,
else => {
- try failDecl(c, begin_loc, name, "unable to tokenize macro definition", .{});
+ try failDecl(c, begin_loc, checked_name, "unable to tokenize macro definition", .{});
continue;
},
};
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, name));
+ assert(first_tok.id == .Identifier and mem.eql(u8, first_tok.bytes, name));
const next = tok_it.peek().?;
switch (next.id) {
.Identifier => {
// if it equals itself, ignore. for example, from stdio.h:
// #define stdin stdin
- if (std.mem.eql(u8, name, next.bytes)) {
+ if (mem.eql(u8, checked_name, next.bytes)) {
continue;
}
},
@@ -2610,12 +4098,12 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
} else false;
(if (macro_fn)
- transMacroFnDefine(c, &tok_it, name, begin_loc)
+ transMacroFnDefine(c, &tok_it, checked_name, begin_loc)
else
- transMacroDefine(c, &tok_it, name, begin_loc)) catch |err| switch (err) {
+ transMacroDefine(c, &tok_it, checked_name, begin_loc)) catch |err| switch (err) {
error.UnsupportedTranslation,
error.ParseError,
- => try failDecl(c, begin_loc, name, "unable to translate macro", .{}),
+ => try failDecl(c, begin_loc, checked_name, "unable to translate macro", .{}),
error.OutOfMemory => |e| return e,
};
},
@@ -2628,37 +4116,28 @@ fn transMacroDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8,
const rp = makeRestorePoint(c);
const scope = &c.global_scope.base;
- const visib_tok = try appendToken(c, .Keyword_pub, "pub");
- const mut_tok = try appendToken(c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(c, name);
- const eq_tok = try appendToken(c, .Equal, "=");
+ const node = try transCreateNodeVarDecl(c, true, true, name);
+ node.eq_token = try appendToken(c, .Equal, "=");
- const init_node = try parseCExpr(rp, it, source_loc, scope);
+ node.init_node = try parseCExpr(rp, it, source_loc, scope);
+ const last = it.next().?;
+ if (last.id != .Eof)
+ return revertAndWarn(
+ rp,
+ error.UnsupportedTranslation,
+ source_loc,
+ "unable to translate C expr, unexpected token: {}",
+ .{last.id},
+ );
- const node = try c.a().create(ast.Node.VarDecl);
- node.* = ast.Node.VarDecl{
- .doc_comments = null,
- .visib_token = visib_tok,
- .thread_local_token = null,
- .name_token = name_tok,
- .eq_token = eq_tok,
- .mut_token = mut_tok,
- .comptime_token = null,
- .extern_export_token = null,
- .lib_name = null,
- .type_node = null,
- .align_node = null,
- .section_node = null,
- .init_node = init_node,
- .semicolon_token = try appendToken(c, .Semicolon, ";"),
- };
+ node.semicolon_token = try appendToken(c, .Semicolon, ";");
_ = try c.global_scope.macro_table.put(name, &node.base);
}
fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {
const rp = makeRestorePoint(c);
- var fndef_scope = Scope.FnDef.init(c);
- const scope = &fndef_scope.base;
+ const block_scope = try Scope.Block.init(c, &c.global_scope.base, null);
+ const scope = &block_scope.base;
const pub_tok = try appendToken(c, .Keyword_pub, "pub");
const inline_tok = try appendToken(c, .Keyword_inline, "inline");
@@ -2676,7 +4155,7 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u
return error.ParseError;
const checked_name = if (try scope.createAlias(c, param_tok.bytes)) |alias| blk: {
- try fndef_scope.params.push(.{ .name = param_tok.bytes, .alias = alias });
+ try block_scope.variables.push(.{ .name = param_tok.bytes, .alias = alias });
break :blk alias;
} else param_tok.bytes;
@@ -2737,6 +4216,15 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u
const return_expr = try transCreateNodeReturnExpr(c);
const expr = try parseCExpr(rp, it, source_loc, scope);
+ const last = it.next().?;
+ if (last.id != .Eof)
+ return revertAndWarn(
+ rp,
+ error.UnsupportedTranslation,
+ source_loc,
+ "unable to translate C expr, unexpected token: {}",
+ .{last.id},
+ );
_ = try appendToken(c, .Semicolon, ";");
try type_of.params.push(expr);
return_expr.rhs = expr;
@@ -2838,7 +4326,10 @@ fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc:
if (it.peek().?.id == .RParen) {
_ = it.next();
- return inner_node;
+ if (it.peek().?.id != .LParen) {
+ return inner_node;
+ }
+ _ = it.next();
}
// hack to get zig fmt to render a comma in builtin calls
@@ -2930,8 +4421,8 @@ fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc:
rp,
error.UnsupportedTranslation,
source_loc,
- "unable to translate C expr",
- .{},
+ "unable to translate C expr, unexpected token: {}",
+ .{tok.id},
),
}
}
@@ -2944,24 +4435,17 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
.Dot => {
const name_tok = it.next().?;
if (name_tok.id != .Identifier)
- return revertAndWarn(
- rp,
- error.ParseError,
- source_loc,
- "unable to translate C expr",
- .{},
- );
-
- const op_token = try appendToken(rp.c, .Period, ".");
- const rhs = try transCreateNodeIdentifier(rp.c, name_tok.bytes);
- const access_node = try rp.c.a().create(ast.Node.InfixOp);
- access_node.* = .{
- .op_token = op_token,
- .lhs = node,
- .op = .Period,
- .rhs = rhs,
- };
- node = &access_node.base;
+ return error.ParseError;
+
+ node = try transCreateNodeFieldAccess(rp.c, node, name_tok.bytes);
+ },
+ .Arrow => {
+ const name_tok = it.next().?;
+ if (name_tok.id != .Identifier)
+ return error.ParseError;
+
+ const deref = try transCreateNodePtrDeref(rp.c, node);
+ node = try transCreateNodeFieldAccess(rp.c, deref, name_tok.bytes);
},
.Asterisk => {
if (it.peek().?.id == .RParen) {
@@ -2977,19 +4461,19 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
// expr * expr
const op_token = try appendToken(rp.c, .Asterisk, "*");
const rhs = try parseCPrimaryExpr(rp, it, source_loc, scope);
- const bitshift_node = try rp.c.a().create(ast.Node.InfixOp);
- bitshift_node.* = .{
+ const mul_node = try rp.c.a().create(ast.Node.InfixOp);
+ mul_node.* = .{
.op_token = op_token,
.lhs = node,
.op = .BitShiftLeft,
.rhs = rhs,
};
- node = &bitshift_node.base;
+ node = &mul_node.base;
}
},
.Shl => {
const op_token = try appendToken(rp.c, .AngleBracketAngleBracketLeft, "<<");
- const rhs = try parseCPrimaryExpr(rp, it, source_loc, scope);
+ const rhs = try parseCExpr(rp, it, source_loc, scope);
const bitshift_node = try rp.c.a().create(ast.Node.InfixOp);
bitshift_node.* = .{
.op_token = op_token,
@@ -2999,6 +4483,42 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
};
node = &bitshift_node.base;
},
+ .Pipe => {
+ const op_token = try appendToken(rp.c, .Pipe, "|");
+ const rhs = try parseCExpr(rp, it, source_loc, scope);
+ const or_node = try rp.c.a().create(ast.Node.InfixOp);
+ or_node.* = .{
+ .op_token = op_token,
+ .lhs = node,
+ .op = .BitOr,
+ .rhs = rhs,
+ };
+ node = &or_node.base;
+ },
+ .LBrace => {
+ const arr_node = try transCreateNodeArrayAccess(rp.c, node);
+ arr_node.op.ArrayAccess = try parseCExpr(rp, it, source_loc, scope);
+ arr_node.rtoken = try appendToken(rp.c, .RBrace, "]");
+ node = &arr_node.base;
+ if (it.next().?.id != .RBrace)
+ return error.ParseError;
+ },
+ .LParen => {
+ const call_node = try transCreateNodeFnCall(rp.c, node);
+ while (true) {
+ const arg = try parseCExpr(rp, it, source_loc, scope);
+ try call_node.op.Call.params.push(arg);
+ const next = it.next().?;
+ if (next.id == .Comma)
+ _ = try appendToken(rp.c, .Comma, ",")
+ else if (next.id == .RParen)
+ break
+ else
+ return error.ParseError;
+ }
+ call_node.rtoken = try appendToken(rp.c, .RParen, ")");
+ node = &call_node.base;
+ },
else => {
_ = it.prev();
return node;
@@ -3028,13 +4548,7 @@ fn parseCPrefixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
},
.Asterisk => {
const prefix_op_expr = try parseCPrefixOpExpr(rp, it, source_loc, scope);
- const node = try rp.c.a().create(ast.Node.SuffixOp);
- node.* = .{
- .lhs = .{ .node = prefix_op_expr },
- .op = .Deref,
- .rtoken = try appendToken(rp.c, .PeriodAsterisk, ".*"),
- };
- return &node.base;
+ return try transCreateNodePtrDeref(rp.c, prefix_op_expr);
},
else => {
_ = it.prev();
@@ -3043,26 +4557,76 @@ fn parseCPrefixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
}
}
-fn tokenSlice(c: *Context, token: ast.TokenIndex) []const u8 {
+fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 {
const tok = c.tree.tokens.at(token);
- return c.source_buffer.toSliceConst()[tok.start..tok.end];
+ return c.source_buffer.toSlice()[tok.start..tok.end];
}
-fn getFnDecl(c: *Context, ref: *ast.Node) ?*ast.Node {
- const init = if (ref.cast(ast.Node.VarDecl)) |v| v.init_node.? else return null;
- const name = if (init.cast(ast.Node.Identifier)) |id|
- tokenSlice(c, id.token)
- else
- return null;
- // TODO a.b.c
- if (c.global_scope.sym_table.get(name)) |kv| {
- if (kv.value.cast(ast.Node.VarDecl)) |val| {
- if (val.type_node) |type_node| {
- if (type_node.cast(ast.Node.PrefixOp)) |casted| {
- if (casted.rhs.id == .FnProto) {
- return casted.rhs;
+fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
+ if (node.id == .ContainerDecl) {
+ return node;
+ } else if (node.id == .PrefixOp) {
+ return node;
+ } else if (node.cast(ast.Node.Identifier)) |ident| {
+ if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |kv| {
+ if (kv.value.cast(ast.Node.VarDecl)) |var_decl|
+ return getContainer(c, var_decl.init_node.?);
+ }
+ } else if (node.cast(ast.Node.InfixOp)) |infix| {
+ if (infix.op != .Period)
+ return null;
+ if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
+ if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
+ var it = container.fields_and_decls.iterator(0);
+ while (it.next()) |field_ref| {
+ const field = field_ref.*.cast(ast.Node.ContainerField).?;
+ const ident = infix.rhs.cast(ast.Node.Identifier).?;
+ if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
+ return getContainer(c, field.type_expr.?);
+ }
+ }
+ }
+ }
+ }
+ return null;
+}
+
+fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
+ if (ref.cast(ast.Node.Identifier)) |ident| {
+ if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |kv| {
+ if (kv.value.cast(ast.Node.VarDecl)) |var_decl| {
+ if (var_decl.type_node) |ty|
+ return getContainer(c, ty);
+ }
+ }
+ } else if (ref.cast(ast.Node.InfixOp)) |infix| {
+ if (infix.op != .Period)
+ return null;
+ if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
+ if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
+ var it = container.fields_and_decls.iterator(0);
+ while (it.next()) |field_ref| {
+ const field = field_ref.*.cast(ast.Node.ContainerField).?;
+ const ident = infix.rhs.cast(ast.Node.Identifier).?;
+ if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
+ return getContainer(c, field.type_expr.?);
}
}
+ } else
+ return ty_node;
+ }
+ }
+ return null;
+}
+
+fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {
+ const init = if (ref.cast(ast.Node.VarDecl)) |v| v.init_node.? else return null;
+ if (getContainerTypeOf(c, init)) |ty_node| {
+ if (ty_node.cast(ast.Node.PrefixOp)) |prefix| {
+ if (prefix.op == .OptionalType) {
+ if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| {
+ return fn_proto;
+ }
}
}
}
@@ -3072,7 +4636,7 @@ fn getFnDecl(c: *Context, ref: *ast.Node) ?*ast.Node {
fn addMacros(c: *Context) !void {
var macro_it = c.global_scope.macro_table.iterator();
while (macro_it.next()) |kv| {
- if (getFnDecl(c, kv.value)) |proto_node| {
+ if (getFnProto(c, kv.value)) |proto_node| {
// If a macro aliases a global variable which is a function pointer, we conclude that
// the macro is intended to represent a function that assumes the function pointer
// variable is non-null and calls it.
diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp
@@ -1571,6 +1571,16 @@ const ZigClangTypedefNameDecl *ZigClangTypedefNameDecl_getCanonicalDecl(const Zi
return reinterpret_cast<const ZigClangTypedefNameDecl *>(decl);
}
+const ZigClangFunctionDecl *ZigClangFunctionDecl_getCanonicalDecl(const ZigClangFunctionDecl *self) {
+ const clang::FunctionDecl *decl = reinterpret_cast<const clang::FunctionDecl*>(self)->getCanonicalDecl();
+ return reinterpret_cast<const ZigClangFunctionDecl *>(decl);
+}
+
+const ZigClangVarDecl *ZigClangVarDecl_getCanonicalDecl(const ZigClangVarDecl *self) {
+ const clang::VarDecl *decl = reinterpret_cast<const clang::VarDecl*>(self)->getCanonicalDecl();
+ return reinterpret_cast<const ZigClangVarDecl *>(decl);
+}
+
const ZigClangRecordDecl *ZigClangRecordDecl_getDefinition(const ZigClangRecordDecl *zig_record_decl) {
const clang::RecordDecl *record_decl = reinterpret_cast<const clang::RecordDecl *>(zig_record_decl);
const clang::RecordDecl *definition = record_decl->getDefinition();
@@ -2161,6 +2171,11 @@ unsigned ZigClangAPFloat_convertToHexString(const ZigClangAPFloat *self, char *D
return casted->convertToHexString(DST, HexDigits, UpperCase, (llvm::APFloat::roundingMode)RM);
}
+double ZigClangAPFloat_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self) {
+ auto casted = reinterpret_cast<const clang::FloatingLiteral *>(self);
+ return casted->getValueAsApproximateDouble();
+}
+
enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self) {
auto casted = reinterpret_cast<const clang::StringLiteral *>(self);
return (ZigClangStringLiteral_StringKind)casted->getKind();
diff --git a/src/zig_clang.h b/src/zig_clang.h
@@ -856,6 +856,8 @@ ZIG_EXTERN_C const struct ZigClangEnumDecl *ZigClangEnumType_getDecl(const struc
ZIG_EXTERN_C const struct ZigClangTagDecl *ZigClangRecordDecl_getCanonicalDecl(const struct ZigClangRecordDecl *record_decl);
ZIG_EXTERN_C const struct ZigClangTagDecl *ZigClangEnumDecl_getCanonicalDecl(const struct ZigClangEnumDecl *);
ZIG_EXTERN_C const struct ZigClangTypedefNameDecl *ZigClangTypedefNameDecl_getCanonicalDecl(const struct ZigClangTypedefNameDecl *);
+ZIG_EXTERN_C const struct ZigClangFunctionDecl *ZigClangFunctionDecl_getCanonicalDecl(const ZigClangFunctionDecl *self);
+ZIG_EXTERN_C const struct ZigClangVarDecl *ZigClangVarDecl_getCanonicalDecl(const ZigClangVarDecl *self);
ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangRecordDecl_getDefinition(const struct ZigClangRecordDecl *);
ZIG_EXTERN_C const struct ZigClangEnumDecl *ZigClangEnumDecl_getDefinition(const struct ZigClangEnumDecl *);
@@ -985,6 +987,7 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDeclStmt_getBeginLoc(const st
ZIG_EXTERN_C unsigned ZigClangAPFloat_convertToHexString(const struct ZigClangAPFloat *self, char *DST,
unsigned HexDigits, bool UpperCase, enum ZigClangAPFloat_roundingMode RM);
+ZIG_EXTERN_C double ZigClangAPFloat_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self);
ZIG_EXTERN_C enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self);
ZIG_EXTERN_C const char *ZigClangStringLiteral_getString_bytes_begin_size(const struct ZigClangStringLiteral *self,
diff --git a/test/translate_c.zig b/test/translate_c.zig
@@ -127,16 +127,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
});
cases.addC_both("add, sub, mul, div, rem",
- \\int s(int a, int b) {
- \\ int c;
+ \\int s() {
+ \\ int a, b, c;
\\ c = a + b;
\\ c = a - b;
\\ c = a * b;
\\ c = a / b;
\\ c = a % b;
\\}
- \\unsigned u(unsigned a, unsigned b) {
- \\ unsigned c;
+ \\unsigned u() {
+ \\ unsigned a, b, c;
\\ c = a + b;
\\ c = a - b;
\\ c = a * b;
@@ -144,7 +144,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ c = a % b;
\\}
, &[_][]const u8{
- \\pub export fn s(a: c_int, b: c_int) c_int {
+ \\pub export fn s() c_int {
+ \\ var a: c_int = undefined;
+ \\ var b: c_int = undefined;
\\ var c: c_int = undefined;
\\ c = (a + b);
\\ c = (a - b);
@@ -152,7 +154,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ c = @divTrunc(a, b);
\\ c = @rem(a, b);
\\}
- \\pub export fn u(a: c_uint, b: c_uint) c_uint {
+ \\pub export fn u() c_uint {
+ \\ var a: c_uint = undefined;
+ \\ var b: c_uint = undefined;
\\ var c: c_uint = undefined;
\\ c = (a +% b);
\\ c = (a -% b);
@@ -162,50 +166,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
- cases.add_both("enums",
- \\enum Foo {
- \\ FooA,
- \\ FooB,
- \\ Foo1,
- \\};
- , &[_][]const u8{
- \\pub const enum_Foo = extern enum {
- \\ A,
- \\ B,
- \\ @"1",
- \\};
- ,
- \\pub const FooA = enum_Foo.A;
- ,
- \\pub const FooB = enum_Foo.B;
- ,
- \\pub const Foo1 = enum_Foo.@"1";
- ,
- \\pub const Foo = enum_Foo;
- });
-
- cases.add_both("enums",
- \\enum Foo {
- \\ FooA = 2,
- \\ FooB = 5,
- \\ Foo1,
- \\};
- , &[_][]const u8{
- \\pub const enum_Foo = extern enum {
- \\ A = 2,
- \\ B = 5,
- \\ @"1" = 6,
- \\};
- ,
- \\pub const FooA = enum_Foo.A;
- ,
- \\pub const FooB = enum_Foo.B;
- ,
- \\pub const Foo1 = enum_Foo.@"1";
- ,
- \\pub const Foo = enum_Foo;
- });
-
cases.add_both("typedef of function in struct field",
\\typedef void lws_callback_function(void);
\\struct Foo {
@@ -228,7 +188,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ struct Foo *foo;
\\};
, &[_][]const u8{
- \\pub const struct_Foo = @OpaqueType()
+ \\pub const struct_Foo = @OpaqueType();
,
\\pub const struct_Bar = extern struct {
\\ foo: ?*struct_Foo,
@@ -426,1240 +386,2280 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
},
);
- /////////////// Cases that pass for only stage2 ////////////////
-
- cases.add_2("Parameterless function prototypes",
- \\void a() {}
- \\void b(void) {}
- \\void c();
- \\void d(void);
+ cases.addC_both("null statements",
+ \\void foo(void) {
+ \\ ;;;;;
+ \\}
, &[_][]const u8{
- \\pub export fn a() void {}
- \\pub export fn b() void {}
- \\pub extern fn c(...) void;
- \\pub extern fn d() void;
+ \\pub export fn foo() void {
+ \\ {}
+ \\ {}
+ \\ {}
+ \\ {}
+ \\ {}
+ \\}
});
- cases.add_2("variable declarations",
- \\extern char arr0[] = "hello";
- \\static char arr1[] = "hello";
- \\char arr2[] = "hello";
- , &[_][]const u8{
- \\pub extern var arr0: [*c]u8 = "hello";
- \\pub var arr1: [*c]u8 = "hello";
- \\pub export var arr2: [*c]u8 = "hello";
- });
+ if (builtin.os != builtin.Os.windows) {
+ // Windows treats this as an enum with type c_int
+ cases.add_both("big negative enum init values when C ABI supports long long enums",
+ \\enum EnumWithInits {
+ \\ VAL01 = 0,
+ \\ VAL02 = 1,
+ \\ VAL03 = 2,
+ \\ VAL04 = 3,
+ \\ VAL05 = -1,
+ \\ VAL06 = -2,
+ \\ VAL07 = -3,
+ \\ VAL08 = -4,
+ \\ VAL09 = VAL02 + VAL08,
+ \\ VAL10 = -1000012000,
+ \\ VAL11 = -1000161000,
+ \\ VAL12 = -1000174001,
+ \\ VAL13 = VAL09,
+ \\ VAL14 = VAL10,
+ \\ VAL15 = VAL11,
+ \\ VAL16 = VAL13,
+ \\ VAL17 = (VAL16 - VAL10 + 1),
+ \\ VAL18 = 0x1000000000000000L,
+ \\ VAL19 = VAL18 + VAL18 + VAL18 - 1,
+ \\ VAL20 = VAL19 + VAL19,
+ \\ VAL21 = VAL20 + 0xFFFFFFFFFFFFFFFF,
+ \\ VAL22 = 0xFFFFFFFFFFFFFFFF + 1,
+ \\ VAL23 = 0xFFFFFFFFFFFFFFFF,
+ \\};
+ , &[_][]const u8{
+ \\pub const enum_EnumWithInits = extern enum(c_longlong) {
+ \\ VAL01 = 0,
+ \\ VAL02 = 1,
+ \\ VAL03 = 2,
+ \\ VAL04 = 3,
+ \\ VAL05 = -1,
+ \\ VAL06 = -2,
+ \\ VAL07 = -3,
+ \\ VAL08 = -4,
+ \\ VAL09 = -3,
+ \\ VAL10 = -1000012000,
+ \\ VAL11 = -1000161000,
+ \\ VAL12 = -1000174001,
+ \\ VAL13 = -3,
+ \\ VAL14 = -1000012000,
+ \\ VAL15 = -1000161000,
+ \\ VAL16 = -3,
+ \\ VAL17 = 1000011998,
+ \\ VAL18 = 1152921504606846976,
+ \\ VAL19 = 3458764513820540927,
+ \\ VAL20 = 6917529027641081854,
+ \\ VAL21 = 6917529027641081853,
+ \\ VAL22 = 0,
+ \\ VAL23 = -1,
+ \\};
+ });
+ }
- cases.add_2("array initializer expr",
- \\static void foo(void){
- \\ char arr[10] ={1};
- \\ char *arr1[10] ={0};
+ cases.addC_both("predefined expressions",
+ \\void foo(void) {
+ \\ __func__;
+ \\ __FUNCTION__;
+ \\ __PRETTY_FUNCTION__;
\\}
, &[_][]const u8{
- \\pub fn foo() void {
- \\ var arr: [10]u8 = .{
- \\ @as(u8, 1),
- \\ } ++ .{0} ** 9;
- \\ var arr1: [10][*c]u8 = .{
- \\ null,
- \\ } ++ .{null} ** 9;
+ \\pub export fn foo() void {
+ \\ _ = "foo";
+ \\ _ = "foo";
+ \\ _ = "void foo(void)";
\\}
});
- cases.add_2("enums",
- \\typedef enum {
- \\ a,
- \\ b,
- \\ c,
- \\} d;
- \\enum {
- \\ e,
- \\ f = 4,
- \\ g,
- \\} h = e;
- \\struct Baz {
- \\ enum {
- \\ i,
- \\ j,
- \\ k,
- \\ } l;
- \\ d m;
- \\};
- \\enum i {
- \\ n,
- \\ o,
- \\ p,
- \\};
+ cases.addC_both("ignore result, no function arguments",
+ \\void foo() {
+ \\ int a;
+ \\ 1;
+ \\ "hey";
+ \\ 1 + 1;
+ \\ 1 - 1;
+ \\ a = 1;
+ \\}
, &[_][]const u8{
- \\pub const a = enum_unnamed_1.a;
- \\pub const b = enum_unnamed_1.b;
- \\pub const c = enum_unnamed_1.c;
- \\pub const enum_unnamed_1 = extern enum {
- \\ a,
- \\ b,
- \\ c,
- \\};
- \\pub const d = enum_unnamed_1;
- \\pub const e = enum_unnamed_2.e;
- \\pub const f = enum_unnamed_2.f;
- \\pub const g = enum_unnamed_2.g;
- \\pub const enum_unnamed_2 = extern enum {
- \\ e = 0,
- \\ f = 4,
- \\ g = 5,
- \\};
- \\pub export var h: enum_unnamed_2 = @as(enum_unnamed_2, e);
- \\pub const i = enum_unnamed_3.i;
- \\pub const j = enum_unnamed_3.j;
- \\pub const k = enum_unnamed_3.k;
- \\pub const enum_unnamed_3 = extern enum {
- \\ i,
- \\ j,
- \\ k,
- \\};
- \\pub const struct_Baz = extern struct {
- \\ l: enum_unnamed_3,
- \\ m: d,
- \\};
- \\pub const n = enum_i.n;
- \\pub const o = enum_i.o;
- \\pub const p = enum_i.p;
- \\pub const enum_i = extern enum {
- \\ n,
- \\ o,
- \\ p,
- \\};
- ,
- \\pub const Baz = struct_Baz;
+ \\pub export fn foo() void {
+ \\ var a: c_int = undefined;
+ \\ _ = 1;
+ \\ _ = "hey";
+ \\ _ = (1 + 1);
+ \\ _ = (1 - 1);
+ \\ a = 1;
+ \\}
});
- cases.add_2("#define a char literal",
- \\#define A_CHAR 'a'
+ cases.add_both("constant size array",
+ \\void func(int array[20]);
, &[_][]const u8{
- \\pub const A_CHAR = 'a';
+ \\pub extern fn func(array: [*c]c_int) void;
});
- cases.add_2("comment after integer literal",
- \\#define SDL_INIT_VIDEO 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ cases.add_both("__cdecl doesn't mess up function pointers",
+ \\void foo(void (__cdecl *fn_ptr)(void));
, &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = 0x00000020;
+ \\pub extern fn foo(fn_ptr: ?extern fn () void) void;
});
- cases.add_2("u integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020u /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ cases.addC_both("void cast",
+ \\void foo() {
+ \\ int a;
+ \\ (void) a;
+ \\}
, &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = @as(c_uint, 0x00000020);
+ \\pub export fn foo() void {
+ \\ var a: c_int = undefined;
+ \\ _ = a;
+ \\}
});
- cases.add_2("l integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020l /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ cases.addC_both("implicit cast to void *",
+ \\void *foo() {
+ \\ unsigned short *x;
+ \\ return x;
+ \\}
, &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = @as(c_long, 0x00000020);
+ \\pub export fn foo() ?*c_void {
+ \\ var x: [*c]c_ushort = undefined;
+ \\ return @ptrCast(?*c_void, x);
+ \\}
});
- cases.add_2("ul integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020ul /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ cases.addC_both("null pointer implicit cast",
+ \\int* foo(void) {
+ \\ return 0;
+ \\}
, &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = @as(c_ulong, 0x00000020);
+ \\pub export fn foo() [*c]c_int {
+ \\ return null;
+ \\}
});
- cases.add_2("lu integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020lu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ cases.add_both("simple union",
+ \\union Foo {
+ \\ int x;
+ \\ double y;
+ \\};
, &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = @as(c_ulong, 0x00000020);
- });
-
- cases.add_2("ll integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020ll /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- , &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = @as(c_longlong, 0x00000020);
- });
-
- cases.add_2("ull integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020ull /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- , &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 0x00000020);
- });
-
- cases.add_2("llu integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020llu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- , &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 0x00000020);
- });
-
- cases.add_2("generate inline func for #define global extern fn",
- \\extern void (*fn_ptr)(void);
- \\#define foo fn_ptr
- \\
- \\extern char (*fn_ptr2)(int, float);
- \\#define bar fn_ptr2
- , &[_][]const u8{
- \\pub extern var fn_ptr: ?extern fn () void;
- ,
- \\pub inline fn foo() void {
- \\ return fn_ptr.?();
- \\}
- ,
- \\pub extern var fn_ptr2: ?extern fn (c_int, f32) u8;
- ,
- \\pub inline fn bar(arg_1: c_int, arg_2: f32) u8 {
- \\ return fn_ptr2.?(arg_1, arg_2);
- \\}
- });
-
- cases.add_2("macros with field targets",
- \\typedef unsigned int GLbitfield;
- \\typedef void (*PFNGLCLEARPROC) (GLbitfield mask);
- \\typedef void(*OpenGLProc)(void);
- \\union OpenGLProcs {
- \\ OpenGLProc ptr[1];
- \\ struct {
- \\ PFNGLCLEARPROC Clear;
- \\ } gl;
- \\};
- \\extern union OpenGLProcs glProcs;
- \\#define glClearUnion glProcs.gl.Clear
- \\#define glClearPFN PFNGLCLEARPROC
- , &[_][]const u8{
- \\pub const GLbitfield = c_uint;
- ,
- \\pub const PFNGLCLEARPROC = ?extern fn (GLbitfield) void;
- ,
- \\pub const OpenGLProc = ?extern fn () void;
- ,
- \\pub const union_OpenGLProcs = extern union {
- \\ ptr: [1]OpenGLProc,
- \\ gl: extern struct {
- \\ Clear: PFNGLCLEARPROC,
- \\ },
+ \\pub const union_Foo = extern union {
+ \\ x: c_int,
+ \\ y: f64,
\\};
,
- \\pub extern var glProcs: union_OpenGLProcs;
- ,
- \\pub const glClearPFN = PFNGLCLEARPROC;
- // , // TODO
- // \\pub inline fn glClearUnion(arg_1: GLbitfield) void {
- // \\ return glProcs.gl.Clear.?(arg_1);
- // \\}
- ,
- \\pub const OpenGLProcs = union_OpenGLProcs;
- });
-
- cases.add_2("macro pointer cast",
- \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
- , &[_][]const u8{
- \\pub const NRF_GPIO = if (@typeId(@TypeOf(NRF_GPIO_BASE)) == .Pointer) @ptrCast([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@TypeOf(NRF_GPIO_BASE)) == .Int) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else @as([*c]NRF_GPIO_Type, NRF_GPIO_BASE);
- });
-
- cases.add_2("basic macro function",
- \\extern int c;
- \\#define BASIC(c) (c*2)
- , &[_][]const u8{
- \\pub extern var c: c_int;
- ,
- \\pub inline fn BASIC(c_1: var) @TypeOf(c_1 * 2) {
- \\ return c_1 * 2;
- \\}
- });
-
- cases.add_2("macro escape sequences",
- \\#define FOO "aoeu\xab derp"
- \\#define FOO2 "aoeu\a derp"
- , &[_][]const u8{
- \\pub const FOO = "aoeu\xab derp";
- ,
- \\pub const FOO2 = "aoeu\x07 derp";
+ \\pub const Foo = union_Foo;
});
- cases.add_2("variable aliasing",
- \\static long a = 2;
- \\static long b = 2;
- \\static int c = 4;
- \\void foo(char c) {
- \\ int a;
- \\ char b = 123;
- \\ b = (char) a;
- \\ {
- \\ int d = 5;
- \\ }
- \\ unsigned d = 440;
+ cases.addC_both("string literal",
+ \\const char *foo(void) {
+ \\ return "bar";
\\}
, &[_][]const u8{
- \\pub var a: c_long = @as(c_long, 2);
- \\pub var b: c_long = @as(c_long, 2);
- \\pub var c: c_int = 4;
- \\pub export fn foo(c_1: u8) void {
- \\ var a_2: c_int = undefined;
- \\ var b_3: u8 = @as(u8, 123);
- \\ b_3 = @as(u8, a_2);
- \\ {
- \\ var d: c_int = 5;
- \\ }
- \\ var d: c_uint = @as(c_uint, 440);
+ \\pub export fn foo() [*c]const u8 {
+ \\ return "bar";
\\}
});
- /////////////// Cases for only stage1 which are TODO items for stage2 ////////////////
-
- if (builtin.os != builtin.Os.windows) {
- // Windows treats this as an enum with type c_int
- cases.add("big negative enum init values when C ABI supports long long enums",
- \\enum EnumWithInits {
- \\ VAL01 = 0,
- \\ VAL02 = 1,
- \\ VAL03 = 2,
- \\ VAL04 = 3,
- \\ VAL05 = -1,
- \\ VAL06 = -2,
- \\ VAL07 = -3,
- \\ VAL08 = -4,
- \\ VAL09 = VAL02 + VAL08,
- \\ VAL10 = -1000012000,
- \\ VAL11 = -1000161000,
- \\ VAL12 = -1000174001,
- \\ VAL13 = VAL09,
- \\ VAL14 = VAL10,
- \\ VAL15 = VAL11,
- \\ VAL16 = VAL13,
- \\ VAL17 = (VAL16 - VAL10 + 1),
- \\ VAL18 = 0x1000000000000000L,
- \\ VAL19 = VAL18 + VAL18 + VAL18 - 1,
- \\ VAL20 = VAL19 + VAL19,
- \\ VAL21 = VAL20 + 0xFFFFFFFFFFFFFFFF,
- \\ VAL22 = 0xFFFFFFFFFFFFFFFF + 1,
- \\ VAL23 = 0xFFFFFFFFFFFFFFFF,
- \\};
- , &[_][]const u8{
- \\pub const enum_EnumWithInits = extern enum(c_longlong) {
- \\ VAL01 = 0,
- \\ VAL02 = 1,
- \\ VAL03 = 2,
- \\ VAL04 = 3,
- \\ VAL05 = -1,
- \\ VAL06 = -2,
- \\ VAL07 = -3,
- \\ VAL08 = -4,
- \\ VAL09 = -3,
- \\ VAL10 = -1000012000,
- \\ VAL11 = -1000161000,
- \\ VAL12 = -1000174001,
- \\ VAL13 = -3,
- \\ VAL14 = -1000012000,
- \\ VAL15 = -1000161000,
- \\ VAL16 = -3,
- \\ VAL17 = 1000011998,
- \\ VAL18 = 1152921504606846976,
- \\ VAL19 = 3458764513820540927,
- \\ VAL20 = 6917529027641081854,
- \\ VAL21 = 6917529027641081853,
- \\ VAL22 = 0,
- \\ VAL23 = -1,
- \\};
- });
- }
-
- cases.add("predefined expressions",
+ cases.addC_both("return void",
\\void foo(void) {
- \\ __func__;
- \\ __FUNCTION__;
- \\ __PRETTY_FUNCTION__;
+ \\ return;
\\}
, &[_][]const u8{
- \\pub fn foo() void {
- \\ _ = "foo";
- \\ _ = "foo";
- \\ _ = "void foo(void)";
+ \\pub export fn foo() void {
+ \\ return;
\\}
});
- cases.add("ignore result, no function arguments",
- \\void foo() {
- \\ int a;
- \\ 1;
- \\ "hey";
- \\ 1 + 1;
- \\ 1 - 1;
- \\ a = 1;
+ cases.addC_both("for loop",
+ \\void foo(void) {
+ \\ for (int i = 0; i; i = i + 1) { }
\\}
, &[_][]const u8{
- \\pub fn foo() void {
- \\ var a: c_int = undefined;
- \\ _ = 1;
- \\ _ = "hey";
- \\ _ = (1 + 1);
- \\ _ = (1 - 1);
- \\ a = 1;
+ \\pub export fn foo() void {
+ \\ {
+ \\ var i: c_int = 0;
+ \\ while (i != 0) : (i = (i + 1)) {}
+ \\ }
\\}
});
- cases.add("for loop with var init but empty body",
+ cases.addC_both("empty for loop",
\\void foo(void) {
- \\ for (int x = 0; x < 10; x++);
+ \\ for (;;) { }
\\}
, &[_][]const u8{
- \\pub fn foo() void {
- \\ {
- \\ var x: c_int = 0;
- \\ while (x < 10) : (x += 1) {}
- \\ }
+ \\pub export fn foo() void {
+ \\ while (true) {}
\\}
});
- cases.add("do while with empty body",
+ cases.addC_both("break statement",
\\void foo(void) {
- \\ do ; while (1);
+ \\ for (;;) {
+ \\ break;
+ \\ }
\\}
- , &[_][]const u8{ // TODO this should be if (1 != 0) break
- \\pub fn foo() void {
+ , &[_][]const u8{
+ \\pub export fn foo() void {
\\ while (true) {
- \\ {}
- \\ if (!1) break;
+ \\ break;
\\ }
\\}
});
- cases.add("for with empty body",
+ cases.addC_both("continue statement",
\\void foo(void) {
- \\ for (;;);
+ \\ for (;;) {
+ \\ continue;
+ \\ }
\\}
, &[_][]const u8{
- \\pub fn foo() void {
- \\ while (true) {}
+ \\pub export fn foo() void {
+ \\ while (true) {
+ \\ continue;
+ \\ }
\\}
});
- cases.add("while with empty body",
- \\void foo(void) {
- \\ while (1);
+ cases.addC_both("pointer casting",
+ \\float *ptrcast() {
+ \\ int *a;
+ \\ return (float *)a;
\\}
, &[_][]const u8{
- \\pub fn foo() void {
- \\ while (1 != 0) {}
+ \\pub export fn ptrcast() [*c]f32 {
+ \\ var a: [*c]c_int = undefined;
+ \\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a));
\\}
});
- cases.addAllowWarnings("simple data types",
- \\#include <stdint.h>
- \\int foo(char a, unsigned char b, signed char c);
- \\int foo(char a, unsigned char b, signed char c); // test a duplicate prototype
- \\void bar(uint8_t a, uint16_t b, uint32_t c, uint64_t d);
- \\void baz(int8_t a, int16_t b, int32_t c, int64_t d);
- , &[_][]const u8{
- \\pub extern fn foo(a: u8, b: u8, c: i8) c_int;
- ,
- \\pub extern fn bar(a: u8, b: u16, c: u32, d: u64) void;
- ,
- \\pub extern fn baz(a: i8, b: i16, c: i32, d: i64) void;
- });
-
- cases.addC("simple function",
- \\int abs(int a) {
- \\ return a < 0 ? -a : 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{
- \\export fn abs(a: c_int) c_int {
- \\ return if (a < 0) -a else a;
+ \\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.add("restrict -> noalias",
- \\void foo(void *restrict bar, void *restrict);
- , &[_][]const u8{
- \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void;
- });
-
- cases.add("qualified struct and enum",
- \\struct Foo {
- \\ int x;
- \\ int y;
- \\};
- \\enum Bar {
- \\ BarA,
- \\ BarB,
- \\};
- \\void func(struct Foo *a, enum Bar **b);
- , &[_][]const u8{
- \\pub const struct_Foo = extern struct {
- \\ x: c_int,
- \\ y: c_int,
- \\};
- ,
- \\pub const enum_Bar = extern enum {
- \\ A,
- \\ B,
- \\};
- ,
- \\pub const BarA = enum_Bar.A;
- ,
- \\pub const BarB = enum_Bar.B;
- ,
- \\pub extern fn func(a: [*c]struct_Foo, b: [*c]([*c]enum_Bar)) void;
- ,
- \\pub const Foo = struct_Foo;
- ,
- \\pub const Bar = enum_Bar;
- });
-
- cases.add("constant size array",
- \\void func(int array[20]);
- , &[_][]const u8{
- \\pub extern fn func(array: [*c]c_int) void;
- });
-
- cases.add("__cdecl doesn't mess up function pointers",
- \\void foo(void (__cdecl *fn_ptr)(void));
- , &[_][]const u8{
- \\pub extern fn foo(fn_ptr: ?extern fn () void) void;
- });
-
- cases.add("macro defines string literal with hex",
- \\#define FOO "aoeu\xab derp"
- \\#define FOO2 "aoeu\x0007a derp"
- \\#define FOO_CHAR '\xfF'
- , &[_][]const u8{
- \\pub const FOO = "aoeu\xab derp";
- ,
- \\pub const FOO2 = "aoeuz derp";
- ,
- \\pub const FOO_CHAR = 255;
- });
-
- cases.add("macro defines string literal with octal",
- \\#define FOO "aoeu\023 derp"
- \\#define FOO2 "aoeu\0234 derp"
- \\#define FOO_CHAR '\077'
+ cases.addC_both("while on non-bool",
+ \\int while_none_bool() {
+ \\ int a;
+ \\ float b;
+ \\ void *c;
+ \\ while (a) return 0;
+ \\ while (b) return 1;
+ \\ while (c) return 2;
+ \\ return 3;
+ \\}
, &[_][]const u8{
- \\pub const FOO = "aoeu\x13 derp";
- ,
- \\pub const FOO2 = "aoeu\x134 derp";
- ,
- \\pub const FOO_CHAR = 63;
+ \\pub export fn while_none_bool() c_int {
+ \\ var a: c_int = undefined;
+ \\ var b: f32 = undefined;
+ \\ var c: ?*c_void = undefined;
+ \\ while (a != 0) return 0;
+ \\ while (b != 0) return 1;
+ \\ while (c != null) return 2;
+ \\ return 3;
+ \\}
});
- cases.addC("post increment",
- \\unsigned foo1(unsigned a) {
- \\ a++;
- \\ return a;
- \\}
- \\int foo2(int a) {
- \\ a++;
- \\ return a;
+ cases.addC_both("for on non-bool",
+ \\int for_none_bool() {
+ \\ int a;
+ \\ float b;
+ \\ void *c;
+ \\ for (;a;) return 0;
+ \\ for (;b;) return 1;
+ \\ for (;c;) return 2;
+ \\ return 3;
\\}
, &[_][]const u8{
- \\pub export fn foo1(_arg_a: c_uint) c_uint {
- \\ var a = _arg_a;
- \\ a +%= 1;
- \\ return a;
- \\}
- \\pub export fn foo2(_arg_a: c_int) c_int {
- \\ var a = _arg_a;
- \\ a += 1;
- \\ return a;
+ \\pub export fn for_none_bool() c_int {
+ \\ var a: c_int = undefined;
+ \\ var b: f32 = undefined;
+ \\ var c: ?*c_void = undefined;
+ \\ while (a != 0) return 0;
+ \\ while (b != 0) return 1;
+ \\ while (c != null) return 2;
+ \\ return 3;
\\}
});
- cases.addC("shift right assign",
- \\int log2(unsigned a) {
- \\ int i = 0;
- \\ while (a > 0) {
- \\ a >>= 1;
- \\ }
- \\ return i;
+ cases.addC_both("bitshift",
+ \\int foo(void) {
+ \\ return (1 << 2) >> 1;
\\}
, &[_][]const u8{
- \\pub export fn log2(_arg_a: c_uint) c_int {
- \\ var a = _arg_a;
- \\ var i: c_int = 0;
- \\ while (a > @as(c_uint, 0)) {
- \\ a >>= @as(@import("std").math.Log2Int(c_uint), 1);
- \\ }
- \\ return i;
+ \\pub export fn foo() c_int {
+ \\ return (1 << @as(@import("std").math.Log2Int(c_int), 2)) >> @as(@import("std").math.Log2Int(c_int), 1);
\\}
});
- cases.addC("if statement",
- \\int max(int a, int b) {
- \\ if (a < b)
- \\ return b;
- \\
- \\ if (a < b)
- \\ return b;
- \\ else
- \\ return a;
- \\
- \\ if (a < b) ; else ;
+ cases.addC_both("sizeof",
+ \\#include <stddef.h>
+ \\size_t size_of(void) {
+ \\ return sizeof(int);
\\}
, &[_][]const u8{
- \\pub export fn max(a: c_int, b: c_int) c_int {
- \\ if (a < b) return b;
- \\ if (a < b) return b else return a;
- \\ if (a < b) {} else {}
+ \\pub export fn size_of() usize {
+ \\ return @sizeOf(c_int);
\\}
});
- cases.addC("==, !=",
- \\int max(int a, int b) {
- \\ if (a == b)
- \\ return a;
- \\ if (a != b)
- \\ return b;
- \\ return a;
+ cases.addC_both("normal deref",
+ \\void foo() {
+ \\ int *x;
+ \\ *x = 1;
\\}
, &[_][]const u8{
- \\pub export fn max(a: c_int, b: c_int) c_int {
- \\ if (a == b) return a;
- \\ if (a != b) return b;
- \\ return a;
+ \\pub export fn foo() void {
+ \\ var x: [*c]c_int = undefined;
+ \\ x.?.* = 1;
\\}
});
- cases.addC("bitwise binary operators",
- \\int max(int a, int b) {
- \\ return (a & b) ^ (a | b);
+ cases.addC_both("address of operator",
+ \\int foo(void) {
+ \\ int x = 1234;
+ \\ int *ptr = &x;
+ \\ return *ptr;
\\}
, &[_][]const u8{
- \\pub export fn max(a: c_int, b: c_int) c_int {
- \\ return (a & b) ^ (a | b);
+ \\pub export fn foo() c_int {
+ \\ var x: c_int = 1234;
+ \\ var ptr: [*c]c_int = &x;
+ \\ return ptr.?.*;
\\}
});
- cases.addC("logical and, logical or",
- \\int max(int a, int b) {
- \\ if (a < b || a == b)
- \\ return b;
- \\ if (a >= b && a == b)
- \\ return a;
- \\ return a;
+ cases.addC_both("bin not",
+ \\int foo() {
+ \\ int x;
+ \\ return ~x;
\\}
, &[_][]const u8{
- \\pub export fn max(a: c_int, b: c_int) c_int {
- \\ if ((a < b) or (a == b)) return b;
- \\ if ((a >= b) and (a == b)) return a;
- \\ return a;
+ \\pub export fn foo() c_int {
+ \\ var x: c_int = undefined;
+ \\ return ~x;
\\}
});
- cases.addC("logical and, logical or on none bool values",
- \\int and_or_none_bool(int a, float b, void *c) {
- \\ if (a && b) return 0;
- \\ if (b && c) return 1;
- \\ if (a && c) return 2;
- \\ if (a || b) return 3;
- \\ if (b || c) return 4;
- \\ if (a || c) return 5;
- \\ return 6;
+ cases.addC_both("bool not",
+ \\int foo() {
+ \\ int a;
+ \\ float b;
+ \\ void *c;
+ \\ return !(a == 0);
+ \\ return !a;
+ \\ return !b;
+ \\ return !c;
\\}
, &[_][]const u8{
- \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
- \\ if ((a != 0) and (b != 0)) return 0;
- \\ if ((b != 0) and (c != null)) return 1;
- \\ if ((a != 0) and (c != null)) return 2;
- \\ if ((a != 0) or (b != 0)) return 3;
- \\ if ((b != 0) or (c != null)) return 4;
- \\ if ((a != 0) or (c != null)) return 5;
- \\ return 6;
+ \\pub export fn foo() c_int {
+ \\ var a: c_int = undefined;
+ \\ var b: f32 = undefined;
+ \\ var c: ?*c_void = undefined;
+ \\ return !(a == 0);
+ \\ return !(a != 0);
+ \\ return !(b != 0);
+ \\ return !(c != null);
\\}
});
- cases.addC("assign",
- \\int max(int a) {
- \\ int tmp;
- \\ tmp = a;
- \\ a = tmp;
+ cases.addC("__extension__ cast",
+ \\int foo(void) {
+ \\ return __extension__ 1;
\\}
, &[_][]const u8{
- \\pub export fn max(_arg_a: c_int) c_int {
- \\ var a = _arg_a;
- \\ var tmp: c_int = undefined;
- \\ tmp = a;
- \\ a = tmp;
+ \\pub export fn foo() c_int {
+ \\ return 1;
\\}
});
- cases.addC("chaining assign",
- \\void max(int a) {
- \\ int b, c;
- \\ c = b = a;
- \\}
- , &[_][]const u8{
- \\pub export fn max(a: c_int) void {
- \\ var b: c_int = undefined;
- \\ var c: c_int = undefined;
- \\ c = (x: {
- \\ const _tmp = a;
- \\ b = _tmp;
- \\ break :x _tmp;
- \\ });
- \\}
- });
+ if (builtin.os != builtin.Os.windows) {
+ // sysv_abi not currently supported on windows
+ cases.add_both("Macro qualified functions",
+ \\void __attribute__((sysv_abi)) foo(void);
+ , &[_][]const u8{
+ \\pub extern fn foo() void;
+ });
+ }
- cases.addC("shift right assign with a fixed size type",
- \\#include <stdint.h>
- \\int log2(uint32_t a) {
- \\ int i = 0;
- \\ while (a > 0) {
- \\ a >>= 1;
- \\ }
- \\ return i;
- \\}
+ /////////////// Cases that pass for only stage2 ////////////////
+
+ cases.add_2("Parameterless function prototypes",
+ \\void a() {}
+ \\void b(void) {}
+ \\void c();
+ \\void d(void);
, &[_][]const u8{
- \\pub export fn log2(_arg_a: u32) c_int {
- \\ var a = _arg_a;
- \\ var i: c_int = 0;
- \\ while (a > @as(c_uint, 0)) {
- \\ a >>= @as(u5, 1);
- \\ }
- \\ return i;
- \\}
+ \\pub export fn a() void {}
+ \\pub export fn b() void {}
+ \\pub extern fn c(...) void;
+ \\pub extern fn d() void;
});
- cases.add("anonymous enum",
- \\enum {
- \\ One,
- \\ Two,
- \\};
+ cases.add_2("variable declarations",
+ \\extern char arr0[] = "hello";
+ \\static char arr1[] = "hello";
+ \\char arr2[] = "hello";
, &[_][]const u8{
- \\pub const One = 0;
- \\pub const Two = 1;
+ \\pub extern var arr0: [*c]u8 = "hello";
+ \\pub var arr1: [*c]u8 = "hello";
+ \\pub export var arr2: [*c]u8 = "hello";
});
- cases.addC("function call",
- \\static void bar(void) { }
- \\static int baz(void) { return 0; }
- \\void foo(void) {
- \\ bar();
- \\ baz();
+ cases.add_2("array initializer expr",
+ \\static void foo(void){
+ \\ char arr[10] ={1};
+ \\ char *arr1[10] ={0};
\\}
, &[_][]const u8{
- \\pub fn bar() void {}
- \\pub fn baz() c_int {
- \\ return 0;
- \\}
- \\pub export fn foo() void {
- \\ bar();
- \\ _ = baz();
+ \\pub fn foo() void {
+ \\ var arr: [10]u8 = .{
+ \\ @as(u8, 1),
+ \\ } ++ .{0} ** 9;
+ \\ var arr1: [10][*c]u8 = .{
+ \\ null,
+ \\ } ++ .{null} ** 9;
\\}
});
- cases.addC("field access expression",
- \\struct Foo {
- \\ int field;
+ cases.add_2("enums",
+ \\typedef enum {
+ \\ a,
+ \\ b,
+ \\ c,
+ \\} d;
+ \\enum {
+ \\ e,
+ \\ f = 4,
+ \\ g,
+ \\} h = e;
+ \\struct Baz {
+ \\ enum {
+ \\ i,
+ \\ j,
+ \\ k,
+ \\ } l;
+ \\ d m;
+ \\};
+ \\enum i {
+ \\ n,
+ \\ o,
+ \\ p,
\\};
- \\int read_field(struct Foo *foo) {
- \\ return foo->field;
- \\}
, &[_][]const u8{
- \\pub const struct_Foo = extern struct {
- \\ field: c_int,
+ \\pub const a = 0;
+ \\pub const b = 1;
+ \\pub const c = 2;
+ \\const enum_unnamed_1 = extern enum {
+ \\ a,
+ \\ b,
+ \\ c,
\\};
- \\pub export fn read_field(foo: [*c]struct_Foo) c_int {
- \\ return foo.*.field;
- \\}
+ \\pub const d = enum_unnamed_1;
+ \\pub const e = 0;
+ \\pub const f = 4;
+ \\pub const g = 5;
+ \\const enum_unnamed_2 = extern enum {
+ \\ e = 0,
+ \\ f = 4,
+ \\ g = 5,
+ \\};
+ \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e);
+ \\pub const i = 0;
+ \\pub const j = 1;
+ \\pub const k = 2;
+ \\const enum_unnamed_3 = extern enum {
+ \\ i,
+ \\ j,
+ \\ k,
+ \\};
+ \\pub const struct_Baz = extern struct {
+ \\ l: enum_unnamed_3,
+ \\ m: d,
+ \\};
+ \\pub const n = 0;
+ \\pub const o = 1;
+ \\pub const p = 2;
+ \\pub const enum_i = extern enum {
+ \\ n,
+ \\ o,
+ \\ p,
+ \\};
+ ,
+ \\pub const Baz = struct_Baz;
});
- cases.addC("null statements",
- \\void foo(void) {
- \\ ;;;;;
- \\}
+ cases.add_2("#define a char literal",
+ \\#define A_CHAR 'a'
, &[_][]const u8{
- \\pub export fn foo() void {
- \\ {}
- \\ {}
- \\ {}
- \\ {}
- \\ {}
- \\}
+ \\pub const A_CHAR = 'a';
});
- cases.add("undefined array global",
- \\int array[100];
+ cases.add_2("comment after integer literal",
+ \\#define SDL_INIT_VIDEO 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
- \\pub var array: [100]c_int = undefined;
+ \\pub const SDL_INIT_VIDEO = 0x00000020;
});
- cases.addC("array access",
- \\int array[100];
- \\int foo(int index) {
- \\ return array[index];
- \\}
+ cases.add_2("u integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020u /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
- \\pub var array: [100]c_int = undefined;
- \\pub export fn foo(index: c_int) c_int {
- \\ return array[index];
- \\}
+ \\pub const SDL_INIT_VIDEO = @as(c_uint, 0x00000020);
});
- cases.addC("c style cast",
- \\int float_to_int(float a) {
- \\ return (int)a;
- \\}
+ cases.add_2("l integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020l /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
- \\pub export fn float_to_int(a: f32) c_int {
- \\ return @as(c_int, a);
- \\}
+ \\pub const SDL_INIT_VIDEO = @as(c_long, 0x00000020);
});
- cases.addC("void cast",
- \\void foo(int a) {
- \\ (void) a;
- \\}
+ cases.add_2("ul integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020ul /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
- \\pub export fn foo(a: c_int) void {
- \\ _ = a;
- \\}
+ \\pub const SDL_INIT_VIDEO = @as(c_ulong, 0x00000020);
});
- cases.addC("implicit cast to void *",
- \\void *foo(unsigned short *x) {
- \\ return x;
- \\}
+ cases.add_2("lu integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020lu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
- \\pub export fn foo(x: [*c]c_ushort) ?*c_void {
- \\ return @ptrCast(?*c_void, x);
- \\}
+ \\pub const SDL_INIT_VIDEO = @as(c_ulong, 0x00000020);
});
- cases.addC("sizeof",
- \\#include <stddef.h>
- \\size_t size_of(void) {
- \\ return sizeof(int);
- \\}
+ cases.add_2("ll integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020ll /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
- \\pub export fn size_of() usize {
- \\ return @sizeOf(c_int);
- \\}
+ \\pub const SDL_INIT_VIDEO = @as(c_longlong, 0x00000020);
});
- cases.addC("null pointer implicit cast",
- \\int* foo(void) {
- \\ return 0;
- \\}
+ cases.add_2("ull integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020ull /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
- \\pub export fn foo() [*c]c_int {
- \\ return null;
- \\}
+ \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 0x00000020);
});
- cases.addC("comma operator",
- \\int foo(void) {
- \\ return 1, 2;
- \\}
+ cases.add_2("llu integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020llu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
- \\pub export fn foo() c_int {
- \\ return x: {
- \\ _ = 1;
- \\ break :x 2;
- \\ };
- \\}
+ \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 0x00000020);
});
- cases.addC("statement expression",
- \\int foo(void) {
- \\ return ({
- \\ int a = 1;
- \\ a;
- \\ });
- \\}
+ cases.add_2("generate inline func for #define global extern fn",
+ \\extern void (*fn_ptr)(void);
+ \\#define foo fn_ptr
+ \\
+ \\extern char (*fn_ptr2)(int, float);
+ \\#define bar fn_ptr2
, &[_][]const u8{
- \\pub export fn foo() c_int {
- \\ return x: {
- \\ var a: c_int = 1;
- \\ break :x a;
- \\ };
+ \\pub extern var fn_ptr: ?extern fn () void;
+ ,
+ \\pub inline fn foo() void {
+ \\ return fn_ptr.?();
\\}
- });
-
- cases.addC("__extension__ cast",
- \\int foo(void) {
- \\ return __extension__ 1;
+ ,
+ \\pub extern var fn_ptr2: ?extern fn (c_int, f32) u8;
+ ,
+ \\pub inline fn bar(arg_1: c_int, arg_2: f32) u8 {
+ \\ return fn_ptr2.?(arg_1, arg_2);
+ \\}
+ });
+
+ cases.add_2("macros with field targets",
+ \\typedef unsigned int GLbitfield;
+ \\typedef void (*PFNGLCLEARPROC) (GLbitfield mask);
+ \\typedef void(*OpenGLProc)(void);
+ \\union OpenGLProcs {
+ \\ OpenGLProc ptr[1];
+ \\ struct {
+ \\ PFNGLCLEARPROC Clear;
+ \\ } gl;
+ \\};
+ \\extern union OpenGLProcs glProcs;
+ \\#define glClearUnion glProcs.gl.Clear
+ \\#define glClearPFN PFNGLCLEARPROC
+ , &[_][]const u8{
+ \\pub const GLbitfield = c_uint;
+ \\pub const PFNGLCLEARPROC = ?extern fn (GLbitfield) void;
+ \\pub const OpenGLProc = ?extern fn () void;
+ \\const struct_unnamed_1 = extern struct {
+ \\ Clear: PFNGLCLEARPROC,
+ \\};
+ \\pub const union_OpenGLProcs = extern union {
+ \\ ptr: [1]OpenGLProc,
+ \\ gl: struct_unnamed_1,
+ \\};
+ \\pub extern var glProcs: union_OpenGLProcs;
+ ,
+ \\pub const glClearPFN = PFNGLCLEARPROC;
+ ,
+ \\pub inline fn glClearUnion(arg_2: GLbitfield) void {
+ \\ return glProcs.gl.Clear.?(arg_2);
+ \\}
+ ,
+ \\pub const OpenGLProcs = union_OpenGLProcs;
+ });
+
+ cases.add_2("macro pointer cast",
+ \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
+ , &[_][]const u8{
+ \\pub const NRF_GPIO = if (@typeId(@TypeOf(NRF_GPIO_BASE)) == .Pointer) @ptrCast([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@TypeOf(NRF_GPIO_BASE)) == .Int) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else @as([*c]NRF_GPIO_Type, NRF_GPIO_BASE);
+ });
+
+ cases.add_2("basic macro function",
+ \\extern int c;
+ \\#define BASIC(c) (c*2)
+ , &[_][]const u8{
+ \\pub extern var c: c_int;
+ ,
+ \\pub inline fn BASIC(c_1: var) @TypeOf(c_1 * 2) {
+ \\ return c_1 * 2;
+ \\}
+ });
+
+ cases.add_2("macro defines string literal with hex",
+ \\#define FOO "aoeu\xab derp"
+ \\#define FOO2 "aoeu\x0007a derp"
+ \\#define FOO_CHAR '\xfF'
+ , &[_][]const u8{
+ \\pub const FOO = "aoeu\xab derp";
+ ,
+ \\pub const FOO2 = "aoeu\x7a derp";
+ ,
+ \\pub const FOO_CHAR = '\xff';
+ });
+
+ cases.add_2("variable aliasing",
+ \\static long a = 2;
+ \\static long b = 2;
+ \\static int c = 4;
+ \\void foo(char c) {
+ \\ int a;
+ \\ char b = 123;
+ \\ b = (char) a;
+ \\ {
+ \\ int d = 5;
+ \\ }
+ \\ unsigned d = 440;
+ \\}
+ , &[_][]const u8{
+ \\pub var a: c_long = @as(c_long, 2);
+ \\pub var b: c_long = @as(c_long, 2);
+ \\pub var c: c_int = 4;
+ \\pub export fn foo(_arg_c_1: u8) void {
+ \\ var c_1 = _arg_c_1;
+ \\ var a_2: c_int = undefined;
+ \\ var b_3: u8 = @as(u8, 123);
+ \\ b_3 = @as(u8, a_2);
+ \\ {
+ \\ var d: c_int = 5;
+ \\ }
+ \\ var d: c_uint = @as(c_uint, 440);
+ \\}
+ });
+
+ cases.add_2("comma operator",
+ \\int foo(char c) {
+ \\ 2, 4;
+ \\ return 2, 4, 6;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo(_arg_c: u8) c_int {
+ \\ var c = _arg_c;
+ \\ _ = 2;
+ \\ _ = 4;
+ \\ _ = 2;
+ \\ _ = 4;
+ \\ return 6;
+ \\}
+ });
+
+ cases.add_2("wors-case assign",
+ \\int foo(char c) {
+ \\ int a;
+ \\ int b;
+ \\ a = b = 2;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo(_arg_c: u8) c_int {
+ \\ var c = _arg_c;
+ \\ var a: c_int = undefined;
+ \\ var b: c_int = undefined;
+ \\ a = blk: {
+ \\ const _tmp_1 = 2;
+ \\ b = _tmp_1;
+ \\ break :blk _tmp_1;
+ \\ };
+ \\}
+ });
+
+ cases.add_2("if statements",
+ \\int foo(char c) {
+ \\ if (2) {
+ \\ int a = 2;
+ \\ }
+ \\ if (2, 5) {
+ \\ int a = 2;
+ \\ }
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo(_arg_c: u8) c_int {
+ \\ var c = _arg_c;
+ \\ if (2 != 0) {
+ \\ var a: c_int = 2;
+ \\ }
+ \\ if ((blk: {
+ \\ _ = 2;
+ \\ break :blk 5;
+ \\ }) != 0) {
+ \\ var a: c_int = 2;
+ \\ }
+ \\}
+ });
+
+ cases.add_2("while loops",
+ \\int foo() {
+ \\ int a = 5;
+ \\ while (2)
+ \\ a = 2;
+ \\ while (4) {
+ \\ int a = 4;
+ \\ a = 9;
+ \\ return 6, a;
+ \\ }
+ \\ do {
+ \\ int a = 2;
+ \\ a = 12;
+ \\ } while (4);
+ \\ do
+ \\ a = 7;
+ \\ while (4);
\\}
, &[_][]const u8{
\\pub export fn foo() c_int {
- \\ return 1;
+ \\ var a: c_int = 5;
+ \\ while (2 != 0) a = 2;
+ \\ while (4 != 0) {
+ \\ var a: c_int = 4;
+ \\ a = 9;
+ \\ _ = 6;
+ \\ return a;
+ \\ }
+ \\ while (true) {
+ \\ var a: c_int = 2;
+ \\ a = 12;
+ \\ if (!(4 != 0)) break;
+ \\ }
+ \\ while (true) {
+ \\ a = 7;
+ \\ if (!(4 != 0)) break;
+ \\ }
\\}
});
- cases.addC("bitshift",
- \\int foo(void) {
- \\ return (1 << 2) >> 1;
+ cases.add_2("for loops",
+ \\int foo() {
+ \\ for (int i = 2, b = 4; i + 2; i = 2) {
+ \\ int a = 2;
+ \\ a = 6, 5, 7;
+ \\ }
+ \\ char i = 2;
\\}
, &[_][]const u8{
\\pub export fn foo() c_int {
- \\ return (1 << @as(@import("std").math.Log2Int(c_int), 2)) >> @as(@import("std").math.Log2Int(c_int), 1);
+ \\ {
+ \\ var i: c_int = 2;
+ \\ var b: c_int = 4;
+ \\ while ((i + 2) != 0) : (i = 2) {
+ \\ var a: c_int = 2;
+ \\ a = 6;
+ \\ _ = 5;
+ \\ _ = 7;
+ \\ }
+ \\ }
+ \\ var i: u8 = @as(u8, 2);
\\}
});
- cases.addC("compound assignment operators",
- \\void foo(void) {
- \\ int a = 0;
- \\ a += (a += 1);
- \\ a -= (a -= 1);
- \\ a *= (a *= 1);
- \\ a &= (a &= 1);
- \\ a |= (a |= 1);
- \\ a ^= (a ^= 1);
- \\ a >>= (a >>= 1);
- \\ a <<= (a <<= 1);
+ cases.add_2("shadowing primitive types",
+ \\unsigned anyerror = 2;
+ , &[_][]const u8{
+ \\pub export var _anyerror: c_uint = @as(c_uint, 2);
+ });
+
+ cases.add_2("floats",
+ \\float a = 3.1415;
+ \\double b = 3.1415;
+ \\int c = 3.1415;
+ \\double d = 3;
+ , &[_][]const u8{
+ \\pub export var a: f32 = @floatCast(f32, 3.1415);
+ \\pub export var b: f64 = 3.1415;
+ \\pub export var c: c_int = @floatToInt(c_int, 3.1415);
+ \\pub export var d: f64 = @intToFloat(f64, 3);
+ });
+
+ cases.add_2("conditional operator",
+ \\int bar(void) {
+ \\ if (2 ? 5 : 5 ? 4 : 6) 2;
+ \\ return 2 ? 5 : 5 ? 4 : 6;
\\}
, &[_][]const u8{
- \\pub export fn foo() void {
- \\ var a: c_int = 0;
- \\ a += (x: {
- \\ const _ref = &a;
- \\ _ref.* = (_ref.* + 1);
- \\ break :x _ref.*;
- \\ });
- \\ a -= (x: {
- \\ const _ref = &a;
- \\ _ref.* = (_ref.* - 1);
- \\ break :x _ref.*;
- \\ });
- \\ a *= (x: {
- \\ const _ref = &a;
- \\ _ref.* = (_ref.* * 1);
- \\ break :x _ref.*;
- \\ });
- \\ a &= (x: {
- \\ const _ref = &a;
- \\ _ref.* = (_ref.* & 1);
- \\ break :x _ref.*;
- \\ });
- \\ a |= (x: {
- \\ const _ref = &a;
- \\ _ref.* = (_ref.* | 1);
- \\ break :x _ref.*;
- \\ });
- \\ a ^= (x: {
- \\ const _ref = &a;
- \\ _ref.* = (_ref.* ^ 1);
- \\ break :x _ref.*;
- \\ });
- \\ a >>= @as(@import("std").math.Log2Int(c_int), (x: {
- \\ const _ref = &a;
- \\ _ref.* = (_ref.* >> @as(@import("std").math.Log2Int(c_int), 1));
- \\ break :x _ref.*;
- \\ }));
- \\ a <<= @as(@import("std").math.Log2Int(c_int), (x: {
- \\ const _ref = &a;
- \\ _ref.* = (_ref.* << @as(@import("std").math.Log2Int(c_int), 1));
- \\ break :x _ref.*;
- \\ }));
+ \\pub export fn bar() c_int {
+ \\ if ((if (2 != 0) 5 else (if (5 != 0) 4 else 6)) != 0) _ = 2;
+ \\ return if (2 != 0) 5 else if (5 != 0) 4 else 6;
+ \\}
+ });
+
+ cases.add_2("switch on int",
+ \\int switch_fn(int i) {
+ \\ int res = 0;
+ \\ switch (i) {
+ \\ case 0:
+ \\ res = 1;
+ \\ case 1 ... 3:
+ \\ res = 2;
+ \\ default:
+ \\ res = 3 * i;
+ \\ break;
+ \\ case 4:
+ \\ res = 5;
+ \\ }
+ \\}
+ , &[_][]const u8{
+ \\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 (i) {
+ \\ 0 => break :__case_0,
+ \\ 1...3 => break :__case_1,
+ \\ else => break :__default,
+ \\ 4 => break :__case_2,
+ \\ }
+ \\ }
+ \\ res = 1;
+ \\ }
+ \\ res = 2;
+ \\ }
+ \\ res = (3 * i);
+ \\ break :__switch;
+ \\ }
+ \\ res = 5;
+ \\ }
+ \\}
+ });
+
+ cases.add_2("type referenced struct",
+ \\struct Foo {
+ \\ struct Bar{
+ \\ int b;
+ \\ };
+ \\ struct Bar c;
+ \\};
+ , &[_][]const u8{
+ \\pub const struct_Bar = extern struct {
+ \\ b: c_int,
+ \\};
+ \\pub const struct_Foo = extern struct {
+ \\ c: struct_Bar,
+ \\};
+ });
+
+ cases.add_2("undefined array global",
+ \\int array[100] = {};
+ , &[_][]const u8{
+ \\pub export var array: [100]c_int = .{0} ** 100;
+ });
+
+ cases.add_2("restrict -> noalias",
+ \\void foo(void *restrict bar, void *restrict);
+ , &[_][]const u8{
+ \\pub extern fn foo(noalias bar: ?*c_void, noalias ?*c_void) void;
+ });
+
+ cases.add_2("assign",
+ \\int max(int a) {
+ \\ int tmp;
+ \\ tmp = a;
+ \\ a = tmp;
\\}
+ , &[_][]const u8{
+ \\pub export fn max(_arg_a: c_int) c_int {
+ \\ var a = _arg_a;
+ \\ var tmp: c_int = undefined;
+ \\ tmp = a;
+ \\ a = tmp;
+ \\}
+ });
+
+ cases.add_2("chaining assign",
+ \\void max(int a) {
+ \\ int b, c;
+ \\ c = b = a;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn max(_arg_a: c_int) void {
+ \\ var a = _arg_a;
+ \\ var b: c_int = undefined;
+ \\ var c: c_int = undefined;
+ \\ c = blk: {
+ \\ const _tmp_1 = a;
+ \\ b = _tmp_1;
+ \\ break :blk _tmp_1;
+ \\ };
+ \\}
+ });
+
+ cases.add_2("anonymous enum",
+ \\enum {
+ \\ One,
+ \\ Two,
+ \\};
+ , &[_][]const u8{
+ \\pub const One = 0;
+ \\pub const Two = 1;
+ \\const enum_unnamed_1 = extern enum {
+ \\ One,
+ \\ Two,
+ \\};
+ });
+
+ cases.add_2("c style cast",
+ \\int float_to_int(float a) {
+ \\ return (int)a;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn float_to_int(_arg_a: f32) c_int {
+ \\ var a = _arg_a;
+ \\ return @floatToInt(c_int, a);
+ \\}
+ });
+
+ 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.add_2("logical and, logical or, on non-bool values, extra parens",
+ \\enum Foo {
+ \\ FooA,
+ \\ FooB,
+ \\ FooC,
+ \\};
+ \\typedef int SomeTypedef;
+ \\int and_or_non_bool(int a, float b, void *c) {
+ \\ enum Foo d = FooA;
+ \\ int e = (a && b);
+ \\ int f = (b && c);
+ \\ int g = (a && c);
+ \\ int h = (a || b);
+ \\ int i = (b || c);
+ \\ int j = (a || c);
+ \\ int k = (a || d);
+ \\ int l = (d && b);
+ \\ int m = (c || d);
+ \\ SomeTypedef td = 44;
+ \\ int o = (td || b);
+ \\ int p = (c && td);
+ \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);
+ \\}
+ , &[_][]const u8{
+ \\pub const enum_Foo = extern enum {
+ \\ A,
+ \\ B,
+ \\ C,
+ \\};
+ \\pub const SomeTypedef = c_int;
+ \\pub export fn and_or_non_bool(_arg_a: c_int, _arg_b: f32, _arg_c: ?*c_void) c_int {
+ \\ var a = _arg_a;
+ \\ var b = _arg_b;
+ \\ var c = _arg_c;
+ \\ var d: enum_Foo = @intToEnum(enum_Foo, FooA);
+ \\ var e: c_int = @boolToInt(((a != 0) and (b != 0)));
+ \\ var f: c_int = @boolToInt(((b != 0) and (c != null)));
+ \\ var g: c_int = @boolToInt(((a != 0) and (c != null)));
+ \\ var h: c_int = @boolToInt(((a != 0) or (b != 0)));
+ \\ var i: c_int = @boolToInt(((b != 0) or (c != null)));
+ \\ var j: c_int = @boolToInt(((a != 0) or (c != null)));
+ \\ var k: c_int = @boolToInt(((a != 0) or (@enumToInt(d) != 0)));
+ \\ var l: c_int = @boolToInt(((@enumToInt(d) != 0) and (b != 0)));
+ \\ var m: c_int = @boolToInt(((c != null) or (@enumToInt(d) != 0)));
+ \\ var td: SomeTypedef = 44;
+ \\ var o: c_int = @boolToInt(((td != 0) or (b != 0)));
+ \\ var p: c_int = @boolToInt(((c != null) and (td != 0)));
+ \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);
+ \\}
+ ,
+ \\pub const Foo = enum_Foo;
+ });
+
+ cases.add_2("qualified struct and enum",
+ \\struct Foo {
+ \\ int x;
+ \\ int y;
+ \\};
+ \\enum Bar {
+ \\ BarA,
+ \\ BarB,
+ \\};
+ \\void func(struct Foo *a, enum Bar **b);
+ , &[_][]const u8{
+ \\pub const struct_Foo = extern struct {
+ \\ x: c_int,
+ \\ y: c_int,
+ \\};
+ ,
+ \\pub const enum_Bar = extern enum {
+ \\ A,
+ \\ B,
+ \\};
+ \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;
+ ,
+ \\pub const Foo = struct_Foo;
+ \\pub const Bar = enum_Bar;
+ });
+
+ cases.add_2("bitwise binary operators, simpler parens",
+ \\int max(int a, int b) {
+ \\ return (a & b) ^ (a | b);
+ \\}
+ , &[_][]const u8{
+ \\pub export fn max(_arg_a: c_int, _arg_b: c_int) c_int {
+ \\ var a = _arg_a;
+ \\ var b = _arg_b;
+ \\ return ((a & b) ^ (a | b));
+ \\}
+ });
+
+ cases.add_2("comparison operators (no if)", // TODO Come up with less contrived tests? Make sure to cover all these comparisons.
+ \\int test_comparisons(int a, int b) {
+ \\ int c = (a < b);
+ \\ int d = (a > b);
+ \\ int e = (a <= b);
+ \\ int f = (a >= b);
+ \\ int g = (c < d);
+ \\ int h = (e < f);
+ \\ int i = (g < h);
+ \\ return i;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn test_comparisons(_arg_a: c_int, _arg_b: c_int) c_int {
+ \\ var a = _arg_a;
+ \\ var b = _arg_b;
+ \\ var c: c_int = @boolToInt((a < b));
+ \\ var d: c_int = @boolToInt((a > b));
+ \\ var e: c_int = @boolToInt((a <= b));
+ \\ var f: c_int = @boolToInt((a >= b));
+ \\ var g: c_int = @boolToInt((c < d));
+ \\ var h: c_int = @boolToInt((e < f));
+ \\ var i: c_int = @boolToInt((g < h));
+ \\ return i;
+ \\}
+ });
+
+ cases.add_2("==, !=",
+ \\int max(int a, int b) {
+ \\ if (a == b)
+ \\ return a;
+ \\ if (a != b)
+ \\ return b;
+ \\ return a;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn max(_arg_a: c_int, _arg_b: c_int) c_int {
+ \\ var a = _arg_a;
+ \\ var b = _arg_b;
+ \\ if (a == b) return a;
+ \\ if (a != b) return b;
+ \\ return a;
+ \\}
+ });
+
+ cases.add_2("typedeffed bool expression",
+ \\typedef char* yes;
+ \\void foo(void) {
+ \\ yes a;
+ \\ if (a) 2;
+ \\}
+ , &[_][]const u8{
+ \\pub const yes = [*c]u8;
+ \\pub export fn foo() void {
+ \\ var a: yes = undefined;
+ \\ if (a != null) _ = 2;
+ \\}
+ });
+
+ cases.add_2("statement expression",
+ \\int foo(void) {
+ \\ return ({
+ \\ int a = 1;
+ \\ a;
+ \\ a;
+ \\ });
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo() c_int {
+ \\ return (blk: {
+ \\ var a: c_int = 1;
+ \\ _ = a;
+ \\ break :blk a;
+ \\ });
+ \\}
+ });
+
+ cases.add_2("field access expression",
+ \\#define ARROW a->b
+ \\#define DOT a.b
+ \\extern struct Foo {
+ \\ int b;
+ \\}a;
+ \\float b = 2.0f;
+ \\int foo(void) {
+ \\ struct Foo *c;
+ \\ a.b;
+ \\ c->b;
+ \\}
+ , &[_][]const u8{
+ \\pub const struct_Foo = extern struct {
+ \\ b: c_int,
+ \\};
+ \\pub extern var a: struct_Foo;
+ \\pub export var b: f32 = 2;
+ \\pub export fn foo() c_int {
+ \\ var c: [*c]struct_Foo = undefined;
+ \\ _ = a.b;
+ \\ _ = c.*.b;
+ \\}
+ ,
+ \\pub const DOT = a.b;
+ ,
+ \\pub const ARROW = a.*.b;
+ });
+
+ cases.add_2("array access",
+ \\#define ACCESS array[2]
+ \\int array[100] = {};
+ \\int foo(int index) {
+ \\ return array[index];
+ \\}
+ , &[_][]const u8{
+ \\pub export var array: [100]c_int = .{0} ** 100;
+ \\pub export fn foo(_arg_index: c_int) c_int {
+ \\ var index = _arg_index;
+ \\ return array[index];
+ \\}
+ ,
+ \\pub const ACCESS = array[2];
+ });
+
+ cases.add_2("macro call",
+ \\#define CALL(arg) bar(arg)
+ , &[_][]const u8{
+ \\pub inline fn CALL(arg: var) @TypeOf(bar(arg)) {
+ \\ return bar(arg);
+ \\}
+ });
+
+ cases.add_2("logical and, logical or",
+ \\int max(int a, int b) {
+ \\ if (a < b || a == b)
+ \\ return b;
+ \\ if (a >= b && a == b)
+ \\ return a;
+ \\ return a;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn max(_arg_a: c_int, _arg_b: c_int) c_int {
+ \\ var a = _arg_a;
+ \\ var b = _arg_b;
+ \\ if ((a < b) or (a == b)) return b;
+ \\ if ((a >= b) and (a == b)) return a;
+ \\ return a;
+ \\}
+ });
+
+ cases.add_2("if statement",
+ \\int max(int a, int b) {
+ \\ if (a < b)
+ \\ return b;
+ \\
+ \\ if (a < b)
+ \\ return b;
+ \\ else
+ \\ return a;
+ \\
+ \\ if (a < b) ; else ;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn max(_arg_a: c_int, _arg_b: c_int) c_int {
+ \\ var a = _arg_a;
+ \\ var b = _arg_b;
+ \\ if (a < b) return b;
+ \\ if (a < b) return b else return a;
+ \\ if (a < b) {} else {}
+ \\}
+ });
+
+ cases.add_2("if on non-bool",
+ \\enum SomeEnum { A, B, C };
+ \\int if_none_bool(int a, float b, void *c, enum SomeEnum d) {
+ \\ if (a) return 0;
+ \\ if (b) return 1;
+ \\ if (c) return 2;
+ \\ if (d) return 3;
+ \\ return 4;
+ \\}
+ , &[_][]const u8{
+ \\pub const enum_SomeEnum = extern enum {
+ \\ A,
+ \\ B,
+ \\ C,
+ \\};
+ \\pub export fn if_none_bool(_arg_a: c_int, _arg_b: f32, _arg_c: ?*c_void, _arg_d: enum_SomeEnum) c_int {
+ \\ var a = _arg_a;
+ \\ var b = _arg_b;
+ \\ var c = _arg_c;
+ \\ var d = _arg_d;
+ \\ if (a != 0) return 0;
+ \\ if (b != 0) return 1;
+ \\ if (c != null) return 2;
+ \\ if (d != 0) return 3;
+ \\ return 4;
+ \\}
+ });
+
+ cases.add_2("simple data types",
+ \\#include <stdint.h>
+ \\int foo(char a, unsigned char b, signed char c);
+ \\int foo(char a, unsigned char b, signed char c); // test a duplicate prototype
+ \\void bar(uint8_t a, uint16_t b, uint32_t c, uint64_t d);
+ \\void baz(int8_t a, int16_t b, int32_t c, int64_t d);
+ , &[_][]const u8{
+ \\pub extern fn foo(a: u8, b: u8, c: i8) c_int;
+ \\pub extern fn bar(a: u8, b: u16, c: u32, d: u64) void;
+ \\pub extern fn baz(a: i8, b: i16, c: i32, d: i64) void;
+ });
+
+ cases.add_2("simple function",
+ \\int abs(int a) {
+ \\ return a < 0 ? -a : a;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn abs(_arg_a: c_int) c_int {
+ \\ var a = _arg_a;
+ \\ return if (a < 0) -a else a;
+ \\}
+ });
+
+ cases.add_2("post increment",
+ \\unsigned foo1(unsigned a) {
+ \\ a++;
+ \\ return a;
+ \\}
+ \\int foo2(int a) {
+ \\ a++;
+ \\ return a;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo1(_arg_a: c_uint) c_uint {
+ \\ var a = _arg_a;
+ \\ a +%= 1;
+ \\ return a;
+ \\}
+ \\pub export fn foo2(_arg_a: c_int) c_int {
+ \\ var a = _arg_a;
+ \\ a += 1;
+ \\ return a;
+ \\}
+ });
+
+ cases.add_2("deref function pointer",
+ \\void foo(void) {}
+ \\int baz(void) { return 0; }
+ \\void bar(void) {
+ \\ void(*f)(void) = foo;
+ \\ int(*b)(void) = baz;
+ \\ f();
+ \\ (*(f))();
+ \\ foo();
+ \\ b();
+ \\ (*(b))();
+ \\ baz();
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo() void {}
+ \\pub export fn baz() c_int {
+ \\ return 0;
+ \\}
+ \\pub export fn bar() void {
+ \\ var f: ?extern fn () void = foo;
+ \\ var b: ?extern fn () c_int = baz;
+ \\ f.?();
+ \\ (f).?();
+ \\ foo();
+ \\ _ = b.?();
+ \\ _ = (b).?();
+ \\ _ = baz();
+ \\}
+ });
+
+ cases.add_2("pre increment/decrement",
+ \\void foo(void) {
+ \\ int i = 0;
+ \\ unsigned u = 0;
+ \\ ++i;
+ \\ --i;
+ \\ ++u;
+ \\ --u;
+ \\ i = ++i;
+ \\ i = --i;
+ \\ u = ++u;
+ \\ u = --u;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo() void {
+ \\ var i: c_int = 0;
+ \\ var u: c_uint = @as(c_uint, 0);
+ \\ i += 1;
+ \\ i -= 1;
+ \\ u +%= 1;
+ \\ u -%= 1;
+ \\ i = (blk: {
+ \\ const _ref_1 = &i;
+ \\ _ref_1.* += 1;
+ \\ break :blk _ref_1.*;
+ \\ });
+ \\ i = (blk: {
+ \\ const _ref_2 = &i;
+ \\ _ref_2.* -= 1;
+ \\ break :blk _ref_2.*;
+ \\ });
+ \\ u = (blk: {
+ \\ const _ref_3 = &u;
+ \\ _ref_3.* +%= 1;
+ \\ break :blk _ref_3.*;
+ \\ });
+ \\ u = (blk: {
+ \\ const _ref_4 = &u;
+ \\ _ref_4.* -%= 1;
+ \\ break :blk _ref_4.*;
+ \\ });
+ \\}
+ });
+
+ cases.add_2("shift right assign",
+ \\int log2(unsigned a) {
+ \\ int i = 0;
+ \\ while (a > 0) {
+ \\ a >>= 1;
+ \\ }
+ \\ return i;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn log2(_arg_a: c_uint) c_int {
+ \\ var a = _arg_a;
+ \\ var i: c_int = 0;
+ \\ while (a > @as(c_uint, 0)) {
+ \\ a >>= @as(@import("std").math.Log2Int(c_int), 1);
+ \\ }
+ \\ return i;
+ \\}
+ });
+
+ cases.add_2("shift right assign with a fixed size type",
+ \\#include <stdint.h>
+ \\int log2(uint32_t a) {
+ \\ int i = 0;
+ \\ while (a > 0) {
+ \\ a >>= 1;
+ \\ }
+ \\ return i;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn log2(_arg_a: u32) c_int {
+ \\ var a = _arg_a;
+ \\ var i: c_int = 0;
+ \\ while (a > @as(c_uint, 0)) {
+ \\ a >>= @as(@import("std").math.Log2Int(c_int), 1);
+ \\ }
+ \\ return i;
+ \\}
+ });
+
+ cases.add_2("compound assignment operators",
+ \\void foo(void) {
+ \\ int a = 0;
+ \\ a += (a += 1);
+ \\ a -= (a -= 1);
+ \\ a *= (a *= 1);
+ \\ a &= (a &= 1);
+ \\ a |= (a |= 1);
+ \\ a ^= (a ^= 1);
+ \\ a >>= (a >>= 1);
+ \\ a <<= (a <<= 1);
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo() void {
+ \\ var a: c_int = 0;
+ \\ a += (blk: {
+ \\ const _ref_1 = &a;
+ \\ _ref_1.* = _ref_1.* + 1;
+ \\ break :blk _ref_1.*;
+ \\ });
+ \\ a -= (blk: {
+ \\ const _ref_2 = &a;
+ \\ _ref_2.* = _ref_2.* - 1;
+ \\ break :blk _ref_2.*;
+ \\ });
+ \\ a *= (blk: {
+ \\ const _ref_3 = &a;
+ \\ _ref_3.* = _ref_3.* * 1;
+ \\ break :blk _ref_3.*;
+ \\ });
+ \\ a &= (blk: {
+ \\ const _ref_4 = &a;
+ \\ _ref_4.* = _ref_4.* & 1;
+ \\ break :blk _ref_4.*;
+ \\ });
+ \\ a |= (blk: {
+ \\ const _ref_5 = &a;
+ \\ _ref_5.* = _ref_5.* | 1;
+ \\ break :blk _ref_5.*;
+ \\ });
+ \\ a ^= (blk: {
+ \\ const _ref_6 = &a;
+ \\ _ref_6.* = _ref_6.* ^ 1;
+ \\ break :blk _ref_6.*;
+ \\ });
+ \\ a >>= @as(@import("std").math.Log2Int(c_int), (blk: {
+ \\ const _ref_7 = &a;
+ \\ _ref_7.* = _ref_7.* >> @as(@import("std").math.Log2Int(c_int), 1);
+ \\ break :blk _ref_7.*;
+ \\ }));
+ \\ a <<= @as(@import("std").math.Log2Int(c_int), (blk: {
+ \\ const _ref_8 = &a;
+ \\ _ref_8.* = _ref_8.* << @as(@import("std").math.Log2Int(c_int), 1);
+ \\ break :blk _ref_8.*;
+ \\ }));
+ \\}
+ });
+
+ cases.add_2("compound assignment operators unsigned",
+ \\void foo(void) {
+ \\ unsigned a = 0;
+ \\ a += (a += 1);
+ \\ a -= (a -= 1);
+ \\ a *= (a *= 1);
+ \\ a &= (a &= 1);
+ \\ a |= (a |= 1);
+ \\ a ^= (a ^= 1);
+ \\ a >>= (a >>= 1);
+ \\ a <<= (a <<= 1);
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo() void {
+ \\ var a: c_uint = @as(c_uint, 0);
+ \\ a +%= (blk: {
+ \\ const _ref_1 = &a;
+ \\ _ref_1.* = _ref_1.* +% @as(c_uint, 1);
+ \\ break :blk _ref_1.*;
+ \\ });
+ \\ a -%= (blk: {
+ \\ const _ref_2 = &a;
+ \\ _ref_2.* = _ref_2.* -% @as(c_uint, 1);
+ \\ break :blk _ref_2.*;
+ \\ });
+ \\ a *%= (blk: {
+ \\ const _ref_3 = &a;
+ \\ _ref_3.* = _ref_3.* *% @as(c_uint, 1);
+ \\ break :blk _ref_3.*;
+ \\ });
+ \\ a &= (blk: {
+ \\ const _ref_4 = &a;
+ \\ _ref_4.* = _ref_4.* & @as(c_uint, 1);
+ \\ break :blk _ref_4.*;
+ \\ });
+ \\ a |= (blk: {
+ \\ const _ref_5 = &a;
+ \\ _ref_5.* = _ref_5.* | @as(c_uint, 1);
+ \\ break :blk _ref_5.*;
+ \\ });
+ \\ a ^= (blk: {
+ \\ const _ref_6 = &a;
+ \\ _ref_6.* = _ref_6.* ^ @as(c_uint, 1);
+ \\ break :blk _ref_6.*;
+ \\ });
+ \\ a >>= @as(@import("std").math.Log2Int(c_uint), (blk: {
+ \\ const _ref_7 = &a;
+ \\ _ref_7.* = _ref_7.* >> @as(@import("std").math.Log2Int(c_int), 1);
+ \\ break :blk _ref_7.*;
+ \\ }));
+ \\ a <<= @as(@import("std").math.Log2Int(c_uint), (blk: {
+ \\ const _ref_8 = &a;
+ \\ _ref_8.* = _ref_8.* << @as(@import("std").math.Log2Int(c_int), 1);
+ \\ break :blk _ref_8.*;
+ \\ }));
+ \\}
+ });
+
+ cases.add_2("post increment/decrement",
+ \\void foo(void) {
+ \\ int i = 0;
+ \\ unsigned u = 0;
+ \\ i++;
+ \\ i--;
+ \\ u++;
+ \\ u--;
+ \\ i = i++;
+ \\ i = i--;
+ \\ u = u++;
+ \\ u = u--;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo() void {
+ \\ var i: c_int = 0;
+ \\ var u: c_uint = @as(c_uint, 0);
+ \\ i += 1;
+ \\ i -= 1;
+ \\ u +%= 1;
+ \\ u -%= 1;
+ \\ i = (blk: {
+ \\ const _ref_1 = &i;
+ \\ const _tmp_2 = _ref_1.*;
+ \\ _ref_1.* += 1;
+ \\ break :blk _tmp_2;
+ \\ });
+ \\ i = (blk: {
+ \\ const _ref_3 = &i;
+ \\ const _tmp_4 = _ref_3.*;
+ \\ _ref_3.* -= 1;
+ \\ break :blk _tmp_4;
+ \\ });
+ \\ u = (blk: {
+ \\ const _ref_5 = &u;
+ \\ const _tmp_6 = _ref_5.*;
+ \\ _ref_5.* +%= 1;
+ \\ break :blk _tmp_6;
+ \\ });
+ \\ u = (blk: {
+ \\ const _ref_7 = &u;
+ \\ const _tmp_8 = _ref_7.*;
+ \\ _ref_7.* -%= 1;
+ \\ break :blk _tmp_8;
+ \\ });
+ \\}
+ });
+
+ cases.add_2("implicit casts",
+ \\#include <stdbool.h>
+ \\
+ \\void fn_int(int x);
+ \\void fn_f32(float x);
+ \\void fn_f64(double x);
+ \\void fn_char(char x);
+ \\void fn_bool(bool x);
+ \\void fn_ptr(void *x);
+ \\
+ \\void call(int q) {
+ \\ fn_int(3.0f);
+ \\ fn_int(3.0);
+ \\ fn_int(3.0L);
+ \\ fn_int('ABCD');
+ \\ fn_f32(3);
+ \\ fn_f64(3);
+ \\ fn_char('3');
+ \\ fn_char('\x1');
+ \\ fn_char(0);
+ \\ fn_f32(3.0f);
+ \\ fn_f64(3.0);
+ \\ fn_bool(123);
+ \\ fn_bool(0);
+ \\ fn_bool(&fn_int);
+ \\ fn_int(&fn_int);
+ \\ fn_ptr(42);
+ \\}
+ , &[_][]const u8{
+ \\pub extern fn fn_int(x: c_int) void;
+ \\pub extern fn fn_f32(x: f32) void;
+ \\pub extern fn fn_f64(x: f64) void;
+ \\pub extern fn fn_char(x: u8) void;
+ \\pub extern fn fn_bool(x: bool) void;
+ \\pub extern fn fn_ptr(x: ?*c_void) void;
+ \\pub export fn call(_arg_q: c_int) void {
+ \\ var q = _arg_q;
+ \\ fn_int(@floatToInt(c_int, 3));
+ \\ fn_int(@floatToInt(c_int, 3));
+ \\ fn_int(@floatToInt(c_int, 3));
+ \\ fn_int(1094861636);
+ \\ fn_f32(@intToFloat(f32, 3));
+ \\ fn_f64(@intToFloat(f64, 3));
+ \\ fn_char(@as(u8, '3'));
+ \\ fn_char(@as(u8, '\x01'));
+ \\ fn_char(@as(u8, 0));
+ \\ fn_f32(3);
+ \\ fn_f64(3);
+ \\ fn_bool(123 != 0);
+ \\ fn_bool(0 != 0);
+ \\ fn_bool(@ptrToInt(&fn_int) != 0);
+ \\ fn_int(@intCast(c_int, @ptrToInt(&fn_int)));
+ \\ fn_ptr(@intToPtr(?*c_void, 42));
+ \\}
+ });
+
+ cases.add_2("function call",
+ \\static void bar(void) { }
+ \\void foo(int *(baz)(void)) {
+ \\ bar();
+ \\ baz();
+ \\}
+ , &[_][]const u8{
+ \\pub fn bar() void {}
+ \\pub export fn foo(_arg_baz: ?extern fn () [*c]c_int) void {
+ \\ var baz = _arg_baz;
+ \\ bar();
+ \\ _ = baz.?();
+ \\}
+ });
+
+ cases.add_2("macro defines string literal with octal",
+ \\#define FOO "aoeu\023 derp"
+ \\#define FOO2 "aoeu\0234 derp"
+ \\#define FOO_CHAR '\077'
+ , &[_][]const u8{
+ \\pub const FOO = "aoeu\x13 derp";
+ ,
+ \\pub const FOO2 = "aoeu\x134 derp";
+ ,
+ \\pub const FOO_CHAR = '\x3f';
+ });
+
+ cases.add_2("enums",
+ \\enum Foo {
+ \\ FooA,
+ \\ FooB,
+ \\ Foo1,
+ \\};
+ , &[_][]const u8{
+ \\pub const enum_Foo = extern enum {
+ \\ A,
+ \\ B,
+ \\ @"1",
+ \\};
+ ,
+ \\pub const FooA = 0;
+ ,
+ \\pub const FooB = 1;
+ ,
+ \\pub const Foo1 = 2;
+ ,
+ \\pub const Foo = enum_Foo;
+ });
+
+ cases.add_2("enums",
+ \\enum Foo {
+ \\ FooA = 2,
+ \\ FooB = 5,
+ \\ Foo1,
+ \\};
+ , &[_][]const u8{
+ \\pub const enum_Foo = extern enum {
+ \\ A = 2,
+ \\ B = 5,
+ \\ @"1" = 6,
+ \\};
+ ,
+ \\pub const FooA = 2;
+ ,
+ \\pub const FooB = 5;
+ ,
+ \\pub const Foo1 = 6;
+ ,
+ \\pub const Foo = enum_Foo;
+ });
+
+ cases.add_2("macro cast",
+ \\#define FOO(bar) baz((void *)(baz))
+ , &[_][]const u8{
+ \\pub inline fn FOO(bar: var) @TypeOf(baz(if (@typeId(@TypeOf(baz)) == .Pointer) @ptrCast([*c]void, baz) else if (@typeId(@TypeOf(baz)) == .Int) @intToPtr([*c]void, baz) else @as([*c]void, baz))) {
+ \\ return baz(if (@typeId(@TypeOf(baz)) == .Pointer) @ptrCast([*c]void, baz) else if (@typeId(@TypeOf(baz)) == .Int) @intToPtr([*c]void, baz) else @as([*c]void, baz));
+ \\}
+ });
+
+ /////////////// Cases for only stage1 because stage2 behavior is better ////////////////
+ cases.addC("Parameterless function prototypes",
+ \\void foo() {}
+ \\void bar(void) {}
+ , &[_][]const u8{
+ \\pub export fn foo() void {}
+ \\pub export fn bar() void {}
+ });
+
+ cases.add("#define a char literal",
+ \\#define A_CHAR 'a'
+ , &[_][]const u8{
+ \\pub const A_CHAR = 97;
+ });
+
+ cases.add("generate inline func for #define global extern fn",
+ \\extern void (*fn_ptr)(void);
+ \\#define foo fn_ptr
+ \\
+ \\extern char (*fn_ptr2)(int, float);
+ \\#define bar fn_ptr2
+ , &[_][]const u8{
+ \\pub extern var fn_ptr: ?extern fn () void;
+ ,
+ \\pub inline fn foo() void {
+ \\ return fn_ptr.?();
+ \\}
+ ,
+ \\pub extern var fn_ptr2: ?extern fn (c_int, f32) u8;
+ ,
+ \\pub inline fn bar(arg0: c_int, arg1: f32) u8 {
+ \\ return fn_ptr2.?(arg0, arg1);
+ \\}
+ });
+ cases.add("comment after integer literal",
+ \\#define SDL_INIT_VIDEO 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ , &[_][]const u8{
+ \\pub const SDL_INIT_VIDEO = 32;
+ });
+
+ cases.add("u integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020u /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ , &[_][]const u8{
+ \\pub const SDL_INIT_VIDEO = @as(c_uint, 32);
+ });
+
+ cases.add("l integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020l /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ , &[_][]const u8{
+ \\pub const SDL_INIT_VIDEO = @as(c_long, 32);
+ });
+
+ cases.add("ul integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020ul /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ , &[_][]const u8{
+ \\pub const SDL_INIT_VIDEO = @as(c_ulong, 32);
+ });
+
+ cases.add("lu integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020lu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ , &[_][]const u8{
+ \\pub const SDL_INIT_VIDEO = @as(c_ulong, 32);
+ });
+
+ cases.add("ll integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020ll /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ , &[_][]const u8{
+ \\pub const SDL_INIT_VIDEO = @as(c_longlong, 32);
+ });
+
+ cases.add("ull integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020ull /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ , &[_][]const u8{
+ \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 32);
+ });
+
+ cases.add("llu integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020llu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ , &[_][]const u8{
+ \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 32);
+ });
+
+ cases.add("macros with field targets",
+ \\typedef unsigned int GLbitfield;
+ \\typedef void (*PFNGLCLEARPROC) (GLbitfield mask);
+ \\typedef void(*OpenGLProc)(void);
+ \\union OpenGLProcs {
+ \\ OpenGLProc ptr[1];
+ \\ struct {
+ \\ PFNGLCLEARPROC Clear;
+ \\ } gl;
+ \\};
+ \\extern union OpenGLProcs glProcs;
+ \\#define glClearUnion glProcs.gl.Clear
+ \\#define glClearPFN PFNGLCLEARPROC
+ , &[_][]const u8{
+ \\pub const GLbitfield = c_uint;
+ ,
+ \\pub const PFNGLCLEARPROC = ?extern fn (GLbitfield) void;
+ ,
+ \\pub const OpenGLProc = ?extern fn () void;
+ ,
+ \\pub const union_OpenGLProcs = extern union {
+ \\ ptr: [1]OpenGLProc,
+ \\ gl: extern struct {
+ \\ Clear: PFNGLCLEARPROC,
+ \\ },
+ \\};
+ ,
+ \\pub extern var glProcs: union_OpenGLProcs;
+ ,
+ \\pub const glClearPFN = PFNGLCLEARPROC;
+ ,
+ \\pub inline fn glClearUnion(arg0: GLbitfield) void {
+ \\ return glProcs.gl.Clear.?(arg0);
+ \\}
+ ,
+ \\pub const OpenGLProcs = union_OpenGLProcs;
+ });
+
+ cases.add("macro pointer cast",
+ \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
+ , &[_][]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;
+ \\ }
+ \\}
+ });
+
+ cases.add("for loop with var init but empty body",
+ \\void foo(void) {
+ \\ for (int x = 0; x < 10; x++);
+ \\}
+ , &[_][]const u8{
+ \\pub fn foo() void {
+ \\ {
+ \\ var x: c_int = 0;
+ \\ while (x < 10) : (x += 1) {}
+ \\ }
+ \\}
+ });
+
+ cases.add("do while with empty body",
+ \\void foo(void) {
+ \\ do ; while (1);
+ \\}
+ , &[_][]const u8{ // TODO this should be if (1 != 0) break
+ \\pub fn foo() void {
+ \\ while (true) {
+ \\ {}
+ \\ if (!1) break;
+ \\ }
+ \\}
+ });
+
+ cases.add("for with empty body",
+ \\void foo(void) {
+ \\ for (;;);
+ \\}
+ , &[_][]const u8{
+ \\pub fn foo() void {
+ \\ while (true) {}
+ \\}
+ });
+
+ cases.add("while with empty body",
+ \\void foo(void) {
+ \\ while (1);
+ \\}
+ , &[_][]const u8{
+ \\pub fn foo() void {
+ \\ while (1 != 0) {}
+ \\}
+ });
+
+ cases.add("undefined array global",
+ \\int array[100];
+ , &[_][]const u8{
+ \\pub var array: [100]c_int = undefined;
+ });
+
+ cases.add("qualified struct and enum",
+ \\struct Foo {
+ \\ int x;
+ \\ int y;
+ \\};
+ \\enum Bar {
+ \\ BarA,
+ \\ BarB,
+ \\};
+ \\void func(struct Foo *a, enum Bar **b);
+ , &[_][]const u8{
+ \\pub const struct_Foo = extern struct {
+ \\ x: c_int,
+ \\ y: c_int,
+ \\};
+ ,
+ \\pub const enum_Bar = extern enum {
+ \\ A,
+ \\ B,
+ \\};
+ ,
+ \\pub const BarA = enum_Bar.A;
+ ,
+ \\pub const BarB = enum_Bar.B;
+ ,
+ \\pub extern fn func(a: [*c]struct_Foo, b: [*c]([*c]enum_Bar)) void;
+ ,
+ \\pub const Foo = struct_Foo;
+ ,
+ \\pub const Bar = enum_Bar;
+ });
+
+ cases.add("restrict -> noalias",
+ \\void foo(void *restrict bar, void *restrict);
+ , &[_][]const u8{
+ \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void;
});
- cases.addC("compound assignment operators unsigned",
- \\void foo(void) {
- \\ unsigned a = 0;
- \\ a += (a += 1);
- \\ a -= (a -= 1);
- \\ a *= (a *= 1);
- \\ a &= (a &= 1);
- \\ a |= (a |= 1);
- \\ a ^= (a ^= 1);
- \\ a >>= (a >>= 1);
- \\ a <<= (a <<= 1);
+ cases.addC("assign",
+ \\int max(int a) {
+ \\ int tmp;
+ \\ tmp = a;
+ \\ a = tmp;
\\}
, &[_][]const u8{
- \\pub export fn foo() void {
- \\ var a: c_uint = @as(c_uint, 0);
- \\ a +%= (x: {
- \\ const _ref = &a;
- \\ _ref.* = (_ref.* +% @as(c_uint, 1));
- \\ break :x _ref.*;
- \\ });
- \\ a -%= (x: {
- \\ const _ref = &a;
- \\ _ref.* = (_ref.* -% @as(c_uint, 1));
- \\ break :x _ref.*;
- \\ });
- \\ a *%= (x: {
- \\ const _ref = &a;
- \\ _ref.* = (_ref.* *% @as(c_uint, 1));
- \\ break :x _ref.*;
- \\ });
- \\ a &= (x: {
- \\ const _ref = &a;
- \\ _ref.* = (_ref.* & @as(c_uint, 1));
- \\ break :x _ref.*;
- \\ });
- \\ a |= (x: {
- \\ const _ref = &a;
- \\ _ref.* = (_ref.* | @as(c_uint, 1));
- \\ break :x _ref.*;
- \\ });
- \\ a ^= (x: {
- \\ const _ref = &a;
- \\ _ref.* = (_ref.* ^ @as(c_uint, 1));
- \\ break :x _ref.*;
- \\ });
- \\ a >>= @as(@import("std").math.Log2Int(c_uint), (x: {
- \\ const _ref = &a;
- \\ _ref.* = (_ref.* >> @as(@import("std").math.Log2Int(c_uint), 1));
- \\ break :x _ref.*;
- \\ }));
- \\ a <<= @as(@import("std").math.Log2Int(c_uint), (x: {
- \\ const _ref = &a;
- \\ _ref.* = (_ref.* << @as(@import("std").math.Log2Int(c_uint), 1));
- \\ break :x _ref.*;
- \\ }));
+ \\pub export fn max(_arg_a: c_int) c_int {
+ \\ var a = _arg_a;
+ \\ var tmp: c_int = undefined;
+ \\ tmp = a;
+ \\ a = tmp;
\\}
});
- cases.addC("post increment/decrement",
- \\void foo(void) {
- \\ int i = 0;
- \\ unsigned u = 0;
- \\ i++;
- \\ i--;
- \\ u++;
- \\ u--;
- \\ i = i++;
- \\ i = i--;
- \\ u = u++;
- \\ u = u--;
+ cases.addC("chaining assign",
+ \\void max(int a) {
+ \\ int b, c;
+ \\ c = b = a;
\\}
, &[_][]const u8{
- \\pub export fn foo() void {
- \\ var i: c_int = 0;
- \\ var u: c_uint = @as(c_uint, 0);
- \\ i += 1;
- \\ i -= 1;
- \\ u +%= 1;
- \\ u -%= 1;
- \\ i = (x: {
- \\ const _ref = &i;
- \\ const _tmp = _ref.*;
- \\ _ref.* += 1;
- \\ break :x _tmp;
- \\ });
- \\ i = (x: {
- \\ const _ref = &i;
- \\ const _tmp = _ref.*;
- \\ _ref.* -= 1;
- \\ break :x _tmp;
- \\ });
- \\ u = (x: {
- \\ const _ref = &u;
- \\ const _tmp = _ref.*;
- \\ _ref.* +%= 1;
- \\ break :x _tmp;
- \\ });
- \\ u = (x: {
- \\ const _ref = &u;
- \\ const _tmp = _ref.*;
- \\ _ref.* -%= 1;
+ \\pub export fn max(a: c_int) void {
+ \\ var b: c_int = undefined;
+ \\ var c: c_int = undefined;
+ \\ c = (x: {
+ \\ const _tmp = a;
+ \\ b = _tmp;
\\ break :x _tmp;
\\ });
\\}
});
- cases.addC("pre increment/decrement",
- \\void foo(void) {
- \\ int i = 0;
- \\ unsigned u = 0;
- \\ ++i;
- \\ --i;
- \\ ++u;
- \\ --u;
- \\ i = ++i;
- \\ i = --i;
- \\ u = ++u;
- \\ u = --u;
- \\}
+ cases.add("anonymous enum",
+ \\enum {
+ \\ One,
+ \\ Two,
+ \\};
, &[_][]const u8{
- \\pub export fn foo() void {
- \\ var i: c_int = 0;
- \\ var u: c_uint = @as(c_uint, 0);
- \\ i += 1;
- \\ i -= 1;
- \\ u +%= 1;
- \\ u -%= 1;
- \\ i = (x: {
- \\ const _ref = &i;
- \\ _ref.* += 1;
- \\ break :x _ref.*;
- \\ });
- \\ i = (x: {
- \\ const _ref = &i;
- \\ _ref.* -= 1;
- \\ break :x _ref.*;
- \\ });
- \\ u = (x: {
- \\ const _ref = &u;
- \\ _ref.* +%= 1;
- \\ break :x _ref.*;
- \\ });
- \\ u = (x: {
- \\ const _ref = &u;
- \\ _ref.* -%= 1;
- \\ break :x _ref.*;
- \\ });
- \\}
+ \\pub const One = 0;
+ \\pub const Two = 1;
});
- cases.addC("do loop",
- \\void foo(void) {
- \\ int a = 2;
- \\ do {
- \\ a--;
- \\ } while (a != 0);
- \\
- \\ int b = 2;
- \\ do
- \\ b--;
- \\ while (b != 0);
+ cases.addC("c style cast",
+ \\int float_to_int(float a) {
+ \\ return (int)a;
\\}
, &[_][]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;
- \\ }
+ \\pub export fn float_to_int(a: f32) c_int {
+ \\ return @as(c_int, a);
\\}
});
- cases.addC("deref function pointer",
- \\void foo(void) {}
- \\int baz(void) { return 0; }
- \\void bar(void) {
- \\ void(*f)(void) = foo;
- \\ int(*b)(void) = baz;
- \\ f();
- \\ (*(f))();
- \\ foo();
- \\ b();
- \\ (*(b))();
- \\ baz();
+ cases.addC("comma operator",
+ \\int foo(void) {
+ \\ return 1, 2;
\\}
, &[_][]const u8{
- \\pub export fn foo() void {}
- \\pub export fn baz() c_int {
- \\ return 0;
- \\}
- \\pub export fn bar() void {
- \\ var f: ?extern fn () void = foo;
- \\ var b: ?extern fn () c_int = baz;
- \\ f.?();
- \\ f.?();
- \\ foo();
- \\ _ = b.?();
- \\ _ = b.?();
- \\ _ = baz();
+ \\pub export fn foo() c_int {
+ \\ return x: {
+ \\ _ = 1;
+ \\ break :x 2;
+ \\ };
\\}
});
- cases.addC("normal deref",
- \\void foo(int *x) {
- \\ *x = 1;
+ 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 foo(x: [*c]c_int) void {
- \\ x.?.* = 1;
+ \\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("simple union",
- \\union Foo {
- \\ int x;
- \\ double y;
- \\};
+ 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 const union_Foo = extern union {
- \\ x: c_int,
- \\ y: f64,
- \\};
- ,
- \\pub const Foo = union_Foo;
+ \\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.add("address of operator",
- \\int foo(void) {
- \\ int x = 1234;
- \\ int *ptr = &x;
- \\ return *ptr;
+ cases.addC("==, !=",
+ \\int max(int a, int b) {
+ \\ if (a == b)
+ \\ return a;
+ \\ if (a != b)
+ \\ return b;
+ \\ return a;
\\}
, &[_][]const u8{
- \\pub fn foo() c_int {
- \\ var x: c_int = 1234;
- \\ var ptr: [*c]c_int = &x;
- \\ return ptr.?.*;
+ \\pub export fn max(a: c_int, b: c_int) c_int {
+ \\ if (a == b) return a;
+ \\ if (a != b) return b;
+ \\ return a;
\\}
});
- cases.add("string literal",
- \\const char *foo(void) {
- \\ return "bar";
+ cases.addC("bitwise binary operators",
+ \\int max(int a, int b) {
+ \\ return (a & b) ^ (a | b);
\\}
, &[_][]const u8{
- \\pub fn foo() [*c]const u8 {
- \\ return "bar";
+ \\pub export fn max(a: c_int, b: c_int) c_int {
+ \\ return (a & b) ^ (a | b);
\\}
});
- cases.add("return void",
- \\void foo(void) {
- \\ return;
+ cases.addC("statement expression",
+ \\int foo(void) {
+ \\ return ({
+ \\ int a = 1;
+ \\ a;
+ \\ });
\\}
, &[_][]const u8{
- \\pub fn foo() void {
- \\ return;
+ \\pub export fn foo() c_int {
+ \\ return x: {
+ \\ var a: c_int = 1;
+ \\ break :x a;
+ \\ };
\\}
});
- cases.add("for loop",
- \\void foo(void) {
- \\ for (int i = 0; i < 10; i += 1) { }
+ cases.addC("field access expression",
+ \\struct Foo {
+ \\ int field;
+ \\};
+ \\int read_field(struct Foo *foo) {
+ \\ return foo->field;
\\}
, &[_][]const u8{
- \\pub fn foo() void {
- \\ {
- \\ var i: c_int = 0;
- \\ while (i < 10) : (i += 1) {}
- \\ }
+ \\pub const struct_Foo = extern struct {
+ \\ field: c_int,
+ \\};
+ \\pub export fn read_field(foo: [*c]struct_Foo) c_int {
+ \\ return foo.*.field;
\\}
});
- cases.add("empty for loop",
- \\void foo(void) {
- \\ for (;;) { }
+ cases.addC("array access",
+ \\int array[100];
+ \\int foo(int index) {
+ \\ return array[index];
\\}
, &[_][]const u8{
- \\pub fn foo() void {
- \\ while (true) {}
+ \\pub var array: [100]c_int = undefined;
+ \\pub export fn foo(index: c_int) c_int {
+ \\ return array[index];
\\}
});
- cases.add("break statement",
- \\void foo(void) {
- \\ for (;;) {
- \\ break;
- \\ }
+ cases.addC("logical and, logical or",
+ \\int max(int a, int b) {
+ \\ if (a < b || a == b)
+ \\ return b;
+ \\ if (a >= b && a == b)
+ \\ return a;
+ \\ return a;
\\}
, &[_][]const u8{
- \\pub fn foo() void {
- \\ while (true) {
- \\ break;
- \\ }
+ \\pub export fn max(a: c_int, b: c_int) c_int {
+ \\ if ((a < b) or (a == b)) return b;
+ \\ if ((a >= b) and (a == b)) return a;
+ \\ return a;
\\}
});
- cases.add("continue statement",
- \\void foo(void) {
- \\ for (;;) {
- \\ continue;
- \\ }
+ cases.addC("if statement",
+ \\int max(int a, int b) {
+ \\ if (a < b)
+ \\ return b;
+ \\
+ \\ if (a < b)
+ \\ return b;
+ \\ else
+ \\ return a;
+ \\
+ \\ if (a < b) ; else ;
\\}
, &[_][]const u8{
- \\pub fn foo() void {
- \\ while (true) {
- \\ continue;
- \\ }
+ \\pub export fn max(a: c_int, b: c_int) c_int {
+ \\ if (a < b) return b;
+ \\ if (a < b) return b else return a;
+ \\ if (a < b) {} else {}
\\}
});
@@ -1683,150 +2683,353 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
- cases.add("pointer casting",
- \\float *ptrcast(int *a) {
- \\ return (float *)a;
+ cases.add("if on non-bool",
+ \\enum SomeEnum { A, B, C };
+ \\int if_none_bool(int a, float b, void *c, enum SomeEnum d) {
+ \\ if (a) return 0;
+ \\ if (b) return 1;
+ \\ if (c) return 2;
+ \\ if (d) return 3;
+ \\ return 4;
\\}
, &[_][]const u8{
- \\fn ptrcast(a: [*c]c_int) [*c]f32 {
- \\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a));
+ \\pub const A = enum_SomeEnum.A;
+ \\pub const B = enum_SomeEnum.B;
+ \\pub const C = enum_SomeEnum.C;
+ \\pub const enum_SomeEnum = extern enum {
+ \\ A,
+ \\ B,
+ \\ C,
+ \\};
+ \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int {
+ \\ if (a != 0) return 0;
+ \\ if (b != 0) return 1;
+ \\ if (c != null) return 2;
+ \\ if (d != @bitCast(enum_SomeEnum, @as(@TagType(enum_SomeEnum), 0))) return 3;
+ \\ return 4;
\\}
});
- cases.add("bin not",
- \\int foo(int x) {
- \\ return ~x;
+ cases.addAllowWarnings("simple data types",
+ \\#include <stdint.h>
+ \\int foo(char a, unsigned char b, signed char c);
+ \\int foo(char a, unsigned char b, signed char c); // test a duplicate prototype
+ \\void bar(uint8_t a, uint16_t b, uint32_t c, uint64_t d);
+ \\void baz(int8_t a, int16_t b, int32_t c, int64_t d);
+ , &[_][]const u8{
+ \\pub extern fn foo(a: u8, b: u8, c: i8) c_int;
+ ,
+ \\pub extern fn bar(a: u8, b: u16, c: u32, d: u64) void;
+ ,
+ \\pub extern fn baz(a: i8, b: i16, c: i32, d: i64) void;
+ });
+
+ cases.addC("simple function",
+ \\int abs(int a) {
+ \\ return a < 0 ? -a : a;
\\}
, &[_][]const u8{
- \\pub fn foo(x: c_int) c_int {
- \\ return ~x;
+ \\pub export fn abs(a: c_int) c_int {
+ \\ return if (a < 0) -a else a;
\\}
});
- cases.add("bool not",
- \\int foo(int a, float b, void *c) {
- \\ return !(a == 0);
- \\ return !a;
- \\ return !b;
- \\ return !c;
+ cases.addC("post increment",
+ \\unsigned foo1(unsigned a) {
+ \\ a++;
+ \\ return a;
+ \\}
+ \\int foo2(int a) {
+ \\ a++;
+ \\ return a;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo1(_arg_a: c_uint) c_uint {
+ \\ var a = _arg_a;
+ \\ a +%= 1;
+ \\ return a;
+ \\}
+ \\pub export fn foo2(_arg_a: c_int) c_int {
+ \\ var a = _arg_a;
+ \\ a += 1;
+ \\ return a;
+ \\}
+ });
+
+ cases.addC("deref function pointer",
+ \\void foo(void) {}
+ \\int baz(void) { return 0; }
+ \\void bar(void) {
+ \\ void(*f)(void) = foo;
+ \\ int(*b)(void) = baz;
+ \\ f();
+ \\ (*(f))();
+ \\ foo();
+ \\ b();
+ \\ (*(b))();
+ \\ baz();
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo() void {}
+ \\pub export fn baz() c_int {
+ \\ return 0;
+ \\}
+ \\pub export fn bar() void {
+ \\ var f: ?extern fn () void = foo;
+ \\ var b: ?extern fn () c_int = baz;
+ \\ f.?();
+ \\ f.?();
+ \\ foo();
+ \\ _ = b.?();
+ \\ _ = b.?();
+ \\ _ = baz();
+ \\}
+ });
+
+ cases.addC("pre increment/decrement",
+ \\void foo(void) {
+ \\ int i = 0;
+ \\ unsigned u = 0;
+ \\ ++i;
+ \\ --i;
+ \\ ++u;
+ \\ --u;
+ \\ i = ++i;
+ \\ i = --i;
+ \\ u = ++u;
+ \\ u = --u;
\\}
, &[_][]const u8{
- \\pub fn foo(a: c_int, b: f32, c: ?*c_void) c_int {
- \\ return !(a == 0);
- \\ return !(a != 0);
- \\ return !(b != 0);
- \\ return !(c != null);
+ \\pub export fn foo() void {
+ \\ var i: c_int = 0;
+ \\ var u: c_uint = @as(c_uint, 0);
+ \\ i += 1;
+ \\ i -= 1;
+ \\ u +%= 1;
+ \\ u -%= 1;
+ \\ i = (x: {
+ \\ const _ref = &i;
+ \\ _ref.* += 1;
+ \\ break :x _ref.*;
+ \\ });
+ \\ i = (x: {
+ \\ const _ref = &i;
+ \\ _ref.* -= 1;
+ \\ break :x _ref.*;
+ \\ });
+ \\ u = (x: {
+ \\ const _ref = &u;
+ \\ _ref.* +%= 1;
+ \\ break :x _ref.*;
+ \\ });
+ \\ u = (x: {
+ \\ const _ref = &u;
+ \\ _ref.* -%= 1;
+ \\ break :x _ref.*;
+ \\ });
\\}
});
- cases.add("primitive types included in defined symbols",
- \\int foo(int u32) {
- \\ return u32;
+ cases.addC("shift right assign",
+ \\int log2(unsigned a) {
+ \\ int i = 0;
+ \\ while (a > 0) {
+ \\ a >>= 1;
+ \\ }
+ \\ return i;
\\}
, &[_][]const u8{
- \\pub fn foo(u32_0: c_int) c_int {
- \\ return u32_0;
+ \\pub export fn log2(_arg_a: c_uint) c_int {
+ \\ var a = _arg_a;
+ \\ var i: c_int = 0;
+ \\ while (a > @as(c_uint, 0)) {
+ \\ a >>= @as(@import("std").math.Log2Int(c_uint), 1);
+ \\ }
+ \\ return i;
\\}
});
- cases.add("if on non-bool",
- \\enum SomeEnum { A, B, C };
- \\int if_none_bool(int a, float b, void *c, enum SomeEnum d) {
- \\ if (a) return 0;
- \\ if (b) return 1;
- \\ if (c) return 2;
- \\ if (d) return 3;
- \\ return 4;
+ cases.addC("shift right assign with a fixed size type",
+ \\#include <stdint.h>
+ \\int log2(uint32_t a) {
+ \\ int i = 0;
+ \\ while (a > 0) {
+ \\ a >>= 1;
+ \\ }
+ \\ return i;
\\}
, &[_][]const u8{
- \\pub const A = enum_SomeEnum.A;
- \\pub const B = enum_SomeEnum.B;
- \\pub const C = enum_SomeEnum.C;
- \\pub const enum_SomeEnum = extern enum {
- \\ A,
- \\ B,
- \\ C,
- \\};
- \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int {
- \\ if (a != 0) return 0;
- \\ if (b != 0) return 1;
- \\ if (c != null) return 2;
- \\ if (d != @bitCast(enum_SomeEnum, @as(@TagType(enum_SomeEnum), 0))) return 3;
- \\ return 4;
+ \\pub export fn log2(_arg_a: u32) c_int {
+ \\ var a = _arg_a;
+ \\ var i: c_int = 0;
+ \\ while (a > @as(c_uint, 0)) {
+ \\ a >>= @as(u5, 1);
+ \\ }
+ \\ return i;
\\}
});
- cases.add("while on non-bool",
- \\int while_none_bool(int a, float b, void *c) {
- \\ while (a) return 0;
- \\ while (b) return 1;
- \\ while (c) return 2;
- \\ return 3;
+ cases.addC("compound assignment operators",
+ \\void foo(void) {
+ \\ int a = 0;
+ \\ a += (a += 1);
+ \\ a -= (a -= 1);
+ \\ a *= (a *= 1);
+ \\ a &= (a &= 1);
+ \\ a |= (a |= 1);
+ \\ a ^= (a ^= 1);
+ \\ a >>= (a >>= 1);
+ \\ a <<= (a <<= 1);
\\}
, &[_][]const u8{
- \\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
- \\ while (a != 0) return 0;
- \\ while (b != 0) return 1;
- \\ while (c != null) return 2;
- \\ return 3;
+ \\pub export fn foo() void {
+ \\ var a: c_int = 0;
+ \\ a += (x: {
+ \\ const _ref = &a;
+ \\ _ref.* = (_ref.* + 1);
+ \\ break :x _ref.*;
+ \\ });
+ \\ a -= (x: {
+ \\ const _ref = &a;
+ \\ _ref.* = (_ref.* - 1);
+ \\ break :x _ref.*;
+ \\ });
+ \\ a *= (x: {
+ \\ const _ref = &a;
+ \\ _ref.* = (_ref.* * 1);
+ \\ break :x _ref.*;
+ \\ });
+ \\ a &= (x: {
+ \\ const _ref = &a;
+ \\ _ref.* = (_ref.* & 1);
+ \\ break :x _ref.*;
+ \\ });
+ \\ a |= (x: {
+ \\ const _ref = &a;
+ \\ _ref.* = (_ref.* | 1);
+ \\ break :x _ref.*;
+ \\ });
+ \\ a ^= (x: {
+ \\ const _ref = &a;
+ \\ _ref.* = (_ref.* ^ 1);
+ \\ break :x _ref.*;
+ \\ });
+ \\ a >>= @as(@import("std").math.Log2Int(c_int), (x: {
+ \\ const _ref = &a;
+ \\ _ref.* = (_ref.* >> @as(@import("std").math.Log2Int(c_int), 1));
+ \\ break :x _ref.*;
+ \\ }));
+ \\ a <<= @as(@import("std").math.Log2Int(c_int), (x: {
+ \\ const _ref = &a;
+ \\ _ref.* = (_ref.* << @as(@import("std").math.Log2Int(c_int), 1));
+ \\ break :x _ref.*;
+ \\ }));
\\}
});
- cases.add("for on non-bool",
- \\int for_none_bool(int a, float b, void *c) {
- \\ for (;a;) return 0;
- \\ for (;b;) return 1;
- \\ for (;c;) return 2;
- \\ return 3;
+ cases.addC("compound assignment operators unsigned",
+ \\void foo(void) {
+ \\ unsigned a = 0;
+ \\ a += (a += 1);
+ \\ a -= (a -= 1);
+ \\ a *= (a *= 1);
+ \\ a &= (a &= 1);
+ \\ a |= (a |= 1);
+ \\ a ^= (a ^= 1);
+ \\ a >>= (a >>= 1);
+ \\ a <<= (a <<= 1);
\\}
, &[_][]const u8{
- \\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
- \\ while (a != 0) return 0;
- \\ while (b != 0) return 1;
- \\ while (c != null) return 2;
- \\ return 3;
+ \\pub export fn foo() void {
+ \\ var a: c_uint = @as(c_uint, 0);
+ \\ a +%= (x: {
+ \\ const _ref = &a;
+ \\ _ref.* = (_ref.* +% @as(c_uint, 1));
+ \\ break :x _ref.*;
+ \\ });
+ \\ a -%= (x: {
+ \\ const _ref = &a;
+ \\ _ref.* = (_ref.* -% @as(c_uint, 1));
+ \\ break :x _ref.*;
+ \\ });
+ \\ a *%= (x: {
+ \\ const _ref = &a;
+ \\ _ref.* = (_ref.* *% @as(c_uint, 1));
+ \\ break :x _ref.*;
+ \\ });
+ \\ a &= (x: {
+ \\ const _ref = &a;
+ \\ _ref.* = (_ref.* & @as(c_uint, 1));
+ \\ break :x _ref.*;
+ \\ });
+ \\ a |= (x: {
+ \\ const _ref = &a;
+ \\ _ref.* = (_ref.* | @as(c_uint, 1));
+ \\ break :x _ref.*;
+ \\ });
+ \\ a ^= (x: {
+ \\ const _ref = &a;
+ \\ _ref.* = (_ref.* ^ @as(c_uint, 1));
+ \\ break :x _ref.*;
+ \\ });
+ \\ a >>= @as(@import("std").math.Log2Int(c_uint), (x: {
+ \\ const _ref = &a;
+ \\ _ref.* = (_ref.* >> @as(@import("std").math.Log2Int(c_uint), 1));
+ \\ break :x _ref.*;
+ \\ }));
+ \\ a <<= @as(@import("std").math.Log2Int(c_uint), (x: {
+ \\ const _ref = &a;
+ \\ _ref.* = (_ref.* << @as(@import("std").math.Log2Int(c_uint), 1));
+ \\ break :x _ref.*;
+ \\ }));
\\}
});
- 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;
- \\ }
+ cases.addC("post increment/decrement",
+ \\void foo(void) {
+ \\ int i = 0;
+ \\ unsigned u = 0;
+ \\ i++;
+ \\ i--;
+ \\ u++;
+ \\ u--;
+ \\ i = i++;
+ \\ i = i--;
+ \\ u = u++;
+ \\ u = u--;
\\}
, &[_][]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;
- \\ }
+ \\pub export fn foo() void {
+ \\ var i: c_int = 0;
+ \\ var u: c_uint = @as(c_uint, 0);
+ \\ i += 1;
+ \\ i -= 1;
+ \\ u +%= 1;
+ \\ u -%= 1;
+ \\ i = (x: {
+ \\ const _ref = &i;
+ \\ const _tmp = _ref.*;
+ \\ _ref.* += 1;
+ \\ break :x _tmp;
+ \\ });
+ \\ i = (x: {
+ \\ const _ref = &i;
+ \\ const _tmp = _ref.*;
+ \\ _ref.* -= 1;
+ \\ break :x _tmp;
+ \\ });
+ \\ u = (x: {
+ \\ const _ref = &u;
+ \\ const _tmp = _ref.*;
+ \\ _ref.* +%= 1;
+ \\ break :x _tmp;
+ \\ });
+ \\ u = (x: {
+ \\ const _ref = &u;
+ \\ const _tmp = _ref.*;
+ \\ _ref.* -%= 1;
+ \\ break :x _tmp;
+ \\ });
\\}
});
@@ -1885,206 +3088,85 @@ 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\"";
+ cases.addC("function call",
+ \\static void bar(void) { }
+ \\void foo(int *(baz)(void)) {
+ \\ bar();
+ \\ baz();
\\}
- \\
, &[_][]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\"";
+ \\pub fn bar() void {}
+ \\pub export fn foo(baz: ?extern fn () [*c]c_int) void {
+ \\ bar();
+ \\ _ = baz.?();
\\}
- \\
});
- if (builtin.os != builtin.Os.windows) {
- // sysv_abi not currently supported on windows
- cases.add("Macro qualified functions",
- \\void __attribute__((sysv_abi)) foo(void);
- , &[_][]const u8{
- \\pub extern fn foo() void;
- });
- }
-
- /////////////// Cases for only stage1 because stage2 behavior is better ////////////////
- cases.addC("Parameterless function prototypes",
- \\void foo() {}
- \\void bar(void) {}
+ cases.add("macro defines string literal with hex",
+ \\#define FOO "aoeu\xab derp"
+ \\#define FOO2 "aoeu\x0007a derp"
+ \\#define FOO_CHAR '\xfF'
, &[_][]const u8{
- \\pub export fn foo() void {}
- \\pub export fn bar() void {}
+ \\pub const FOO = "aoeu\xab derp";
+ ,
+ \\pub const FOO2 = "aoeuz derp";
+ ,
+ \\pub const FOO_CHAR = 255;
});
- cases.add("#define a char literal",
- \\#define A_CHAR 'a'
+ cases.add("macro defines string literal with octal",
+ \\#define FOO "aoeu\023 derp"
+ \\#define FOO2 "aoeu\0234 derp"
+ \\#define FOO_CHAR '\077'
, &[_][]const u8{
- \\pub const A_CHAR = 97;
+ \\pub const FOO = "aoeu\x13 derp";
+ ,
+ \\pub const FOO2 = "aoeu\x134 derp";
+ ,
+ \\pub const FOO_CHAR = 63;
});
- cases.add("generate inline func for #define global extern fn",
- \\extern void (*fn_ptr)(void);
- \\#define foo fn_ptr
- \\
- \\extern char (*fn_ptr2)(int, float);
- \\#define bar fn_ptr2
+ cases.add("enums",
+ \\enum Foo {
+ \\ FooA,
+ \\ FooB,
+ \\ Foo1,
+ \\};
, &[_][]const u8{
- \\pub extern var fn_ptr: ?extern fn () void;
+ \\pub const enum_Foo = extern enum {
+ \\ A,
+ \\ B,
+ \\ @"1",
+ \\};
,
- \\pub inline fn foo() void {
- \\ return fn_ptr.?();
- \\}
+ \\pub const FooA = enum_Foo.A;
,
- \\pub extern var fn_ptr2: ?extern fn (c_int, f32) u8;
+ \\pub const FooB = enum_Foo.B;
,
- \\pub inline fn bar(arg0: c_int, arg1: f32) u8 {
- \\ return fn_ptr2.?(arg0, arg1);
- \\}
- });
- cases.add("comment after integer literal",
- \\#define SDL_INIT_VIDEO 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- , &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = 32;
- });
-
- cases.add("u integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020u /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- , &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = @as(c_uint, 32);
- });
-
- cases.add("l integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020l /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- , &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = @as(c_long, 32);
- });
-
- cases.add("ul integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020ul /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- , &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = @as(c_ulong, 32);
- });
-
- cases.add("lu integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020lu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- , &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = @as(c_ulong, 32);
- });
-
- cases.add("ll integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020ll /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- , &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = @as(c_longlong, 32);
- });
-
- cases.add("ull integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020ull /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- , &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 32);
- });
-
- cases.add("llu integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020llu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- , &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 32);
+ \\pub const Foo1 = enum_Foo.@"1";
+ ,
+ \\pub const Foo = enum_Foo;
});
- cases.add("macros with field targets",
- \\typedef unsigned int GLbitfield;
- \\typedef void (*PFNGLCLEARPROC) (GLbitfield mask);
- \\typedef void(*OpenGLProc)(void);
- \\union OpenGLProcs {
- \\ OpenGLProc ptr[1];
- \\ struct {
- \\ PFNGLCLEARPROC Clear;
- \\ } gl;
+ cases.add("enums",
+ \\enum Foo {
+ \\ FooA = 2,
+ \\ FooB = 5,
+ \\ Foo1,
\\};
- \\extern union OpenGLProcs glProcs;
- \\#define glClearUnion glProcs.gl.Clear
- \\#define glClearPFN PFNGLCLEARPROC
, &[_][]const u8{
- \\pub const GLbitfield = c_uint;
- ,
- \\pub const PFNGLCLEARPROC = ?extern fn (GLbitfield) void;
- ,
- \\pub const OpenGLProc = ?extern fn () void;
- ,
- \\pub const union_OpenGLProcs = extern union {
- \\ ptr: [1]OpenGLProc,
- \\ gl: extern struct {
- \\ Clear: PFNGLCLEARPROC,
- \\ },
+ \\pub const enum_Foo = extern enum {
+ \\ A = 2,
+ \\ B = 5,
+ \\ @"1" = 6,
\\};
,
- \\pub extern var glProcs: union_OpenGLProcs;
+ \\pub const FooA = enum_Foo.A;
,
- \\pub const glClearPFN = PFNGLCLEARPROC;
+ \\pub const FooB = enum_Foo.B;
,
- \\pub inline fn glClearUnion(arg0: GLbitfield) void {
- \\ return glProcs.gl.Clear.?(arg0);
- \\}
+ \\pub const Foo1 = enum_Foo.@"1";
,
- \\pub const OpenGLProcs = union_OpenGLProcs;
- });
-
- cases.add("macro pointer cast",
- \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
- , &[_][]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);
+ \\pub const Foo = enum_Foo;
});
}