Merge pull request #3935 from Vexu/translate-c-2

Translate-c-2 the rest
This commit is contained in:
Andrew Kelley
2019-12-22 16:12:56 -05:00
committed by GitHub
10 changed files with 4793 additions and 1935 deletions
+3 -1
View File
@@ -582,7 +582,9 @@ pub fn formatAsciiChar(
comptime Errors: type,
output: fn (@TypeOf(context), []const u8) Errors!void,
) Errors!void {
return output(context, @as(*const [1]u8, &c)[0..]);
if (std.ascii.isPrint(c))
return output(context, @as(*const [1]u8, &c)[0..]);
return format(context, Errors, output, "\\x{x:0<2}", .{c});
}
pub fn formatBuf(
+16 -16
View File
@@ -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,
+8 -8
View File
@@ -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;
+1 -1
View File
@@ -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
+214 -114
View File
@@ -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"));
}
+99 -7
View File
@@ -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;
+2310 -746
View File
@@ -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,
},
.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;
}
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 {
fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block {
var scope = inner;
while (true) : (scope = scope.parent orelse unreachable) {
if (scope.id == .Block) return @fieldParentPtr(Scope.Block, "base", scope);
while (true) {
switch (scope.id) {
.Root => unreachable,
.Block => return @fieldParentPtr(Block, "base", scope),
.Condition => {
// comma operator used
return try Block.init(c, scope, "blk");
},
else => scope = scope.parent.?,
}
}
}
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,130 +517,295 @@ 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);
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);
const bare_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, record_decl)));
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 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});
if (ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl) or bare_name.len == 0)
return;
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 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);
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 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, "=");
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 extern_tok = try appendToken(c, .Keyword_extern, "extern");
const container_tok = try appendToken(c, .Keyword_enum, "enum");
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 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
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 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);
const eq_tok = try appendToken(c, .Equal, "=");
const init_node = try transCreateNodeIdentifier(c, alias.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 = 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, ";"),
};
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);
}
@@ -651,9 +837,45 @@ fn transStmt(
.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),
.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;
},
.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;
},
.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,
@@ -674,91 +896,159 @@ fn transBinaryOperator(
) 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) {
.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)
else
try transCreateNodeInfixOp(rp, scope, stmt, .Add, .Plus, "+", true);
return maybeSuppressResult(rp, scope, result_used, node);
},
.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);
},
.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);
.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);
} else {
// unsigned/float division uses the operator
const node = try transCreateNodeInfixOp(rp, scope, stmt, .Mod, .Percent, "%", true);
return maybeSuppressResult(rp, scope, result_used, node);
}
},
.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},
),
.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 {
op_token = try appendToken(rp.c, .Plus, "+");
op_id = .Add;
}
},
.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,6 +1249,151 @@ fn transImplicitCastExpr(
}
}
fn transBoolExpr(
rp: RestorePoint,
scope: *Scope,
expr: *const ZigClangExpr,
used: ResultUsed,
lrvalue: LRValue,
grouped: bool,
) TransError!*ast.Node {
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);
}
}
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 => {},
}
return false;
}
fn finishBoolExpr(
rp: RestorePoint,
scope: *Scope,
loc: ZigClangSourceLocation,
ty: *const ZigClangType,
node: *ast.Node,
used: ResultUsed,
) TransError!*ast.Node {
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,
@@ -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,6 +1716,813 @@ fn transImplicitValueInitExpr(
};
}
fn transIfStmt(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangIfStmt,
) TransError!*ast.Node {
// if (c) t
// if (c) t else e
const if_node = try transCreateNodeIf(rp.c);
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_node.body = try transStmt(rp, scope, ZigClangIfStmt_getThen(stmt), .unused, .r_value);
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 transWhileLoop(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangWhileStmt,
) TransError!*ast.Node {
const while_node = try transCreateNodeWhile(rp.c);
var cond_scope = Scope{
.parent = scope,
.id = .Condition,
};
while_node.condition = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangWhileStmt_getCond(stmt)), .used, .r_value, false);
_ = try appendToken(rp.c, .RParen, ")");
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 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,
};
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;
};
// 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, ";");
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;
}
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);
}
var cond_scope = Scope{
.parent = scope,
.id = .Condition,
};
const while_node = try transCreateNodeWhile(rp.c);
while_node.condition = if (ZigClangForStmt_getCond(stmt)) |cond|
try transBoolExpr(rp, &cond_scope, 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;
}
fn transSwitch(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangSwitchStmt,
) TransError!*ast.Node {
const switch_node = try transCreateNodeSwitch(rp.c);
var switch_scope = Scope.Switch{
.base = .{
.id = .Switch,
.parent = scope,
},
.cases = &switch_node.cases,
.pending_block = undefined,
};
var cond_scope = Scope{
.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, "}");
const block_scope = try Scope.Block.init(rp.c, &switch_scope.base, null);
// tmp block that all statements will go before being picked up by a case or default
const block = try transCreateNodeBlock(rp.c, null);
block_scope.block_node = block;
const switch_block = try transCreateNodeBlock(rp.c, null);
try switch_block.statements.push(&switch_node.base);
switch_scope.pending_block = switch_block;
const last = try transStmt(rp, &block_scope.base, ZigClangSwitchStmt_getBody(stmt), .unused, .r_value);
_ = try appendToken(rp.c, .Semicolon, ";");
// take all pending statements
var it = last.cast(ast.Node.Block).?.statements.iterator(0);
while (it.next()) |n| {
try switch_scope.pending_block.statements.push(n.*);
}
switch_scope.pending_block.label = try appendIdentifier(rp.c, "__switch");
_ = try appendToken(rp.c, .Colon, ":");
if (!switch_scope.has_default) {
const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
else_prong.expr = &(try transCreateNodeBreak(rp.c, "__switch")).base;
_ = try appendToken(rp.c, .Comma, ",");
try switch_node.cases.push(&else_prong.base);
}
switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}");
return &switch_scope.pending_block.base;
}
fn transCase(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangCaseStmt,
) TransError!*ast.Node {
const block_scope = scope.findBlockScope(rp.c) catch unreachable;
const switch_scope = scope.getSwitch();
const label = try std.fmt.allocPrint(rp.c.a(), "__case_{}", .{switch_scope.cases.len - @boolToInt(switch_scope.has_default)});
_ = try appendToken(rp.c, .Semicolon, ";");
const expr = if (ZigClangCaseStmt_getRHS(stmt)) |rhs| blk: {
const lhs_node = try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value);
const ellips = try appendToken(rp.c, .Ellipsis3, "...");
const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
const node = try rp.c.a().create(ast.Node.InfixOp);
node.* = .{
.op_token = ellips,
.lhs = lhs_node,
.op = .Range,
.rhs = rhs_node,
};
break :blk &node.base;
} else
try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value);
const switch_prong = try transCreateNodeSwitchCase(rp.c, expr);
switch_prong.expr = &(try transCreateNodeBreak(rp.c, label)).base;
_ = try appendToken(rp.c, .Comma, ",");
try switch_scope.cases.push(&switch_prong.base);
const block = try transCreateNodeBlock(rp.c, null);
switch_scope.pending_block.label = try appendIdentifier(rp.c, label);
_ = try appendToken(rp.c, .Colon, ":");
switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}");
try block.statements.push(&switch_scope.pending_block.base);
// take all pending statements
var it = block_scope.block_node.statements.iterator(0);
while (it.next()) |n| {
try switch_scope.pending_block.statements.push(n.*);
}
block_scope.block_node.statements.shrink(0);
switch_scope.pending_block = block;
return transStmt(rp, scope, ZigClangCaseStmt_getSubStmt(stmt), .unused, .r_value);
}
fn transDefault(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangDefaultStmt,
) TransError!*ast.Node {
const block_scope = scope.findBlockScope(rp.c) catch unreachable;
const switch_scope = scope.getSwitch();
const label = "__default";
switch_scope.has_default = true;
_ = try appendToken(rp.c, .Semicolon, ";");
const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
else_prong.expr = &(try transCreateNodeBreak(rp.c, label)).base;
_ = try appendToken(rp.c, .Comma, ",");
try switch_scope.cases.push(&else_prong.base);
const block = try transCreateNodeBlock(rp.c, null);
switch_scope.pending_block.label = try appendIdentifier(rp.c, label);
_ = try appendToken(rp.c, .Colon, ":");
switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}");
try block.statements.push(&switch_scope.pending_block.base);
// take all pending statements
var it = block_scope.block_node.statements.iterator(0);
while (it.next()) |n| {
try switch_scope.pending_block.statements.push(n.*);
}
block_scope.block_node.statements.shrink(0);
switch_scope.pending_block = block;
return transStmt(rp, scope, ZigClangDefaultStmt_getSubStmt(stmt), .unused, .r_value);
}
fn transConstantExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangExpr, used: ResultUsed) TransError!*ast.Node {
var result: ZigClangExprEvalResult = undefined;
if (!ZigClangExpr_EvaluateAsConstantExpr(expr, &result, .EvaluateForCodeGen, rp.c.clang_context))
return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangExpr_getBeginLoc(expr), "invalid constant expression", .{});
return maybeSuppressResult(rp, scope, used, try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&result.Val)));
}
fn 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,
};
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);
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, ")");
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;
}
}
return maybeSuppressResult(rp, scope, result_used, &node.base);
}
fn qualTypeGetFnProto(qt: ZigClangQualType, is_ptr: *bool) ?*const ZigClangFunctionProtoType {
const canon = ZigClangQualType_getCanonicalType(qt);
var ty = ZigClangQualType_getTypePtr(canon);
is_ptr.* = false;
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;
}
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
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)}),
}
}
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;
}
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;
}
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);
_ = try appendToken(rp.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 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;
}
fn transCPtrCast(
rp: RestorePoint,
loc: ZigClangSourceLocation,
@@ -1252,6 +2560,61 @@ fn transCPtrCast(
return &ptrcast_node.base;
}
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;
}
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);
}
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 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,
@@ -1259,12 +2622,20 @@ fn maybeSuppressResult(
result: *ast.Node,
) 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, ";");
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.* = ast.Node.InfixOp{
op_node.* = .{
.op_token = op_token,
.lhs = lhs,
.op = .Assign,
@@ -1282,264 +2653,6 @@ fn transQualType(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSou
return transType(rp, ZigClangQualType_getTypePtr(qt), source_loc);
}
fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) TypeError!*ast.Node {
const rp = makeRestorePoint(c);
const record_loc = ZigClangRecordDecl_getLocation(record_decl);
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 {
return revertAndWarn(
rp,
error.UnsupportedType,
record_loc,
"unsupported record type",
.{},
);
}
const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse {
return transCreateNodeOpaqueType(c);
};
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)) {
rp.activate();
const node = try transCreateNodeOpaqueType(c);
try emitWarning(c, field_loc, "{} demoted to opaque type - has bitfield", .{container_kind_name});
return node;
}
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 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, "}");
return &container_node.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);
const visib_tok = try appendToken(c, .Keyword_pub, "pub");
const const_tok = try appendToken(c, .Keyword_const, "const");
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 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, "=");
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;
}
}
const extern_tok = try appendToken(c, .Keyword_extern, "extern");
const container_tok = try appendToken(c, .Keyword_enum, "enum");
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 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 => {
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,
},
};
_ = 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 std.mem.startsWith(u8, enum_val_name, bare_name))
enum_val_name[bare_name.len..]
else
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.
try addEnumTopLevel(c, name, field_name, enum_val_name);
}
container_node.rbrace_token = try appendToken(c, .RBrace, "}");
break :blk &container_node.base;
} else
try transCreateNodeOpaqueType(c);
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 = 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,
};
try addTopLevelDecl(c, name, &node.base);
if (!is_unnamed)
try c.alias_list.push(.{ .alias = bare_name, .name = name });
return transCreateNodeIdentifier(c, name);
}
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, "=");
const enum_ident = try transCreateNodeIdentifier(c, enum_name);
const period_tok = try appendToken(c, .Period, ".");
const field_ident = try transCreateNodeIdentifier(c, field_name);
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,
};
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,
};
try addTopLevelDecl(c, field_name, &node.base);
}
fn isCBuiltinType(qt: ZigClangQualType, kind: ZigClangBuiltinTypeKind) bool {
const c_type = qualTypeCanon(qt);
if (ZigClangType_getTypeClass(c_type) != .Builtin)
@@ -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",
.{},
);
return error.ParseError;
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;
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,24 +4557,32 @@ 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.?);
}
}
}
@@ -3069,10 +4591,52 @@ fn getFnDecl(c: *Context, ref: *ast.Node) ?*ast.Node {
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;
}
}
}
}
return null;
}
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.
+15
View File
@@ -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();
+3
View File
@@ -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,
+2124 -1042
View File
@@ -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,6 +386,432 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
},
);
cases.addC_both("null statements",
\\void foo(void) {
\\ ;;;;;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ {}
\\ {}
\\ {}
\\ {}
\\ {}
\\}
});
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.addC_both("predefined expressions",
\\void foo(void) {
\\ __func__;
\\ __FUNCTION__;
\\ __PRETTY_FUNCTION__;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ _ = "foo";
\\ _ = "foo";
\\ _ = "void foo(void)";
\\}
});
cases.addC_both("ignore result, no function arguments",
\\void foo() {
\\ int a;
\\ 1;
\\ "hey";
\\ 1 + 1;
\\ 1 - 1;
\\ a = 1;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: c_int = undefined;
\\ _ = 1;
\\ _ = "hey";
\\ _ = (1 + 1);
\\ _ = (1 - 1);
\\ a = 1;
\\}
});
cases.add_both("constant size array",
\\void func(int array[20]);
, &[_][]const u8{
\\pub extern fn func(array: [*c]c_int) void;
});
cases.add_both("__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.addC_both("void cast",
\\void foo() {
\\ int a;
\\ (void) a;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: c_int = undefined;
\\ _ = a;
\\}
});
cases.addC_both("implicit cast to void *",
\\void *foo() {
\\ unsigned short *x;
\\ return x;
\\}
, &[_][]const u8{
\\pub export fn foo() ?*c_void {
\\ var x: [*c]c_ushort = undefined;
\\ return @ptrCast(?*c_void, x);
\\}
});
cases.addC_both("null pointer implicit cast",
\\int* foo(void) {
\\ return 0;
\\}
, &[_][]const u8{
\\pub export fn foo() [*c]c_int {
\\ return null;
\\}
});
cases.add_both("simple union",
\\union Foo {
\\ int x;
\\ double y;
\\};
, &[_][]const u8{
\\pub const union_Foo = extern union {
\\ x: c_int,
\\ y: f64,
\\};
,
\\pub const Foo = union_Foo;
});
cases.addC_both("string literal",
\\const char *foo(void) {
\\ return "bar";
\\}
, &[_][]const u8{
\\pub export fn foo() [*c]const u8 {
\\ return "bar";
\\}
});
cases.addC_both("return void",
\\void foo(void) {
\\ return;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ return;
\\}
});
cases.addC_both("for loop",
\\void foo(void) {
\\ for (int i = 0; i; i = i + 1) { }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ {
\\ var i: c_int = 0;
\\ while (i != 0) : (i = (i + 1)) {}
\\ }
\\}
});
cases.addC_both("empty for loop",
\\void foo(void) {
\\ for (;;) { }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ while (true) {}
\\}
});
cases.addC_both("break statement",
\\void foo(void) {
\\ for (;;) {
\\ break;
\\ }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ while (true) {
\\ break;
\\ }
\\}
});
cases.addC_both("continue statement",
\\void foo(void) {
\\ for (;;) {
\\ continue;
\\ }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ while (true) {
\\ continue;
\\ }
\\}
});
cases.addC_both("pointer casting",
\\float *ptrcast() {
\\ int *a;
\\ return (float *)a;
\\}
, &[_][]const u8{
\\pub export fn ptrcast() [*c]f32 {
\\ var a: [*c]c_int = undefined;
\\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a));
\\}
});
cases.addC_both("pointer conversion with different alignment",
\\void test_ptr_cast() {
\\ void *p;
\\ {
\\ char *to_char = (char *)p;
\\ short *to_short = (short *)p;
\\ int *to_int = (int *)p;
\\ long long *to_longlong = (long long *)p;
\\ }
\\ {
\\ char *to_char = p;
\\ short *to_short = p;
\\ int *to_int = p;
\\ long long *to_longlong = p;
\\ }
\\}
, &[_][]const u8{
\\pub export fn test_ptr_cast() void {
\\ var p: ?*c_void = undefined;
\\ {
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
\\ }
\\ {
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
\\ }
\\}
});
cases.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 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_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 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_both("bitshift",
\\int foo(void) {
\\ return (1 << 2) >> 1;
\\}
, &[_][]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);
\\}
});
cases.addC_both("sizeof",
\\#include <stddef.h>
\\size_t size_of(void) {
\\ return sizeof(int);
\\}
, &[_][]const u8{
\\pub export fn size_of() usize {
\\ return @sizeOf(c_int);
\\}
});
cases.addC_both("normal deref",
\\void foo() {
\\ int *x;
\\ *x = 1;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var x: [*c]c_int = undefined;
\\ x.?.* = 1;
\\}
});
cases.addC_both("address of operator",
\\int foo(void) {
\\ int x = 1234;
\\ int *ptr = &x;
\\ return *ptr;
\\}
, &[_][]const u8{
\\pub export fn foo() c_int {
\\ var x: c_int = 1234;
\\ var ptr: [*c]c_int = &x;
\\ return ptr.?.*;
\\}
});
cases.addC_both("bin not",
\\int foo() {
\\ int x;
\\ return ~x;
\\}
, &[_][]const u8{
\\pub export fn foo() c_int {
\\ var x: c_int = undefined;
\\ return ~x;
\\}
});
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 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("__extension__ cast",
\\int foo(void) {
\\ return __extension__ 1;
\\}
, &[_][]const u8{
\\pub export fn foo() c_int {
\\ return 1;
\\}
});
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 that pass for only stage2 ////////////////
cases.add_2("Parameterless function prototypes",
@@ -491,28 +877,28 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ p,
\\};
, &[_][]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 {
\\pub const a = 0;
\\pub const b = 1;
\\pub const c = 2;
\\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 {
\\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 = @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 {
\\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,
@@ -521,9 +907,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ 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 n = 0;
\\pub const o = 1;
\\pub const p = 2;
\\pub const enum_i = extern enum {
\\ n,
\\ o,
@@ -622,25 +1008,22 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\#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: extern struct {
\\ Clear: PFNGLCLEARPROC,
\\ },
\\ gl: struct_unnamed_1,
\\};
,
\\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 inline fn glClearUnion(arg_2: GLbitfield) void {
\\ return glProcs.gl.Clear.?(arg_2);
\\}
,
\\pub const OpenGLProcs = union_OpenGLProcs;
});
@@ -662,13 +1045,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
cases.add_2("macro escape sequences",
cases.add_2("macro defines string literal with hex",
\\#define FOO "aoeu\xab derp"
\\#define FOO2 "aoeu\a derp"
\\#define FOO2 "aoeu\x0007a derp"
\\#define FOO_CHAR '\xfF'
, &[_][]const u8{
\\pub const FOO = "aoeu\xab derp";
,
\\pub const FOO2 = "aoeu\x07 derp";
\\pub const FOO2 = "aoeu\x7a derp";
,
\\pub const FOO_CHAR = '\xff';
});
cases.add_2("variable aliasing",
@@ -688,7 +1074,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\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 {
\\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);
@@ -699,96 +1086,1258 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
/////////////// 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",
\\void foo(void) {
\\ __func__;
\\ __FUNCTION__;
\\ __PRETTY_FUNCTION__;
cases.add_2("comma operator",
\\int foo(char c) {
\\ 2, 4;
\\ return 2, 4, 6;
\\}
, &[_][]const u8{
\\pub fn foo() void {
\\ _ = "foo";
\\ _ = "foo";
\\ _ = "void foo(void)";
\\pub export fn foo(_arg_c: u8) c_int {
\\ var c = _arg_c;
\\ _ = 2;
\\ _ = 4;
\\ _ = 2;
\\ _ = 4;
\\ return 6;
\\}
});
cases.add("ignore result, no function arguments",
\\void foo() {
cases.add_2("wors-case assign",
\\int foo(char c) {
\\ int a;
\\ 1;
\\ "hey";
\\ 1 + 1;
\\ 1 - 1;
\\ a = 1;
\\ int b;
\\ a = b = 2;
\\}
, &[_][]const u8{
\\pub fn foo() void {
\\pub export fn foo(_arg_c: u8) c_int {
\\ var c = _arg_c;
\\ var a: c_int = undefined;
\\ _ = 1;
\\ _ = "hey";
\\ _ = (1 + 1);
\\ _ = (1 - 1);
\\ a = 1;
\\ 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 {
\\ 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.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 {
\\ {
\\ 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.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 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;
\\ }
\\}
});
@@ -838,34 +2387,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
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);
cases.add("undefined array global",
\\int array[100];
, &[_][]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{
\\export fn abs(a: c_int) c_int {
\\ return if (a < 0) -a else a;
\\}
});
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;
\\pub var array: [100]c_int = undefined;
});
cases.add("qualified struct and enum",
@@ -900,165 +2425,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub const Bar = enum_Bar;
});
cases.add("constant size array",
\\void func(int array[20]);
cases.add("restrict -> noalias",
\\void foo(void *restrict bar, void *restrict);
, &[_][]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'
, &[_][]const u8{
\\pub const FOO = "aoeu\x13 derp";
,
\\pub const FOO2 = "aoeu\x134 derp";
,
\\pub const FOO_CHAR = 63;
});
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("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_uint), 1);
\\ }
\\ return i;
\\}
});
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 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 {}
\\}
});
cases.addC("==, !=",
\\int max(int a, int b) {
\\ if (a == b)
\\ return a;
\\ if (a != b)
\\ return b;
\\ return a;
\\}
, &[_][]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;
\\}
});
cases.addC("bitwise binary operators",
\\int max(int a, int b) {
\\ return (a & b) ^ (a | b);
\\}
, &[_][]const u8{
\\pub export fn max(a: c_int, b: c_int) c_int {
\\ return (a & b) ^ (a | b);
\\}
});
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 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.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;
\\}
, &[_][]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 extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void;
});
cases.addC("assign",
@@ -1093,26 +2463,6 @@ pub fn addCases(cases: *tests.TranslateCContext) 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;
\\}
, &[_][]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;
\\}
});
cases.add("anonymous enum",
\\enum {
\\ One,
@@ -1123,72 +2473,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub const Two = 1;
});
cases.addC("function call",
\\static void bar(void) { }
\\static int baz(void) { return 0; }
\\void foo(void) {
\\ bar();
\\ baz();
\\}
, &[_][]const u8{
\\pub fn bar() void {}
\\pub fn baz() c_int {
\\ return 0;
\\}
\\pub export fn foo() void {
\\ bar();
\\ _ = baz();
\\}
});
cases.addC("field access expression",
\\struct Foo {
\\ int field;
\\};
\\int read_field(struct Foo *foo) {
\\ return foo->field;
\\}
, &[_][]const u8{
\\pub const struct_Foo = extern struct {
\\ field: c_int,
\\};
\\pub export fn read_field(foo: [*c]struct_Foo) c_int {
\\ return foo.*.field;
\\}
});
cases.addC("null statements",
\\void foo(void) {
\\ ;;;;;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ {}
\\ {}
\\ {}
\\ {}
\\ {}
\\}
});
cases.add("undefined array global",
\\int array[100];
, &[_][]const u8{
\\pub var array: [100]c_int = undefined;
});
cases.addC("array access",
\\int array[100];
\\int foo(int index) {
\\ return array[index];
\\}
, &[_][]const u8{
\\pub var array: [100]c_int = undefined;
\\pub export fn foo(index: c_int) c_int {
\\ return array[index];
\\}
});
cases.addC("c style cast",
\\int float_to_int(float a) {
\\ return (int)a;
@@ -1199,47 +2483,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
cases.addC("void cast",
\\void foo(int a) {
\\ (void) a;
\\}
, &[_][]const u8{
\\pub export fn foo(a: c_int) void {
\\ _ = a;
\\}
});
cases.addC("implicit cast to void *",
\\void *foo(unsigned short *x) {
\\ return x;
\\}
, &[_][]const u8{
\\pub export fn foo(x: [*c]c_ushort) ?*c_void {
\\ return @ptrCast(?*c_void, x);
\\}
});
cases.addC("sizeof",
\\#include <stddef.h>
\\size_t size_of(void) {
\\ return sizeof(int);
\\}
, &[_][]const u8{
\\pub export fn size_of() usize {
\\ return @sizeOf(c_int);
\\}
});
cases.addC("null pointer implicit cast",
\\int* foo(void) {
\\ return 0;
\\}
, &[_][]const u8{
\\pub export fn foo() [*c]c_int {
\\ return null;
\\}
});
cases.addC("comma operator",
\\int foo(void) {
\\ return 1, 2;
@@ -1253,6 +2496,93 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
cases.addC("escape sequences",
\\const char *escapes() {
\\char a = '\'',
\\ b = '\\',
\\ c = '\a',
\\ d = '\b',
\\ e = '\f',
\\ f = '\n',
\\ g = '\r',
\\ h = '\t',
\\ i = '\v',
\\ j = '\0',
\\ k = '\"';
\\ return "\'\\\a\b\f\n\r\t\v\0\"";
\\}
\\
, &[_][]const u8{
\\pub export fn escapes() [*c]const u8 {
\\ var a: u8 = @as(u8, '\'');
\\ var b: u8 = @as(u8, '\\');
\\ var c: u8 = @as(u8, '\x07');
\\ var d: u8 = @as(u8, '\x08');
\\ var e: u8 = @as(u8, '\x0c');
\\ var f: u8 = @as(u8, '\n');
\\ var g: u8 = @as(u8, '\r');
\\ var h: u8 = @as(u8, '\t');
\\ var i: u8 = @as(u8, '\x0b');
\\ var j: u8 = @as(u8, '\x00');
\\ var k: u8 = @as(u8, '\"');
\\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\"";
\\}
\\
});
cases.addC("do loop",
\\void foo(void) {
\\ int a = 2;
\\ do {
\\ a--;
\\ } while (a != 0);
\\
\\ int b = 2;
\\ do
\\ b--;
\\ while (b != 0);
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: c_int = 2;
\\ while (true) {
\\ a -= 1;
\\ if (!(a != 0)) break;
\\ }
\\ var b: c_int = 2;
\\ while (true) {
\\ b -= 1;
\\ if (!(b != 0)) break;
\\ }
\\}
});
cases.addC("==, !=",
\\int max(int a, int b) {
\\ if (a == b)
\\ return a;
\\ if (a != b)
\\ return b;
\\ return a;
\\}
, &[_][]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;
\\}
});
cases.addC("bitwise binary operators",
\\int max(int a, int b) {
\\ return (a & b) ^ (a | b);
\\}
, &[_][]const u8{
\\pub export fn max(a: c_int, b: c_int) c_int {
\\ return (a & b) ^ (a | b);
\\}
});
cases.addC("statement expression",
\\int foo(void) {
\\ return ({
@@ -1269,23 +2599,273 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
cases.addC("__extension__ cast",
\\int foo(void) {
\\ return __extension__ 1;
cases.addC("field access expression",
\\struct Foo {
\\ int field;
\\};
\\int read_field(struct Foo *foo) {
\\ return foo->field;
\\}
, &[_][]const u8{
\\pub export fn foo() c_int {
\\ return 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.addC("bitshift",
\\int foo(void) {
\\ return (1 << 2) >> 1;
cases.addC("array access",
\\int array[100];
\\int foo(int index) {
\\ return array[index];
\\}
, &[_][]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);
\\pub var array: [100]c_int = undefined;
\\pub export fn foo(index: c_int) c_int {
\\ return array[index];
\\}
});
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 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.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 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 {}
\\}
});
cases.add("variable name shadowing",
\\int foo(void) {
\\ int x = 1;
\\ {
\\ int x = 2;
\\ x += 1;
\\ }
\\ return x;
\\}
, &[_][]const u8{
\\pub fn foo() c_int {
\\ var x: c_int = 1;
\\ {
\\ var x_0: c_int = 2;
\\ x_0 += 1;
\\ }
\\ return x;
\\}
});
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{
\\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.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 export fn abs(a: c_int) c_int {
\\ return if (a < 0) -a else a;
\\}
});
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 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.addC("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_uint), 1);
\\ }
\\ return i;
\\}
});
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 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;
\\}
});
@@ -1453,383 +3033,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
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 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.addC("do loop",
\\void foo(void) {
\\ int a = 2;
\\ do {
\\ a--;
\\ } while (a != 0);
\\
\\ int b = 2;
\\ do
\\ b--;
\\ while (b != 0);
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: c_int = 2;
\\ while (true) {
\\ a -= 1;
\\ if (!(a != 0)) break;
\\ }
\\ var b: c_int = 2;
\\ while (true) {
\\ b -= 1;
\\ if (!(b != 0)) break;
\\ }
\\}
});
cases.addC("deref function pointer",
\\void foo(void) {}
\\int baz(void) { return 0; }
\\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("normal deref",
\\void foo(int *x) {
\\ *x = 1;
\\}
, &[_][]const u8{
\\pub export fn foo(x: [*c]c_int) void {
\\ x.?.* = 1;
\\}
});
cases.add("simple union",
\\union Foo {
\\ int x;
\\ double y;
\\};
, &[_][]const u8{
\\pub const union_Foo = extern union {
\\ x: c_int,
\\ y: f64,
\\};
,
\\pub const Foo = union_Foo;
});
cases.add("address of operator",
\\int foo(void) {
\\ int x = 1234;
\\ int *ptr = &x;
\\ return *ptr;
\\}
, &[_][]const u8{
\\pub fn foo() c_int {
\\ var x: c_int = 1234;
\\ var ptr: [*c]c_int = &x;
\\ return ptr.?.*;
\\}
});
cases.add("string literal",
\\const char *foo(void) {
\\ return "bar";
\\}
, &[_][]const u8{
\\pub fn foo() [*c]const u8 {
\\ return "bar";
\\}
});
cases.add("return void",
\\void foo(void) {
\\ return;
\\}
, &[_][]const u8{
\\pub fn foo() void {
\\ return;
\\}
});
cases.add("for loop",
\\void foo(void) {
\\ for (int i = 0; i < 10; i += 1) { }
\\}
, &[_][]const u8{
\\pub fn foo() void {
\\ {
\\ var i: c_int = 0;
\\ while (i < 10) : (i += 1) {}
\\ }
\\}
});
cases.add("empty for loop",
\\void foo(void) {
\\ for (;;) { }
\\}
, &[_][]const u8{
\\pub fn foo() void {
\\ while (true) {}
\\}
});
cases.add("break statement",
\\void foo(void) {
\\ for (;;) {
\\ break;
\\ }
\\}
, &[_][]const u8{
\\pub fn foo() void {
\\ while (true) {
\\ break;
\\ }
\\}
});
cases.add("continue statement",
\\void foo(void) {
\\ for (;;) {
\\ continue;
\\ }
\\}
, &[_][]const u8{
\\pub fn foo() void {
\\ while (true) {
\\ continue;
\\ }
\\}
});
cases.add("variable name shadowing",
\\int foo(void) {
\\ int x = 1;
\\ {
\\ int x = 2;
\\ x += 1;
\\ }
\\ return x;
\\}
, &[_][]const u8{
\\pub fn foo() c_int {
\\ var x: c_int = 1;
\\ {
\\ var x_0: c_int = 2;
\\ x_0 += 1;
\\ }
\\ return x;
\\}
});
cases.add("pointer casting",
\\float *ptrcast(int *a) {
\\ return (float *)a;
\\}
, &[_][]const u8{
\\fn ptrcast(a: [*c]c_int) [*c]f32 {
\\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a));
\\}
});
cases.add("bin not",
\\int foo(int x) {
\\ return ~x;
\\}
, &[_][]const u8{
\\pub fn foo(x: c_int) c_int {
\\ return ~x;
\\}
});
cases.add("bool not",
\\int foo(int a, float b, void *c) {
\\ return !(a == 0);
\\ return !a;
\\ return !b;
\\ return !c;
\\}
, &[_][]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);
\\}
});
cases.add("primitive types included in defined symbols",
\\int foo(int u32) {
\\ return u32;
\\}
, &[_][]const u8{
\\pub fn foo(u32_0: c_int) c_int {
\\ return u32_0;
\\}
});
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{
\\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("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 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;
\\}
});
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;
\\}
, &[_][]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;
\\}
});
cases.add("switch on int",
\\int switch_fn(int i) {
\\ int res = 0;
\\ switch (i) {
\\ case 0:
\\ res = 1;
\\ case 1:
\\ res = 2;
\\ default:
\\ res = 3 * i;
\\ break;
\\ case 2:
\\ res = 5;
\\ }
\\}
, &[_][]const u8{
\\pub fn switch_fn(i: c_int) c_int {
\\ var res: c_int = 0;
\\ __switch: {
\\ __case_2: {
\\ __default: {
\\ __case_1: {
\\ __case_0: {
\\ switch (i) {
\\ 0 => break :__case_0,
\\ 1 => break :__case_1,
\\ else => break :__default,
\\ 2 => break :__case_2,
\\ }
\\ }
\\ res = 1;
\\ }
\\ res = 2;
\\ }
\\ res = (3 * i);
\\ break :__switch;
\\ }
\\ res = 5;
\\ }
\\}
});
cases.addC("implicit casts",
\\#include <stdbool.h>
\\
@@ -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;
\\ }
cases.addC("function call",
\\static void bar(void) { }
\\void foo(int *(baz)(void)) {
\\ bar();
\\ baz();
\\}
, &[_][]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));
\\ }
\\pub fn bar() void {}
\\pub export fn foo(baz: ?extern fn () [*c]c_int) void {
\\ bar();
\\ _ = baz.?();
\\}
});
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.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 escapes() [*c]const u8 {
\\ var a: u8 = @as(u8, '\'');
\\ var b: u8 = @as(u8, '\\');
\\ var c: u8 = @as(u8, '\x07');
\\ var d: u8 = @as(u8, '\x08');
\\ var e: u8 = @as(u8, '\x0c');
\\ var f: u8 = @as(u8, '\n');
\\ var g: u8 = @as(u8, '\r');
\\ var h: u8 = @as(u8, '\t');
\\ var i: u8 = @as(u8, '\x0b');
\\ var j: u8 = @as(u8, '\x00');
\\ var k: u8 = @as(u8, '\"');
\\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\"";
\\}
\\
});
if (builtin.os != builtin.Os.windows) {
// sysv_abi not currently supported on windows
cases.add("Macro qualified functions",
\\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) {}
, &[_][]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 const FOO = "aoeu\xab derp";
,
\\pub inline fn foo() void {
\\ return fn_ptr.?();
\\}
\\pub const FOO2 = "aoeuz derp";
,
\\pub extern var fn_ptr2: ?extern fn (c_int, f32) u8;
\\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'
, &[_][]const u8{
\\pub const FOO = "aoeu\x13 derp";
,
\\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;
\\pub const FOO2 = "aoeu\x134 derp";
,
\\pub const FOO_CHAR = 63;
});
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;
cases.add("enums",
\\enum Foo {
\\ FooA,
\\ FooB,
\\ 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,
\\ B,
\\ @"1",
\\};
,
\\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;
\\pub const Foo = enum_Foo;
});
cases.add("macro pointer cast",
\\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
cases.add("enums",
\\enum Foo {
\\ FooA = 2,
\\ FooB = 5,
\\ Foo1,
\\};
, &[_][]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 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;
});
}