commit 91ef68f9b1121d2a08f175a81f88321664c90a61 (tree)
parent 9a4da6c8d8d55f47609b8e900e9a4bb1ac34d5e7
Author: Andrew Kelley <superjoe30@gmail.com>
Date: Wed, 29 Nov 2017 16:34:50 -0500
Merge remote-tracking branch 'origin/master' into llvm6
Diffstat:
32 files changed, 6925 insertions(+), 4631 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -339,7 +339,7 @@ set(ZIG_SOURCES
"${CMAKE_SOURCE_DIR}/src/target.cpp"
"${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
"${CMAKE_SOURCE_DIR}/src/util.cpp"
- "${CMAKE_SOURCE_DIR}/src/parsec.cpp"
+ "${CMAKE_SOURCE_DIR}/src/translate_c.cpp"
"${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
)
diff --git a/build.zig b/build.zig
@@ -58,5 +58,5 @@ pub fn build(b: &Builder) {
test_step.dependOn(tests.addCompileErrorTests(b, test_filter));
test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));
test_step.dependOn(tests.addDebugSafetyTests(b, test_filter));
- test_step.dependOn(tests.addParseCTests(b, test_filter));
+ test_step.dependOn(tests.addTranslateCTests(b, test_filter));
}
diff --git a/ci/travis_osx_script b/ci/travis_osx_script
@@ -22,4 +22,4 @@ make install
./zig build --build-file ../build.zig test-compile-errors --verbose
./zig build --build-file ../build.zig test-asm-link --verbose
./zig build --build-file ../build.zig test-debug-safety --verbose
-./zig build --build-file ../build.zig test-parsec --verbose
+./zig build --build-file ../build.zig test-translate-c --verbose
diff --git a/doc/home.html.in b/doc/home.html.in
@@ -70,10 +70,14 @@
<li><a href="#mersenne">Mersenne Twister Random Number Generator</a></li>
</ul>
<h3 id="hello">Hello World</h3>
- <pre><code class="zig">const io = @import("std").io;
+ <pre><code class="zig">const std = @import("std");
pub fn main() -> %void {
- %return io.stdout.printf("Hello, world!\n");
+ // If this program is run without stdout attached, exit with an error.
+ var stdout_file = %return std.io.getStdOut();
+ // If this program encounters pipe failure when printing to stdout, exit
+ // with an error.
+ %return stdout_file.write("Hello, world!\n");
}</code></pre>
<p>Build this with:</p>
<pre>zig build-exe hello.zig</pre>
diff --git a/doc/langref.html.in b/doc/langref.html.in
@@ -75,6 +75,7 @@
<li><a href="#slices">Slices</a></li>
<li><a href="#struct">struct</a></li>
<li><a href="#enum">enum</a></li>
+ <li><a href="#union">union</a></li>
<li><a href="#switch">switch</a></li>
<li><a href="#while">while</a></li>
<li><a href="#for">for</a></li>
@@ -209,6 +210,7 @@
<li><a href="#undef-invalid-error-code">Invalid Error Code</a></li>
<li><a href="#undef-invalid-enum-cast">Invalid Enum Cast</a></li>
<li><a href="#undef-incorrect-pointer-alignment">Incorrect Pointer Alignment</a></li>
+ <li><a href="#undef-bad-union-field">Wrong Union Field Access</a></li>
</ul>
</li>
<li><a href="#memory">Memory</a></li>
@@ -2189,6 +2191,8 @@ Test 4/4 enum builtins...OK</code></pre>
<li><a href="#builtin-enumTagName">@enumTagName</a></li>
<li><a href="#builtin-memberCount">@memberCount</a></li>
</ul>
+ <h2 id="union">union</h2>
+ <p>TODO union documentation</p>
<h2 id="switch">switch</h2>
<pre><code class="zig">const assert = @import("std").debug.assert;
const builtin = @import("builtin");
@@ -5117,6 +5121,9 @@ comptime {
<h3 id="undef-incorrect-pointer-alignment">Incorrect Pointer Alignment</h3>
<p>TODO</p>
+ <h3 id="undef-bad-union-field">Wrong Union Field Access</h3>
+ <p>TODO</p>
+
<h2 id="memory">Memory</h2>
<p>TODO: explain no default allocator in zig</p>
<p>TODO: show how to use the allocator interface</p>
@@ -5405,10 +5412,14 @@ const c = @cImport({
export fn decode_base_64(dest_ptr: &u8, dest_len: usize,
source_ptr: &const u8, source_len: usize) -> usize
{
- const src = source_ptr[0...source_len];
- const dest = dest_ptr[0...dest_len];
- return base64.decode(dest, src).len;
-}</code></pre>
+ const src = source_ptr[0..source_len];
+ const dest = dest_ptr[0..dest_len];
+ const base64_decoder = base64.standard_decoder_unsafe;
+ const decoded_size = base64_decoder.calcSize(src);
+ base64_decoder.decode(dest[0..decoded_size], src);
+ return decoded_size;
+}
+</code></pre>
<h4>test.c</h4>
<pre><code class="c">// This header is generated by zig from base64.zig
#include "base64.h"
diff --git a/example/mix_o_files/base64.zig b/example/mix_o_files/base64.zig
@@ -3,5 +3,8 @@ const base64 = @import("std").base64;
export fn decode_base_64(dest_ptr: &u8, dest_len: usize, source_ptr: &const u8, source_len: usize) -> usize {
const src = source_ptr[0..source_len];
const dest = dest_ptr[0..dest_len];
- return base64.decode(dest, src).len;
+ const base64_decoder = base64.standard_decoder_unsafe;
+ const decoded_size = base64_decoder.calcSize(src);
+ base64_decoder.decode(dest[0..decoded_size], src);
+ return decoded_size;
}
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
@@ -208,7 +208,7 @@ fn printUsage(outstream: &io.OutStream) -> %void {
\\ build-exe [source] create executable from source or object files
\\ build-lib [source] create library from source or object files
\\ build-obj [source] create object from source or assembly
- \\ parsec [source] convert c code to zig code
+ \\ translate-c [source] convert c code to zig code
\\ targets list available compilation targets
\\ test [source] create and run a test build
\\ version print version number and exit
diff --git a/src/all_types.hpp b/src/all_types.hpp
@@ -36,6 +36,7 @@ struct IrInstructionCast;
struct IrBasicBlock;
struct ScopeDecls;
struct ZigWindowsSDK;
+struct Tld;
struct IrGotoItem {
AstNode *source_node;
@@ -59,7 +60,9 @@ struct IrExecutable {
Buf *c_import_buf;
AstNode *source_node;
IrExecutable *parent_exec;
+ IrExecutable *source_exec;
Scope *begin_scope;
+ ZigList<Tld *> tld_list;
};
enum OutType {
@@ -73,6 +76,7 @@ enum ConstParentId {
ConstParentIdNone,
ConstParentIdStruct,
ConstParentIdArray,
+ ConstParentIdUnion,
};
struct ConstParent {
@@ -87,6 +91,9 @@ struct ConstParent {
ConstExprValue *struct_val;
size_t field_index;
} p_struct;
+ struct {
+ ConstExprValue *union_val;
+ } p_union;
} data;
};
@@ -100,6 +107,12 @@ struct ConstStructValue {
ConstParent parent;
};
+struct ConstUnionValue {
+ uint64_t tag;
+ ConstExprValue *payload;
+ ConstParent parent;
+};
+
enum ConstArraySpecial {
ConstArraySpecialNone,
ConstArraySpecialUndef,
@@ -238,6 +251,7 @@ struct ConstExprValue {
ErrorTableEntry *x_pure_err;
ConstEnumValue x_enum;
ConstStructValue x_struct;
+ ConstUnionValue x_union;
ConstArrayValue x_array;
ConstPtrValue x_ptr;
ImportTableEntry *x_import;
@@ -336,6 +350,13 @@ struct TypeEnumField {
uint32_t gen_index;
};
+struct TypeUnionField {
+ Buf *name;
+ TypeTableEntry *type_entry;
+ uint32_t value;
+ uint32_t gen_index;
+};
+
enum NodeType {
NodeTypeRoot,
NodeTypeFnProto,
@@ -1021,14 +1042,19 @@ struct TypeTableEntryEnumTag {
LLVMValueRef name_table;
};
+uint32_t type_ptr_hash(const TypeTableEntry *ptr);
+bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b);
+
struct TypeTableEntryUnion {
AstNode *decl_node;
ContainerLayout layout;
uint32_t src_field_count;
uint32_t gen_field_count;
- TypeStructField *fields;
- uint64_t size_bytes;
+ TypeUnionField *fields;
bool is_invalid; // true if any fields are invalid
+ TypeTableEntry *tag_type;
+ LLVMTypeRef union_type_ref;
+
ScopeDecls *decls_scope;
// set this flag temporarily to detect infinite loops
@@ -1039,6 +1065,13 @@ struct TypeTableEntryUnion {
bool zero_bits_loop_flag;
bool zero_bits_known;
+ uint32_t abi_alignment; // also figured out with zero_bits pass
+
+ size_t gen_union_index;
+ size_t gen_tag_index;
+
+ uint32_t union_size_bytes;
+ TypeTableEntry *most_aligned_union_member;
};
struct FnGenParamInfo {
@@ -1287,6 +1320,7 @@ enum PanicMsgId {
PanicMsgIdUnwrapMaybeFail,
PanicMsgIdInvalidErrorCode,
PanicMsgIdIncorrectAlignment,
+ PanicMsgIdBadUnionField,
PanicMsgIdCount,
};
@@ -1796,6 +1830,7 @@ enum IrInstructionId {
IrInstructionIdFieldPtr,
IrInstructionIdStructFieldPtr,
IrInstructionIdEnumFieldPtr,
+ IrInstructionIdUnionFieldPtr,
IrInstructionIdElemPtr,
IrInstructionIdVarPtr,
IrInstructionIdCall,
@@ -1805,6 +1840,7 @@ enum IrInstructionId {
IrInstructionIdContainerInitList,
IrInstructionIdContainerInitFields,
IrInstructionIdStructInit,
+ IrInstructionIdUnionInit,
IrInstructionIdUnreachable,
IrInstructionIdTypeOf,
IrInstructionIdToPtrType,
@@ -2060,6 +2096,14 @@ struct IrInstructionEnumFieldPtr {
bool is_const;
};
+struct IrInstructionUnionFieldPtr {
+ IrInstruction base;
+
+ IrInstruction *union_ptr;
+ TypeUnionField *field;
+ bool is_const;
+};
+
struct IrInstructionElemPtr {
IrInstruction base;
@@ -2150,6 +2194,15 @@ struct IrInstructionStructInit {
LLVMValueRef tmp_ptr;
};
+struct IrInstructionUnionInit {
+ IrInstruction base;
+
+ TypeTableEntry *union_type;
+ TypeUnionField *field;
+ IrInstruction *init_value;
+ LLVMValueRef tmp_ptr;
+};
+
struct IrInstructionUnreachable {
IrInstruction base;
};
diff --git a/src/analyze.cpp b/src/analyze.cpp
@@ -28,7 +28,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type);
ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
if (node->owner->c_import_node != nullptr) {
- // if this happens, then parsec generated code that
+ // if this happens, then translate_c generated code that
// failed semantic analysis, which isn't supposed to happen
ErrorMsg *err = add_node_error(g, node->owner->c_import_node,
buf_sprintf("compiler bug: @cImport generated invalid zig code"));
@@ -48,7 +48,7 @@ ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
if (node->owner->c_import_node != nullptr) {
- // if this happens, then parsec generated code that
+ // if this happens, then translate_c generated code that
// failed semantic analysis, which isn't supposed to happen
Buf *note_path = buf_create_from_str("?.c");
@@ -338,7 +338,7 @@ TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) {
TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const,
bool is_volatile, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count)
{
- assert(child_type->id != TypeTableEntryIdInvalid);
+ assert(!type_is_invalid(child_type));
TypeId type_id = {};
TypeTableEntry **parent_pointer = nullptr;
@@ -1008,11 +1008,12 @@ TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKi
}
size_t line = decl_node ? decl_node->line : 0;
+ unsigned dwarf_kind = ZigLLVMTag_DW_structure_type();
ImportTableEntry *import = get_scope_import(scope);
entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), name);
entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
- ZigLLVMTag_DW_structure_type(), name,
+ dwarf_kind, name,
ZigLLVMFileToScope(import->di_file), import->di_file, (unsigned)(line + 1));
buf_init_from_str(&entry->name, name);
@@ -1285,7 +1286,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
return;
resolve_enum_zero_bits(g, enum_type);
- if (enum_type->data.enumeration.is_invalid)
+ if (type_is_invalid(enum_type))
return;
AstNode *decl_node = enum_type->data.enumeration.decl_node;
@@ -1834,7 +1835,246 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
}
static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
- zig_panic("TODO");
+ assert(union_type->id == TypeTableEntryIdUnion);
+
+ if (union_type->data.unionation.complete)
+ return;
+
+ resolve_union_zero_bits(g, union_type);
+ if (type_is_invalid(union_type))
+ return;
+
+ AstNode *decl_node = union_type->data.unionation.decl_node;
+
+ if (union_type->data.unionation.embedded_in_current) {
+ if (!union_type->data.unionation.reported_infinite_err) {
+ union_type->data.unionation.reported_infinite_err = true;
+ add_node_error(g, decl_node, buf_sprintf("union '%s' contains itself", buf_ptr(&union_type->name)));
+ }
+ return;
+ }
+
+ assert(!union_type->data.unionation.zero_bits_loop_flag);
+ assert(decl_node->type == NodeTypeContainerDecl);
+ assert(union_type->di_type);
+
+ uint32_t field_count = union_type->data.unionation.src_field_count;
+
+ assert(union_type->data.unionation.fields);
+
+ uint32_t gen_field_count = union_type->data.unionation.gen_field_count;
+ ZigLLVMDIType **union_inner_di_types = allocate<ZigLLVMDIType*>(gen_field_count);
+
+ TypeTableEntry *most_aligned_union_member = nullptr;
+ uint64_t size_of_most_aligned_member_in_bits = 0;
+ uint64_t biggest_align_in_bits = 0;
+ uint64_t biggest_size_in_bits = 0;
+
+ bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto);
+ ZigLLVMDIEnumerator **di_enumerators = allocate<ZigLLVMDIEnumerator*>(field_count);
+
+ Scope *scope = &union_type->data.unionation.decls_scope->base;
+ ImportTableEntry *import = get_scope_import(scope);
+
+ // set temporary flag
+ union_type->data.unionation.embedded_in_current = true;
+
+ for (uint32_t i = 0; i < field_count; i += 1) {
+ AstNode *field_node = decl_node->data.container_decl.fields.at(i);
+ TypeUnionField *type_union_field = &union_type->data.unionation.fields[i];
+ TypeTableEntry *field_type = type_union_field->type_entry;
+
+ ensure_complete_type(g, field_type);
+ if (type_is_invalid(field_type)) {
+ union_type->data.unionation.is_invalid = true;
+ continue;
+ }
+
+ if (!type_has_bits(field_type))
+ continue;
+
+ di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(type_union_field->name), i);
+
+ uint64_t store_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref);
+ uint64_t abi_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, field_type->type_ref);
+
+ assert(store_size_in_bits > 0);
+ assert(abi_align_in_bits > 0);
+
+ union_inner_di_types[type_union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
+ ZigLLVMTypeToScope(union_type->di_type), buf_ptr(type_union_field->name),
+ import->di_file, (unsigned)(field_node->line + 1),
+ store_size_in_bits,
+ abi_align_in_bits,
+ 0,
+ 0, field_type->di_type);
+
+ biggest_size_in_bits = max(biggest_size_in_bits, store_size_in_bits);
+
+ if (!most_aligned_union_member || abi_align_in_bits > biggest_align_in_bits) {
+ most_aligned_union_member = field_type;
+ biggest_align_in_bits = abi_align_in_bits;
+ size_of_most_aligned_member_in_bits = store_size_in_bits;
+ }
+ }
+
+ // unset temporary flag
+ union_type->data.unionation.embedded_in_current = false;
+ union_type->data.unionation.complete = true;
+ union_type->data.unionation.union_size_bytes = biggest_size_in_bits / 8;
+ union_type->data.unionation.most_aligned_union_member = most_aligned_union_member;
+
+ if (union_type->data.unionation.is_invalid)
+ return;
+
+ if (union_type->zero_bits) {
+ union_type->type_ref = LLVMVoidType();
+
+ uint64_t debug_size_in_bits = 0;
+ uint64_t debug_align_in_bits = 0;
+ ZigLLVMDIType **di_root_members = nullptr;
+ size_t debug_member_count = 0;
+ ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
+ ZigLLVMFileToScope(import->di_file),
+ buf_ptr(&union_type->name),
+ import->di_file, (unsigned)(decl_node->line + 1),
+ debug_size_in_bits,
+ debug_align_in_bits,
+ 0, di_root_members, (int)debug_member_count, 0, "");
+
+ ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);
+ union_type->di_type = replacement_di_type;
+ return;
+ }
+
+ assert(most_aligned_union_member != nullptr);
+
+ bool want_safety = auto_layout && (field_count >= 2);
+ uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits;
+
+
+ if (!want_safety) {
+ if (padding_in_bits > 0) {
+ TypeTableEntry *u8_type = get_int_type(g, false, 8);
+ TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8);
+ LLVMTypeRef union_element_types[] = {
+ most_aligned_union_member->type_ref,
+ padding_array->type_ref,
+ };
+ LLVMStructSetBody(union_type->type_ref, union_element_types, 2, false);
+ } else {
+ LLVMStructSetBody(union_type->type_ref, &most_aligned_union_member->type_ref, 1, false);
+ }
+ union_type->data.unionation.union_type_ref = union_type->type_ref;
+ union_type->data.unionation.gen_tag_index = SIZE_MAX;
+ union_type->data.unionation.gen_union_index = SIZE_MAX;
+
+ assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type->type_ref) >= biggest_align_in_bits);
+ assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type->type_ref) >= biggest_size_in_bits);
+
+ // create debug type for union
+ ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
+ ZigLLVMFileToScope(import->di_file), buf_ptr(&union_type->name),
+ import->di_file, (unsigned)(decl_node->line + 1),
+ biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types,
+ gen_field_count, 0, "");
+
+ ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);
+ union_type->di_type = replacement_di_type;
+ return;
+ }
+
+ LLVMTypeRef union_type_ref;
+ if (padding_in_bits > 0) {
+ TypeTableEntry *u8_type = get_int_type(g, false, 8);
+ TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8);
+ LLVMTypeRef union_element_types[] = {
+ most_aligned_union_member->type_ref,
+ padding_array->type_ref,
+ };
+ union_type_ref = LLVMStructType(union_element_types, 2, false);
+ } else {
+ union_type_ref = most_aligned_union_member->type_ref;
+ }
+ union_type->data.unionation.union_type_ref = union_type_ref;
+
+ assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type_ref) >= biggest_align_in_bits);
+ assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type_ref) >= biggest_size_in_bits);
+
+ // create llvm type for root struct
+ TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);
+ TypeTableEntry *tag_type_entry = tag_int_type;
+ union_type->data.unionation.tag_type = tag_type_entry;
+ uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref);
+
+ if (align_of_tag_in_bits >= biggest_align_in_bits) {
+ union_type->data.unionation.gen_tag_index = 0;
+ union_type->data.unionation.gen_union_index = 1;
+ } else {
+ union_type->data.unionation.gen_union_index = 0;
+ union_type->data.unionation.gen_tag_index = 1;
+ }
+
+ LLVMTypeRef root_struct_element_types[2];
+ root_struct_element_types[union_type->data.unionation.gen_tag_index] = tag_type_entry->type_ref;
+ root_struct_element_types[union_type->data.unionation.gen_union_index] = union_type_ref;
+ LLVMStructSetBody(union_type->type_ref, root_struct_element_types, 2, false);
+
+
+ // create debug type for root struct
+
+ // create debug type for tag
+ uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref);
+ uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref);
+ ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
+ ZigLLVMTypeToScope(union_type->di_type), "AnonEnum",
+ import->di_file, (unsigned)(decl_node->line + 1),
+ tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count,
+ tag_type_entry->di_type, "");
+
+ // create debug type for union
+ ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
+ ZigLLVMTypeToScope(union_type->di_type), "AnonUnion",
+ import->di_file, (unsigned)(decl_node->line + 1),
+ biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types,
+ gen_field_count, 0, "");
+
+ uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->type_ref,
+ union_type->data.unionation.gen_union_index);
+ uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->type_ref,
+ union_type->data.unionation.gen_tag_index);
+
+ ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
+ ZigLLVMTypeToScope(union_type->di_type), "union_field",
+ import->di_file, (unsigned)(decl_node->line + 1),
+ biggest_size_in_bits,
+ biggest_align_in_bits,
+ union_offset_in_bits,
+ 0, union_di_type);
+ ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
+ ZigLLVMTypeToScope(union_type->di_type), "tag_field",
+ import->di_file, (unsigned)(decl_node->line + 1),
+ tag_debug_size_in_bits,
+ tag_debug_align_in_bits,
+ tag_offset_in_bits,
+ 0, tag_di_type);
+
+ ZigLLVMDIType *di_root_members[2];
+ di_root_members[union_type->data.unionation.gen_tag_index] = tag_member_di_type;
+ di_root_members[union_type->data.unionation.gen_union_index] = union_member_di_type;
+
+ uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, union_type->type_ref);
+ uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, union_type->type_ref);
+ ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
+ ZigLLVMFileToScope(import->di_file),
+ buf_ptr(&union_type->name),
+ import->di_file, (unsigned)(decl_node->line + 1),
+ debug_size_in_bits,
+ debug_align_in_bits,
+ 0, nullptr, di_root_members, 2, 0, nullptr, "");
+
+ ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);
+ union_type->di_type = replacement_di_type;
}
static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
@@ -1873,7 +2113,7 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
type_enum_field->value = i;
type_ensure_zero_bits_known(g, field_type);
- if (field_type->id == TypeTableEntryIdInvalid) {
+ if (type_is_invalid(field_type)) {
enum_type->data.enumeration.is_invalid = true;
continue;
}
@@ -1980,7 +2220,69 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
}
static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
- zig_panic("TODO resolve_union_zero_bits");
+ assert(union_type->id == TypeTableEntryIdUnion);
+
+ if (union_type->data.unionation.zero_bits_known)
+ return;
+
+ if (union_type->data.unionation.zero_bits_loop_flag) {
+ union_type->data.unionation.zero_bits_known = true;
+ return;
+ }
+
+ union_type->data.unionation.zero_bits_loop_flag = true;
+
+ AstNode *decl_node = union_type->data.unionation.decl_node;
+ assert(decl_node->type == NodeTypeContainerDecl);
+ assert(union_type->di_type);
+
+ assert(!union_type->data.unionation.fields);
+ uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length;
+ union_type->data.unionation.src_field_count = field_count;
+ union_type->data.unionation.fields = allocate<TypeUnionField>(field_count);
+
+ uint32_t biggest_align_bytes = 0;
+
+ Scope *scope = &union_type->data.unionation.decls_scope->base;
+
+ uint32_t gen_field_index = 0;
+ for (uint32_t i = 0; i < field_count; i += 1) {
+ AstNode *field_node = decl_node->data.container_decl.fields.at(i);
+ TypeUnionField *type_union_field = &union_type->data.unionation.fields[i];
+ type_union_field->name = field_node->data.struct_field.name;
+ TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type);
+ type_union_field->type_entry = field_type;
+ type_union_field->value = i;
+
+ type_ensure_zero_bits_known(g, field_type);
+ if (type_is_invalid(field_type)) {
+ union_type->data.unionation.is_invalid = true;
+ continue;
+ }
+
+ if (!type_has_bits(field_type))
+ continue;
+
+ type_union_field->gen_index = gen_field_index;
+ gen_field_index += 1;
+
+ uint32_t field_align_bytes = get_abi_alignment(g, field_type);
+ if (field_align_bytes > biggest_align_bytes) {
+ biggest_align_bytes = field_align_bytes;
+ }
+ }
+
+ bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto);
+
+ union_type->data.unionation.zero_bits_loop_flag = false;
+ union_type->data.unionation.gen_field_count = gen_field_index;
+ union_type->zero_bits = (gen_field_index == 0 && (field_count < 2 || !auto_layout));
+ union_type->data.unionation.zero_bits_known = true;
+
+ // also compute abi_alignment
+ if (!union_type->zero_bits) {
+ union_type->data.unionation.abi_alignment = biggest_align_bytes;
+ }
}
static void get_fully_qualified_decl_name_internal(Buf *buf, Scope *scope, uint8_t sep) {
@@ -2851,6 +3153,18 @@ TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) {
return nullptr;
}
+TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) {
+ assert(type_entry->id == TypeTableEntryIdUnion);
+ assert(type_entry->data.unionation.complete);
+ for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
+ TypeUnionField *field = &type_entry->data.unionation.fields[i];
+ if (buf_eql_buf(field->name, name)) {
+ return field;
+ }
+ }
+ return nullptr;
+}
+
static bool is_container(TypeTableEntry *type_entry) {
switch (type_entry->id) {
case TypeTableEntryIdInvalid:
@@ -4703,6 +5017,8 @@ ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) {
return &value->data.x_array.s_none.parent;
} else if (type_entry->id == TypeTableEntryIdStruct) {
return &value->data.x_struct.parent;
+ } else if (type_entry->id == TypeTableEntryIdUnion) {
+ return &value->data.x_union.parent;
}
return nullptr;
}
@@ -4914,7 +5230,8 @@ uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) {
assert(type_entry->data.enumeration.abi_alignment != 0);
return type_entry->data.enumeration.abi_alignment;
} else if (type_entry->id == TypeTableEntryIdUnion) {
- zig_panic("TODO");
+ assert(type_entry->data.unionation.abi_alignment != 0);
+ return type_entry->data.unionation.abi_alignment;
} else if (type_entry->id == TypeTableEntryIdOpaque) {
return 1;
} else {
@@ -4929,3 +5246,11 @@ TypeTableEntry *get_align_amt_type(CodeGen *g) {
}
return g->align_amt_type;
}
+
+uint32_t type_ptr_hash(const TypeTableEntry *ptr) {
+ return hash_ptr((void*)ptr);
+}
+
+bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b) {
+ return a == b;
+}
diff --git a/src/analyze.hpp b/src/analyze.hpp
@@ -63,6 +63,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry);
TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name);
ScopeDecls *get_container_scope(TypeTableEntry *type_entry);
TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name);
+TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name);
bool is_container_ref(TypeTableEntry *type_entry);
void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
void scan_import(CodeGen *g, ImportTableEntry *import);
diff --git a/src/c_tokenizer.cpp b/src/c_tokenizer.cpp
@@ -120,6 +120,7 @@ static void begin_token(CTokenize *ctok, CTokId id) {
case CTokIdLParen:
case CTokIdRParen:
case CTokIdEOF:
+ case CTokIdDot:
break;
}
}
@@ -216,9 +217,8 @@ void tokenize_c_macro(CTokenize *ctok, const uint8_t *c) {
buf_append_char(&ctok->buf, '0');
break;
case '.':
- begin_token(ctok, CTokIdNumLitFloat);
- ctok->state = CTokStateFloat;
- buf_init_from_str(&ctok->buf, "0.");
+ begin_token(ctok, CTokIdDot);
+ end_token(ctok);
break;
case '(':
begin_token(ctok, CTokIdLParen);
@@ -238,6 +238,8 @@ void tokenize_c_macro(CTokenize *ctok, const uint8_t *c) {
break;
case CTokStateFloat:
switch (*c) {
+ case '.':
+ break;
case 'e':
case 'E':
buf_append_char(&ctok->buf, 'e');
diff --git a/src/c_tokenizer.hpp b/src/c_tokenizer.hpp
@@ -21,6 +21,7 @@ enum CTokId {
CTokIdLParen,
CTokIdRParen,
CTokIdEOF,
+ CTokIdDot,
};
enum CNumLitSuffix {
diff --git a/src/codegen.cpp b/src/codegen.cpp
@@ -15,7 +15,7 @@
#include "ir.hpp"
#include "link.hpp"
#include "os.hpp"
-#include "parsec.hpp"
+#include "translate_c.hpp"
#include "target.hpp"
#include "zig_llvm.hpp"
@@ -810,6 +810,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
return buf_create_from_str("invalid error code");
case PanicMsgIdIncorrectAlignment:
return buf_create_from_str("incorrect alignment");
+ case PanicMsgIdBadUnionField:
+ return buf_create_from_str("access of inactive union field");
}
zig_unreachable();
}
@@ -2393,6 +2395,50 @@ static LLVMValueRef ir_render_enum_field_ptr(CodeGen *g, IrExecutable *executabl
return bitcasted_union_field_ptr;
}
+static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executable,
+ IrInstructionUnionFieldPtr *instruction)
+{
+ TypeTableEntry *union_ptr_type = instruction->union_ptr->value.type;
+ assert(union_ptr_type->id == TypeTableEntryIdPointer);
+ TypeTableEntry *union_type = union_ptr_type->data.pointer.child_type;
+ assert(union_type->id == TypeTableEntryIdUnion);
+
+ TypeUnionField *field = instruction->field;
+
+ if (!type_has_bits(field->type_entry))
+ return nullptr;
+
+ LLVMValueRef union_ptr = ir_llvm_value(g, instruction->union_ptr);
+ LLVMTypeRef field_type_ref = LLVMPointerType(field->type_entry->type_ref, 0);
+
+ if (union_type->data.unionation.gen_tag_index == SIZE_MAX) {
+ LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, 0, "");
+ LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, "");
+ return bitcasted_union_field_ptr;
+ }
+
+ if (ir_want_debug_safety(g, &instruction->base)) {
+ LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_tag_index, "");
+ LLVMValueRef tag_value = gen_load_untyped(g, tag_field_ptr, 0, false, "");
+ LLVMValueRef expected_tag_value = LLVMConstInt(union_type->data.unionation.tag_type->type_ref,
+ field->value, false);
+
+ LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckOk");
+ LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckFail");
+ LLVMValueRef ok_val = LLVMBuildICmp(g->builder, LLVMIntEQ, tag_value, expected_tag_value, "");
+ LLVMBuildCondBr(g->builder, ok_val, ok_block, bad_block);
+
+ LLVMPositionBuilderAtEnd(g->builder, bad_block);
+ gen_debug_safety_crash(g, PanicMsgIdBadUnionField);
+
+ LLVMPositionBuilderAtEnd(g->builder, ok_block);
+ }
+
+ LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_union_index, "");
+ LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, "");
+ return bitcasted_union_field_ptr;
+}
+
static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok) {
const char *ptr = buf_ptr(node->data.asm_expr.asm_template) + tok->start + 2;
size_t len = tok->end - tok->start - 2;
@@ -3365,6 +3411,42 @@ static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable,
return instruction->tmp_ptr;
}
+static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, IrInstructionUnionInit *instruction) {
+ TypeUnionField *type_union_field = instruction->field;
+
+ if (!type_has_bits(type_union_field->type_entry))
+ return nullptr;
+
+ uint32_t field_align_bytes = get_abi_alignment(g, type_union_field->type_entry);
+ TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, type_union_field->type_entry,
+ false, false, field_align_bytes,
+ 0, 0);
+
+ LLVMValueRef uncasted_union_ptr;
+ // Even if safety is off in this block, if the union type has the safety field, we have to populate it
+ // correctly. Otherwise safety code somewhere other than here could fail.
+ TypeTableEntry *union_type = instruction->union_type;
+ if (union_type->data.unionation.gen_tag_index != SIZE_MAX) {
+ LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr,
+ union_type->data.unionation.gen_tag_index, "");
+ LLVMValueRef tag_value = LLVMConstInt(union_type->data.unionation.tag_type->type_ref,
+ type_union_field->value, false);
+ gen_store_untyped(g, tag_value, tag_field_ptr, 0, false);
+
+ uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr,
+ (unsigned)union_type->data.unionation.gen_union_index, "");
+ } else {
+ uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, (unsigned)0, "");
+ }
+
+ LLVMValueRef field_ptr = LLVMBuildBitCast(g->builder, uncasted_union_ptr, ptr_type->type_ref, "");
+ LLVMValueRef value = ir_llvm_value(g, instruction->init_value);
+
+ gen_assign_raw(g, field_ptr, ptr_type, value);
+
+ return instruction->tmp_ptr;
+}
+
static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *executable,
IrInstructionContainerInitList *instruction)
{
@@ -3486,6 +3568,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_struct_field_ptr(g, executable, (IrInstructionStructFieldPtr *)instruction);
case IrInstructionIdEnumFieldPtr:
return ir_render_enum_field_ptr(g, executable, (IrInstructionEnumFieldPtr *)instruction);
+ case IrInstructionIdUnionFieldPtr:
+ return ir_render_union_field_ptr(g, executable, (IrInstructionUnionFieldPtr *)instruction);
case IrInstructionIdAsm:
return ir_render_asm(g, executable, (IrInstructionAsm *)instruction);
case IrInstructionIdTestNonNull:
@@ -3544,6 +3628,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_init_enum(g, executable, (IrInstructionInitEnum *)instruction);
case IrInstructionIdStructInit:
return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction);
+ case IrInstructionIdUnionInit:
+ return ir_render_union_init(g, executable, (IrInstructionUnionInit *)instruction);
case IrInstructionIdPtrCast:
return ir_render_ptr_cast(g, executable, (IrInstructionPtrCast *)instruction);
case IrInstructionIdBitCast:
@@ -3595,6 +3681,7 @@ static void ir_render(CodeGen *g, FnTableEntry *fn_entry) {
static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index);
static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index);
+static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *array_const_val);
static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) {
switch (parent->id) {
@@ -3608,6 +3695,8 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent
case ConstParentIdArray:
return gen_const_ptr_array_recursive(g, parent->data.p_array.array_val,
parent->data.p_array.elem_index);
+ case ConstParentIdUnion:
+ return gen_const_ptr_union_recursive(g, parent->data.p_union.union_val);
}
zig_unreachable();
}
@@ -3637,6 +3726,18 @@ static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *s
return LLVMConstInBoundsGEP(base_ptr, indices, 2);
}
+static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val) {
+ ConstParent *parent = &union_const_val->data.x_union.parent;
+ LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent);
+
+ TypeTableEntry *u32 = g->builtin_types.entry_u32;
+ LLVMValueRef indices[] = {
+ LLVMConstNull(u32->type_ref),
+ LLVMConstInt(u32->type_ref, 0, false),
+ };
+ return LLVMConstInBoundsGEP(base_ptr, indices, 2);
+}
+
static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, ConstExprValue *const_val) {
switch (const_val->special) {
case ConstValSpecialRuntime:
@@ -3872,10 +3973,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
return LLVMConstNamedStruct(type_entry->type_ref, fields, type_entry->data.structure.gen_field_count);
}
}
- case TypeTableEntryIdUnion:
- {
- zig_panic("TODO");
- }
case TypeTableEntryIdArray:
{
uint64_t len = type_entry->data.array.len;
@@ -3898,6 +3995,55 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
return LLVMConstArray(element_type_ref, values, (unsigned)len);
}
}
+ case TypeTableEntryIdUnion:
+ {
+ LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref;
+ ConstExprValue *payload_value = const_val->data.x_union.payload;
+ assert(payload_value != nullptr);
+
+ if (!type_has_bits(payload_value->type)) {
+ return LLVMGetUndef(union_type_ref);
+ }
+
+ uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, payload_value->type->type_ref);
+ uint64_t pad_bytes = type_entry->data.unionation.union_size_bytes - field_type_bytes;
+ LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value);
+ bool make_unnamed_struct = is_llvm_value_unnamed_type(payload_value->type, correctly_typed_value) ||
+ payload_value->type != type_entry->data.unionation.most_aligned_union_member;
+
+ LLVMValueRef union_value_ref;
+ {
+ if (pad_bytes == 0) {
+ union_value_ref = correctly_typed_value;
+ } else {
+ LLVMValueRef fields[2];
+ fields[0] = correctly_typed_value;
+ fields[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes));
+ if (make_unnamed_struct || type_entry->data.unionation.gen_tag_index != SIZE_MAX) {
+ union_value_ref = LLVMConstStruct(fields, 2, false);
+ } else {
+ union_value_ref = LLVMConstNamedStruct(union_type_ref, fields, 2);
+ }
+ }
+ }
+
+ if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) {
+ return union_value_ref;
+ }
+
+ LLVMValueRef tag_value = LLVMConstInt(type_entry->data.unionation.tag_type->type_ref, const_val->data.x_union.tag, false);
+
+ LLVMValueRef fields[2];
+ fields[type_entry->data.unionation.gen_union_index] = union_value_ref;
+ fields[type_entry->data.unionation.gen_tag_index] = tag_value;
+
+ if (make_unnamed_struct) {
+ return LLVMConstStruct(fields, 2, false);
+ } else {
+ return LLVMConstNamedStruct(type_entry->type_ref, fields, 2);
+ }
+
+ }
case TypeTableEntryIdEnum:
{
LLVMTypeRef tag_type_ref = type_entry->data.enumeration.tag_type->type_ref;
@@ -4376,6 +4522,9 @@ static void do_code_gen(CodeGen *g) {
} else if (instruction->id == IrInstructionIdStructInit) {
IrInstructionStructInit *struct_init_instruction = (IrInstructionStructInit *)instruction;
slot = &struct_init_instruction->tmp_ptr;
+ } else if (instruction->id == IrInstructionIdUnionInit) {
+ IrInstructionUnionInit *union_init_instruction = (IrInstructionUnionInit *)instruction;
+ slot = &union_init_instruction->tmp_ptr;
} else if (instruction->id == IrInstructionIdCall) {
IrInstructionCall *call_instruction = (IrInstructionCall *)instruction;
slot = &call_instruction->tmp_ptr;
@@ -5204,7 +5353,7 @@ static void init(CodeGen *g) {
define_builtin_compile_vars(g);
}
-void codegen_parsec(CodeGen *g, Buf *full_path) {
+void codegen_translate_c(CodeGen *g, Buf *full_path) {
find_libc_include_path(g);
Buf *src_basename = buf_alloc();
diff --git a/src/codegen.hpp b/src/codegen.hpp
@@ -56,7 +56,7 @@ PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir,
void codegen_add_assembly(CodeGen *g, Buf *path);
void codegen_add_object(CodeGen *g, Buf *object_path);
-void codegen_parsec(CodeGen *g, Buf *path);
+void codegen_translate_c(CodeGen *g, Buf *path);
#endif
diff --git a/src/ir.cpp b/src/ir.cpp
@@ -11,7 +11,7 @@
#include "ir.hpp"
#include "ir_print.hpp"
#include "os.hpp"
-#include "parsec.hpp"
+#include "translate_c.hpp"
#include "range_set.hpp"
#include "softfloat.hpp"
@@ -227,6 +227,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumFieldPtr *)
return IrInstructionIdEnumFieldPtr;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionFieldPtr *) {
+ return IrInstructionIdUnionFieldPtr;
+}
+
static constexpr IrInstructionId ir_instruction_id(IrInstructionElemPtr *) {
return IrInstructionIdElemPtr;
}
@@ -351,6 +355,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionStructInit *) {
return IrInstructionIdStructInit;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionInit *) {
+ return IrInstructionIdUnionInit;
+}
+
static constexpr IrInstructionId ir_instruction_id(IrInstructionMinValue *) {
return IrInstructionIdMinValue;
}
@@ -922,6 +930,27 @@ static IrInstruction *ir_build_enum_field_ptr_from(IrBuilder *irb, IrInstruction
return new_instruction;
}
+static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
+ IrInstruction *union_ptr, TypeUnionField *field)
+{
+ IrInstructionUnionFieldPtr *instruction = ir_build_instruction<IrInstructionUnionFieldPtr>(irb, scope, source_node);
+ instruction->union_ptr = union_ptr;
+ instruction->field = field;
+
+ ir_ref_instruction(union_ptr, irb->current_basic_block);
+
+ return &instruction->base;
+}
+
+static IrInstruction *ir_build_union_field_ptr_from(IrBuilder *irb, IrInstruction *old_instruction,
+ IrInstruction *union_ptr, TypeUnionField *type_union_field)
+{
+ IrInstruction *new_instruction = ir_build_union_field_ptr(irb, old_instruction->scope,
+ old_instruction->source_node, union_ptr, type_union_field);
+ ir_link_new_instruction(new_instruction, old_instruction);
+ return new_instruction;
+}
+
static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node,
FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
bool is_comptime, bool is_inline)
@@ -1112,6 +1141,28 @@ static IrInstruction *ir_build_struct_init_from(IrBuilder *irb, IrInstruction *o
return new_instruction;
}
+static IrInstruction *ir_build_union_init(IrBuilder *irb, Scope *scope, AstNode *source_node,
+ TypeTableEntry *union_type, TypeUnionField *field, IrInstruction *init_value)
+{
+ IrInstructionUnionInit *union_init_instruction = ir_build_instruction<IrInstructionUnionInit>(irb, scope, source_node);
+ union_init_instruction->union_type = union_type;
+ union_init_instruction->field = field;
+ union_init_instruction->init_value = init_value;
+
+ ir_ref_instruction(init_value, irb->current_basic_block);
+
+ return &union_init_instruction->base;
+}
+
+static IrInstruction *ir_build_union_init_from(IrBuilder *irb, IrInstruction *old_instruction,
+ TypeTableEntry *union_type, TypeUnionField *field, IrInstruction *init_value)
+{
+ IrInstruction *new_instruction = ir_build_union_init(irb, old_instruction->scope,
+ old_instruction->source_node, union_type, field, init_value);
+ ir_link_new_instruction(new_instruction, old_instruction);
+ return new_instruction;
+}
+
static IrInstruction *ir_build_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionUnreachable *unreachable_instruction =
ir_build_instruction<IrInstructionUnreachable>(irb, scope, source_node);
@@ -2422,6 +2473,13 @@ static IrInstruction *ir_instruction_enumfieldptr_get_dep(IrInstructionEnumField
}
}
+static IrInstruction *ir_instruction_unionfieldptr_get_dep(IrInstructionUnionFieldPtr *instruction, size_t index) {
+ switch (index) {
+ case 0: return instruction->union_ptr;
+ default: return nullptr;
+ }
+}
+
static IrInstruction *ir_instruction_elemptr_get_dep(IrInstructionElemPtr *instruction, size_t index) {
switch (index) {
case 0: return instruction->array_ptr;
@@ -2485,6 +2543,13 @@ static IrInstruction *ir_instruction_structinit_get_dep(IrInstructionStructInit
return nullptr;
}
+static IrInstruction *ir_instruction_unioninit_get_dep(IrInstructionUnionInit *instruction, size_t index) {
+ switch (index) {
+ case 0: return instruction->init_value;
+ default: return nullptr;
+ }
+}
+
static IrInstruction *ir_instruction_unreachable_get_dep(IrInstructionUnreachable *instruction, size_t index) {
return nullptr;
}
@@ -3099,6 +3164,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
return ir_instruction_structfieldptr_get_dep((IrInstructionStructFieldPtr *) instruction, index);
case IrInstructionIdEnumFieldPtr:
return ir_instruction_enumfieldptr_get_dep((IrInstructionEnumFieldPtr *) instruction, index);
+ case IrInstructionIdUnionFieldPtr:
+ return ir_instruction_unionfieldptr_get_dep((IrInstructionUnionFieldPtr *) instruction, index);
case IrInstructionIdElemPtr:
return ir_instruction_elemptr_get_dep((IrInstructionElemPtr *) instruction, index);
case IrInstructionIdVarPtr:
@@ -3117,6 +3184,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
return ir_instruction_containerinitfields_get_dep((IrInstructionContainerInitFields *) instruction, index);
case IrInstructionIdStructInit:
return ir_instruction_structinit_get_dep((IrInstructionStructInit *) instruction, index);
+ case IrInstructionIdUnionInit:
+ return ir_instruction_unioninit_get_dep((IrInstructionUnionInit *) instruction, index);
case IrInstructionIdUnreachable:
return ir_instruction_unreachable_get_dep((IrInstructionUnreachable *) instruction, index);
case IrInstructionIdTypeOf:
@@ -6233,8 +6302,9 @@ static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char
buf_appendf(name, ")");
return name;
} else {
+ //Note: C-imports do not have valid location information
return buf_sprintf("(anonymous %s at %s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ")", kind_name,
- buf_ptr(source_node->owner->path), source_node->line + 1, source_node->column + 1);
+ (source_node->owner->path != nullptr) ? buf_ptr(source_node->owner->path) : "(null)", source_node->line + 1, source_node->column + 1);
}
}
}
@@ -6263,6 +6333,9 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope,
}
irb->codegen->resolve_queue.append(&tld_container->base);
+ // Add this to the list to mark as invalid if analyzing this exec fails.
+ irb->exec->tld_list.append(&tld_container->base);
+
return ir_build_const_type(irb, parent_scope, node, container_type);
}
@@ -6485,6 +6558,20 @@ static bool ir_goto_pass2(IrBuilder *irb) {
return true;
}
+static void invalidate_exec(IrExecutable *exec) {
+ if (exec->invalid)
+ return;
+
+ exec->invalid = true;
+
+ for (size_t i = 0; i < exec->tld_list.length; i += 1) {
+ exec->tld_list.items[i]->resolution = TldResolutionInvalid;
+ }
+
+ if (exec->source_exec != nullptr)
+ invalidate_exec(exec->source_exec);
+}
+
bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_executable) {
assert(node->owner);
@@ -6508,7 +6595,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
}
if (!ir_goto_pass2(irb)) {
- irb->exec->invalid = true;
+ invalidate_exec(ir_executable);
return false;
}
@@ -6534,7 +6621,7 @@ static void add_call_stack_errors(CodeGen *codegen, IrExecutable *exec, ErrorMsg
}
static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, Buf *msg) {
- exec->invalid = true;
+ invalidate_exec(exec);
ErrorMsg *err_msg = add_node_error(codegen, source_node, msg);
if (exec->parent_exec) {
add_call_stack_errors(codegen, exec, err_msg, 10);
@@ -7897,6 +7984,9 @@ static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instructio
ConstExprValue *const_val = &const_instr->value;
const_val->type = pointee_type;
type_ensure_zero_bits_known(ira->codegen, type_entry);
+ if (type_is_invalid(type_entry)) {
+ return ira->codegen->invalid_instruction;
+ }
const_val->data.x_type = get_pointer_to_type_extra(ira->codegen, type_entry,
ptr_is_const, ptr_is_volatile, get_abi_alignment(ira->codegen, type_entry), 0, 0);
return const_instr;
@@ -7984,6 +8074,7 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node
IrExecutable analyzed_executable = {0};
analyzed_executable.source_node = source_node;
analyzed_executable.parent_exec = parent_exec;
+ analyzed_executable.source_exec = &ir_executable;
analyzed_executable.name = exec_name;
analyzed_executable.is_inline = true;
analyzed_executable.fn_entry = fn_entry;
@@ -10279,30 +10370,40 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
bool is_const = (var->value->type->id == TypeTableEntryIdMetaType) ? is_const_ptr : var->src_is_const;
bool is_volatile = (var->value->type->id == TypeTableEntryIdMetaType) ? is_volatile_ptr : false;
- if (mem_slot && mem_slot->special != ConstValSpecialRuntime) {
- ConstPtrMut ptr_mut;
- if (comptime_var_mem) {
- ptr_mut = ConstPtrMutComptimeVar;
- } else if (var->gen_is_const) {
- ptr_mut = ConstPtrMutComptimeConst;
- } else {
- assert(!comptime_var_mem);
- ptr_mut = ConstPtrMutRuntimeVar;
+ if (mem_slot != nullptr) {
+ switch (mem_slot->special) {
+ case ConstValSpecialRuntime:
+ goto no_mem_slot;
+ case ConstValSpecialStatic: // fallthrough
+ case ConstValSpecialUndef: {
+ ConstPtrMut ptr_mut;
+ if (comptime_var_mem) {
+ ptr_mut = ConstPtrMutComptimeVar;
+ } else if (var->gen_is_const) {
+ ptr_mut = ConstPtrMutComptimeConst;
+ } else {
+ assert(!comptime_var_mem);
+ ptr_mut = ConstPtrMutRuntimeVar;
+ }
+ return ir_get_const_ptr(ira, instruction, mem_slot, var->value->type,
+ ptr_mut, is_const, is_volatile, var->align_bytes);
+ }
}
- return ir_get_const_ptr(ira, instruction, mem_slot, var->value->type,
- ptr_mut, is_const, is_volatile, var->align_bytes);
- } else {
- IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb,
- instruction->scope, instruction->source_node, var, is_const, is_volatile);
- var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type,
- var->src_is_const, is_volatile, var->align_bytes, 0, 0);
- type_ensure_zero_bits_known(ira->codegen, var->value->type);
+ zig_unreachable();
+ }
- bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr);
- var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack;
+no_mem_slot:
- return var_ptr_instruction;
- }
+ IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb,
+ instruction->scope, instruction->source_node, var, is_const, is_volatile);
+ var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type,
+ var->src_is_const, is_volatile, var->align_bytes, 0, 0);
+ type_ensure_zero_bits_known(ira->codegen, var->value->type);
+
+ bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr);
+ var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack;
+
+ return var_ptr_instruction;
}
static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction,
@@ -10408,10 +10509,11 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
result = ir_eval_const_value(ira->codegen, exec_scope, body_node, return_type,
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry,
nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec);
- if (type_is_invalid(result->value.type))
- return ira->codegen->builtin_types.entry_invalid;
ira->codegen->memoized_fn_eval_table.put(exec_scope, result);
+
+ if (type_is_invalid(result->value.type))
+ return ira->codegen->builtin_types.entry_invalid;
}
ConstExprValue *out_val = ir_build_const_from(ira, &call_instruction->base);
@@ -11417,8 +11519,20 @@ static TypeTableEntry *ir_analyze_container_member_access_inner(IrAnalyze *ira,
return ir_analyze_ref(ira, &field_ptr_instruction->base, bound_fn_value, true, false);
}
}
+ const char *prefix_name;
+ if (is_slice(bare_struct_type)) {
+ prefix_name = "";
+ } else if (bare_struct_type->id == TypeTableEntryIdStruct) {
+ prefix_name = "struct ";
+ } else if (bare_struct_type->id == TypeTableEntryIdEnum) {
+ prefix_name = "enum ";
+ } else if (bare_struct_type->id == TypeTableEntryIdUnion) {
+ prefix_name = "union ";
+ } else {
+ prefix_name = "";
+ }
ir_add_error_node(ira, field_ptr_instruction->base.source_node,
- buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name)));
+ buf_sprintf("no member named '%s' in %s'%s'", buf_ptr(field_name), prefix_name, buf_ptr(&bare_struct_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
@@ -11428,14 +11542,13 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field
{
TypeTableEntry *bare_type = container_ref_type(container_type);
ensure_complete_type(ira->codegen, bare_type);
+ if (type_is_invalid(bare_type))
+ return ira->codegen->builtin_types.entry_invalid;
assert(container_ptr->value.type->id == TypeTableEntryIdPointer);
bool is_const = container_ptr->value.type->data.pointer.is_const;
bool is_volatile = container_ptr->value.type->data.pointer.is_volatile;
if (bare_type->id == TypeTableEntryIdStruct) {
- if (bare_type->data.structure.is_invalid)
- return ira->codegen->builtin_types.entry_invalid;
-
TypeStructField *field = find_struct_type_field(bare_type, field_name);
if (field) {
bool is_packed = (bare_type->data.structure.layout == ContainerLayoutPacked);
@@ -11476,9 +11589,6 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field
field_ptr_instruction, container_ptr, container_type);
}
} else if (bare_type->id == TypeTableEntryIdEnum) {
- if (bare_type->data.enumeration.is_invalid)
- return ira->codegen->builtin_types.entry_invalid;
-
TypeEnumField *field = find_enum_type_field(bare_type, field_name);
if (field) {
ir_build_enum_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field);
@@ -11489,7 +11599,15 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field
field_ptr_instruction, container_ptr, container_type);
}
} else if (bare_type->id == TypeTableEntryIdUnion) {
- zig_panic("TODO");
+ TypeUnionField *field = find_union_type_field(bare_type, field_name);
+ if (field) {
+ ir_build_union_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field);
+ return get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile,
+ get_abi_alignment(ira->codegen, field->type_entry), 0, 0);
+ } else {
+ return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
+ field_ptr_instruction, container_ptr, container_type);
+ }
} else {
zig_unreachable();
}
@@ -13033,9 +13151,71 @@ static TypeTableEntry *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionR
return ir_analyze_ref(ira, &ref_instruction->base, value, ref_instruction->is_const, ref_instruction->is_volatile);
}
+static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrInstruction *instruction,
+ TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)
+{
+ assert(container_type->id == TypeTableEntryIdUnion);
+
+ ensure_complete_type(ira->codegen, container_type);
+
+ if (instr_field_count != 1) {
+ ir_add_error(ira, instruction,
+ buf_sprintf("union initialization expects exactly one field"));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ IrInstructionContainerInitFieldsField *field = &fields[0];
+ IrInstruction *field_value = field->value->other;
+ if (type_is_invalid(field_value->value.type))
+ return ira->codegen->builtin_types.entry_invalid;
+
+ TypeUnionField *type_field = find_union_type_field(container_type, field->name);
+ if (!type_field) {
+ ir_add_error_node(ira, field->source_node,
+ buf_sprintf("no member named '%s' in union '%s'",
+ buf_ptr(field->name), buf_ptr(&container_type->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ if (type_is_invalid(type_field->type_entry))
+ return ira->codegen->builtin_types.entry_invalid;
+
+ IrInstruction *casted_field_value = ir_implicit_cast(ira, field_value, type_field->type_entry);
+ if (casted_field_value == ira->codegen->invalid_instruction)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope);
+ if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime) {
+ ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk);
+ if (!field_val)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ ConstExprValue *out_val = ir_build_const_from(ira, instruction);
+ out_val->data.x_union.payload = field_val;
+ out_val->data.x_union.tag = type_field->value;
+
+ ConstParent *parent = get_const_val_parent(ira->codegen, field_val);
+ if (parent != nullptr) {
+ parent->id = ConstParentIdUnion;
+ parent->data.p_union.union_val = out_val;
+ }
+
+ return container_type;
+ }
+
+ IrInstruction *new_instruction = ir_build_union_init_from(&ira->new_irb, instruction,
+ container_type, type_field, casted_field_value);
+
+ ir_add_alloca(ira, new_instruction, container_type);
+ return container_type;
+}
+
static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction,
TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)
{
+ if (container_type->id == TypeTableEntryIdUnion) {
+ return ir_analyze_container_init_fields_union(ira, instruction, container_type, instr_field_count, fields);
+ }
if (container_type->id != TypeTableEntryIdStruct || is_slice(container_type)) {
ir_add_error(ira, instruction,
buf_sprintf("type '%s' does not support struct initialization syntax",
@@ -13043,8 +13223,7 @@ static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstru
return ira->codegen->builtin_types.entry_invalid;
}
- if (!type_is_complete(container_type))
- resolve_container_type(ira->codegen, container_type);
+ ensure_complete_type(ira->codegen, container_type);
size_t actual_field_count = container_type->data.structure.src_field_count;
@@ -13070,7 +13249,7 @@ static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstru
TypeStructField *type_field = find_struct_type_field(container_type, field->name);
if (!type_field) {
ir_add_error_node(ira, field->source_node,
- buf_sprintf("no member named '%s' in '%s'",
+ buf_sprintf("no member named '%s' in struct '%s'",
buf_ptr(field->name), buf_ptr(&container_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
@@ -15657,8 +15836,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
case IrInstructionIdIntToErr:
case IrInstructionIdErrToInt:
case IrInstructionIdStructInit:
+ case IrInstructionIdUnionInit:
case IrInstructionIdStructFieldPtr:
case IrInstructionIdEnumFieldPtr:
+ case IrInstructionIdUnionFieldPtr:
case IrInstructionIdInitEnum:
case IrInstructionIdMaybeWrap:
case IrInstructionIdErrWrapCode:
@@ -15968,6 +16149,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdContainerInitList:
case IrInstructionIdContainerInitFields:
case IrInstructionIdStructInit:
+ case IrInstructionIdUnionInit:
case IrInstructionIdFieldPtr:
case IrInstructionIdElemPtr:
case IrInstructionIdVarPtr:
@@ -15977,6 +16159,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdArrayLen:
case IrInstructionIdStructFieldPtr:
case IrInstructionIdEnumFieldPtr:
+ case IrInstructionIdUnionFieldPtr:
case IrInstructionIdArrayType:
case IrInstructionIdSliceType:
case IrInstructionIdSizeOf:
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
@@ -290,6 +290,15 @@ static void ir_print_struct_init(IrPrint *irp, IrInstructionStructInit *instruct
fprintf(irp->f, "} // struct init");
}
+static void ir_print_union_init(IrPrint *irp, IrInstructionUnionInit *instruction) {
+ Buf *field_name = instruction->field->name;
+
+ fprintf(irp->f, "%s {", buf_ptr(&instruction->union_type->name));
+ fprintf(irp->f, ".%s = ", buf_ptr(field_name));
+ ir_print_other_instruction(irp, instruction->init_value);
+ fprintf(irp->f, "} // union init");
+}
+
static void ir_print_unreachable(IrPrint *irp, IrInstructionUnreachable *instruction) {
fprintf(irp->f, "unreachable");
}
@@ -359,6 +368,13 @@ static void ir_print_enum_field_ptr(IrPrint *irp, IrInstructionEnumFieldPtr *ins
fprintf(irp->f, ")");
}
+static void ir_print_union_field_ptr(IrPrint *irp, IrInstructionUnionFieldPtr *instruction) {
+ fprintf(irp->f, "@UnionFieldPtr(&");
+ ir_print_other_instruction(irp, instruction->union_ptr);
+ fprintf(irp->f, ".%s", buf_ptr(instruction->field->name));
+ fprintf(irp->f, ")");
+}
+
static void ir_print_set_debug_safety(IrPrint *irp, IrInstructionSetDebugSafety *instruction) {
fprintf(irp->f, "@setDebugSafety(");
ir_print_other_instruction(irp, instruction->scope_value);
@@ -1023,6 +1039,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdStructInit:
ir_print_struct_init(irp, (IrInstructionStructInit *)instruction);
break;
+ case IrInstructionIdUnionInit:
+ ir_print_union_init(irp, (IrInstructionUnionInit *)instruction);
+ break;
case IrInstructionIdUnreachable:
ir_print_unreachable(irp, (IrInstructionUnreachable *)instruction);
break;
@@ -1056,6 +1075,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdEnumFieldPtr:
ir_print_enum_field_ptr(irp, (IrInstructionEnumFieldPtr *)instruction);
break;
+ case IrInstructionIdUnionFieldPtr:
+ ir_print_union_field_ptr(irp, (IrInstructionUnionFieldPtr *)instruction);
+ break;
case IrInstructionIdSetDebugSafety:
ir_print_set_debug_safety(irp, (IrInstructionSetDebugSafety *)instruction);
break;
diff --git a/src/main.cpp b/src/main.cpp
@@ -23,7 +23,7 @@ static int usage(const char *arg0) {
" build-exe [source] create executable from source or object files\n"
" build-lib [source] create library from source or object files\n"
" build-obj [source] create object from source or assembly\n"
- " parsec [source] convert c code to zig code\n"
+ " translate-c [source] convert c code to zig code\n"
" targets list available compilation targets\n"
" test [source] create and run a test build\n"
" version print version number and exit\n"
@@ -229,7 +229,7 @@ enum Cmd {
CmdTest,
CmdVersion,
CmdZen,
- CmdParseC,
+ CmdTranslateC,
CmdTargets,
};
@@ -632,8 +632,8 @@ int main(int argc, char **argv) {
cmd = CmdVersion;
} else if (strcmp(arg, "zen") == 0) {
cmd = CmdZen;
- } else if (strcmp(arg, "parsec") == 0) {
- cmd = CmdParseC;
+ } else if (strcmp(arg, "translate-c") == 0) {
+ cmd = CmdTranslateC;
} else if (strcmp(arg, "test") == 0) {
cmd = CmdTest;
out_type = OutTypeExe;
@@ -646,7 +646,7 @@ int main(int argc, char **argv) {
} else {
switch (cmd) {
case CmdBuild:
- case CmdParseC:
+ case CmdTranslateC:
case CmdTest:
if (!in_file) {
in_file = arg;
@@ -703,13 +703,13 @@ int main(int argc, char **argv) {
switch (cmd) {
case CmdBuild:
- case CmdParseC:
+ case CmdTranslateC:
case CmdTest:
{
if (cmd == CmdBuild && !in_file && objects.length == 0 && asm_files.length == 0) {
fprintf(stderr, "Expected source file argument or at least one --object or --assembly argument.\n");
return usage(arg0);
- } else if ((cmd == CmdParseC || cmd == CmdTest) && !in_file) {
+ } else if ((cmd == CmdTranslateC || cmd == CmdTest) && !in_file) {
fprintf(stderr, "Expected source file argument.\n");
return usage(arg0);
} else if (cmd == CmdBuild && out_type == OutTypeObj && objects.length != 0) {
@@ -719,7 +719,7 @@ int main(int argc, char **argv) {
assert(cmd != CmdBuild || out_type != OutTypeUnknown);
- bool need_name = (cmd == CmdBuild || cmd == CmdParseC);
+ bool need_name = (cmd == CmdBuild || cmd == CmdTranslateC);
Buf *in_file_buf = nullptr;
@@ -742,7 +742,7 @@ int main(int argc, char **argv) {
return usage(arg0);
}
- Buf *zig_root_source_file = (cmd == CmdParseC) ? nullptr : in_file_buf;
+ Buf *zig_root_source_file = (cmd == CmdTranslateC) ? nullptr : in_file_buf;
Buf *full_cache_dir = buf_alloc();
os_path_resolve(buf_create_from_str("."),
@@ -841,8 +841,8 @@ int main(int argc, char **argv) {
if (timing_info)
codegen_print_timing_report(g, stdout);
return EXIT_SUCCESS;
- } else if (cmd == CmdParseC) {
- codegen_parsec(g, in_file_buf);
+ } else if (cmd == CmdTranslateC) {
+ codegen_translate_c(g, in_file_buf);
ast_render(g, stdout, g->root_import->root, 4);
if (timing_info)
codegen_print_timing_report(g, stdout);
diff --git a/src/parsec.cpp b/src/parsec.cpp
@@ -1,3514 +0,0 @@
-/*
- * Copyright (c) 2015 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "all_types.hpp"
-#include "analyze.hpp"
-#include "c_tokenizer.hpp"
-#include "error.hpp"
-#include "ir.hpp"
-#include "os.hpp"
-#include "parsec.hpp"
-#include "parser.hpp"
-
-
-#include <clang/Frontend/ASTUnit.h>
-#include <clang/Frontend/CompilerInstance.h>
-#include <clang/AST/Expr.h>
-
-#include <string.h>
-
-using namespace clang;
-
-struct MacroSymbol {
- Buf *name;
- Buf *value;
-};
-
-struct Alias {
- Buf *new_name;
- Buf *canon_name;
-};
-
-struct Context {
- ImportTableEntry *import;
- ZigList<ErrorMsg *> *errors;
- VisibMod visib_mod;
- VisibMod export_visib_mod;
- AstNode *root;
- HashMap<const void *, AstNode *, ptr_hash, ptr_eq> decl_table;
- HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> macro_table;
- HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> global_table;
- SourceManager *source_manager;
- ZigList<Alias> aliases;
- ZigList<MacroSymbol> macro_symbols;
- AstNode *source_node;
- bool warnings_on;
-
- CodeGen *codegen;
- ASTContext *ctx;
-
- HashMap<Buf *, bool, buf_hash, buf_eql_buf> ptr_params;
-};
-
-static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl);
-static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl);
-static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_decl);
-
-
-ATTRIBUTE_PRINTF(3, 4)
-static void emit_warning(Context *c, const SourceLocation &sl, const char *format, ...) {
- if (!c->warnings_on) {
- return;
- }
-
- va_list ap;
- va_start(ap, format);
- Buf *msg = buf_vprintf(format, ap);
- va_end(ap);
-
- StringRef filename = c->source_manager->getFilename(sl);
- const char *filename_bytes = (const char *)filename.bytes_begin();
- Buf *path;
- if (filename_bytes) {
- path = buf_create_from_str(filename_bytes);
- } else {
- path = buf_sprintf("(no file)");
- }
- unsigned line = c->source_manager->getSpellingLineNumber(sl);
- unsigned column = c->source_manager->getSpellingColumnNumber(sl);
- fprintf(stderr, "%s:%u:%u: warning: %s\n", buf_ptr(path), line, column, buf_ptr(msg));
-}
-
-static void add_global_weak_alias(Context *c, Buf *new_name, Buf *canon_name) {
- Alias *alias = c->aliases.add_one();
- alias->new_name = new_name;
- alias->canon_name = canon_name;
-}
-
-static AstNode * trans_create_node(Context *c, NodeType id) {
- AstNode *node = allocate<AstNode>(1);
- node->type = id;
- node->owner = c->import;
- // TODO line/column. mapping to C file??
- return node;
-}
-
-static AstNode *trans_create_node_float_lit(Context *c, double value) {
- AstNode *node = trans_create_node(c, NodeTypeFloatLiteral);
- node->data.float_literal.bigfloat = allocate<BigFloat>(1);
- bigfloat_init_64(node->data.float_literal.bigfloat, value);
- return node;
-}
-
-static AstNode *trans_create_node_symbol(Context *c, Buf *name) {
- AstNode *node = trans_create_node(c, NodeTypeSymbol);
- node->data.symbol_expr.symbol = name;
- return node;
-}
-
-static AstNode *trans_create_node_symbol_str(Context *c, const char *name) {
- return trans_create_node_symbol(c, buf_create_from_str(name));
-}
-
-static AstNode *trans_create_node_builtin_fn_call(Context *c, Buf *name) {
- AstNode *node = trans_create_node(c, NodeTypeFnCallExpr);
- node->data.fn_call_expr.fn_ref_expr = trans_create_node_symbol(c, name);
- node->data.fn_call_expr.is_builtin = true;
- return node;
-}
-
-static AstNode *trans_create_node_builtin_fn_call_str(Context *c, const char *name) {
- return trans_create_node_builtin_fn_call(c, buf_create_from_str(name));
-}
-
-static AstNode *trans_create_node_opaque(Context *c) {
- return trans_create_node_builtin_fn_call_str(c, "OpaqueType");
-}
-
-static AstNode *trans_create_node_fn_call_1(Context *c, AstNode *fn_ref_expr, AstNode *arg1) {
- AstNode *node = trans_create_node(c, NodeTypeFnCallExpr);
- node->data.fn_call_expr.fn_ref_expr = fn_ref_expr;
- node->data.fn_call_expr.params.append(arg1);
- return node;
-}
-
-static AstNode *trans_create_node_field_access(Context *c, AstNode *container, Buf *field_name) {
- AstNode *node = trans_create_node(c, NodeTypeFieldAccessExpr);
- if (container->type == NodeTypeSymbol) {
- assert(container->data.symbol_expr.symbol != nullptr);
- }
- node->data.field_access_expr.struct_expr = container;
- node->data.field_access_expr.field_name = field_name;
- return node;
-}
-
-static AstNode *trans_create_node_field_access_str(Context *c, AstNode *container, const char *field_name) {
- return trans_create_node_field_access(c, container, buf_create_from_str(field_name));
-}
-
-static AstNode *trans_create_node_prefix_op(Context *c, PrefixOp op, AstNode *child_node) {
- AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr);
- node->data.prefix_op_expr.prefix_op = op;
- node->data.prefix_op_expr.primary_expr = child_node;
- return node;
-}
-
-static AstNode *trans_create_node_bin_op(Context *c, AstNode *lhs_node, BinOpType op, AstNode *rhs_node) {
- AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
- node->data.bin_op_expr.op1 = lhs_node;
- node->data.bin_op_expr.bin_op = op;
- node->data.bin_op_expr.op2 = rhs_node;
- return node;
-}
-
-static AstNode *maybe_suppress_result(Context *c, bool result_used, AstNode *node) {
- if (result_used) return node;
- return trans_create_node_bin_op(c,
- trans_create_node_symbol_str(c, "_"),
- BinOpTypeAssign,
- node);
-}
-
-static AstNode *trans_create_node_addr_of(Context *c, bool is_const, bool is_volatile, AstNode *child_node) {
- AstNode *node = trans_create_node(c, NodeTypeAddrOfExpr);
- node->data.addr_of_expr.is_const = is_const;
- node->data.addr_of_expr.is_volatile = is_volatile;
- node->data.addr_of_expr.op_expr = child_node;
- return node;
-}
-
-static AstNode *trans_create_node_str_lit_c(Context *c, Buf *buf) {
- AstNode *node = trans_create_node(c, NodeTypeStringLiteral);
- node->data.string_literal.buf = buf;
- node->data.string_literal.c = true;
- return node;
-}
-
-static AstNode *trans_create_node_str_lit_non_c(Context *c, Buf *buf) {
- AstNode *node = trans_create_node(c, NodeTypeStringLiteral);
- node->data.string_literal.buf = buf;
- node->data.string_literal.c = false;
- return node;
-}
-
-static AstNode *trans_create_node_unsigned_negative(Context *c, uint64_t x, bool is_negative) {
- AstNode *node = trans_create_node(c, NodeTypeIntLiteral);
- node->data.int_literal.bigint = allocate<BigInt>(1);
- bigint_init_data(node->data.int_literal.bigint, &x, 1, is_negative);
- return node;
-}
-
-static AstNode *trans_create_node_unsigned(Context *c, uint64_t x) {
- return trans_create_node_unsigned_negative(c, x, false);
-}
-
-static AstNode *trans_create_node_cast(Context *c, AstNode *dest, AstNode *src) {
- AstNode *node = trans_create_node(c, NodeTypeFnCallExpr);
- node->data.fn_call_expr.fn_ref_expr = dest;
- node->data.fn_call_expr.params.resize(1);
- node->data.fn_call_expr.params.items[0] = src;
- return node;
-}
-
-static AstNode *trans_create_node_unsigned_negative_type(Context *c, uint64_t x, bool is_negative,
- const char *type_name)
-{
- AstNode *lit_node = trans_create_node_unsigned_negative(c, x, is_negative);
- return trans_create_node_cast(c, trans_create_node_symbol_str(c, type_name), lit_node);
-}
-
-static AstNode *trans_create_node_array_type(Context *c, AstNode *size_node, AstNode *child_type_node) {
- AstNode *node = trans_create_node(c, NodeTypeArrayType);
- node->data.array_type.size = size_node;
- node->data.array_type.child_type = child_type_node;
- return node;
-}
-
-static AstNode *trans_create_node_var_decl(Context *c, VisibMod visib_mod, bool is_const, Buf *var_name,
- AstNode *type_node, AstNode *init_node)
-{
- AstNode *node = trans_create_node(c, NodeTypeVariableDeclaration);
- node->data.variable_declaration.visib_mod = visib_mod;
- node->data.variable_declaration.symbol = var_name;
- node->data.variable_declaration.is_const = is_const;
- node->data.variable_declaration.type = type_node;
- node->data.variable_declaration.expr = init_node;
- return node;
-}
-
-static AstNode *trans_create_node_var_decl_global(Context *c, bool is_const, Buf *var_name, AstNode *type_node,
- AstNode *init_node)
-{
- return trans_create_node_var_decl(c, c->visib_mod, is_const, var_name, type_node, init_node);
-}
-
-static AstNode *trans_create_node_var_decl_local(Context *c, bool is_const, Buf *var_name, AstNode *type_node,
- AstNode *init_node)
-{
- return trans_create_node_var_decl(c, VisibModPrivate, is_const, var_name, type_node, init_node);
-}
-
-
-static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, Buf *var_name, AstNode *src_proto_node) {
- AstNode *fn_def = trans_create_node(c, NodeTypeFnDef);
- AstNode *fn_proto = trans_create_node(c, NodeTypeFnProto);
- fn_proto->data.fn_proto.visib_mod = c->visib_mod;
- fn_proto->data.fn_proto.name = fn_name;
- fn_proto->data.fn_proto.is_inline = true;
- fn_proto->data.fn_proto.return_type = src_proto_node->data.fn_proto.return_type; // TODO ok for these to alias?
-
- fn_def->data.fn_def.fn_proto = fn_proto;
- fn_proto->data.fn_proto.fn_def_node = fn_def;
-
- AstNode *unwrap_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, trans_create_node_symbol(c, var_name));
- AstNode *fn_call_node = trans_create_node(c, NodeTypeFnCallExpr);
- fn_call_node->data.fn_call_expr.fn_ref_expr = unwrap_node;
-
- for (size_t i = 0; i < src_proto_node->data.fn_proto.params.length; i += 1) {
- AstNode *src_param_node = src_proto_node->data.fn_proto.params.at(i);
- Buf *param_name = src_param_node->data.param_decl.name;
- if (!param_name) param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i);
-
- AstNode *dest_param_node = trans_create_node(c, NodeTypeParamDecl);
- dest_param_node->data.param_decl.name = param_name;
- dest_param_node->data.param_decl.type = src_param_node->data.param_decl.type;
- dest_param_node->data.param_decl.is_noalias = src_param_node->data.param_decl.is_noalias;
- fn_proto->data.fn_proto.params.append(dest_param_node);
-
- fn_call_node->data.fn_call_expr.params.append(trans_create_node_symbol(c, param_name));
-
- }
-
- AstNode *block = trans_create_node(c, NodeTypeBlock);
- block->data.block.statements.resize(1);
- block->data.block.statements.items[0] = fn_call_node;
- block->data.block.last_statement_is_result_expression = true;
-
- fn_def->data.fn_def.body = block;
- return fn_def;
-}
-
-static AstNode *trans_create_node_unwrap_null(Context *c, AstNode *child) {
- return trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, child);
-}
-
-static AstNode *get_global(Context *c, Buf *name) {
- {
- auto entry = c->global_table.maybe_get(name);
- if (entry) {
- return entry->value;
- }
- }
- {
- auto entry = c->macro_table.maybe_get(name);
- if (entry)
- return entry->value;
- }
- return nullptr;
-}
-
-static void add_top_level_decl(Context *c, Buf *name, AstNode *node) {
- c->global_table.put(name, node);
- c->root->data.root.top_level_decls.append(node);
-}
-
-static AstNode *add_global_var(Context *c, Buf *var_name, AstNode *value_node) {
- bool is_const = true;
- AstNode *type_node = nullptr;
- AstNode *node = trans_create_node_var_decl_global(c, is_const, var_name, type_node, value_node);
- add_top_level_decl(c, var_name, node);
- return node;
-}
-
-static const char *decl_name(const Decl *decl) {
- const NamedDecl *named_decl = static_cast<const NamedDecl *>(decl);
- return (const char *)named_decl->getName().bytes_begin();
-}
-
-static AstNode *trans_create_node_apint(Context *c, const llvm::APSInt &aps_int) {
- AstNode *node = trans_create_node(c, NodeTypeIntLiteral);
- node->data.int_literal.bigint = allocate<BigInt>(1);
- bigint_init_data(node->data.int_literal.bigint, aps_int.getRawData(), aps_int.getNumWords(), aps_int.isNegative());
- return node;
-
-}
-
-static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc);
-
-static bool is_c_void_type(AstNode *node) {
- return (node->type == NodeTypeSymbol && buf_eql_str(node->data.symbol_expr.symbol, "c_void"));
-}
-
-static AstNode* trans_c_cast(Context *c, const SourceLocation &source_location, const QualType &qt, AstNode *expr) {
- // TODO: maybe widen to increase size
- // TODO: maybe bitcast to change sign
- // TODO: maybe truncate to reduce size
- return trans_create_node_fn_call_1(c, trans_qual_type(c, qt, source_location), expr);
-}
-
-static bool qual_type_is_fn_ptr(Context *c, const QualType &qt) {
- const Type *ty = qt.getTypePtr();
- if (ty->getTypeClass() != Type::Pointer) {
- return false;
- }
- const PointerType *pointer_ty = static_cast<const PointerType*>(ty);
- QualType child_qt = pointer_ty->getPointeeType();
- const Type *child_ty = child_qt.getTypePtr();
- if (child_ty->getTypeClass() != Type::Paren) {
- return false;
- }
- const ParenType *paren_ty = static_cast<const ParenType *>(child_ty);
- return paren_ty->getInnerType().getTypePtr()->getTypeClass() == Type::FunctionProto;
-}
-
-static uint32_t qual_type_int_bit_width(Context *c, const QualType &qt, const SourceLocation &source_loc) {
- const Type *ty = qt.getTypePtr();
- switch (ty->getTypeClass()) {
- case Type::Builtin:
- {
- const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(ty);
- switch (builtin_ty->getKind()) {
- case BuiltinType::Char_U:
- case BuiltinType::UChar:
- case BuiltinType::Char_S:
- case BuiltinType::SChar:
- return 8;
- case BuiltinType::UInt128:
- case BuiltinType::Int128:
- return 128;
- default:
- return 0;
- }
- zig_unreachable();
- }
- case Type::Typedef:
- {
- const TypedefType *typedef_ty = static_cast<const TypedefType*>(ty);
- const TypedefNameDecl *typedef_decl = typedef_ty->getDecl();
- const char *type_name = decl_name(typedef_decl);
- if (strcmp(type_name, "uint8_t") == 0 || strcmp(type_name, "int8_t") == 0) {
- return 8;
- } else if (strcmp(type_name, "uint16_t") == 0 || strcmp(type_name, "int16_t") == 0) {
- return 16;
- } else if (strcmp(type_name, "uint32_t") == 0 || strcmp(type_name, "int32_t") == 0) {
- return 32;
- } else if (strcmp(type_name, "uint64_t") == 0 || strcmp(type_name, "int64_t") == 0) {
- return 64;
- } else {
- return 0;
- }
- }
- default:
- return 0;
- }
- zig_unreachable();
-}
-
-
-static AstNode *qual_type_to_log2_int_ref(Context *c, const QualType &qt,
- const SourceLocation &source_loc)
-{
- uint32_t int_bit_width = qual_type_int_bit_width(c, qt, source_loc);
- if (int_bit_width != 0) {
- // we can perform the log2 now.
- uint64_t cast_bit_width = log2_u64(int_bit_width);
- return trans_create_node_symbol(c, buf_sprintf("u%" ZIG_PRI_u64, cast_bit_width));
- }
-
- AstNode *zig_type_node = trans_qual_type(c, qt, source_loc);
-
-// @import("std").math.Log2Int(c_long);
-//
-// FnCall
-// FieldAccess
-// FieldAccess
-// FnCall (.builtin = true)
-// Symbol "import"
-// StringLiteral "std"
-// Symbol "math"
-// Symbol "Log2Int"
-// zig_type_node
-
- AstNode *import_fn_call = trans_create_node_builtin_fn_call_str(c, "import");
- import_fn_call->data.fn_call_expr.params.append(trans_create_node_str_lit_non_c(c, buf_create_from_str("std")));
- AstNode *inner_field_access = trans_create_node_field_access_str(c, import_fn_call, "math");
- AstNode *outer_field_access = trans_create_node_field_access_str(c, inner_field_access, "Log2Int");
- AstNode *log2int_fn_call = trans_create_node_fn_call_1(c, outer_field_access, zig_type_node);
-
- return log2int_fn_call;
-}
-
-static bool qual_type_child_is_fn_proto(const QualType &qt) {
- if (qt.getTypePtr()->getTypeClass() == Type::Paren) {
- const ParenType *paren_type = static_cast<const ParenType *>(qt.getTypePtr());
- if (paren_type->getInnerType()->getTypeClass() == Type::FunctionProto) {
- return true;
- }
- } else if (qt.getTypePtr()->getTypeClass() == Type::Attributed) {
- const AttributedType *attr_type = static_cast<const AttributedType *>(qt.getTypePtr());
- return qual_type_child_is_fn_proto(attr_type->getEquivalentType());
- }
- return false;
-}
-
-static QualType resolve_any_typedef(Context *c, QualType qt) {
- const Type * ty = qt.getTypePtr();
- if (ty->getTypeClass() != Type::Typedef)
- return qt;
- const TypedefType *typedef_ty = static_cast<const TypedefType*>(ty);
- const TypedefNameDecl *typedef_decl = typedef_ty->getDecl();
- return typedef_decl->getUnderlyingType();
-}
-
-static bool c_is_signed_integer(Context *c, QualType qt) {
- const Type *c_type = resolve_any_typedef(c, qt).getTypePtr();
- if (c_type->getTypeClass() != Type::Builtin)
- return false;
- const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(c_type);
- switch (builtin_ty->getKind()) {
- case BuiltinType::SChar:
- case BuiltinType::Short:
- case BuiltinType::Int:
- case BuiltinType::Long:
- case BuiltinType::LongLong:
- case BuiltinType::Int128:
- case BuiltinType::WChar_S:
- return true;
- default:
- return false;
- }
-}
-
-static bool c_is_unsigned_integer(Context *c, QualType qt) {
- const Type *c_type = resolve_any_typedef(c, qt).getTypePtr();
- if (c_type->getTypeClass() != Type::Builtin)
- return false;
- const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(c_type);
- switch (builtin_ty->getKind()) {
- case BuiltinType::Char_U:
- case BuiltinType::UChar:
- case BuiltinType::Char_S:
- case BuiltinType::UShort:
- case BuiltinType::UInt:
- case BuiltinType::ULong:
- case BuiltinType::ULongLong:
- case BuiltinType::UInt128:
- case BuiltinType::WChar_U:
- return true;
- default:
- return false;
- }
-}
-
-static bool c_is_float(Context *c, QualType qt) {
- const Type *c_type = qt.getTypePtr();
- if (c_type->getTypeClass() != Type::Builtin)
- return false;
- const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(c_type);
- switch (builtin_ty->getKind()) {
- case BuiltinType::Half:
- case BuiltinType::Float:
- case BuiltinType::Double:
- case BuiltinType::Float128:
- case BuiltinType::LongDouble:
- return true;
- default:
- return false;
- }
-}
-
-static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) {
- if (c_is_signed_integer(c, qt) || c_is_float(c, qt)) {
- // float and signed integer overflow is undefined behavior.
- return false;
- } else {
- // unsigned integer overflow wraps around.
- return true;
- }
-}
-
-enum TransLRValue {
- TransLValue,
- TransRValue,
-};
-
-static AstNode *trans_stmt(Context *c, bool result_used, AstNode *block, Stmt *stmt, TransLRValue lrval);
-static AstNode *const skip_add_to_block_node = (AstNode *) 0x2;
-
-static AstNode *trans_expr(Context *c, bool result_used, AstNode *block, Expr *expr, TransLRValue lrval) {
- return trans_stmt(c, result_used, block, expr, lrval);
-}
-
-static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) {
- switch (ty->getTypeClass()) {
- case Type::Builtin:
- {
- const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(ty);
- switch (builtin_ty->getKind()) {
- case BuiltinType::Void:
- return trans_create_node_symbol_str(c, "c_void");
- case BuiltinType::Bool:
- return trans_create_node_symbol_str(c, "bool");
- case BuiltinType::Char_U:
- case BuiltinType::UChar:
- case BuiltinType::Char_S:
- return trans_create_node_symbol_str(c, "u8");
- case BuiltinType::SChar:
- return trans_create_node_symbol_str(c, "i8");
- case BuiltinType::UShort:
- return trans_create_node_symbol_str(c, "c_ushort");
- case BuiltinType::UInt:
- return trans_create_node_symbol_str(c, "c_uint");
- case BuiltinType::ULong:
- return trans_create_node_symbol_str(c, "c_ulong");
- case BuiltinType::ULongLong:
- return trans_create_node_symbol_str(c, "c_ulonglong");
- case BuiltinType::Short:
- return trans_create_node_symbol_str(c, "c_short");
- case BuiltinType::Int:
- return trans_create_node_symbol_str(c, "c_int");
- case BuiltinType::Long:
- return trans_create_node_symbol_str(c, "c_long");
- case BuiltinType::LongLong:
- return trans_create_node_symbol_str(c, "c_longlong");
- case BuiltinType::UInt128:
- return trans_create_node_symbol_str(c, "u128");
- case BuiltinType::Int128:
- return trans_create_node_symbol_str(c, "i128");
- case BuiltinType::Float:
- return trans_create_node_symbol_str(c, "f32");
- case BuiltinType::Double:
- return trans_create_node_symbol_str(c, "f64");
- case BuiltinType::Float128:
- return trans_create_node_symbol_str(c, "f128");
- case BuiltinType::Float16:
- return trans_create_node_symbol_str(c, "f16");
- case BuiltinType::LongDouble:
- return trans_create_node_symbol_str(c, "c_longdouble");
- case BuiltinType::WChar_U:
- case BuiltinType::Char16:
- case BuiltinType::Char32:
- case BuiltinType::WChar_S:
- case BuiltinType::Half:
- case BuiltinType::NullPtr:
- case BuiltinType::ObjCId:
- case BuiltinType::ObjCClass:
- case BuiltinType::ObjCSel:
- case BuiltinType::OMPArraySection:
- case BuiltinType::Dependent:
- case BuiltinType::Overload:
- case BuiltinType::BoundMember:
- case BuiltinType::PseudoObject:
- case BuiltinType::UnknownAny:
- case BuiltinType::BuiltinFn:
- case BuiltinType::ARCUnbridgedCast:
-
- case BuiltinType::OCLImage1dRO:
- case BuiltinType::OCLImage1dArrayRO:
- case BuiltinType::OCLImage1dBufferRO:
- case BuiltinType::OCLImage2dRO:
- case BuiltinType::OCLImage2dArrayRO:
- case BuiltinType::OCLImage2dDepthRO:
- case BuiltinType::OCLImage2dArrayDepthRO:
- case BuiltinType::OCLImage2dMSAARO:
- case BuiltinType::OCLImage2dArrayMSAARO:
- case BuiltinType::OCLImage2dMSAADepthRO:
- case BuiltinType::OCLImage2dArrayMSAADepthRO:
- case BuiltinType::OCLImage3dRO:
- case BuiltinType::OCLImage1dWO:
- case BuiltinType::OCLImage1dArrayWO:
- case BuiltinType::OCLImage1dBufferWO:
- case BuiltinType::OCLImage2dWO:
- case BuiltinType::OCLImage2dArrayWO:
- case BuiltinType::OCLImage2dDepthWO:
- case BuiltinType::OCLImage2dArrayDepthWO:
- case BuiltinType::OCLImage2dMSAAWO:
- case BuiltinType::OCLImage2dArrayMSAAWO:
- case BuiltinType::OCLImage2dMSAADepthWO:
- case BuiltinType::OCLImage2dArrayMSAADepthWO:
- case BuiltinType::OCLImage3dWO:
- case BuiltinType::OCLImage1dRW:
- case BuiltinType::OCLImage1dArrayRW:
- case BuiltinType::OCLImage1dBufferRW:
- case BuiltinType::OCLImage2dRW:
- case BuiltinType::OCLImage2dArrayRW:
- case BuiltinType::OCLImage2dDepthRW:
- case BuiltinType::OCLImage2dArrayDepthRW:
- case BuiltinType::OCLImage2dMSAARW:
- case BuiltinType::OCLImage2dArrayMSAARW:
- case BuiltinType::OCLImage2dMSAADepthRW:
- case BuiltinType::OCLImage2dArrayMSAADepthRW:
- case BuiltinType::OCLImage3dRW:
- case BuiltinType::OCLSampler:
- case BuiltinType::OCLEvent:
- case BuiltinType::OCLClkEvent:
- case BuiltinType::OCLQueue:
- case BuiltinType::OCLReserveID:
- emit_warning(c, source_loc, "unsupported builtin type");
- return nullptr;
- }
- break;
- }
- case Type::Pointer:
- {
- const PointerType *pointer_ty = static_cast<const PointerType*>(ty);
- QualType child_qt = pointer_ty->getPointeeType();
- AstNode *child_node = trans_qual_type(c, child_qt, source_loc);
- if (child_node == nullptr) {
- emit_warning(c, source_loc, "pointer to unsupported type");
- return nullptr;
- }
-
- if (qual_type_child_is_fn_proto(child_qt)) {
- return trans_create_node_prefix_op(c, PrefixOpMaybe, child_node);
- }
-
- AstNode *pointer_node = trans_create_node_addr_of(c, child_qt.isConstQualified(),
- child_qt.isVolatileQualified(), child_node);
- return trans_create_node_prefix_op(c, PrefixOpMaybe, pointer_node);
- }
- case Type::Typedef:
- {
- const TypedefType *typedef_ty = static_cast<const TypedefType*>(ty);
- const TypedefNameDecl *typedef_decl = typedef_ty->getDecl();
- return resolve_typedef_decl(c, typedef_decl);
- }
- case Type::Elaborated:
- {
- const ElaboratedType *elaborated_ty = static_cast<const ElaboratedType*>(ty);
- switch (elaborated_ty->getKeyword()) {
- case ETK_Struct:
- return trans_qual_type(c, elaborated_ty->getNamedType(), source_loc);
- case ETK_Enum:
- return trans_qual_type(c, elaborated_ty->getNamedType(), source_loc);
- case ETK_Interface:
- case ETK_Union:
- case ETK_Class:
- case ETK_Typename:
- case ETK_None:
- emit_warning(c, source_loc, "unsupported elaborated type");
- return nullptr;
- }
- }
- case Type::FunctionProto:
- {
- const FunctionProtoType *fn_proto_ty = static_cast<const FunctionProtoType*>(ty);
-
- AstNode *proto_node = trans_create_node(c, NodeTypeFnProto);
- switch (fn_proto_ty->getCallConv()) {
- case CC_C: // __attribute__((cdecl))
- proto_node->data.fn_proto.cc = CallingConventionC;
- proto_node->data.fn_proto.is_extern = true;
- break;
- case CC_X86StdCall: // __attribute__((stdcall))
- proto_node->data.fn_proto.cc = CallingConventionStdcall;
- break;
- case CC_X86FastCall: // __attribute__((fastcall))
- emit_warning(c, source_loc, "unsupported calling convention: x86 fastcall");
- return nullptr;
- case CC_X86ThisCall: // __attribute__((thiscall))
- emit_warning(c, source_loc, "unsupported calling convention: x86 thiscall");
- return nullptr;
- case CC_X86VectorCall: // __attribute__((vectorcall))
- emit_warning(c, source_loc, "unsupported calling convention: x86 vectorcall");
- return nullptr;
- case CC_X86Pascal: // __attribute__((pascal))
- emit_warning(c, source_loc, "unsupported calling convention: x86 pascal");
- return nullptr;
- case CC_Win64: // __attribute__((ms_abi))
- emit_warning(c, source_loc, "unsupported calling convention: win64");
- return nullptr;
- case CC_X86_64SysV: // __attribute__((sysv_abi))
- emit_warning(c, source_loc, "unsupported calling convention: x86 64sysv");
- return nullptr;
- case CC_X86RegCall:
- emit_warning(c, source_loc, "unsupported calling convention: x86 reg");
- return nullptr;
- case CC_AAPCS: // __attribute__((pcs("aapcs")))
- emit_warning(c, source_loc, "unsupported calling convention: aapcs");
- return nullptr;
- case CC_AAPCS_VFP: // __attribute__((pcs("aapcs-vfp")))
- emit_warning(c, source_loc, "unsupported calling convention: aapcs-vfp");
- return nullptr;
- case CC_IntelOclBicc: // __attribute__((intel_ocl_bicc))
- emit_warning(c, source_loc, "unsupported calling convention: intel_ocl_bicc");
- return nullptr;
- case CC_SpirFunction: // default for OpenCL functions on SPIR target
- emit_warning(c, source_loc, "unsupported calling convention: SPIR function");
- return nullptr;
- case CC_OpenCLKernel:
- emit_warning(c, source_loc, "unsupported calling convention: OpenCLKernel");
- return nullptr;
- case CC_Swift:
- emit_warning(c, source_loc, "unsupported calling convention: Swift");
- return nullptr;
- case CC_PreserveMost:
- emit_warning(c, source_loc, "unsupported calling convention: PreserveMost");
- return nullptr;
- case CC_PreserveAll:
- emit_warning(c, source_loc, "unsupported calling convention: PreserveAll");
- return nullptr;
- }
-
- proto_node->data.fn_proto.is_var_args = fn_proto_ty->isVariadic();
- size_t param_count = fn_proto_ty->getNumParams();
-
- if (fn_proto_ty->getNoReturnAttr()) {
- proto_node->data.fn_proto.return_type = trans_create_node_symbol_str(c, "noreturn");
- } else {
- proto_node->data.fn_proto.return_type = trans_qual_type(c, fn_proto_ty->getReturnType(),
- source_loc);
- if (proto_node->data.fn_proto.return_type == nullptr) {
- emit_warning(c, source_loc, "unsupported function proto return type");
- return nullptr;
- }
- // convert c_void to actual void (only for return type)
- if (is_c_void_type(proto_node->data.fn_proto.return_type)) {
- proto_node->data.fn_proto.return_type = nullptr;
- }
- }
-
- //emit_warning(c, source_loc, "TODO figure out fn prototype fn name");
- const char *fn_name = nullptr;
- if (fn_name != nullptr) {
- proto_node->data.fn_proto.name = buf_create_from_str(fn_name);
- }
-
- for (size_t i = 0; i < param_count; i += 1) {
- QualType qt = fn_proto_ty->getParamType(i);
- AstNode *param_type_node = trans_qual_type(c, qt, source_loc);
-
- if (param_type_node == nullptr) {
- emit_warning(c, source_loc, "unresolved function proto parameter type");
- return nullptr;
- }
-
- AstNode *param_node = trans_create_node(c, NodeTypeParamDecl);
- //emit_warning(c, source_loc, "TODO figure out fn prototype param name");
- const char *param_name = nullptr;
- if (param_name != nullptr) {
- param_node->data.param_decl.name = buf_create_from_str(param_name);
- }
- param_node->data.param_decl.is_noalias = qt.isRestrictQualified();
- param_node->data.param_decl.type = param_type_node;
- proto_node->data.fn_proto.params.append(param_node);
- }
- // TODO check for always_inline attribute
- // TODO check for align attribute
-
- return proto_node;
- }
- case Type::Record:
- {
- const RecordType *record_ty = static_cast<const RecordType*>(ty);
- return resolve_record_decl(c, record_ty->getDecl());
- }
- case Type::Enum:
- {
- const EnumType *enum_ty = static_cast<const EnumType*>(ty);
- return resolve_enum_decl(c, enum_ty->getDecl());
- }
- case Type::ConstantArray:
- {
- const ConstantArrayType *const_arr_ty = static_cast<const ConstantArrayType *>(ty);
- AstNode *child_type_node = trans_qual_type(c, const_arr_ty->getElementType(), source_loc);
- if (child_type_node == nullptr) {
- emit_warning(c, source_loc, "unresolved array element type");
- return nullptr;
- }
- uint64_t size = const_arr_ty->getSize().getLimitedValue();
- AstNode *size_node = trans_create_node_unsigned(c, size);
- return trans_create_node_array_type(c, size_node, child_type_node);
- }
- case Type::Paren:
- {
- const ParenType *paren_ty = static_cast<const ParenType *>(ty);
- return trans_qual_type(c, paren_ty->getInnerType(), source_loc);
- }
- case Type::Decayed:
- {
- const DecayedType *decayed_ty = static_cast<const DecayedType *>(ty);
- return trans_qual_type(c, decayed_ty->getDecayedType(), source_loc);
- }
- case Type::Attributed:
- {
- const AttributedType *attributed_ty = static_cast<const AttributedType *>(ty);
- return trans_qual_type(c, attributed_ty->getEquivalentType(), source_loc);
- }
- case Type::BlockPointer:
- case Type::LValueReference:
- case Type::RValueReference:
- case Type::MemberPointer:
- case Type::IncompleteArray:
- case Type::VariableArray:
- case Type::DependentSizedArray:
- case Type::DependentSizedExtVector:
- case Type::Vector:
- case Type::ExtVector:
- case Type::FunctionNoProto:
- case Type::UnresolvedUsing:
- case Type::Adjusted:
- case Type::TypeOfExpr:
- case Type::TypeOf:
- case Type::Decltype:
- case Type::UnaryTransform:
- case Type::TemplateTypeParm:
- case Type::SubstTemplateTypeParm:
- case Type::SubstTemplateTypeParmPack:
- case Type::TemplateSpecialization:
- case Type::Auto:
- case Type::InjectedClassName:
- case Type::DependentName:
- case Type::DependentTemplateSpecialization:
- case Type::PackExpansion:
- case Type::ObjCObject:
- case Type::ObjCInterface:
- case Type::Complex:
- case Type::ObjCObjectPointer:
- case Type::Atomic:
- case Type::Pipe:
- case Type::ObjCTypeParam:
- case Type::DeducedTemplateSpecialization:
- case Type::DependentAddressSpace:
- emit_warning(c, source_loc, "unsupported type: '%s'", ty->getTypeClassName());
- return nullptr;
- }
- zig_unreachable();
-}
-
-static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc) {
- return trans_type(c, qt.getTypePtr(), source_loc);
-}
-
-static AstNode *trans_compound_stmt(Context *c, AstNode *parent, CompoundStmt *stmt) {
- AstNode *child_block = trans_create_node(c, NodeTypeBlock);
- for (CompoundStmt::body_iterator it = stmt->body_begin(), end_it = stmt->body_end(); it != end_it; ++it) {
- AstNode *child_node = trans_stmt(c, false, child_block, *it, TransRValue);
- if (child_node == nullptr)
- return nullptr;
- if (child_node != skip_add_to_block_node)
- child_block->data.block.statements.append(child_node);
- }
- return child_block;
-}
-
-static AstNode *trans_return_stmt(Context *c, AstNode *block, ReturnStmt *stmt) {
- Expr *value_expr = stmt->getRetValue();
- if (value_expr == nullptr) {
- emit_warning(c, stmt->getLocStart(), "TODO handle C return void");
- return nullptr;
- } else {
- AstNode *return_node = trans_create_node(c, NodeTypeReturnExpr);
- return_node->data.return_expr.expr = trans_expr(c, true, block, value_expr, TransRValue);
- if (return_node->data.return_expr.expr == nullptr)
- return nullptr;
- return return_node;
- }
-}
-
-static AstNode *trans_integer_literal(Context *c, IntegerLiteral *stmt) {
- llvm::APSInt result;
- if (!stmt->EvaluateAsInt(result, *c->ctx)) {
- emit_warning(c, stmt->getLocStart(), "invalid integer literal");
- return nullptr;
- }
- return trans_create_node_apint(c, result);
-}
-
-static AstNode *trans_conditional_operator(Context *c, bool result_used, AstNode *block, ConditionalOperator *stmt) {
- AstNode *node = trans_create_node(c, NodeTypeIfBoolExpr);
-
- Expr *cond_expr = stmt->getCond();
- Expr *true_expr = stmt->getTrueExpr();
- Expr *false_expr = stmt->getFalseExpr();
-
- node->data.if_bool_expr.condition = trans_expr(c, true, block, cond_expr, TransRValue);
- if (node->data.if_bool_expr.condition == nullptr)
- return nullptr;
-
- node->data.if_bool_expr.then_block = trans_expr(c, result_used, block, true_expr, TransRValue);
- if (node->data.if_bool_expr.then_block == nullptr)
- return nullptr;
-
- node->data.if_bool_expr.else_node = trans_expr(c, result_used, block, false_expr, TransRValue);
- if (node->data.if_bool_expr.else_node == nullptr)
- return nullptr;
-
- return maybe_suppress_result(c, result_used, node);
-}
-
-static AstNode *trans_create_bin_op(Context *c, AstNode *block, Expr *lhs, BinOpType bin_op, Expr *rhs) {
- AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
- node->data.bin_op_expr.bin_op = bin_op;
-
- node->data.bin_op_expr.op1 = trans_expr(c, true, block, lhs, TransRValue);
- if (node->data.bin_op_expr.op1 == nullptr)
- return nullptr;
-
- node->data.bin_op_expr.op2 = trans_expr(c, true, block, rhs, TransRValue);
- if (node->data.bin_op_expr.op2 == nullptr)
- return nullptr;
-
- return node;
-}
-
-static AstNode *trans_create_assign(Context *c, bool result_used, AstNode *block, Expr *lhs, Expr *rhs) {
- if (!result_used) {
- // common case
- AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
- node->data.bin_op_expr.bin_op = BinOpTypeAssign;
-
- node->data.bin_op_expr.op1 = trans_expr(c, true, block, lhs, TransLValue);
- if (node->data.bin_op_expr.op1 == nullptr)
- return nullptr;
-
- node->data.bin_op_expr.op2 = trans_expr(c, true, block, rhs, TransRValue);
- if (node->data.bin_op_expr.op2 == nullptr)
- return nullptr;
-
- return node;
- } else {
- // worst case
- // c: lhs = rhs
- // zig: {
- // zig: const _tmp = rhs;
- // zig: lhs = _tmp;
- // zig: _tmp
- // zig: }
-
- AstNode *child_block = trans_create_node(c, NodeTypeBlock);
-
- // const _tmp = rhs;
- AstNode *rhs_node = trans_expr(c, true, child_block, rhs, TransRValue);
- if (rhs_node == nullptr) return nullptr;
- // TODO: avoid name collisions with generated variable names
- Buf* tmp_var_name = buf_create_from_str("_tmp");
- AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, rhs_node);
- child_block->data.block.statements.append(tmp_var_decl);
-
- // lhs = _tmp;
- AstNode *lhs_node = trans_expr(c, true, child_block, lhs, TransLValue);
- if (lhs_node == nullptr) return nullptr;
- child_block->data.block.statements.append(
- trans_create_node_bin_op(c, lhs_node, BinOpTypeAssign,
- trans_create_node_symbol(c, tmp_var_name)));
-
- // _tmp
- child_block->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name));
- child_block->data.block.last_statement_is_result_expression = true;
-
- return child_block;
- }
-}
-
-static AstNode *trans_create_shift_op(Context *c, AstNode *block, QualType result_type, Expr *lhs_expr, BinOpType bin_op, Expr *rhs_expr) {
- const SourceLocation &rhs_location = rhs_expr->getLocStart();
- AstNode *rhs_type = qual_type_to_log2_int_ref(c, result_type, rhs_location);
- // lhs >> u5(rh)
-
- AstNode *lhs = trans_expr(c, true, block, lhs_expr, TransLValue);
- if (lhs == nullptr) return nullptr;
-
- AstNode *rhs = trans_expr(c, true, block, rhs_expr, TransRValue);
- if (rhs == nullptr) return nullptr;
- AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs);
-
- return trans_create_node_bin_op(c, lhs, bin_op, coerced_rhs);
-}
-
-static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *block, BinaryOperator *stmt) {
- switch (stmt->getOpcode()) {
- case BO_PtrMemD:
- emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_PtrMemD");
- return nullptr;
- case BO_PtrMemI:
- emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_PtrMemI");
- return nullptr;
- case BO_Mul:
- return trans_create_bin_op(c, block, stmt->getLHS(),
- qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeMultWrap : BinOpTypeMult,
- stmt->getRHS());
- case BO_Div:
- if (qual_type_has_wrapping_overflow(c, stmt->getType())) {
- // unsigned/float division uses the operator
- return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeDiv, stmt->getRHS());
- } else {
- // signed integer division uses @divTrunc
- AstNode *fn_call = trans_create_node_builtin_fn_call_str(c, "divTrunc");
- AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue);
- if (lhs == nullptr) return nullptr;
- fn_call->data.fn_call_expr.params.append(lhs);
- AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransLValue);
- if (rhs == nullptr) return nullptr;
- fn_call->data.fn_call_expr.params.append(rhs);
- return fn_call;
- }
- case BO_Rem:
- if (qual_type_has_wrapping_overflow(c, stmt->getType())) {
- // unsigned/float division uses the operator
- return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeMod, stmt->getRHS());
- } else {
- // signed integer division uses @rem
- AstNode *fn_call = trans_create_node_builtin_fn_call_str(c, "rem");
- AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue);
- if (lhs == nullptr) return nullptr;
- fn_call->data.fn_call_expr.params.append(lhs);
- AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransLValue);
- if (rhs == nullptr) return nullptr;
- fn_call->data.fn_call_expr.params.append(rhs);
- return fn_call;
- }
- case BO_Add:
- return trans_create_bin_op(c, block, stmt->getLHS(),
- qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeAddWrap : BinOpTypeAdd,
- stmt->getRHS());
- case BO_Sub:
- return trans_create_bin_op(c, block, stmt->getLHS(),
- qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeSubWrap : BinOpTypeSub,
- stmt->getRHS());
- case BO_Shl:
- return trans_create_shift_op(c, block, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftLeft, stmt->getRHS());
- case BO_Shr:
- return trans_create_shift_op(c, block, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftRight, stmt->getRHS());
- case BO_LT:
- return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpLessThan, stmt->getRHS());
- case BO_GT:
- return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpGreaterThan, stmt->getRHS());
- case BO_LE:
- return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpLessOrEq, stmt->getRHS());
- case BO_GE:
- return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpGreaterOrEq, stmt->getRHS());
- case BO_EQ:
- return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpEq, stmt->getRHS());
- case BO_NE:
- return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpNotEq, stmt->getRHS());
- case BO_And:
- return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBinAnd, stmt->getRHS());
- case BO_Xor:
- return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBinXor, stmt->getRHS());
- case BO_Or:
- return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBinOr, stmt->getRHS());
- case BO_LAnd:
- return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBoolAnd, stmt->getRHS());
- case BO_LOr:
- // TODO: int vs bool
- return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS());
- case BO_Assign:
- return trans_create_assign(c, result_used, block, stmt->getLHS(), stmt->getRHS());
- case BO_Comma:
- {
- block = trans_create_node(c, NodeTypeBlock);
- AstNode *lhs = trans_expr(c, false, block, stmt->getLHS(), TransRValue);
- if (lhs == nullptr) return nullptr;
- block->data.block.statements.append(maybe_suppress_result(c, false, lhs));
- AstNode *rhs = trans_expr(c, result_used, block, stmt->getRHS(), TransRValue);
- if (rhs == nullptr) return nullptr;
- block->data.block.statements.append(maybe_suppress_result(c, result_used, rhs));
- block->data.block.last_statement_is_result_expression = true;
- return block;
- }
- case BO_MulAssign:
- case BO_DivAssign:
- case BO_RemAssign:
- case BO_AddAssign:
- case BO_SubAssign:
- case BO_ShlAssign:
- case BO_ShrAssign:
- case BO_AndAssign:
- case BO_XorAssign:
- case BO_OrAssign:
- zig_unreachable();
- }
-
- zig_unreachable();
-}
-
-static AstNode *trans_create_compound_assign_shift(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) {
- const SourceLocation &rhs_location = stmt->getRHS()->getLocStart();
- AstNode *rhs_type = qual_type_to_log2_int_ref(c, stmt->getComputationLHSType(), rhs_location);
-
- bool use_intermediate_casts = stmt->getComputationLHSType().getTypePtr() != stmt->getComputationResultType().getTypePtr();
- if (!use_intermediate_casts && !result_used) {
- // simple common case, where the C and Zig are identical:
- // lhs >>= rhs
- AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue);
- if (lhs == nullptr) return nullptr;
-
- AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransRValue);
- if (rhs == nullptr) return nullptr;
- AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs);
-
- return trans_create_node_bin_op(c, lhs, assign_op, coerced_rhs);
- } else {
- // need more complexity. worst case, this looks like this:
- // c: lhs >>= rhs
- // zig: {
- // zig: const _ref = &lhs;
- // zig: *_ref = result_type(operation_type(*_ref) >> u5(rhs));
- // zig: *_ref
- // zig: }
- // where u5 is the appropriate type
-
- AstNode *child_block = trans_create_node(c, NodeTypeBlock);
-
- // const _ref = &lhs;
- AstNode *lhs = trans_expr(c, true, child_block, stmt->getLHS(), TransLValue);
- if (lhs == nullptr) return nullptr;
- AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs);
- // TODO: avoid name collisions with generated variable names
- Buf* tmp_var_name = buf_create_from_str("_ref");
- AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs);
- child_block->data.block.statements.append(tmp_var_decl);
-
- // *_ref = result_type(operation_type(*_ref) >> u5(rhs));
-
- AstNode *rhs = trans_expr(c, true, child_block, stmt->getRHS(), TransRValue);
- if (rhs == nullptr) return nullptr;
- AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs);
-
- AstNode *assign_statement = trans_create_node_bin_op(c,
- trans_create_node_prefix_op(c, PrefixOpDereference,
- trans_create_node_symbol(c, tmp_var_name)),
- BinOpTypeAssign,
- trans_c_cast(c, rhs_location,
- stmt->getComputationResultType(),
- trans_create_node_bin_op(c,
- trans_c_cast(c, rhs_location,
- stmt->getComputationLHSType(),
- trans_create_node_prefix_op(c, PrefixOpDereference,
- trans_create_node_symbol(c, tmp_var_name))),
- bin_op,
- coerced_rhs)));
- child_block->data.block.statements.append(assign_statement);
-
- if (result_used) {
- // *_ref
- child_block->data.block.statements.append(
- trans_create_node_prefix_op(c, PrefixOpDereference,
- trans_create_node_symbol(c, tmp_var_name)));
- child_block->data.block.last_statement_is_result_expression = true;
- }
-
- return child_block;
- }
-}
-
-static AstNode *trans_create_compound_assign(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) {
- if (!result_used) {
- // simple common case, where the C and Zig are identical:
- // lhs += rhs
- AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue);
- if (lhs == nullptr) return nullptr;
- AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransRValue);
- if (rhs == nullptr) return nullptr;
- return trans_create_node_bin_op(c, lhs, assign_op, rhs);
- } else {
- // need more complexity. worst case, this looks like this:
- // c: lhs += rhs
- // zig: {
- // zig: const _ref = &lhs;
- // zig: *_ref = *_ref + rhs;
- // zig: *_ref
- // zig: }
-
- AstNode *child_block = trans_create_node(c, NodeTypeBlock);
-
- // const _ref = &lhs;
- AstNode *lhs = trans_expr(c, true, child_block, stmt->getLHS(), TransLValue);
- if (lhs == nullptr) return nullptr;
- AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs);
- // TODO: avoid name collisions with generated variable names
- Buf* tmp_var_name = buf_create_from_str("_ref");
- AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs);
- child_block->data.block.statements.append(tmp_var_decl);
-
- // *_ref = *_ref + rhs;
-
- AstNode *rhs = trans_expr(c, true, child_block, stmt->getRHS(), TransRValue);
- if (rhs == nullptr) return nullptr;
-
- AstNode *assign_statement = trans_create_node_bin_op(c,
- trans_create_node_prefix_op(c, PrefixOpDereference,
- trans_create_node_symbol(c, tmp_var_name)),
- BinOpTypeAssign,
- trans_create_node_bin_op(c,
- trans_create_node_prefix_op(c, PrefixOpDereference,
- trans_create_node_symbol(c, tmp_var_name)),
- bin_op,
- rhs));
- child_block->data.block.statements.append(assign_statement);
-
- // *_ref
- child_block->data.block.statements.append(
- trans_create_node_prefix_op(c, PrefixOpDereference,
- trans_create_node_symbol(c, tmp_var_name)));
- child_block->data.block.last_statement_is_result_expression = true;
-
- return child_block;
- }
-}
-
-
-static AstNode *trans_compound_assign_operator(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt) {
- switch (stmt->getOpcode()) {
- case BO_MulAssign:
- if (qual_type_has_wrapping_overflow(c, stmt->getType()))
- return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignTimesWrap, BinOpTypeMultWrap);
- else
- return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignTimes, BinOpTypeMult);
- case BO_DivAssign:
- emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_DivAssign");
- return nullptr;
- case BO_RemAssign:
- emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_RemAssign");
- return nullptr;
- case BO_AddAssign:
- if (qual_type_has_wrapping_overflow(c, stmt->getType()))
- return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignPlusWrap, BinOpTypeAddWrap);
- else
- return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignPlus, BinOpTypeAdd);
- case BO_SubAssign:
- if (qual_type_has_wrapping_overflow(c, stmt->getType()))
- return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignMinusWrap, BinOpTypeSubWrap);
- else
- return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignMinus, BinOpTypeSub);
- case BO_ShlAssign:
- return trans_create_compound_assign_shift(c, result_used, block, stmt, BinOpTypeAssignBitShiftLeft, BinOpTypeBitShiftLeft);
- case BO_ShrAssign:
- return trans_create_compound_assign_shift(c, result_used, block, stmt, BinOpTypeAssignBitShiftRight, BinOpTypeBitShiftRight);
- case BO_AndAssign:
- return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitAnd, BinOpTypeBinAnd);
- case BO_XorAssign:
- return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitXor, BinOpTypeBinXor);
- case BO_OrAssign:
- return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitOr, BinOpTypeBinOr);
- case BO_PtrMemD:
- case BO_PtrMemI:
- case BO_Assign:
- case BO_Mul:
- case BO_Div:
- case BO_Rem:
- case BO_Add:
- case BO_Sub:
- case BO_Shl:
- case BO_Shr:
- case BO_LT:
- case BO_GT:
- case BO_LE:
- case BO_GE:
- case BO_EQ:
- case BO_NE:
- case BO_And:
- case BO_Xor:
- case BO_Or:
- case BO_LAnd:
- case BO_LOr:
- case BO_Comma:
- zig_unreachable();
- }
-
- zig_unreachable();
-}
-
-static AstNode *trans_implicit_cast_expr(Context *c, AstNode *block, ImplicitCastExpr *stmt) {
- switch (stmt->getCastKind()) {
- case CK_LValueToRValue:
- return trans_expr(c, true, block, stmt->getSubExpr(), TransRValue);
- case CK_IntegralCast:
- {
- AstNode *target_node = trans_expr(c, true, block, stmt->getSubExpr(), TransRValue);
- if (target_node == nullptr)
- return nullptr;
- return trans_c_cast(c, stmt->getExprLoc(), stmt->getType(), target_node);
- }
- case CK_FunctionToPointerDecay:
- case CK_ArrayToPointerDecay:
- {
- AstNode *target_node = trans_expr(c, true, block, stmt->getSubExpr(), TransRValue);
- if (target_node == nullptr)
- return nullptr;
- return target_node;
- }
- case CK_BitCast:
- {
- AstNode *target_node = trans_expr(c, true, block, stmt->getSubExpr(), TransRValue);
- if (target_node == nullptr)
- return nullptr;
-
- AstNode *dest_type_node = trans_qual_type(c, stmt->getType(), stmt->getLocStart());
-
- AstNode *node = trans_create_node_builtin_fn_call_str(c, "ptrCast");
- node->data.fn_call_expr.params.append(dest_type_node);
- node->data.fn_call_expr.params.append(target_node);
- return node;
- }
- case CK_NullToPointer:
- return trans_create_node(c, NodeTypeNullLiteral);
- case CK_Dependent:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_Dependent");
- return nullptr;
- case CK_LValueBitCast:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_LValueBitCast");
- return nullptr;
- case CK_NoOp:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NoOp");
- return nullptr;
- case CK_BaseToDerived:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BaseToDerived");
- return nullptr;
- case CK_DerivedToBase:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_DerivedToBase");
- return nullptr;
- case CK_UncheckedDerivedToBase:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_UncheckedDerivedToBase");
- return nullptr;
- case CK_Dynamic:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_Dynamic");
- return nullptr;
- case CK_ToUnion:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ToUnion");
- return nullptr;
- case CK_NullToMemberPointer:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NullToMemberPointer");
- return nullptr;
- case CK_BaseToDerivedMemberPointer:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BaseToDerivedMemberPointer");
- return nullptr;
- case CK_DerivedToBaseMemberPointer:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_DerivedToBaseMemberPointer");
- return nullptr;
- case CK_MemberPointerToBoolean:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_MemberPointerToBoolean");
- return nullptr;
- case CK_ReinterpretMemberPointer:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ReinterpretMemberPointer");
- return nullptr;
- case CK_UserDefinedConversion:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_UserDefinedConversion");
- return nullptr;
- case CK_ConstructorConversion:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ConstructorConversion");
- return nullptr;
- case CK_IntegralToPointer:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralToPointer");
- return nullptr;
- case CK_PointerToIntegral:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_PointerToIntegral");
- return nullptr;
- case CK_PointerToBoolean:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_PointerToBoolean");
- return nullptr;
- case CK_ToVoid:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ToVoid");
- return nullptr;
- case CK_VectorSplat:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_VectorSplat");
- return nullptr;
- case CK_IntegralToBoolean:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralToBoolean");
- return nullptr;
- case CK_IntegralToFloating:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralToFloating");
- return nullptr;
- case CK_FloatingToIntegral:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingToIntegral");
- return nullptr;
- case CK_FloatingToBoolean:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingToBoolean");
- return nullptr;
- case CK_BooleanToSignedIntegral:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BooleanToSignedIntegral");
- return nullptr;
- case CK_FloatingCast:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingCast");
- return nullptr;
- case CK_CPointerToObjCPointerCast:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_CPointerToObjCPointerCast");
- return nullptr;
- case CK_BlockPointerToObjCPointerCast:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BlockPointerToObjCPointerCast");
- return nullptr;
- case CK_AnyPointerToBlockPointerCast:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_AnyPointerToBlockPointerCast");
- return nullptr;
- case CK_ObjCObjectLValueCast:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ObjCObjectLValueCast");
- return nullptr;
- case CK_FloatingRealToComplex:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingRealToComplex");
- return nullptr;
- case CK_FloatingComplexToReal:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingComplexToReal");
- return nullptr;
- case CK_FloatingComplexToBoolean:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingComplexToBoolean");
- return nullptr;
- case CK_FloatingComplexCast:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingComplexCast");
- return nullptr;
- case CK_FloatingComplexToIntegralComplex:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingComplexToIntegralComplex");
- return nullptr;
- case CK_IntegralRealToComplex:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralRealToComplex");
- return nullptr;
- case CK_IntegralComplexToReal:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralComplexToReal");
- return nullptr;
- case CK_IntegralComplexToBoolean:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralComplexToBoolean");
- return nullptr;
- case CK_IntegralComplexCast:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralComplexCast");
- return nullptr;
- case CK_IntegralComplexToFloatingComplex:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralComplexToFloatingComplex");
- return nullptr;
- case CK_ARCProduceObject:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ARCProduceObject");
- return nullptr;
- case CK_ARCConsumeObject:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ARCConsumeObject");
- return nullptr;
- case CK_ARCReclaimReturnedObject:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ARCReclaimReturnedObject");
- return nullptr;
- case CK_ARCExtendBlockObject:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ARCExtendBlockObject");
- return nullptr;
- case CK_AtomicToNonAtomic:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_AtomicToNonAtomic");
- return nullptr;
- case CK_NonAtomicToAtomic:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NonAtomicToAtomic");
- return nullptr;
- case CK_CopyAndAutoreleaseBlockObject:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_CopyAndAutoreleaseBlockObject");
- return nullptr;
- case CK_BuiltinFnToFnPtr:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BuiltinFnToFnPtr");
- return nullptr;
- case CK_ZeroToOCLEvent:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ZeroToOCLEvent");
- return nullptr;
- case CK_ZeroToOCLQueue:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ZeroToOCLQueue");
- return nullptr;
- case CK_AddressSpaceConversion:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_AddressSpaceConversion");
- return nullptr;
- case CK_IntToOCLSampler:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntToOCLSampler");
- return nullptr;
- }
- zig_unreachable();
-}
-
-static AstNode *trans_decl_ref_expr(Context *c, DeclRefExpr *stmt, TransLRValue lrval) {
- ValueDecl *value_decl = stmt->getDecl();
- Buf *symbol_name = buf_create_from_str(decl_name(value_decl));
- if (lrval == TransLValue) {
- c->ptr_params.put(symbol_name, true);
- }
- return trans_create_node_symbol(c, symbol_name);
-}
-
-static AstNode *trans_create_post_crement(Context *c, bool result_used, AstNode *block, UnaryOperator *stmt, BinOpType assign_op) {
- Expr *op_expr = stmt->getSubExpr();
-
- if (!result_used) {
- // common case
- // c: expr++
- // zig: expr += 1
- return trans_create_node_bin_op(c,
- trans_expr(c, true, block, op_expr, TransLValue),
- assign_op,
- trans_create_node_unsigned(c, 1));
- } else {
- // worst case
- // c: expr++
- // zig: {
- // zig: const _ref = &expr;
- // zig: const _tmp = *_ref;
- // zig: *_ref += 1;
- // zig: _tmp
- // zig: }
- AstNode *child_block = trans_create_node(c, NodeTypeBlock);
-
- // const _ref = &expr;
- AstNode *expr = trans_expr(c, true, child_block, op_expr, TransLValue);
- if (expr == nullptr) return nullptr;
- AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr);
- // TODO: avoid name collisions with generated variable names
- Buf* ref_var_name = buf_create_from_str("_ref");
- AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr);
- child_block->data.block.statements.append(ref_var_decl);
-
- // const _tmp = *_ref;
- Buf* tmp_var_name = buf_create_from_str("_tmp");
- AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr,
- trans_create_node_prefix_op(c, PrefixOpDereference,
- trans_create_node_symbol(c, ref_var_name)));
- child_block->data.block.statements.append(tmp_var_decl);
-
- // *_ref += 1;
- AstNode *assign_statement = trans_create_node_bin_op(c,
- trans_create_node_prefix_op(c, PrefixOpDereference,
- trans_create_node_symbol(c, ref_var_name)),
- assign_op,
- trans_create_node_unsigned(c, 1));
- child_block->data.block.statements.append(assign_statement);
-
- // _tmp
- child_block->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name));
- child_block->data.block.last_statement_is_result_expression = true;
-
- return child_block;
- }
-}
-
-static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *block, UnaryOperator *stmt) {
- switch (stmt->getOpcode()) {
- case UO_PostInc:
- if (qual_type_has_wrapping_overflow(c, stmt->getType()))
- return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignPlusWrap);
- else
- return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignPlus);
- case UO_PostDec:
- if (qual_type_has_wrapping_overflow(c, stmt->getType()))
- return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignMinusWrap);
- else
- return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignMinus);
- case UO_PreInc:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_PreInc");
- return nullptr;
- case UO_PreDec:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_PreDec");
- return nullptr;
- case UO_AddrOf:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_AddrOf");
- return nullptr;
- case UO_Deref:
- {
- bool is_fn_ptr = qual_type_is_fn_ptr(c, stmt->getSubExpr()->getType());
- AstNode *value_node = trans_expr(c, result_used, block, stmt->getSubExpr(), TransRValue);
- if (is_fn_ptr)
- return value_node;
- AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, value_node);
- return trans_create_node_prefix_op(c, PrefixOpDereference, unwrapped);
- }
- case UO_Plus:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Plus");
- return nullptr;
- case UO_Minus:
- {
- Expr *op_expr = stmt->getSubExpr();
- if (!qual_type_has_wrapping_overflow(c, op_expr->getType())) {
- AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr);
- node->data.prefix_op_expr.prefix_op = PrefixOpNegation;
-
- node->data.prefix_op_expr.primary_expr = trans_expr(c, true, block, op_expr, TransRValue);
- if (node->data.prefix_op_expr.primary_expr == nullptr)
- return nullptr;
-
- return node;
- } else if (c_is_unsigned_integer(c, op_expr->getType())) {
- // we gotta emit 0 -% x
- AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
- node->data.bin_op_expr.op1 = trans_create_node_unsigned(c, 0);
-
- node->data.bin_op_expr.op2 = trans_expr(c, true, block, op_expr, TransRValue);
- if (node->data.bin_op_expr.op2 == nullptr)
- return nullptr;
-
- node->data.bin_op_expr.bin_op = BinOpTypeSubWrap;
- return node;
- } else {
- emit_warning(c, stmt->getLocStart(), "C negation with non float non integer");
- return nullptr;
- }
- }
- case UO_Not:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Not");
- return nullptr;
- case UO_LNot:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_LNot");
- return nullptr;
- case UO_Real:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Real");
- return nullptr;
- case UO_Imag:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Imag");
- return nullptr;
- case UO_Extension:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Extension");
- return nullptr;
- case UO_Coawait:
- emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Coawait");
- return nullptr;
- }
- zig_unreachable();
-}
-
-static AstNode *trans_local_declaration(Context *c, AstNode *block, DeclStmt *stmt) {
- for (auto iter = stmt->decl_begin(); iter != stmt->decl_end(); iter++) {
- Decl *decl = *iter;
- switch (decl->getKind()) {
- case Decl::Var: {
- VarDecl *var_decl = (VarDecl *)decl;
- QualType qual_type = var_decl->getTypeSourceInfo()->getType();
- AstNode *init_node = nullptr;
- if (var_decl->hasInit()) {
- init_node = trans_expr(c, true, block, var_decl->getInit(), TransRValue);
- if (init_node == nullptr)
- return nullptr;
-
- }
- AstNode *type_node = trans_qual_type(c, qual_type, stmt->getLocStart());
- if (type_node == nullptr)
- return nullptr;
-
- Buf *symbol_name = buf_create_from_str(decl_name(var_decl));
-
- AstNode *node = trans_create_node_var_decl_local(c, qual_type.isConstQualified(),
- symbol_name, type_node, init_node);
- block->data.block.statements.append(node);
- continue;
- }
- case Decl::AccessSpec:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind AccessSpec");
- return nullptr;
- case Decl::Block:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Block");
- return nullptr;
- case Decl::Captured:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Captured");
- return nullptr;
- case Decl::ClassScopeFunctionSpecialization:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassScopeFunctionSpecialization");
- return nullptr;
- case Decl::Empty:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Empty");
- return nullptr;
- case Decl::Export:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Export");
- return nullptr;
- case Decl::ExternCContext:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ExternCContext");
- return nullptr;
- case Decl::FileScopeAsm:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind FileScopeAsm");
- return nullptr;
- case Decl::Friend:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Friend");
- return nullptr;
- case Decl::FriendTemplate:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind FriendTemplate");
- return nullptr;
- case Decl::Import:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Import");
- return nullptr;
- case Decl::LinkageSpec:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind LinkageSpec");
- return nullptr;
- case Decl::Label:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Label");
- return nullptr;
- case Decl::Namespace:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Namespace");
- return nullptr;
- case Decl::NamespaceAlias:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind NamespaceAlias");
- return nullptr;
- case Decl::ObjCCompatibleAlias:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCCompatibleAlias");
- return nullptr;
- case Decl::ObjCCategory:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCCategory");
- return nullptr;
- case Decl::ObjCCategoryImpl:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCCategoryImpl");
- return nullptr;
- case Decl::ObjCImplementation:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCImplementation");
- return nullptr;
- case Decl::ObjCInterface:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCInterface");
- return nullptr;
- case Decl::ObjCProtocol:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCProtocol");
- return nullptr;
- case Decl::ObjCMethod:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCMethod");
- return nullptr;
- case Decl::ObjCProperty:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCProperty");
- return nullptr;
- case Decl::BuiltinTemplate:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind BuiltinTemplate");
- return nullptr;
- case Decl::ClassTemplate:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassTemplate");
- return nullptr;
- case Decl::FunctionTemplate:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind FunctionTemplate");
- return nullptr;
- case Decl::TypeAliasTemplate:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TypeAliasTemplate");
- return nullptr;
- case Decl::VarTemplate:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind VarTemplate");
- return nullptr;
- case Decl::TemplateTemplateParm:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TemplateTemplateParm");
- return nullptr;
- case Decl::Enum:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Enum");
- return nullptr;
- case Decl::Record:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Record");
- return nullptr;
- case Decl::CXXRecord:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXRecord");
- return nullptr;
- case Decl::ClassTemplateSpecialization:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassTemplateSpecialization");
- return nullptr;
- case Decl::ClassTemplatePartialSpecialization:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassTemplatePartialSpecialization");
- return nullptr;
- case Decl::TemplateTypeParm:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TemplateTypeParm");
- return nullptr;
- case Decl::ObjCTypeParam:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCTypeParam");
- return nullptr;
- case Decl::TypeAlias:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TypeAlias");
- return nullptr;
- case Decl::Typedef:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Typedef");
- return nullptr;
- case Decl::UnresolvedUsingTypename:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UnresolvedUsingTypename");
- return nullptr;
- case Decl::Using:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Using");
- return nullptr;
- case Decl::UsingDirective:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UsingDirective");
- return nullptr;
- case Decl::UsingPack:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UsingPack");
- return nullptr;
- case Decl::UsingShadow:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UsingShadow");
- return nullptr;
- case Decl::ConstructorUsingShadow:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ConstructorUsingShadow");
- return nullptr;
- case Decl::Binding:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Binding");
- return nullptr;
- case Decl::Field:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Field");
- return nullptr;
- case Decl::ObjCAtDefsField:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCAtDefsField");
- return nullptr;
- case Decl::ObjCIvar:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCIvar");
- return nullptr;
- case Decl::Function:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Function");
- return nullptr;
- case Decl::CXXDeductionGuide:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXDeductionGuide");
- return nullptr;
- case Decl::CXXMethod:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXMethod");
- return nullptr;
- case Decl::CXXConstructor:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXConstructor");
- return nullptr;
- case Decl::CXXConversion:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXConversion");
- return nullptr;
- case Decl::CXXDestructor:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXDestructor");
- return nullptr;
- case Decl::MSProperty:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind MSProperty");
- return nullptr;
- case Decl::NonTypeTemplateParm:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind NonTypeTemplateParm");
- return nullptr;
- case Decl::Decomposition:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Decomposition");
- return nullptr;
- case Decl::ImplicitParam:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ImplicitParam");
- return nullptr;
- case Decl::OMPCapturedExpr:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind OMPCapturedExpr");
- return nullptr;
- case Decl::ParmVar:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ParmVar");
- return nullptr;
- case Decl::VarTemplateSpecialization:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind VarTemplateSpecialization");
- return nullptr;
- case Decl::VarTemplatePartialSpecialization:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind VarTemplatePartialSpecialization");
- return nullptr;
- case Decl::EnumConstant:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind EnumConstant");
- return nullptr;
- case Decl::IndirectField:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind IndirectField");
- return nullptr;
- case Decl::OMPDeclareReduction:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind OMPDeclareReduction");
- return nullptr;
- case Decl::UnresolvedUsingValue:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UnresolvedUsingValue");
- return nullptr;
- case Decl::OMPThreadPrivate:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind OMPThreadPrivate");
- return nullptr;
- case Decl::ObjCPropertyImpl:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCPropertyImpl");
- return nullptr;
- case Decl::PragmaComment:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind PragmaComment");
- return nullptr;
- case Decl::PragmaDetectMismatch:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind PragmaDetectMismatch");
- return nullptr;
- case Decl::StaticAssert:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind StaticAssert");
- return nullptr;
- case Decl::TranslationUnit:
- emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TranslationUnit");
- return nullptr;
- }
- zig_unreachable();
- }
-
- // declarations were already added
- return skip_add_to_block_node;
-}
-
-static AstNode *trans_while_loop(Context *c, AstNode *block, WhileStmt *stmt) {
- AstNode *while_node = trans_create_node(c, NodeTypeWhileExpr);
-
- while_node->data.while_expr.condition = trans_expr(c, true, block, stmt->getCond(), TransRValue);
- if (while_node->data.while_expr.condition == nullptr)
- return nullptr;
-
- while_node->data.while_expr.body = trans_stmt(c, false, block, stmt->getBody(), TransRValue);
- if (while_node->data.while_expr.body == nullptr)
- return nullptr;
-
- return while_node;
-}
-
-static AstNode *trans_if_statement(Context *c, AstNode *block, IfStmt *stmt) {
- // if (c) t
- // if (c) t else e
- AstNode *if_node = trans_create_node(c, NodeTypeIfBoolExpr);
-
- // TODO: condition != 0
- AstNode *condition_node = trans_expr(c, true, block, stmt->getCond(), TransRValue);
- if (condition_node == nullptr)
- return nullptr;
- if_node->data.if_bool_expr.condition = condition_node;
-
- if_node->data.if_bool_expr.then_block = trans_stmt(c, false, block, stmt->getThen(), TransRValue);
- if (if_node->data.if_bool_expr.then_block == nullptr)
- return nullptr;
-
- if (stmt->getElse() != nullptr) {
- if_node->data.if_bool_expr.else_node = trans_stmt(c, false, block, stmt->getElse(), TransRValue);
- if (if_node->data.if_bool_expr.else_node == nullptr)
- return nullptr;
- }
-
- return if_node;
-}
-
-static AstNode *trans_call_expr(Context *c, bool result_used, AstNode *block, CallExpr *stmt) {
- AstNode *node = trans_create_node(c, NodeTypeFnCallExpr);
-
- AstNode *callee_raw_node = trans_expr(c, true, block, stmt->getCallee(), TransRValue);
- if (callee_raw_node == nullptr)
- return nullptr;
-
- AstNode *callee_node;
- if (qual_type_is_fn_ptr(c, stmt->getCallee()->getType())) {
- callee_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, callee_raw_node);
- } else {
- callee_node = callee_raw_node;
- }
-
- node->data.fn_call_expr.fn_ref_expr = callee_node;
-
- unsigned num_args = stmt->getNumArgs();
- Expr **args = stmt->getArgs();
- for (unsigned i = 0; i < num_args; i += 1) {
- AstNode *arg_node = trans_expr(c, true, block, args[i], TransRValue);
- if (arg_node == nullptr)
- return nullptr;
-
- node->data.fn_call_expr.params.append(arg_node);
- }
-
- return node;
-}
-
-static AstNode *trans_member_expr(Context *c, AstNode *block, MemberExpr *stmt) {
- AstNode *container_node = trans_expr(c, true, block, stmt->getBase(), TransRValue);
- if (container_node == nullptr)
- return nullptr;
-
- if (stmt->isArrow()) {
- container_node = trans_create_node_unwrap_null(c, container_node);
- }
-
- const char *name = decl_name(stmt->getMemberDecl());
-
- AstNode *node = trans_create_node_field_access_str(c, container_node, name);
- return node;
-}
-
-static AstNode *trans_array_subscript_expr(Context *c, AstNode *block, ArraySubscriptExpr *stmt) {
- AstNode *container_node = trans_expr(c, true, block, stmt->getBase(), TransRValue);
- if (container_node == nullptr)
- return nullptr;
-
- AstNode *idx_node = trans_expr(c, true, block, stmt->getIdx(), TransRValue);
- if (idx_node == nullptr)
- return nullptr;
-
-
- AstNode *node = trans_create_node(c, NodeTypeArrayAccessExpr);
- node->data.array_access_expr.array_ref_expr = container_node;
- node->data.array_access_expr.subscript = idx_node;
- return node;
-}
-
-static AstNode *trans_c_style_cast_expr(Context *c, bool result_used, AstNode *block,
- CStyleCastExpr *stmt, TransLRValue lrvalue)
-{
- AstNode *sub_expr_node = trans_expr(c, result_used, block, stmt->getSubExpr(), lrvalue);
- if (sub_expr_node == nullptr)
- return nullptr;
-
- return trans_c_cast(c, stmt->getLocStart(), stmt->getType(), sub_expr_node);
-}
-
-static AstNode *trans_unary_expr_or_type_trait_expr(Context *c, AstNode *block, UnaryExprOrTypeTraitExpr *stmt) {
- AstNode *type_node = trans_qual_type(c, stmt->getTypeOfArgument(), stmt->getLocStart());
- if (type_node == nullptr)
- return nullptr;
-
- AstNode *node = trans_create_node_builtin_fn_call_str(c, "sizeOf");
- node->data.fn_call_expr.params.append(type_node);
- return node;
-}
-
-static AstNode *trans_do_loop(Context *c, AstNode *block, DoStmt *stmt) {
- stmt->getBody();
- stmt->getCond();
-
- AstNode *while_node = trans_create_node(c, NodeTypeWhileExpr);
-
- AstNode *true_node = trans_create_node(c, NodeTypeBoolLiteral);
- true_node->data.bool_literal.value = true;
- while_node->data.while_expr.condition = true_node;
-
- AstNode *body_node;
- if (stmt->getBody()->getStmtClass() == Stmt::CompoundStmtClass) {
- // 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: }
- body_node = trans_stmt(c, false, block, stmt->getBody(), TransRValue);
- if (body_node == nullptr) return nullptr;
- assert(body_node->type == NodeTypeBlock);
- } else {
- // 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: }
- body_node = trans_create_node(c, NodeTypeBlock);
- AstNode *child_statement = trans_stmt(c, false, body_node, stmt->getBody(), TransRValue);
- if (child_statement == nullptr) return nullptr;
- body_node->data.block.statements.append(child_statement);
- }
-
- // if (!cond) break;
- AstNode *condition_node = trans_expr(c, true, body_node, stmt->getCond(), TransRValue);
- if (condition_node == nullptr) return nullptr;
- AstNode *terminator_node = trans_create_node(c, NodeTypeIfBoolExpr);
- terminator_node->data.if_bool_expr.condition = trans_create_node_prefix_op(c, PrefixOpBoolNot, condition_node);
- terminator_node->data.if_bool_expr.then_block = trans_create_node(c, NodeTypeBreak);
- body_node->data.block.statements.append(terminator_node);
-
- while_node->data.while_expr.body = body_node;
-
- return while_node;
-}
-
-static AstNode *trans_stmt(Context *c, bool result_used, AstNode *block, Stmt *stmt, TransLRValue lrvalue) {
- Stmt::StmtClass sc = stmt->getStmtClass();
- switch (sc) {
- case Stmt::ReturnStmtClass:
- return trans_return_stmt(c, block, (ReturnStmt *)stmt);
- case Stmt::CompoundStmtClass:
- return trans_compound_stmt(c, block, (CompoundStmt *)stmt);
- case Stmt::IntegerLiteralClass:
- return trans_integer_literal(c, (IntegerLiteral *)stmt);
- case Stmt::ConditionalOperatorClass:
- return trans_conditional_operator(c, result_used, block, (ConditionalOperator *)stmt);
- case Stmt::BinaryOperatorClass:
- return trans_binary_operator(c, result_used, block, (BinaryOperator *)stmt);
- case Stmt::CompoundAssignOperatorClass:
- return trans_compound_assign_operator(c, result_used, block, (CompoundAssignOperator *)stmt);
- case Stmt::ImplicitCastExprClass:
- return trans_implicit_cast_expr(c, block, (ImplicitCastExpr *)stmt);
- case Stmt::DeclRefExprClass:
- return trans_decl_ref_expr(c, (DeclRefExpr *)stmt, lrvalue);
- case Stmt::UnaryOperatorClass:
- return trans_unary_operator(c, result_used, block, (UnaryOperator *)stmt);
- case Stmt::DeclStmtClass:
- return trans_local_declaration(c, block, (DeclStmt *)stmt);
- case Stmt::WhileStmtClass:
- return trans_while_loop(c, block, (WhileStmt *)stmt);
- case Stmt::IfStmtClass:
- return trans_if_statement(c, block, (IfStmt *)stmt);
- case Stmt::CallExprClass:
- return trans_call_expr(c, result_used, block, (CallExpr *)stmt);
- case Stmt::NullStmtClass:
- return skip_add_to_block_node;
- case Stmt::MemberExprClass:
- return trans_member_expr(c, block, (MemberExpr *)stmt);
- case Stmt::ArraySubscriptExprClass:
- return trans_array_subscript_expr(c, block, (ArraySubscriptExpr *)stmt);
- case Stmt::CStyleCastExprClass:
- return trans_c_style_cast_expr(c, result_used, block, (CStyleCastExpr *)stmt, lrvalue);
- case Stmt::UnaryExprOrTypeTraitExprClass:
- return trans_unary_expr_or_type_trait_expr(c, block, (UnaryExprOrTypeTraitExpr *)stmt);
- case Stmt::DoStmtClass:
- return trans_do_loop(c, block, (DoStmt *)stmt);
- case Stmt::CaseStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CaseStmtClass");
- return nullptr;
- case Stmt::DefaultStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C DefaultStmtClass");
- return nullptr;
- case Stmt::SwitchStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C SwitchStmtClass");
- return nullptr;
- case Stmt::NoStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C NoStmtClass");
- return nullptr;
- case Stmt::GCCAsmStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C GCCAsmStmtClass");
- return nullptr;
- case Stmt::MSAsmStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C MSAsmStmtClass");
- return nullptr;
- case Stmt::AttributedStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C AttributedStmtClass");
- return nullptr;
- case Stmt::BreakStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C BreakStmtClass");
- return nullptr;
- case Stmt::CXXCatchStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXCatchStmtClass");
- return nullptr;
- case Stmt::CXXForRangeStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXForRangeStmtClass");
- return nullptr;
- case Stmt::CXXTryStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTryStmtClass");
- return nullptr;
- case Stmt::CapturedStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CapturedStmtClass");
- return nullptr;
- case Stmt::ContinueStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ContinueStmtClass");
- return nullptr;
- case Stmt::CoreturnStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CoreturnStmtClass");
- return nullptr;
- case Stmt::CoroutineBodyStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CoroutineBodyStmtClass");
- return nullptr;
- case Stmt::BinaryConditionalOperatorClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C BinaryConditionalOperatorClass");
- return nullptr;
- case Stmt::AddrLabelExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C AddrLabelExprClass");
- return nullptr;
- case Stmt::ArrayInitIndexExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayInitIndexExprClass");
- return nullptr;
- case Stmt::ArrayInitLoopExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayInitLoopExprClass");
- return nullptr;
- case Stmt::ArrayTypeTraitExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayTypeTraitExprClass");
- return nullptr;
- case Stmt::AsTypeExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C AsTypeExprClass");
- return nullptr;
- case Stmt::AtomicExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C AtomicExprClass");
- return nullptr;
- case Stmt::BlockExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C BlockExprClass");
- return nullptr;
- case Stmt::CXXBindTemporaryExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXBindTemporaryExprClass");
- return nullptr;
- case Stmt::CXXBoolLiteralExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXBoolLiteralExprClass");
- return nullptr;
- case Stmt::CXXConstructExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXConstructExprClass");
- return nullptr;
- case Stmt::CXXTemporaryObjectExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTemporaryObjectExprClass");
- return nullptr;
- case Stmt::CXXDefaultArgExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDefaultArgExprClass");
- return nullptr;
- case Stmt::CXXDefaultInitExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDefaultInitExprClass");
- return nullptr;
- case Stmt::CXXDeleteExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDeleteExprClass");
- return nullptr;
- case Stmt::CXXDependentScopeMemberExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDependentScopeMemberExprClass");
- return nullptr;
- case Stmt::CXXFoldExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXFoldExprClass");
- return nullptr;
- case Stmt::CXXInheritedCtorInitExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXInheritedCtorInitExprClass");
- return nullptr;
- case Stmt::CXXNewExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNewExprClass");
- return nullptr;
- case Stmt::CXXNoexceptExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNoexceptExprClass");
- return nullptr;
- case Stmt::CXXNullPtrLiteralExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNullPtrLiteralExprClass");
- return nullptr;
- case Stmt::CXXPseudoDestructorExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXPseudoDestructorExprClass");
- return nullptr;
- case Stmt::CXXScalarValueInitExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXScalarValueInitExprClass");
- return nullptr;
- case Stmt::CXXStdInitializerListExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXStdInitializerListExprClass");
- return nullptr;
- case Stmt::CXXThisExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXThisExprClass");
- return nullptr;
- case Stmt::CXXThrowExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXThrowExprClass");
- return nullptr;
- case Stmt::CXXTypeidExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTypeidExprClass");
- return nullptr;
- case Stmt::CXXUnresolvedConstructExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXUnresolvedConstructExprClass");
- return nullptr;
- case Stmt::CXXUuidofExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXUuidofExprClass");
- return nullptr;
- case Stmt::CUDAKernelCallExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CUDAKernelCallExprClass");
- return nullptr;
- case Stmt::CXXMemberCallExprClass:
- (void)result_used;
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXMemberCallExprClass");
- return nullptr;
- case Stmt::CXXOperatorCallExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXOperatorCallExprClass");
- return nullptr;
- case Stmt::UserDefinedLiteralClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C UserDefinedLiteralClass");
- return nullptr;
- case Stmt::CXXFunctionalCastExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXFunctionalCastExprClass");
- return nullptr;
- case Stmt::CXXConstCastExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXConstCastExprClass");
- return nullptr;
- case Stmt::CXXDynamicCastExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDynamicCastExprClass");
- return nullptr;
- case Stmt::CXXReinterpretCastExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXReinterpretCastExprClass");
- return nullptr;
- case Stmt::CXXStaticCastExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CXXStaticCastExprClass");
- return nullptr;
- case Stmt::ObjCBridgedCastExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBridgedCastExprClass");
- return nullptr;
- case Stmt::CharacterLiteralClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CharacterLiteralClass");
- return nullptr;
- case Stmt::ChooseExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ChooseExprClass");
- return nullptr;
- case Stmt::CompoundLiteralExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CompoundLiteralExprClass");
- return nullptr;
- case Stmt::ConvertVectorExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ConvertVectorExprClass");
- return nullptr;
- case Stmt::CoawaitExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CoawaitExprClass");
- return nullptr;
- case Stmt::CoyieldExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C CoyieldExprClass");
- return nullptr;
- case Stmt::DependentCoawaitExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C DependentCoawaitExprClass");
- return nullptr;
- case Stmt::DependentScopeDeclRefExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C DependentScopeDeclRefExprClass");
- return nullptr;
- case Stmt::DesignatedInitExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C DesignatedInitExprClass");
- return nullptr;
- case Stmt::DesignatedInitUpdateExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C DesignatedInitUpdateExprClass");
- return nullptr;
- case Stmt::ExprWithCleanupsClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ExprWithCleanupsClass");
- return nullptr;
- case Stmt::ExpressionTraitExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ExpressionTraitExprClass");
- return nullptr;
- case Stmt::ExtVectorElementExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ExtVectorElementExprClass");
- return nullptr;
- case Stmt::FloatingLiteralClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C FloatingLiteralClass");
- return nullptr;
- case Stmt::FunctionParmPackExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C FunctionParmPackExprClass");
- return nullptr;
- case Stmt::GNUNullExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C GNUNullExprClass");
- return nullptr;
- case Stmt::GenericSelectionExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C GenericSelectionExprClass");
- return nullptr;
- case Stmt::ImaginaryLiteralClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ImaginaryLiteralClass");
- return nullptr;
- case Stmt::ImplicitValueInitExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ImplicitValueInitExprClass");
- return nullptr;
- case Stmt::InitListExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C InitListExprClass");
- return nullptr;
- case Stmt::LambdaExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C LambdaExprClass");
- return nullptr;
- case Stmt::MSPropertyRefExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C MSPropertyRefExprClass");
- return nullptr;
- case Stmt::MSPropertySubscriptExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C MSPropertySubscriptExprClass");
- return nullptr;
- case Stmt::MaterializeTemporaryExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C MaterializeTemporaryExprClass");
- return nullptr;
- case Stmt::NoInitExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C NoInitExprClass");
- return nullptr;
- case Stmt::OMPArraySectionExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPArraySectionExprClass");
- return nullptr;
- case Stmt::ObjCArrayLiteralClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCArrayLiteralClass");
- return nullptr;
- case Stmt::ObjCAvailabilityCheckExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAvailabilityCheckExprClass");
- return nullptr;
- case Stmt::ObjCBoolLiteralExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBoolLiteralExprClass");
- return nullptr;
- case Stmt::ObjCBoxedExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBoxedExprClass");
- return nullptr;
- case Stmt::ObjCDictionaryLiteralClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCDictionaryLiteralClass");
- return nullptr;
- case Stmt::ObjCEncodeExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCEncodeExprClass");
- return nullptr;
- case Stmt::ObjCIndirectCopyRestoreExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIndirectCopyRestoreExprClass");
- return nullptr;
- case Stmt::ObjCIsaExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIsaExprClass");
- return nullptr;
- case Stmt::ObjCIvarRefExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIvarRefExprClass");
- return nullptr;
- case Stmt::ObjCMessageExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCMessageExprClass");
- return nullptr;
- case Stmt::ObjCPropertyRefExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCPropertyRefExprClass");
- return nullptr;
- case Stmt::ObjCProtocolExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCProtocolExprClass");
- return nullptr;
- case Stmt::ObjCSelectorExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCSelectorExprClass");
- return nullptr;
- case Stmt::ObjCStringLiteralClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCStringLiteralClass");
- return nullptr;
- case Stmt::ObjCSubscriptRefExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCSubscriptRefExprClass");
- return nullptr;
- case Stmt::OffsetOfExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OffsetOfExprClass");
- return nullptr;
- case Stmt::OpaqueValueExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OpaqueValueExprClass");
- return nullptr;
- case Stmt::UnresolvedLookupExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C UnresolvedLookupExprClass");
- return nullptr;
- case Stmt::UnresolvedMemberExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C UnresolvedMemberExprClass");
- return nullptr;
- case Stmt::PackExpansionExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C PackExpansionExprClass");
- return nullptr;
- case Stmt::ParenExprClass:
- return trans_expr(c, result_used, block, ((ParenExpr*)stmt)->getSubExpr(), lrvalue);
- case Stmt::ParenListExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ParenListExprClass");
- return nullptr;
- case Stmt::PredefinedExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C PredefinedExprClass");
- return nullptr;
- case Stmt::PseudoObjectExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C PseudoObjectExprClass");
- return nullptr;
- case Stmt::ShuffleVectorExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ShuffleVectorExprClass");
- return nullptr;
- case Stmt::SizeOfPackExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C SizeOfPackExprClass");
- return nullptr;
- case Stmt::StmtExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C StmtExprClass");
- return nullptr;
- case Stmt::StringLiteralClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C StringLiteralClass");
- return nullptr;
- case Stmt::SubstNonTypeTemplateParmExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C SubstNonTypeTemplateParmExprClass");
- return nullptr;
- case Stmt::SubstNonTypeTemplateParmPackExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C SubstNonTypeTemplateParmPackExprClass");
- return nullptr;
- case Stmt::TypeTraitExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C TypeTraitExprClass");
- return nullptr;
- case Stmt::TypoExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C TypoExprClass");
- return nullptr;
- case Stmt::VAArgExprClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C VAArgExprClass");
- return nullptr;
- case Stmt::ForStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ForStmtClass");
- return nullptr;
- case Stmt::GotoStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C GotoStmtClass");
- return nullptr;
- case Stmt::IndirectGotoStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C IndirectGotoStmtClass");
- return nullptr;
- case Stmt::LabelStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C LabelStmtClass");
- return nullptr;
- case Stmt::MSDependentExistsStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C MSDependentExistsStmtClass");
- return nullptr;
- case Stmt::OMPAtomicDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPAtomicDirectiveClass");
- return nullptr;
- case Stmt::OMPBarrierDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPBarrierDirectiveClass");
- return nullptr;
- case Stmt::OMPCancelDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCancelDirectiveClass");
- return nullptr;
- case Stmt::OMPCancellationPointDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCancellationPointDirectiveClass");
- return nullptr;
- case Stmt::OMPCriticalDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCriticalDirectiveClass");
- return nullptr;
- case Stmt::OMPFlushDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPFlushDirectiveClass");
- return nullptr;
- case Stmt::OMPDistributeDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeDirectiveClass");
- return nullptr;
- case Stmt::OMPDistributeParallelForDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeParallelForDirectiveClass");
- return nullptr;
- case Stmt::OMPDistributeParallelForSimdDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeParallelForSimdDirectiveClass");
- return nullptr;
- case Stmt::OMPDistributeSimdDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeSimdDirectiveClass");
- return nullptr;
- case Stmt::OMPForDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPForDirectiveClass");
- return nullptr;
- case Stmt::OMPForSimdDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPForSimdDirectiveClass");
- return nullptr;
- case Stmt::OMPParallelForDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelForDirectiveClass");
- return nullptr;
- case Stmt::OMPParallelForSimdDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelForSimdDirectiveClass");
- return nullptr;
- case Stmt::OMPSimdDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSimdDirectiveClass");
- return nullptr;
- case Stmt::OMPTargetParallelForSimdDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelForSimdDirectiveClass");
- return nullptr;
- case Stmt::OMPTargetSimdDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetSimdDirectiveClass");
- return nullptr;
- case Stmt::OMPTargetTeamsDistributeDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeDirectiveClass");
- return nullptr;
- case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeParallelForDirectiveClass");
- return nullptr;
- case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeParallelForSimdDirectiveClass");
- return nullptr;
- case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeSimdDirectiveClass");
- return nullptr;
- case Stmt::OMPTaskLoopDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskLoopDirectiveClass");
- return nullptr;
- case Stmt::OMPTaskLoopSimdDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskLoopSimdDirectiveClass");
- return nullptr;
- case Stmt::OMPTeamsDistributeDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeDirectiveClass");
- return nullptr;
- case Stmt::OMPTeamsDistributeParallelForDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeParallelForDirectiveClass");
- return nullptr;
- case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeParallelForSimdDirectiveClass");
- return nullptr;
- case Stmt::OMPTeamsDistributeSimdDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeSimdDirectiveClass");
- return nullptr;
- case Stmt::OMPMasterDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPMasterDirectiveClass");
- return nullptr;
- case Stmt::OMPOrderedDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPOrderedDirectiveClass");
- return nullptr;
- case Stmt::OMPParallelDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelDirectiveClass");
- return nullptr;
- case Stmt::OMPParallelSectionsDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelSectionsDirectiveClass");
- return nullptr;
- case Stmt::OMPSectionDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSectionDirectiveClass");
- return nullptr;
- case Stmt::OMPSectionsDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSectionsDirectiveClass");
- return nullptr;
- case Stmt::OMPSingleDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSingleDirectiveClass");
- return nullptr;
- case Stmt::OMPTargetDataDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetDataDirectiveClass");
- return nullptr;
- case Stmt::OMPTargetDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetDirectiveClass");
- return nullptr;
- case Stmt::OMPTargetEnterDataDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetEnterDataDirectiveClass");
- return nullptr;
- case Stmt::OMPTargetExitDataDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetExitDataDirectiveClass");
- return nullptr;
- case Stmt::OMPTargetParallelDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelDirectiveClass");
- return nullptr;
- case Stmt::OMPTargetParallelForDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelForDirectiveClass");
- return nullptr;
- case Stmt::OMPTargetTeamsDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDirectiveClass");
- return nullptr;
- case Stmt::OMPTargetUpdateDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetUpdateDirectiveClass");
- return nullptr;
- case Stmt::OMPTaskDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskDirectiveClass");
- return nullptr;
- case Stmt::OMPTaskgroupDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskgroupDirectiveClass");
- return nullptr;
- case Stmt::OMPTaskwaitDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskwaitDirectiveClass");
- return nullptr;
- case Stmt::OMPTaskyieldDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskyieldDirectiveClass");
- return nullptr;
- case Stmt::OMPTeamsDirectiveClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDirectiveClass");
- return nullptr;
- case Stmt::ObjCAtCatchStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtCatchStmtClass");
- return nullptr;
- case Stmt::ObjCAtFinallyStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtFinallyStmtClass");
- return nullptr;
- case Stmt::ObjCAtSynchronizedStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtSynchronizedStmtClass");
- return nullptr;
- case Stmt::ObjCAtThrowStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtThrowStmtClass");
- return nullptr;
- case Stmt::ObjCAtTryStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtTryStmtClass");
- return nullptr;
- case Stmt::ObjCAutoreleasePoolStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAutoreleasePoolStmtClass");
- return nullptr;
- case Stmt::ObjCForCollectionStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCForCollectionStmtClass");
- return nullptr;
- case Stmt::SEHExceptStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C SEHExceptStmtClass");
- return nullptr;
- case Stmt::SEHFinallyStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C SEHFinallyStmtClass");
- return nullptr;
- case Stmt::SEHLeaveStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C SEHLeaveStmtClass");
- return nullptr;
- case Stmt::SEHTryStmtClass:
- emit_warning(c, stmt->getLocStart(), "TODO handle C SEHTryStmtClass");
- return nullptr;
- }
- zig_unreachable();
-}
-
-static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
- Buf *fn_name = buf_create_from_str(decl_name(fn_decl));
-
- if (get_global(c, fn_name)) {
- // we already saw this function
- return;
- }
-
- AstNode *proto_node = trans_qual_type(c, fn_decl->getType(), fn_decl->getLocation());
- if (proto_node == nullptr) {
- emit_warning(c, fn_decl->getLocation(), "unable to resolve prototype of function '%s'", buf_ptr(fn_name));
- return;
- }
-
- proto_node->data.fn_proto.name = fn_name;
- proto_node->data.fn_proto.is_extern = !fn_decl->hasBody();
-
- StorageClass sc = fn_decl->getStorageClass();
- if (sc == SC_None) {
- proto_node->data.fn_proto.visib_mod = fn_decl->hasBody() ? c->export_visib_mod : c->visib_mod;
- } else if (sc == SC_Extern || sc == SC_Static) {
- proto_node->data.fn_proto.visib_mod = c->visib_mod;
- } else if (sc == SC_PrivateExtern) {
- emit_warning(c, fn_decl->getLocation(), "unsupported storage class: private extern");
- return;
- } else {
- emit_warning(c, fn_decl->getLocation(), "unsupported storage class: unknown");
- return;
- }
-
- for (size_t i = 0; i < proto_node->data.fn_proto.params.length; i += 1) {
- AstNode *param_node = proto_node->data.fn_proto.params.at(i);
- const ParmVarDecl *param = fn_decl->getParamDecl(i);
- const char *name = decl_name(param);
- Buf *proto_param_name;
- if (strlen(name) != 0) {
- proto_param_name = buf_create_from_str(name);
- } else {
- proto_param_name = param_node->data.param_decl.name;
- if (proto_param_name == nullptr) {
- proto_param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i);
- }
- }
- param_node->data.param_decl.name = proto_param_name;
- }
-
- if (!fn_decl->hasBody()) {
- // just a prototype
- add_top_level_decl(c, proto_node->data.fn_proto.name, proto_node);
- return;
- }
-
- // actual function definition with body
- c->ptr_params.clear();
- Stmt *body = fn_decl->getBody();
- AstNode *actual_body_node = trans_stmt(c, false, nullptr, body, TransRValue);
- assert(actual_body_node != skip_add_to_block_node);
- if (actual_body_node == nullptr) {
- emit_warning(c, fn_decl->getLocation(), "unable to translate function");
- return;
- }
-
- // it worked
-
- assert(actual_body_node->type == NodeTypeBlock);
- AstNode *body_node_with_param_inits = trans_create_node(c, NodeTypeBlock);
-
- for (size_t i = 0; i < proto_node->data.fn_proto.params.length; i += 1) {
- AstNode *param_node = proto_node->data.fn_proto.params.at(i);
- Buf *good_name = param_node->data.param_decl.name;
-
- if (c->ptr_params.maybe_get(good_name) != nullptr) {
- // TODO: avoid name collisions
- Buf *mangled_name = buf_sprintf("_arg_%s", buf_ptr(good_name));
- param_node->data.param_decl.name = mangled_name;
-
- // var c_name = _mangled_name;
- AstNode *parameter_init = trans_create_node_var_decl_local(c, false, good_name, nullptr, trans_create_node_symbol(c, mangled_name));
-
- body_node_with_param_inits->data.block.statements.append(parameter_init);
- }
- }
-
- for (size_t i = 0; i < actual_body_node->data.block.statements.length; i += 1) {
- body_node_with_param_inits->data.block.statements.append(actual_body_node->data.block.statements.at(i));
- }
-
- AstNode *fn_def_node = trans_create_node(c, NodeTypeFnDef);
- fn_def_node->data.fn_def.fn_proto = proto_node;
- fn_def_node->data.fn_def.body = body_node_with_param_inits;
-
- proto_node->data.fn_proto.fn_def_node = fn_def_node;
- add_top_level_decl(c, fn_def_node->data.fn_def.fn_proto->data.fn_proto.name, fn_def_node);
-}
-
-static AstNode *resolve_typdef_as_builtin(Context *c, const TypedefNameDecl *typedef_decl, const char *primitive_name) {
- AstNode *node = trans_create_node_symbol_str(c, primitive_name);
- c->decl_table.put(typedef_decl, node);
- return node;
-}
-
-static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_decl) {
- auto existing_entry = c->decl_table.maybe_get((void*)typedef_decl->getCanonicalDecl());
- if (existing_entry) {
- return existing_entry->value;
- }
- QualType child_qt = typedef_decl->getUnderlyingType();
- Buf *type_name = buf_create_from_str(decl_name(typedef_decl));
-
- if (buf_eql_str(type_name, "uint8_t")) {
- return resolve_typdef_as_builtin(c, typedef_decl, "u8");
- } else if (buf_eql_str(type_name, "int8_t")) {
- return resolve_typdef_as_builtin(c, typedef_decl, "i8");
- } else if (buf_eql_str(type_name, "uint16_t")) {
- return resolve_typdef_as_builtin(c, typedef_decl, "u16");
- } else if (buf_eql_str(type_name, "int16_t")) {
- return resolve_typdef_as_builtin(c, typedef_decl, "i16");
- } else if (buf_eql_str(type_name, "uint32_t")) {
- return resolve_typdef_as_builtin(c, typedef_decl, "u32");
- } else if (buf_eql_str(type_name, "int32_t")) {
- return resolve_typdef_as_builtin(c, typedef_decl, "i32");
- } else if (buf_eql_str(type_name, "uint64_t")) {
- return resolve_typdef_as_builtin(c, typedef_decl, "u64");
- } else if (buf_eql_str(type_name, "int64_t")) {
- return resolve_typdef_as_builtin(c, typedef_decl, "i64");
- } else if (buf_eql_str(type_name, "intptr_t")) {
- return resolve_typdef_as_builtin(c, typedef_decl, "isize");
- } else if (buf_eql_str(type_name, "uintptr_t")) {
- return resolve_typdef_as_builtin(c, typedef_decl, "usize");
- } else if (buf_eql_str(type_name, "ssize_t")) {
- return resolve_typdef_as_builtin(c, typedef_decl, "isize");
- } else if (buf_eql_str(type_name, "size_t")) {
- return resolve_typdef_as_builtin(c, typedef_decl, "usize");
- }
-
- // if the underlying type is anonymous, we can special case it to just
- // use the name of this typedef
- // TODO
-
- AstNode *type_node = trans_qual_type(c, child_qt, typedef_decl->getLocation());
- if (type_node == nullptr) {
- emit_warning(c, typedef_decl->getLocation(), "typedef %s - unresolved child type", buf_ptr(type_name));
- c->decl_table.put(typedef_decl, nullptr);
- return nullptr;
- }
- add_global_var(c, type_name, type_node);
-
- AstNode *symbol_node = trans_create_node_symbol(c, type_name);
- c->decl_table.put(typedef_decl->getCanonicalDecl(), symbol_node);
- return symbol_node;
-}
-
-struct AstNode *demote_enum_to_opaque(Context *c, const EnumDecl *enum_decl,
- Buf *full_type_name, Buf *bare_name)
-{
- AstNode *opaque_node = trans_create_node_opaque(c);
- if (full_type_name == nullptr) {
- c->decl_table.put(enum_decl->getCanonicalDecl(), opaque_node);
- return opaque_node;
- }
- AstNode *symbol_node = trans_create_node_symbol(c, full_type_name);
- add_global_weak_alias(c, bare_name, full_type_name);
- add_global_var(c, full_type_name, opaque_node);
- c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node);
- return symbol_node;
-}
-
-static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {
- auto existing_entry = c->decl_table.maybe_get((void*)enum_decl->getCanonicalDecl());
- if (existing_entry) {
- return existing_entry->value;
- }
-
- const char *raw_name = decl_name(enum_decl);
- bool is_anonymous = (raw_name[0] == 0);
- Buf *bare_name = is_anonymous ? nullptr : buf_create_from_str(raw_name);
- Buf *full_type_name = is_anonymous ? nullptr : buf_sprintf("enum_%s", buf_ptr(bare_name));
-
- const EnumDecl *enum_def = enum_decl->getDefinition();
- if (!enum_def) {
- return demote_enum_to_opaque(c, enum_decl, full_type_name, bare_name);
- }
-
- bool pure_enum = true;
- uint32_t field_count = 0;
- for (auto it = enum_def->enumerator_begin(),
- it_end = enum_def->enumerator_end();
- it != it_end; ++it, field_count += 1)
- {
- const EnumConstantDecl *enum_const = *it;
- if (enum_const->getInitExpr()) {
- pure_enum = false;
- }
- }
-
- AstNode *tag_int_type = trans_qual_type(c, enum_decl->getIntegerType(), enum_decl->getLocation());
- assert(tag_int_type);
-
- if (pure_enum) {
- AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl);
- enum_node->data.container_decl.kind = ContainerKindEnum;
- enum_node->data.container_decl.layout = ContainerLayoutExtern;
- enum_node->data.container_decl.init_arg_expr = tag_int_type;
-
- enum_node->data.container_decl.fields.resize(field_count);
- uint32_t i = 0;
- for (auto it = enum_def->enumerator_begin(),
- it_end = enum_def->enumerator_end();
- it != it_end; ++it, i += 1)
- {
- const EnumConstantDecl *enum_const = *it;
-
- Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
- Buf *field_name;
- if (bare_name != nullptr && buf_starts_with_buf(enum_val_name, bare_name)) {
- field_name = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name));
- } else {
- field_name = enum_val_name;
- }
-
- AstNode *field_node = trans_create_node(c, NodeTypeStructField);
- field_node->data.struct_field.name = field_name;
- field_node->data.struct_field.type = nullptr;
- enum_node->data.container_decl.fields.items[i] = field_node;
-
- // 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
- if (is_anonymous) {
- AstNode *lit_node = trans_create_node_unsigned(c, i);
- add_global_var(c, enum_val_name, lit_node);
- } else {
- AstNode *field_access_node = trans_create_node_field_access(c,
- trans_create_node_symbol(c, full_type_name), field_name);
- add_global_var(c, enum_val_name, field_access_node);
- }
- }
-
- if (is_anonymous) {
- c->decl_table.put(enum_decl->getCanonicalDecl(), enum_node);
- return enum_node;
- } else {
- AstNode *symbol_node = trans_create_node_symbol(c, full_type_name);
- add_global_weak_alias(c, bare_name, full_type_name);
- add_global_var(c, full_type_name, enum_node);
- c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node);
- return enum_node;
- }
- }
-
- // TODO after issue #305 is solved, make this be an enum with tag_int_type
- // as the integer type and set the custom enum values
- AstNode *enum_node = tag_int_type;
-
-
- // add variables for all the values with enum_node
- for (auto it = enum_def->enumerator_begin(),
- it_end = enum_def->enumerator_end();
- it != it_end; ++it)
- {
- const EnumConstantDecl *enum_const = *it;
-
- Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
- AstNode *int_node = trans_create_node_apint(c, enum_const->getInitVal());
- AstNode *var_node = add_global_var(c, enum_val_name, int_node);
- var_node->data.variable_declaration.type = tag_int_type;
- }
-
- if (is_anonymous) {
- c->decl_table.put(enum_decl->getCanonicalDecl(), enum_node);
- return enum_node;
- } else {
- AstNode *symbol_node = trans_create_node_symbol(c, full_type_name);
- add_global_weak_alias(c, bare_name, full_type_name);
- add_global_var(c, full_type_name, enum_node);
- c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node);
- return symbol_node;
- }
-}
-
-static AstNode *demote_struct_to_opaque(Context *c, const RecordDecl *record_decl,
- Buf *full_type_name, Buf *bare_name)
-{
- AstNode *opaque_node = trans_create_node_opaque(c);
- if (full_type_name == nullptr) {
- c->decl_table.put(record_decl->getCanonicalDecl(), opaque_node);
- return opaque_node;
- }
- AstNode *symbol_node = trans_create_node_symbol(c, full_type_name);
- add_global_weak_alias(c, bare_name, full_type_name);
- add_global_var(c, full_type_name, opaque_node);
- c->decl_table.put(record_decl->getCanonicalDecl(), symbol_node);
- return symbol_node;
-}
-
-static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) {
- auto existing_entry = c->decl_table.maybe_get((void*)record_decl->getCanonicalDecl());
- if (existing_entry) {
- return existing_entry->value;
- }
-
- const char *raw_name = decl_name(record_decl);
-
- if (!record_decl->isStruct()) {
- emit_warning(c, record_decl->getLocation(), "skipping record %s, not a struct", raw_name);
- c->decl_table.put(record_decl->getCanonicalDecl(), nullptr);
- return nullptr;
- }
-
- bool is_anonymous = record_decl->isAnonymousStructOrUnion() || raw_name[0] == 0;
- Buf *bare_name = is_anonymous ? nullptr : buf_create_from_str(raw_name);
- Buf *full_type_name = (bare_name == nullptr) ? nullptr : buf_sprintf("struct_%s", buf_ptr(bare_name));
-
- RecordDecl *record_def = record_decl->getDefinition();
- if (record_def == nullptr) {
- return demote_struct_to_opaque(c, record_decl, full_type_name, bare_name);
- }
-
- // count fields and validate
- uint32_t field_count = 0;
- for (auto it = record_def->field_begin(),
- it_end = record_def->field_end();
- it != it_end; ++it, field_count += 1)
- {
- const FieldDecl *field_decl = *it;
-
- if (field_decl->isBitField()) {
- emit_warning(c, field_decl->getLocation(), "struct %s demoted to opaque type - has bitfield",
- is_anonymous ? "(anon)" : buf_ptr(bare_name));
- return demote_struct_to_opaque(c, record_decl, full_type_name, bare_name);
- }
- }
-
- AstNode *struct_node = trans_create_node(c, NodeTypeContainerDecl);
- struct_node->data.container_decl.kind = ContainerKindStruct;
- struct_node->data.container_decl.layout = ContainerLayoutExtern;
-
- // TODO handle attribute packed
-
- struct_node->data.container_decl.fields.resize(field_count);
-
- // must be before fields in case a circular reference happens
- if (is_anonymous) {
- c->decl_table.put(record_decl->getCanonicalDecl(), struct_node);
- } else {
- c->decl_table.put(record_decl->getCanonicalDecl(), trans_create_node_symbol(c, full_type_name));
- }
-
- uint32_t i = 0;
- for (auto it = record_def->field_begin(),
- it_end = record_def->field_end();
- it != it_end; ++it, i += 1)
- {
- const FieldDecl *field_decl = *it;
-
- AstNode *field_node = trans_create_node(c, NodeTypeStructField);
- field_node->data.struct_field.name = buf_create_from_str(decl_name(field_decl));
- field_node->data.struct_field.type = trans_qual_type(c, field_decl->getType(), field_decl->getLocation());
-
- if (field_node->data.struct_field.type == nullptr) {
- emit_warning(c, field_decl->getLocation(),
- "struct %s demoted to opaque type - unresolved type",
- is_anonymous ? "(anon)" : buf_ptr(bare_name));
-
- return demote_struct_to_opaque(c, record_decl, full_type_name, bare_name);
- }
-
- struct_node->data.container_decl.fields.items[i] = field_node;
- }
-
- if (is_anonymous) {
- return struct_node;
- } else {
- add_global_weak_alias(c, bare_name, full_type_name);
- add_global_var(c, full_type_name, struct_node);
- return trans_create_node_symbol(c, full_type_name);
- }
-}
-
-static void visit_var_decl(Context *c, const VarDecl *var_decl) {
- Buf *name = buf_create_from_str(decl_name(var_decl));
-
- switch (var_decl->getTLSKind()) {
- case VarDecl::TLS_None:
- break;
- case VarDecl::TLS_Static:
- emit_warning(c, var_decl->getLocation(),
- "ignoring variable '%s' - static thread local storage", buf_ptr(name));
- return;
- case VarDecl::TLS_Dynamic:
- emit_warning(c, var_decl->getLocation(),
- "ignoring variable '%s' - dynamic thread local storage", buf_ptr(name));
- return;
- }
-
- QualType qt = var_decl->getType();
- AstNode *var_type = trans_qual_type(c, qt, var_decl->getLocation());
- if (var_type == nullptr) {
- emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - unresolved type", buf_ptr(name));
- return;
- }
-
- bool is_extern = var_decl->hasExternalStorage();
- bool is_static = var_decl->isFileVarDecl();
- bool is_const = qt.isConstQualified();
-
- if (is_static && !is_extern) {
- AstNode *init_node;
- if (var_decl->hasInit()) {
- APValue *ap_value = var_decl->evaluateValue();
- if (ap_value == nullptr) {
- emit_warning(c, var_decl->getLocation(),
- "ignoring variable '%s' - unable to evaluate initializer", buf_ptr(name));
- return;
- }
- switch (ap_value->getKind()) {
- case APValue::Int:
- init_node = trans_create_node_apint(c, ap_value->getInt());
- break;
- case APValue::Uninitialized:
- init_node = trans_create_node(c, NodeTypeUndefinedLiteral);
- break;
- case APValue::Float:
- case APValue::ComplexInt:
- case APValue::ComplexFloat:
- case APValue::LValue:
- case APValue::Vector:
- case APValue::Array:
- case APValue::Struct:
- case APValue::Union:
- case APValue::MemberPointer:
- case APValue::AddrLabelDiff:
- emit_warning(c, var_decl->getLocation(),
- "ignoring variable '%s' - unrecognized initializer value kind", buf_ptr(name));
- return;
- }
- } else {
- init_node = trans_create_node(c, NodeTypeUndefinedLiteral);
- }
-
- AstNode *var_node = trans_create_node_var_decl_global(c, is_const, name, var_type, init_node);
- add_top_level_decl(c, name, var_node);
- return;
- }
-
- if (is_extern) {
- AstNode *var_node = trans_create_node_var_decl_global(c, is_const, name, var_type, nullptr);
- var_node->data.variable_declaration.is_extern = true;
- add_top_level_decl(c, name, var_node);
- return;
- }
-
- emit_warning(c, var_decl->getLocation(),
- "ignoring variable '%s' - non-extern, non-static variable", buf_ptr(name));
- return;
-}
-
-static bool decl_visitor(void *context, const Decl *decl) {
- Context *c = (Context*)context;
-
- switch (decl->getKind()) {
- case Decl::Function:
- visit_fn_decl(c, static_cast<const FunctionDecl*>(decl));
- break;
- case Decl::Typedef:
- resolve_typedef_decl(c, static_cast<const TypedefNameDecl *>(decl));
- break;
- case Decl::Enum:
- resolve_enum_decl(c, static_cast<const EnumDecl *>(decl));
- break;
- case Decl::Record:
- resolve_record_decl(c, static_cast<const RecordDecl *>(decl));
- break;
- case Decl::Var:
- visit_var_decl(c, static_cast<const VarDecl *>(decl));
- break;
- default:
- emit_warning(c, decl->getLocation(), "ignoring %s decl", decl->getDeclKindName());
- }
-
- return true;
-}
-
-static bool name_exists(Context *c, Buf *name) {
- return get_global(c, name) != nullptr;
-}
-
-static void render_aliases(Context *c) {
- for (size_t i = 0; i < c->aliases.length; i += 1) {
- Alias *alias = &c->aliases.at(i);
- if (name_exists(c, alias->new_name))
- continue;
-
- add_global_var(c, alias->new_name, trans_create_node_symbol(c, alias->canon_name));
- }
-}
-
-static void render_macros(Context *c) {
- auto it = c->macro_table.entry_iterator();
- for (;;) {
- auto *entry = it.next();
- if (!entry)
- break;
-
- AstNode *value_node = entry->value;
- if (value_node->type == NodeTypeFnDef) {
- add_top_level_decl(c, value_node->data.fn_def.fn_proto->data.fn_proto.name, value_node);
- } else {
- add_global_var(c, entry->key, value_node);
- }
- }
-}
-
-static AstNode *parse_ctok_num_lit(Context *c, CTokenize *ctok, size_t *tok_i, bool negate) {
- CTok *tok = &ctok->tokens.at(*tok_i);
- if (tok->id == CTokIdNumLitInt) {
- *tok_i += 1;
- switch (tok->data.num_lit_int.suffix) {
- case CNumLitSuffixNone:
- return trans_create_node_unsigned_negative(c, tok->data.num_lit_int.x, negate);
- case CNumLitSuffixL:
- return trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate, "c_long");
- case CNumLitSuffixU:
- return trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate, "c_uint");
- case CNumLitSuffixLU:
- return trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate, "c_ulong");
- case CNumLitSuffixLL:
- return trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate, "c_longlong");
- case CNumLitSuffixLLU:
- return trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate, "c_ulonglong");
- }
- zig_unreachable();
- } else if (tok->id == CTokIdNumLitFloat) {
- *tok_i += 1;
- double value = negate ? -tok->data.num_lit_float : tok->data.num_lit_float;
- return trans_create_node_float_lit(c, value);
- }
- return nullptr;
-}
-
-static AstNode *parse_ctok(Context *c, CTokenize *ctok, size_t *tok_i) {
- CTok *tok = &ctok->tokens.at(*tok_i);
- switch (tok->id) {
- case CTokIdCharLit:
- *tok_i += 1;
- return trans_create_node_unsigned(c, tok->data.char_lit);
- case CTokIdStrLit:
- *tok_i += 1;
- return trans_create_node_str_lit_c(c, buf_create_from_buf(&tok->data.str_lit));
- case CTokIdMinus:
- *tok_i += 1;
- return parse_ctok_num_lit(c, ctok, tok_i, true);
- case CTokIdNumLitInt:
- case CTokIdNumLitFloat:
- return parse_ctok_num_lit(c, ctok, tok_i, false);
- case CTokIdSymbol:
- {
- *tok_i += 1;
- Buf *symbol_name = buf_create_from_buf(&tok->data.symbol);
- return trans_create_node_symbol(c, symbol_name);
- }
- case CTokIdLParen:
- {
- *tok_i += 1;
- AstNode *inner_node = parse_ctok(c, ctok, tok_i);
-
- CTok *next_tok = &ctok->tokens.at(*tok_i);
- if (next_tok->id != CTokIdRParen) {
- return nullptr;
- }
- *tok_i += 1;
- return inner_node;
- }
- case CTokIdEOF:
- case CTokIdRParen:
- // not able to make sense of this
- return nullptr;
- }
- zig_unreachable();
-}
-
-static void process_macro(Context *c, CTokenize *ctok, Buf *name, const char *char_ptr) {
- tokenize_c_macro(ctok, (const uint8_t *)char_ptr);
-
- if (ctok->error) {
- return;
- }
-
- size_t tok_i = 0;
- CTok *name_tok = &ctok->tokens.at(tok_i);
- assert(name_tok->id == CTokIdSymbol && buf_eql_buf(&name_tok->data.symbol, name));
- tok_i += 1;
-
- AstNode *result_node = parse_ctok(c, ctok, &tok_i);
- if (result_node == nullptr) {
- return;
- }
- CTok *eof_tok = &ctok->tokens.at(tok_i);
- if (eof_tok->id != CTokIdEOF) {
- return;
- }
- if (result_node->type == NodeTypeSymbol) {
- // if it equals itself, ignore. for example, from stdio.h:
- // #define stdin stdin
- Buf *symbol_name = result_node->data.symbol_expr.symbol;
- if (buf_eql_buf(name, symbol_name)) {
- return;
- }
- c->macro_symbols.append({name, symbol_name});
- } else {
- c->macro_table.put(name, result_node);
- }
-}
-
-static void process_symbol_macros(Context *c) {
- for (size_t i = 0; i < c->macro_symbols.length; i += 1) {
- MacroSymbol ms = c->macro_symbols.at(i);
-
- // Check if this macro aliases another top level declaration
- AstNode *existing_node = get_global(c, ms.value);
- if (!existing_node || name_exists(c, ms.name))
- continue;
-
- // 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.
- if (existing_node->type == NodeTypeVariableDeclaration) {
- AstNode *var_type = existing_node->data.variable_declaration.type;
- if (var_type != nullptr && var_type->type == NodeTypePrefixOpExpr &&
- var_type->data.prefix_op_expr.prefix_op == PrefixOpMaybe)
- {
- AstNode *fn_proto_node = var_type->data.prefix_op_expr.primary_expr;
- if (fn_proto_node->type == NodeTypeFnProto) {
- AstNode *inline_fn_node = trans_create_node_inline_fn(c, ms.name, ms.value, fn_proto_node);
- c->macro_table.put(ms.name, inline_fn_node);
- continue;
- }
- }
- }
-
- add_global_var(c, ms.name, trans_create_node_symbol(c, ms.value));
- }
-}
-
-static void process_preprocessor_entities(Context *c, ASTUnit &unit) {
- CTokenize ctok = {{0}};
-
- // TODO if we see #undef, delete it from the table
-
- for (PreprocessedEntity *entity : unit.getLocalPreprocessingEntities()) {
- switch (entity->getKind()) {
- case PreprocessedEntity::InvalidKind:
- case PreprocessedEntity::InclusionDirectiveKind:
- case PreprocessedEntity::MacroExpansionKind:
- continue;
- case PreprocessedEntity::MacroDefinitionKind:
- {
- MacroDefinitionRecord *macro = static_cast<MacroDefinitionRecord *>(entity);
- const char *raw_name = macro->getName()->getNameStart();
- SourceRange range = macro->getSourceRange();
- SourceLocation begin_loc = range.getBegin();
- SourceLocation end_loc = range.getEnd();
-
- if (begin_loc == end_loc) {
- // this means it is a macro without a value
- // we don't care about such things
- continue;
- }
- Buf *name = buf_create_from_str(raw_name);
- if (name_exists(c, name)) {
- continue;
- }
-
- const char *begin_c = c->source_manager->getCharacterData(begin_loc);
- process_macro(c, &ctok, name, begin_c);
- }
- }
- }
-}
-
-int parse_h_buf(ImportTableEntry *import, ZigList<ErrorMsg *> *errors, Buf *source,
- CodeGen *codegen, AstNode *source_node)
-{
- int err;
- Buf tmp_file_path = BUF_INIT;
- if ((err = os_buf_to_tmp_file(source, buf_create_from_str(".h"), &tmp_file_path))) {
- return err;
- }
-
- err = parse_h_file(import, errors, buf_ptr(&tmp_file_path), codegen, source_node);
-
- os_delete_file(&tmp_file_path);
-
- return err;
-}
-
-int parse_h_file(ImportTableEntry *import, ZigList<ErrorMsg *> *errors, const char *target_file,
- CodeGen *codegen, AstNode *source_node)
-{
- Context context = {0};
- Context *c = &context;
- c->warnings_on = codegen->verbose_cimport;
- c->import = import;
- c->errors = errors;
- if (buf_ends_with_str(buf_create_from_str(target_file), ".h")) {
- c->visib_mod = VisibModPub;
- c->export_visib_mod = VisibModPub;
- } else {
- c->visib_mod = VisibModPub;
- c->export_visib_mod = VisibModExport;
- }
- c->decl_table.init(8);
- c->macro_table.init(8);
- c->global_table.init(8);
- c->ptr_params.init(8);
- c->codegen = codegen;
- c->source_node = source_node;
-
- ZigList<const char *> clang_argv = {0};
-
- clang_argv.append("-x");
- clang_argv.append("c");
-
- if (c->codegen->is_native_target) {
- char *ZIG_PARSEC_CFLAGS = getenv("ZIG_NATIVE_PARSEC_CFLAGS");
- if (ZIG_PARSEC_CFLAGS) {
- Buf tmp_buf = BUF_INIT;
- char *start = ZIG_PARSEC_CFLAGS;
- char *space = strstr(start, " ");
- while (space) {
- if (space - start > 0) {
- buf_init_from_mem(&tmp_buf, start, space - start);
- clang_argv.append(buf_ptr(buf_create_from_buf(&tmp_buf)));
- }
- start = space + 1;
- space = strstr(start, " ");
- }
- buf_init_from_str(&tmp_buf, start);
- clang_argv.append(buf_ptr(buf_create_from_buf(&tmp_buf)));
- }
- }
-
- clang_argv.append("-isystem");
- clang_argv.append(buf_ptr(codegen->zig_c_headers_dir));
-
- clang_argv.append("-isystem");
- clang_argv.append(buf_ptr(codegen->libc_include_dir));
-
- // windows c runtime requires -D_DEBUG if using debug libraries
- if (codegen->build_mode == BuildModeDebug) {
- clang_argv.append("-D_DEBUG");
- }
-
- for (size_t i = 0; i < codegen->clang_argv_len; i += 1) {
- clang_argv.append(codegen->clang_argv[i]);
- }
-
- // we don't need spell checking and it slows things down
- clang_argv.append("-fno-spell-checking");
-
- // this gives us access to preprocessing entities, presumably at
- // the cost of performance
- clang_argv.append("-Xclang");
- clang_argv.append("-detailed-preprocessing-record");
-
- if (!c->codegen->is_native_target) {
- clang_argv.append("-target");
- clang_argv.append(buf_ptr(&c->codegen->triple_str));
- }
-
- clang_argv.append(target_file);
-
- // to make the [start...end] argument work
- clang_argv.append(nullptr);
-
- IntrusiveRefCntPtr<DiagnosticsEngine> diags(CompilerInstance::createDiagnostics(new DiagnosticOptions));
-
- std::shared_ptr<PCHContainerOperations> pch_container_ops = std::make_shared<PCHContainerOperations>();
-
- bool skip_function_bodies = false;
- bool only_local_decls = true;
- bool capture_diagnostics = true;
- bool user_files_are_volatile = true;
- bool allow_pch_with_compiler_errors = false;
- bool single_file_parse = false;
- bool for_serialization = false;
- const char *resources_path = buf_ptr(codegen->zig_c_headers_dir);
- std::unique_ptr<ASTUnit> err_unit;
- std::unique_ptr<ASTUnit> ast_unit(ASTUnit::LoadFromCommandLine(
- &clang_argv.at(0), &clang_argv.last(),
- pch_container_ops, diags, resources_path,
- only_local_decls, capture_diagnostics, None, true, 0, TU_Complete,
- false, false, allow_pch_with_compiler_errors, skip_function_bodies,
- single_file_parse, user_files_are_volatile, for_serialization, None, &err_unit,
- nullptr));
-
- // Early failures in LoadFromCommandLine may return with ErrUnit unset.
- if (!ast_unit && !err_unit) {
- return ErrorFileSystem;
- }
-
- if (diags->getClient()->getNumErrors() > 0) {
- if (ast_unit) {
- err_unit = std::move(ast_unit);
- }
-
- for (ASTUnit::stored_diag_iterator it = err_unit->stored_diag_begin(),
- it_end = err_unit->stored_diag_end();
- it != it_end; ++it)
- {
- switch (it->getLevel()) {
- case DiagnosticsEngine::Ignored:
- case DiagnosticsEngine::Note:
- case DiagnosticsEngine::Remark:
- case DiagnosticsEngine::Warning:
- continue;
- case DiagnosticsEngine::Error:
- case DiagnosticsEngine::Fatal:
- break;
- }
- StringRef msg_str_ref = it->getMessage();
- Buf *msg = buf_create_from_str((const char *)msg_str_ref.bytes_begin());
- FullSourceLoc fsl = it->getLocation();
- if (fsl.hasManager()) {
- FileID file_id = fsl.getFileID();
- StringRef filename = fsl.getManager().getFilename(fsl);
- unsigned line = fsl.getSpellingLineNumber() - 1;
- unsigned column = fsl.getSpellingColumnNumber() - 1;
- unsigned offset = fsl.getManager().getFileOffset(fsl);
- const char *source = (const char *)fsl.getManager().getBufferData(file_id).bytes_begin();
- Buf *path;
- if (filename.empty()) {
- path = buf_alloc();
- } else {
- path = buf_create_from_mem((const char *)filename.bytes_begin(), filename.size());
- }
-
- ErrorMsg *err_msg = err_msg_create_with_offset(path, line, column, offset, source, msg);
-
- c->errors->append(err_msg);
- } else {
- // NOTE the only known way this gets triggered right now is if you have a lot of errors
- // clang emits "too many errors emitted, stopping now"
- fprintf(stderr, "unexpected error from clang: %s\n", buf_ptr(msg));
- }
- }
-
- return 0;
- }
-
- c->ctx = &ast_unit->getASTContext();
- c->source_manager = &ast_unit->getSourceManager();
- c->root = trans_create_node(c, NodeTypeRoot);
-
- ast_unit->visitLocalTopLevelDecls(c, decl_visitor);
-
- process_preprocessor_entities(c, *ast_unit);
-
- process_symbol_macros(c);
- render_macros(c);
- render_aliases(c);
-
- import->root = c->root;
-
- return 0;
-}
diff --git a/src/translate_c.cpp b/src/translate_c.cpp
@@ -0,0 +1,4324 @@
+/*
+ * Copyright (c) 2015 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#include "all_types.hpp"
+#include "analyze.hpp"
+#include "c_tokenizer.hpp"
+#include "error.hpp"
+#include "ir.hpp"
+#include "os.hpp"
+#include "translate_c.hpp"
+#include "parser.hpp"
+
+
+#include <clang/Frontend/ASTUnit.h>
+#include <clang/Frontend/CompilerInstance.h>
+#include <clang/AST/Expr.h>
+
+#include <string.h>
+
+using namespace clang;
+
+struct Alias {
+ Buf *new_name;
+ Buf *canon_name;
+};
+
+enum TransScopeId {
+ TransScopeIdSwitch,
+ TransScopeIdVar,
+ TransScopeIdBlock,
+ TransScopeIdRoot,
+ TransScopeIdWhile,
+};
+
+struct TransScope {
+ TransScopeId id;
+ TransScope *parent;
+};
+
+struct TransScopeSwitch {
+ TransScope base;
+ AstNode *switch_node;
+ uint32_t case_index;
+ bool found_default;
+ Buf *end_label_name;
+};
+
+struct TransScopeVar {
+ TransScope base;
+ Buf *c_name;
+ Buf *zig_name;
+};
+
+struct TransScopeBlock {
+ TransScope base;
+ AstNode *node;
+};
+
+struct TransScopeRoot {
+ TransScope base;
+};
+
+struct TransScopeWhile {
+ TransScope base;
+ AstNode *node;
+};
+
+struct Context {
+ ImportTableEntry *import;
+ ZigList<ErrorMsg *> *errors;
+ VisibMod visib_mod;
+ VisibMod export_visib_mod;
+ AstNode *root;
+ HashMap<const void *, AstNode *, ptr_hash, ptr_eq> decl_table;
+ HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> macro_table;
+ HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> global_table;
+ SourceManager *source_manager;
+ ZigList<Alias> aliases;
+ AstNode *source_node;
+ bool warnings_on;
+
+ CodeGen *codegen;
+ ASTContext *ctx;
+
+ TransScopeRoot *global_scope;
+ HashMap<Buf *, bool, buf_hash, buf_eql_buf> ptr_params;
+};
+
+enum ResultUsed {
+ ResultUsedNo,
+ ResultUsedYes,
+};
+
+enum TransLRValue {
+ TransLValue,
+ TransRValue,
+};
+
+static TransScopeRoot *trans_scope_root_create(Context *c);
+static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope);
+static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope);
+static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name);
+static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope);
+
+static TransScopeBlock *trans_scope_block_find(TransScope *scope);
+static TransScopeSwitch *trans_scope_switch_find(TransScope *scope);
+
+static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl);
+static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl);
+static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_decl);
+
+static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
+ ResultUsed result_used, TransLRValue lrval,
+ AstNode **out_node, TransScope **out_child_scope,
+ TransScope **out_node_scope);
+static TransScope *trans_stmt(Context *c, TransScope *scope, const Stmt *stmt, AstNode **out_node);
+static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, TransLRValue lrval);
+static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc);
+
+
+ATTRIBUTE_PRINTF(3, 4)
+static void emit_warning(Context *c, const SourceLocation &sl, const char *format, ...) {
+ if (!c->warnings_on) {
+ return;
+ }
+
+ va_list ap;
+ va_start(ap, format);
+ Buf *msg = buf_vprintf(format, ap);
+ va_end(ap);
+
+ StringRef filename = c->source_manager->getFilename(c->source_manager->getSpellingLoc(sl));
+ const char *filename_bytes = (const char *)filename.bytes_begin();
+ Buf *path;
+ if (filename_bytes) {
+ path = buf_create_from_str(filename_bytes);
+ } else {
+ path = buf_sprintf("(no file)");
+ }
+ unsigned line = c->source_manager->getSpellingLineNumber(sl);
+ unsigned column = c->source_manager->getSpellingColumnNumber(sl);
+ fprintf(stderr, "%s:%u:%u: warning: %s\n", buf_ptr(path), line, column, buf_ptr(msg));
+}
+
+static void add_global_weak_alias(Context *c, Buf *new_name, Buf *canon_name) {
+ Alias *alias = c->aliases.add_one();
+ alias->new_name = new_name;
+ alias->canon_name = canon_name;
+}
+
+static Buf *trans_lookup_zig_symbol(Context *c, TransScope *scope, Buf *c_symbol_name) {
+ while (scope != nullptr) {
+ if (scope->id == TransScopeIdVar) {
+ TransScopeVar *var_scope = (TransScopeVar *)scope;
+ if (buf_eql_buf(var_scope->c_name, c_symbol_name)) {
+ return var_scope->zig_name;
+ }
+ }
+ scope = scope->parent;
+ }
+ return c_symbol_name;
+}
+
+static AstNode * trans_create_node(Context *c, NodeType id) {
+ AstNode *node = allocate<AstNode>(1);
+ node->type = id;
+ node->owner = c->import;
+ // TODO line/column. mapping to C file??
+ return node;
+}
+
+static AstNode *trans_create_node_float_lit(Context *c, double value) {
+ AstNode *node = trans_create_node(c, NodeTypeFloatLiteral);
+ node->data.float_literal.bigfloat = allocate<BigFloat>(1);
+ bigfloat_init_64(node->data.float_literal.bigfloat, value);
+ return node;
+}
+
+static AstNode *trans_create_node_symbol(Context *c, Buf *name) {
+ AstNode *node = trans_create_node(c, NodeTypeSymbol);
+ node->data.symbol_expr.symbol = name;
+ return node;
+}
+
+static AstNode *trans_create_node_symbol_str(Context *c, const char *name) {
+ return trans_create_node_symbol(c, buf_create_from_str(name));
+}
+
+static AstNode *trans_create_node_builtin_fn_call(Context *c, Buf *name) {
+ AstNode *node = trans_create_node(c, NodeTypeFnCallExpr);
+ node->data.fn_call_expr.fn_ref_expr = trans_create_node_symbol(c, name);
+ node->data.fn_call_expr.is_builtin = true;
+ return node;
+}
+
+static AstNode *trans_create_node_builtin_fn_call_str(Context *c, const char *name) {
+ return trans_create_node_builtin_fn_call(c, buf_create_from_str(name));
+}
+
+static AstNode *trans_create_node_opaque(Context *c) {
+ return trans_create_node_builtin_fn_call_str(c, "OpaqueType");
+}
+
+static AstNode *trans_create_node_fn_call_1(Context *c, AstNode *fn_ref_expr, AstNode *arg1) {
+ AstNode *node = trans_create_node(c, NodeTypeFnCallExpr);
+ node->data.fn_call_expr.fn_ref_expr = fn_ref_expr;
+ node->data.fn_call_expr.params.append(arg1);
+ return node;
+}
+
+static AstNode *trans_create_node_field_access(Context *c, AstNode *container, Buf *field_name) {
+ AstNode *node = trans_create_node(c, NodeTypeFieldAccessExpr);
+ if (container->type == NodeTypeSymbol) {
+ assert(container->data.symbol_expr.symbol != nullptr);
+ }
+ node->data.field_access_expr.struct_expr = container;
+ node->data.field_access_expr.field_name = field_name;
+ return node;
+}
+
+static AstNode *trans_create_node_field_access_str(Context *c, AstNode *container, const char *field_name) {
+ return trans_create_node_field_access(c, container, buf_create_from_str(field_name));
+}
+
+static AstNode *trans_create_node_prefix_op(Context *c, PrefixOp op, AstNode *child_node) {
+ AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr);
+ node->data.prefix_op_expr.prefix_op = op;
+ node->data.prefix_op_expr.primary_expr = child_node;
+ return node;
+}
+
+static AstNode *trans_create_node_bin_op(Context *c, AstNode *lhs_node, BinOpType op, AstNode *rhs_node) {
+ AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
+ node->data.bin_op_expr.op1 = lhs_node;
+ node->data.bin_op_expr.bin_op = op;
+ node->data.bin_op_expr.op2 = rhs_node;
+ return node;
+}
+
+static AstNode *maybe_suppress_result(Context *c, ResultUsed result_used, AstNode *node) {
+ if (result_used == ResultUsedYes) return node;
+ return trans_create_node_bin_op(c,
+ trans_create_node_symbol_str(c, "_"),
+ BinOpTypeAssign,
+ node);
+}
+
+static AstNode *trans_create_node_addr_of(Context *c, bool is_const, bool is_volatile, AstNode *child_node) {
+ AstNode *node = trans_create_node(c, NodeTypeAddrOfExpr);
+ node->data.addr_of_expr.is_const = is_const;
+ node->data.addr_of_expr.is_volatile = is_volatile;
+ node->data.addr_of_expr.op_expr = child_node;
+ return node;
+}
+
+static AstNode *trans_create_node_goto(Context *c, Buf *label_name) {
+ AstNode *goto_node = trans_create_node(c, NodeTypeGoto);
+ goto_node->data.goto_expr.name = label_name;
+ return goto_node;
+}
+
+static AstNode *trans_create_node_label(Context *c, Buf *label_name) {
+ AstNode *label_node = trans_create_node(c, NodeTypeLabel);
+ label_node->data.label.name = label_name;
+ return label_node;
+}
+
+static AstNode *trans_create_node_bool(Context *c, bool value) {
+ AstNode *bool_node = trans_create_node(c, NodeTypeBoolLiteral);
+ bool_node->data.bool_literal.value = value;
+ return bool_node;
+}
+
+static AstNode *trans_create_node_str_lit_c(Context *c, Buf *buf) {
+ AstNode *node = trans_create_node(c, NodeTypeStringLiteral);
+ node->data.string_literal.buf = buf;
+ node->data.string_literal.c = true;
+ return node;
+}
+
+static AstNode *trans_create_node_str_lit_non_c(Context *c, Buf *buf) {
+ AstNode *node = trans_create_node(c, NodeTypeStringLiteral);
+ node->data.string_literal.buf = buf;
+ node->data.string_literal.c = false;
+ return node;
+}
+
+static AstNode *trans_create_node_unsigned_negative(Context *c, uint64_t x, bool is_negative) {
+ AstNode *node = trans_create_node(c, NodeTypeIntLiteral);
+ node->data.int_literal.bigint = allocate<BigInt>(1);
+ bigint_init_data(node->data.int_literal.bigint, &x, 1, is_negative);
+ return node;
+}
+
+static AstNode *trans_create_node_unsigned(Context *c, uint64_t x) {
+ return trans_create_node_unsigned_negative(c, x, false);
+}
+
+static AstNode *trans_create_node_cast(Context *c, AstNode *dest, AstNode *src) {
+ AstNode *node = trans_create_node(c, NodeTypeFnCallExpr);
+ node->data.fn_call_expr.fn_ref_expr = dest;
+ node->data.fn_call_expr.params.resize(1);
+ node->data.fn_call_expr.params.items[0] = src;
+ return node;
+}
+
+static AstNode *trans_create_node_unsigned_negative_type(Context *c, uint64_t x, bool is_negative,
+ const char *type_name)
+{
+ AstNode *lit_node = trans_create_node_unsigned_negative(c, x, is_negative);
+ return trans_create_node_cast(c, trans_create_node_symbol_str(c, type_name), lit_node);
+}
+
+static AstNode *trans_create_node_array_type(Context *c, AstNode *size_node, AstNode *child_type_node) {
+ AstNode *node = trans_create_node(c, NodeTypeArrayType);
+ node->data.array_type.size = size_node;
+ node->data.array_type.child_type = child_type_node;
+ return node;
+}
+
+static AstNode *trans_create_node_var_decl(Context *c, VisibMod visib_mod, bool is_const, Buf *var_name,
+ AstNode *type_node, AstNode *init_node)
+{
+ AstNode *node = trans_create_node(c, NodeTypeVariableDeclaration);
+ node->data.variable_declaration.visib_mod = visib_mod;
+ node->data.variable_declaration.symbol = var_name;
+ node->data.variable_declaration.is_const = is_const;
+ node->data.variable_declaration.type = type_node;
+ node->data.variable_declaration.expr = init_node;
+ return node;
+}
+
+static AstNode *trans_create_node_var_decl_global(Context *c, bool is_const, Buf *var_name, AstNode *type_node,
+ AstNode *init_node)
+{
+ return trans_create_node_var_decl(c, c->visib_mod, is_const, var_name, type_node, init_node);
+}
+
+static AstNode *trans_create_node_var_decl_local(Context *c, bool is_const, Buf *var_name, AstNode *type_node,
+ AstNode *init_node)
+{
+ return trans_create_node_var_decl(c, VisibModPrivate, is_const, var_name, type_node, init_node);
+}
+
+static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *ref_node, AstNode *src_proto_node) {
+ AstNode *fn_def = trans_create_node(c, NodeTypeFnDef);
+ AstNode *fn_proto = trans_create_node(c, NodeTypeFnProto);
+ fn_proto->data.fn_proto.visib_mod = c->visib_mod;
+ fn_proto->data.fn_proto.name = fn_name;
+ fn_proto->data.fn_proto.is_inline = true;
+ fn_proto->data.fn_proto.return_type = src_proto_node->data.fn_proto.return_type; // TODO ok for these to alias?
+
+ fn_def->data.fn_def.fn_proto = fn_proto;
+ fn_proto->data.fn_proto.fn_def_node = fn_def;
+
+ AstNode *unwrap_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, ref_node);
+ AstNode *fn_call_node = trans_create_node(c, NodeTypeFnCallExpr);
+ fn_call_node->data.fn_call_expr.fn_ref_expr = unwrap_node;
+
+ for (size_t i = 0; i < src_proto_node->data.fn_proto.params.length; i += 1) {
+ AstNode *src_param_node = src_proto_node->data.fn_proto.params.at(i);
+ Buf *param_name = src_param_node->data.param_decl.name;
+ if (!param_name) param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i);
+
+ AstNode *dest_param_node = trans_create_node(c, NodeTypeParamDecl);
+ dest_param_node->data.param_decl.name = param_name;
+ dest_param_node->data.param_decl.type = src_param_node->data.param_decl.type;
+ dest_param_node->data.param_decl.is_noalias = src_param_node->data.param_decl.is_noalias;
+ fn_proto->data.fn_proto.params.append(dest_param_node);
+
+ fn_call_node->data.fn_call_expr.params.append(trans_create_node_symbol(c, param_name));
+
+ }
+
+ AstNode *block = trans_create_node(c, NodeTypeBlock);
+ block->data.block.statements.resize(1);
+ block->data.block.statements.items[0] = fn_call_node;
+ block->data.block.last_statement_is_result_expression = true;
+
+ fn_def->data.fn_def.body = block;
+ return fn_def;
+}
+
+static AstNode *trans_create_node_unwrap_null(Context *c, AstNode *child) {
+ return trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, child);
+}
+
+static AstNode *get_global(Context *c, Buf *name) {
+ {
+ auto entry = c->global_table.maybe_get(name);
+ if (entry) {
+ return entry->value;
+ }
+ }
+ {
+ auto entry = c->macro_table.maybe_get(name);
+ if (entry)
+ return entry->value;
+ }
+ if (c->codegen->primitive_type_table.maybe_get(name) != nullptr) {
+ return trans_create_node_symbol(c, name);
+ }
+ return nullptr;
+}
+
+static void add_top_level_decl(Context *c, Buf *name, AstNode *node) {
+ c->global_table.put(name, node);
+ c->root->data.root.top_level_decls.append(node);
+}
+
+static AstNode *add_global_var(Context *c, Buf *var_name, AstNode *value_node) {
+ bool is_const = true;
+ AstNode *type_node = nullptr;
+ AstNode *node = trans_create_node_var_decl_global(c, is_const, var_name, type_node, value_node);
+ add_top_level_decl(c, var_name, node);
+ return node;
+}
+
+static Buf *string_ref_to_buf(StringRef string_ref) {
+ return buf_create_from_mem((const char *)string_ref.bytes_begin(), string_ref.size());
+}
+
+static const char *decl_name(const Decl *decl) {
+ const NamedDecl *named_decl = static_cast<const NamedDecl *>(decl);
+ return (const char *)named_decl->getName().bytes_begin();
+}
+
+static AstNode *trans_create_node_apint(Context *c, const llvm::APSInt &aps_int) {
+ AstNode *node = trans_create_node(c, NodeTypeIntLiteral);
+ node->data.int_literal.bigint = allocate<BigInt>(1);
+ bigint_init_data(node->data.int_literal.bigint, aps_int.getRawData(), aps_int.getNumWords(), aps_int.isNegative());
+ return node;
+
+}
+
+static const Type *qual_type_canon(QualType qt) {
+ return qt.getCanonicalType().getTypePtr();
+}
+
+static QualType get_expr_qual_type(Context *c, const Expr *expr) {
+ // String literals in C are `char *` but they should really be `const char *`.
+ if (expr->getStmtClass() == Stmt::ImplicitCastExprClass) {
+ const ImplicitCastExpr *cast_expr = static_cast<const ImplicitCastExpr *>(expr);
+ if (cast_expr->getCastKind() == CK_ArrayToPointerDecay) {
+ const Expr *sub_expr = cast_expr->getSubExpr();
+ if (sub_expr->getStmtClass() == Stmt::StringLiteralClass) {
+ QualType array_qt = sub_expr->getType();
+ const ArrayType *array_type = static_cast<const ArrayType *>(array_qt.getTypePtr());
+ QualType pointee_qt = array_type->getElementType();
+ pointee_qt.addConst();
+ return c->ctx->getPointerType(pointee_qt);
+ }
+ }
+ }
+ return expr->getType();
+}
+
+static AstNode *get_expr_type(Context *c, const Expr *expr) {
+ return trans_qual_type(c, get_expr_qual_type(c, expr), expr->getLocStart());
+}
+
+static bool qual_types_equal(QualType t1, QualType t2) {
+ if (t1.isConstQualified() != t2.isConstQualified()) {
+ return false;
+ }
+ if (t1.isVolatileQualified() != t2.isVolatileQualified()) {
+ return false;
+ }
+ if (t1.isRestrictQualified() != t2.isRestrictQualified()) {
+ return false;
+ }
+ return t1.getTypePtr() == t2.getTypePtr();
+}
+
+static bool is_c_void_type(AstNode *node) {
+ return (node->type == NodeTypeSymbol && buf_eql_str(node->data.symbol_expr.symbol, "c_void"));
+}
+
+static bool expr_types_equal(Context *c, const Expr *expr1, const Expr *expr2) {
+ QualType t1 = get_expr_qual_type(c, expr1);
+ QualType t2 = get_expr_qual_type(c, expr2);
+
+ return qual_types_equal(t1, t2);
+}
+
+static bool qual_type_is_ptr(QualType qt) {
+ const Type *ty = qual_type_canon(qt);
+ return ty->getTypeClass() == Type::Pointer;
+}
+
+static bool qual_type_is_fn_ptr(Context *c, QualType qt) {
+ const Type *ty = qual_type_canon(qt);
+ if (ty->getTypeClass() != Type::Pointer) {
+ return false;
+ }
+ const PointerType *pointer_ty = static_cast<const PointerType*>(ty);
+ QualType child_qt = pointer_ty->getPointeeType();
+ const Type *child_ty = child_qt.getTypePtr();
+ return child_ty->getTypeClass() == Type::FunctionProto;
+}
+
+static uint32_t qual_type_int_bit_width(Context *c, const QualType &qt, const SourceLocation &source_loc) {
+ const Type *ty = qt.getTypePtr();
+ switch (ty->getTypeClass()) {
+ case Type::Builtin:
+ {
+ const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(ty);
+ switch (builtin_ty->getKind()) {
+ case BuiltinType::Char_U:
+ case BuiltinType::UChar:
+ case BuiltinType::Char_S:
+ case BuiltinType::SChar:
+ return 8;
+ case BuiltinType::UInt128:
+ case BuiltinType::Int128:
+ return 128;
+ default:
+ return 0;
+ }
+ zig_unreachable();
+ }
+ case Type::Typedef:
+ {
+ const TypedefType *typedef_ty = static_cast<const TypedefType*>(ty);
+ const TypedefNameDecl *typedef_decl = typedef_ty->getDecl();
+ const char *type_name = decl_name(typedef_decl);
+ if (strcmp(type_name, "uint8_t") == 0 || strcmp(type_name, "int8_t") == 0) {
+ return 8;
+ } else if (strcmp(type_name, "uint16_t") == 0 || strcmp(type_name, "int16_t") == 0) {
+ return 16;
+ } else if (strcmp(type_name, "uint32_t") == 0 || strcmp(type_name, "int32_t") == 0) {
+ return 32;
+ } else if (strcmp(type_name, "uint64_t") == 0 || strcmp(type_name, "int64_t") == 0) {
+ return 64;
+ } else {
+ return 0;
+ }
+ }
+ default:
+ return 0;
+ }
+ zig_unreachable();
+}
+
+
+static AstNode *qual_type_to_log2_int_ref(Context *c, const QualType &qt,
+ const SourceLocation &source_loc)
+{
+ uint32_t int_bit_width = qual_type_int_bit_width(c, qt, source_loc);
+ if (int_bit_width != 0) {
+ // we can perform the log2 now.
+ uint64_t cast_bit_width = log2_u64(int_bit_width);
+ return trans_create_node_symbol(c, buf_sprintf("u%" ZIG_PRI_u64, cast_bit_width));
+ }
+
+ AstNode *zig_type_node = trans_qual_type(c, qt, source_loc);
+
+// @import("std").math.Log2Int(c_long);
+//
+// FnCall
+// FieldAccess
+// FieldAccess
+// FnCall (.builtin = true)
+// Symbol "import"
+// StringLiteral "std"
+// Symbol "math"
+// Symbol "Log2Int"
+// zig_type_node
+
+ AstNode *import_fn_call = trans_create_node_builtin_fn_call_str(c, "import");
+ import_fn_call->data.fn_call_expr.params.append(trans_create_node_str_lit_non_c(c, buf_create_from_str("std")));
+ AstNode *inner_field_access = trans_create_node_field_access_str(c, import_fn_call, "math");
+ AstNode *outer_field_access = trans_create_node_field_access_str(c, inner_field_access, "Log2Int");
+ AstNode *log2int_fn_call = trans_create_node_fn_call_1(c, outer_field_access, zig_type_node);
+
+ return log2int_fn_call;
+}
+
+static bool qual_type_child_is_fn_proto(const QualType &qt) {
+ if (qt.getTypePtr()->getTypeClass() == Type::Paren) {
+ const ParenType *paren_type = static_cast<const ParenType *>(qt.getTypePtr());
+ if (paren_type->getInnerType()->getTypeClass() == Type::FunctionProto) {
+ return true;
+ }
+ } else if (qt.getTypePtr()->getTypeClass() == Type::Attributed) {
+ const AttributedType *attr_type = static_cast<const AttributedType *>(qt.getTypePtr());
+ return qual_type_child_is_fn_proto(attr_type->getEquivalentType());
+ }
+ return false;
+}
+
+static AstNode* trans_c_cast(Context *c, const SourceLocation &source_location, QualType dest_type,
+ QualType src_type, AstNode *expr)
+{
+ if (qual_types_equal(dest_type, src_type)) {
+ return expr;
+ }
+ if (qual_type_is_ptr(dest_type) && qual_type_is_ptr(src_type)) {
+ AstNode *ptr_cast_node = trans_create_node_builtin_fn_call_str(c, "ptrCast");
+ ptr_cast_node->data.fn_call_expr.params.append(trans_qual_type(c, dest_type, source_location));
+ ptr_cast_node->data.fn_call_expr.params.append(expr);
+ return ptr_cast_node;
+ }
+ // TODO: maybe widen to increase size
+ // TODO: maybe bitcast to change sign
+ // TODO: maybe truncate to reduce size
+ return trans_create_node_fn_call_1(c, trans_qual_type(c, dest_type, source_location), expr);
+}
+
+static bool c_is_signed_integer(Context *c, QualType qt) {
+ const Type *c_type = qual_type_canon(qt);
+ if (c_type->getTypeClass() != Type::Builtin)
+ return false;
+ const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(c_type);
+ switch (builtin_ty->getKind()) {
+ case BuiltinType::SChar:
+ case BuiltinType::Short:
+ case BuiltinType::Int:
+ case BuiltinType::Long:
+ case BuiltinType::LongLong:
+ case BuiltinType::Int128:
+ case BuiltinType::WChar_S:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool c_is_unsigned_integer(Context *c, QualType qt) {
+ const Type *c_type = qual_type_canon(qt);
+ if (c_type->getTypeClass() != Type::Builtin)
+ return false;
+ const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(c_type);
+ switch (builtin_ty->getKind()) {
+ case BuiltinType::Char_U:
+ case BuiltinType::UChar:
+ case BuiltinType::Char_S:
+ case BuiltinType::UShort:
+ case BuiltinType::UInt:
+ case BuiltinType::ULong:
+ case BuiltinType::ULongLong:
+ case BuiltinType::UInt128:
+ case BuiltinType::WChar_U:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool c_is_float(Context *c, QualType qt) {
+ const Type *c_type = qt.getTypePtr();
+ if (c_type->getTypeClass() != Type::Builtin)
+ return false;
+ const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(c_type);
+ switch (builtin_ty->getKind()) {
+ case BuiltinType::Half:
+ case BuiltinType::Float:
+ case BuiltinType::Double:
+ case BuiltinType::Float128:
+ case BuiltinType::LongDouble:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) {
+ if (c_is_signed_integer(c, qt) || c_is_float(c, qt)) {
+ // float and signed integer overflow is undefined behavior.
+ return false;
+ } else {
+ // unsigned integer overflow wraps around.
+ return true;
+ }
+}
+
+static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) {
+ switch (ty->getTypeClass()) {
+ case Type::Builtin:
+ {
+ const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(ty);
+ switch (builtin_ty->getKind()) {
+ case BuiltinType::Void:
+ return trans_create_node_symbol_str(c, "c_void");
+ case BuiltinType::Bool:
+ return trans_create_node_symbol_str(c, "bool");
+ case BuiltinType::Char_U:
+ case BuiltinType::UChar:
+ case BuiltinType::Char_S:
+ return trans_create_node_symbol_str(c, "u8");
+ case BuiltinType::SChar:
+ return trans_create_node_symbol_str(c, "i8");
+ case BuiltinType::UShort:
+ return trans_create_node_symbol_str(c, "c_ushort");
+ case BuiltinType::UInt:
+ return trans_create_node_symbol_str(c, "c_uint");
+ case BuiltinType::ULong:
+ return trans_create_node_symbol_str(c, "c_ulong");
+ case BuiltinType::ULongLong:
+ return trans_create_node_symbol_str(c, "c_ulonglong");
+ case BuiltinType::Short:
+ return trans_create_node_symbol_str(c, "c_short");
+ case BuiltinType::Int:
+ return trans_create_node_symbol_str(c, "c_int");
+ case BuiltinType::Long:
+ return trans_create_node_symbol_str(c, "c_long");
+ case BuiltinType::LongLong:
+ return trans_create_node_symbol_str(c, "c_longlong");
+ case BuiltinType::UInt128:
+ return trans_create_node_symbol_str(c, "u128");
+ case BuiltinType::Int128:
+ return trans_create_node_symbol_str(c, "i128");
+ case BuiltinType::Float:
+ return trans_create_node_symbol_str(c, "f32");
+ case BuiltinType::Double:
+ return trans_create_node_symbol_str(c, "f64");
+ case BuiltinType::Float128:
+ return trans_create_node_symbol_str(c, "f128");
+ case BuiltinType::Float16:
+ return trans_create_node_symbol_str(c, "f16");
+ case BuiltinType::LongDouble:
+ return trans_create_node_symbol_str(c, "c_longdouble");
+ case BuiltinType::WChar_U:
+ case BuiltinType::Char16:
+ case BuiltinType::Char32:
+ case BuiltinType::WChar_S:
+ case BuiltinType::Half:
+ case BuiltinType::NullPtr:
+ case BuiltinType::ObjCId:
+ case BuiltinType::ObjCClass:
+ case BuiltinType::ObjCSel:
+ case BuiltinType::OMPArraySection:
+ case BuiltinType::Dependent:
+ case BuiltinType::Overload:
+ case BuiltinType::BoundMember:
+ case BuiltinType::PseudoObject:
+ case BuiltinType::UnknownAny:
+ case BuiltinType::BuiltinFn:
+ case BuiltinType::ARCUnbridgedCast:
+
+ case BuiltinType::OCLImage1dRO:
+ case BuiltinType::OCLImage1dArrayRO:
+ case BuiltinType::OCLImage1dBufferRO:
+ case BuiltinType::OCLImage2dRO:
+ case BuiltinType::OCLImage2dArrayRO:
+ case BuiltinType::OCLImage2dDepthRO:
+ case BuiltinType::OCLImage2dArrayDepthRO:
+ case BuiltinType::OCLImage2dMSAARO:
+ case BuiltinType::OCLImage2dArrayMSAARO:
+ case BuiltinType::OCLImage2dMSAADepthRO:
+ case BuiltinType::OCLImage2dArrayMSAADepthRO:
+ case BuiltinType::OCLImage3dRO:
+ case BuiltinType::OCLImage1dWO:
+ case BuiltinType::OCLImage1dArrayWO:
+ case BuiltinType::OCLImage1dBufferWO:
+ case BuiltinType::OCLImage2dWO:
+ case BuiltinType::OCLImage2dArrayWO:
+ case BuiltinType::OCLImage2dDepthWO:
+ case BuiltinType::OCLImage2dArrayDepthWO:
+ case BuiltinType::OCLImage2dMSAAWO:
+ case BuiltinType::OCLImage2dArrayMSAAWO:
+ case BuiltinType::OCLImage2dMSAADepthWO:
+ case BuiltinType::OCLImage2dArrayMSAADepthWO:
+ case BuiltinType::OCLImage3dWO:
+ case BuiltinType::OCLImage1dRW:
+ case BuiltinType::OCLImage1dArrayRW:
+ case BuiltinType::OCLImage1dBufferRW:
+ case BuiltinType::OCLImage2dRW:
+ case BuiltinType::OCLImage2dArrayRW:
+ case BuiltinType::OCLImage2dDepthRW:
+ case BuiltinType::OCLImage2dArrayDepthRW:
+ case BuiltinType::OCLImage2dMSAARW:
+ case BuiltinType::OCLImage2dArrayMSAARW:
+ case BuiltinType::OCLImage2dMSAADepthRW:
+ case BuiltinType::OCLImage2dArrayMSAADepthRW:
+ case BuiltinType::OCLImage3dRW:
+ case BuiltinType::OCLSampler:
+ case BuiltinType::OCLEvent:
+ case BuiltinType::OCLClkEvent:
+ case BuiltinType::OCLQueue:
+ case BuiltinType::OCLReserveID:
+ emit_warning(c, source_loc, "unsupported builtin type");
+ return nullptr;
+ }
+ break;
+ }
+ case Type::Pointer:
+ {
+ const PointerType *pointer_ty = static_cast<const PointerType*>(ty);
+ QualType child_qt = pointer_ty->getPointeeType();
+ AstNode *child_node = trans_qual_type(c, child_qt, source_loc);
+ if (child_node == nullptr) {
+ emit_warning(c, source_loc, "pointer to unsupported type");
+ return nullptr;
+ }
+
+ if (qual_type_child_is_fn_proto(child_qt)) {
+ return trans_create_node_prefix_op(c, PrefixOpMaybe, child_node);
+ }
+
+ AstNode *pointer_node = trans_create_node_addr_of(c, child_qt.isConstQualified(),
+ child_qt.isVolatileQualified(), child_node);
+ return trans_create_node_prefix_op(c, PrefixOpMaybe, pointer_node);
+ }
+ case Type::Typedef:
+ {
+ const TypedefType *typedef_ty = static_cast<const TypedefType*>(ty);
+ const TypedefNameDecl *typedef_decl = typedef_ty->getDecl();
+ return resolve_typedef_decl(c, typedef_decl);
+ }
+ case Type::Elaborated:
+ {
+ const ElaboratedType *elaborated_ty = static_cast<const ElaboratedType*>(ty);
+ switch (elaborated_ty->getKeyword()) {
+ case ETK_Struct:
+ case ETK_Enum:
+ case ETK_Union:
+ return trans_qual_type(c, elaborated_ty->getNamedType(), source_loc);
+ case ETK_Interface:
+ case ETK_Class:
+ case ETK_Typename:
+ case ETK_None:
+ emit_warning(c, source_loc, "unsupported elaborated type");
+ return nullptr;
+ }
+ }
+ case Type::FunctionProto:
+ {
+ const FunctionProtoType *fn_proto_ty = static_cast<const FunctionProtoType*>(ty);
+
+ AstNode *proto_node = trans_create_node(c, NodeTypeFnProto);
+ switch (fn_proto_ty->getCallConv()) {
+ case CC_C: // __attribute__((cdecl))
+ proto_node->data.fn_proto.cc = CallingConventionC;
+ proto_node->data.fn_proto.is_extern = true;
+ break;
+ case CC_X86StdCall: // __attribute__((stdcall))
+ proto_node->data.fn_proto.cc = CallingConventionStdcall;
+ break;
+ case CC_X86FastCall: // __attribute__((fastcall))
+ emit_warning(c, source_loc, "unsupported calling convention: x86 fastcall");
+ return nullptr;
+ case CC_X86ThisCall: // __attribute__((thiscall))
+ emit_warning(c, source_loc, "unsupported calling convention: x86 thiscall");
+ return nullptr;
+ case CC_X86VectorCall: // __attribute__((vectorcall))
+ emit_warning(c, source_loc, "unsupported calling convention: x86 vectorcall");
+ return nullptr;
+ case CC_X86Pascal: // __attribute__((pascal))
+ emit_warning(c, source_loc, "unsupported calling convention: x86 pascal");
+ return nullptr;
+ case CC_Win64: // __attribute__((ms_abi))
+ emit_warning(c, source_loc, "unsupported calling convention: win64");
+ return nullptr;
+ case CC_X86_64SysV: // __attribute__((sysv_abi))
+ emit_warning(c, source_loc, "unsupported calling convention: x86 64sysv");
+ return nullptr;
+ case CC_X86RegCall:
+ emit_warning(c, source_loc, "unsupported calling convention: x86 reg");
+ return nullptr;
+ case CC_AAPCS: // __attribute__((pcs("aapcs")))
+ emit_warning(c, source_loc, "unsupported calling convention: aapcs");
+ return nullptr;
+ case CC_AAPCS_VFP: // __attribute__((pcs("aapcs-vfp")))
+ emit_warning(c, source_loc, "unsupported calling convention: aapcs-vfp");
+ return nullptr;
+ case CC_IntelOclBicc: // __attribute__((intel_ocl_bicc))
+ emit_warning(c, source_loc, "unsupported calling convention: intel_ocl_bicc");
+ return nullptr;
+ case CC_SpirFunction: // default for OpenCL functions on SPIR target
+ emit_warning(c, source_loc, "unsupported calling convention: SPIR function");
+ return nullptr;
+ case CC_OpenCLKernel:
+ emit_warning(c, source_loc, "unsupported calling convention: OpenCLKernel");
+ return nullptr;
+ case CC_Swift:
+ emit_warning(c, source_loc, "unsupported calling convention: Swift");
+ return nullptr;
+ case CC_PreserveMost:
+ emit_warning(c, source_loc, "unsupported calling convention: PreserveMost");
+ return nullptr;
+ case CC_PreserveAll:
+ emit_warning(c, source_loc, "unsupported calling convention: PreserveAll");
+ return nullptr;
+ }
+
+ proto_node->data.fn_proto.is_var_args = fn_proto_ty->isVariadic();
+ size_t param_count = fn_proto_ty->getNumParams();
+
+ if (fn_proto_ty->getNoReturnAttr()) {
+ proto_node->data.fn_proto.return_type = trans_create_node_symbol_str(c, "noreturn");
+ } else {
+ proto_node->data.fn_proto.return_type = trans_qual_type(c, fn_proto_ty->getReturnType(),
+ source_loc);
+ if (proto_node->data.fn_proto.return_type == nullptr) {
+ emit_warning(c, source_loc, "unsupported function proto return type");
+ return nullptr;
+ }
+ // convert c_void to actual void (only for return type)
+ // we do want to look at the AstNode instead of QualType, because
+ // if they do something like:
+ // typedef Foo void;
+ // void foo(void) -> Foo;
+ // we want to keep the return type AST node.
+ if (is_c_void_type(proto_node->data.fn_proto.return_type)) {
+ proto_node->data.fn_proto.return_type = nullptr;
+ }
+ }
+
+ //emit_warning(c, source_loc, "TODO figure out fn prototype fn name");
+ const char *fn_name = nullptr;
+ if (fn_name != nullptr) {
+ proto_node->data.fn_proto.name = buf_create_from_str(fn_name);
+ }
+
+ for (size_t i = 0; i < param_count; i += 1) {
+ QualType qt = fn_proto_ty->getParamType(i);
+ AstNode *param_type_node = trans_qual_type(c, qt, source_loc);
+
+ if (param_type_node == nullptr) {
+ emit_warning(c, source_loc, "unresolved function proto parameter type");
+ return nullptr;
+ }
+
+ AstNode *param_node = trans_create_node(c, NodeTypeParamDecl);
+ //emit_warning(c, source_loc, "TODO figure out fn prototype param name");
+ const char *param_name = nullptr;
+ if (param_name != nullptr) {
+ param_node->data.param_decl.name = buf_create_from_str(param_name);
+ }
+ param_node->data.param_decl.is_noalias = qt.isRestrictQualified();
+ param_node->data.param_decl.type = param_type_node;
+ proto_node->data.fn_proto.params.append(param_node);
+ }
+ // TODO check for always_inline attribute
+ // TODO check for align attribute
+
+ return proto_node;
+ }
+ case Type::Record:
+ {
+ const RecordType *record_ty = static_cast<const RecordType*>(ty);
+ return resolve_record_decl(c, record_ty->getDecl());
+ }
+ case Type::Enum:
+ {
+ const EnumType *enum_ty = static_cast<const EnumType*>(ty);
+ return resolve_enum_decl(c, enum_ty->getDecl());
+ }
+ case Type::ConstantArray:
+ {
+ const ConstantArrayType *const_arr_ty = static_cast<const ConstantArrayType *>(ty);
+ AstNode *child_type_node = trans_qual_type(c, const_arr_ty->getElementType(), source_loc);
+ if (child_type_node == nullptr) {
+ emit_warning(c, source_loc, "unresolved array element type");
+ return nullptr;
+ }
+ uint64_t size = const_arr_ty->getSize().getLimitedValue();
+ AstNode *size_node = trans_create_node_unsigned(c, size);
+ return trans_create_node_array_type(c, size_node, child_type_node);
+ }
+ case Type::Paren:
+ {
+ const ParenType *paren_ty = static_cast<const ParenType *>(ty);
+ return trans_qual_type(c, paren_ty->getInnerType(), source_loc);
+ }
+ case Type::Decayed:
+ {
+ const DecayedType *decayed_ty = static_cast<const DecayedType *>(ty);
+ return trans_qual_type(c, decayed_ty->getDecayedType(), source_loc);
+ }
+ case Type::Attributed:
+ {
+ const AttributedType *attributed_ty = static_cast<const AttributedType *>(ty);
+ return trans_qual_type(c, attributed_ty->getEquivalentType(), source_loc);
+ }
+ case Type::BlockPointer:
+ case Type::LValueReference:
+ case Type::RValueReference:
+ case Type::MemberPointer:
+ case Type::IncompleteArray:
+ case Type::VariableArray:
+ case Type::DependentSizedArray:
+ case Type::DependentSizedExtVector:
+ case Type::Vector:
+ case Type::ExtVector:
+ case Type::FunctionNoProto:
+ case Type::UnresolvedUsing:
+ case Type::Adjusted:
+ case Type::TypeOfExpr:
+ case Type::TypeOf:
+ case Type::Decltype:
+ case Type::UnaryTransform:
+ case Type::TemplateTypeParm:
+ case Type::SubstTemplateTypeParm:
+ case Type::SubstTemplateTypeParmPack:
+ case Type::TemplateSpecialization:
+ case Type::Auto:
+ case Type::InjectedClassName:
+ case Type::DependentName:
+ case Type::DependentTemplateSpecialization:
+ case Type::PackExpansion:
+ case Type::ObjCObject:
+ case Type::ObjCInterface:
+ case Type::Complex:
+ case Type::ObjCObjectPointer:
+ case Type::Atomic:
+ case Type::Pipe:
+ case Type::ObjCTypeParam:
+ case Type::DeducedTemplateSpecialization:
+ case Type::DependentAddressSpace:
+ emit_warning(c, source_loc, "unsupported type: '%s'", ty->getTypeClassName());
+ return nullptr;
+ }
+ zig_unreachable();
+}
+
+static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc) {
+ return trans_type(c, qt.getTypePtr(), source_loc);
+}
+
+static int trans_compound_stmt_inline(Context *c, TransScope *scope, const CompoundStmt *stmt,
+ AstNode *block_node, TransScope **out_node_scope)
+{
+ assert(block_node->type == NodeTypeBlock);
+ for (CompoundStmt::const_body_iterator it = stmt->body_begin(), end_it = stmt->body_end(); it != end_it; ++it) {
+ AstNode *child_node;
+ scope = trans_stmt(c, scope, *it, &child_node);
+ if (scope == nullptr)
+ return ErrorUnexpected;
+ if (child_node != nullptr)
+ block_node->data.block.statements.append(child_node);
+ }
+ if (out_node_scope != nullptr) {
+ *out_node_scope = scope;
+ }
+ return ErrorNone;
+}
+
+static AstNode *trans_compound_stmt(Context *c, TransScope *scope, const CompoundStmt *stmt,
+ TransScope **out_node_scope)
+{
+ TransScopeBlock *child_scope_block = trans_scope_block_create(c, scope);
+ if (trans_compound_stmt_inline(c, &child_scope_block->base, stmt, child_scope_block->node, out_node_scope))
+ return nullptr;
+ return child_scope_block->node;
+}
+
+static AstNode *trans_return_stmt(Context *c, TransScope *scope, const ReturnStmt *stmt) {
+ const Expr *value_expr = stmt->getRetValue();
+ if (value_expr == nullptr) {
+ return trans_create_node(c, NodeTypeReturnExpr);
+ } else {
+ AstNode *return_node = trans_create_node(c, NodeTypeReturnExpr);
+ return_node->data.return_expr.expr = trans_expr(c, ResultUsedYes, scope, value_expr, TransRValue);
+ if (return_node->data.return_expr.expr == nullptr)
+ return nullptr;
+ return return_node;
+ }
+}
+
+static AstNode *trans_integer_literal(Context *c, const IntegerLiteral *stmt) {
+ llvm::APSInt result;
+ if (!stmt->EvaluateAsInt(result, *c->ctx)) {
+ emit_warning(c, stmt->getLocStart(), "invalid integer literal");
+ return nullptr;
+ }
+ return trans_create_node_apint(c, result);
+}
+
+static AstNode *trans_conditional_operator(Context *c, ResultUsed result_used, TransScope *scope,
+ const ConditionalOperator *stmt)
+{
+ AstNode *node = trans_create_node(c, NodeTypeIfBoolExpr);
+
+ Expr *cond_expr = stmt->getCond();
+ Expr *true_expr = stmt->getTrueExpr();
+ Expr *false_expr = stmt->getFalseExpr();
+
+ node->data.if_bool_expr.condition = trans_expr(c, ResultUsedYes, scope, cond_expr, TransRValue);
+ if (node->data.if_bool_expr.condition == nullptr)
+ return nullptr;
+
+ node->data.if_bool_expr.then_block = trans_expr(c, result_used, scope, true_expr, TransRValue);
+ if (node->data.if_bool_expr.then_block == nullptr)
+ return nullptr;
+
+ node->data.if_bool_expr.else_node = trans_expr(c, result_used, scope, false_expr, TransRValue);
+ if (node->data.if_bool_expr.else_node == nullptr)
+ return nullptr;
+
+ return maybe_suppress_result(c, result_used, node);
+}
+
+static AstNode *trans_create_bin_op(Context *c, TransScope *scope, Expr *lhs, BinOpType bin_op, Expr *rhs) {
+ AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
+ node->data.bin_op_expr.bin_op = bin_op;
+
+ node->data.bin_op_expr.op1 = trans_expr(c, ResultUsedYes, scope, lhs, TransRValue);
+ if (node->data.bin_op_expr.op1 == nullptr)
+ return nullptr;
+
+ node->data.bin_op_expr.op2 = trans_expr(c, ResultUsedYes, scope, rhs, TransRValue);
+ if (node->data.bin_op_expr.op2 == nullptr)
+ return nullptr;
+
+ return node;
+}
+
+static AstNode *trans_create_assign(Context *c, ResultUsed result_used, TransScope *scope, Expr *lhs, Expr *rhs) {
+ if (result_used == ResultUsedNo) {
+ // common case
+ AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
+ node->data.bin_op_expr.bin_op = BinOpTypeAssign;
+
+ node->data.bin_op_expr.op1 = trans_expr(c, ResultUsedYes, scope, lhs, TransLValue);
+ if (node->data.bin_op_expr.op1 == nullptr)
+ return nullptr;
+
+ node->data.bin_op_expr.op2 = trans_expr(c, ResultUsedYes, scope, rhs, TransRValue);
+ if (node->data.bin_op_expr.op2 == nullptr)
+ return nullptr;
+
+ return node;
+ } else {
+ // worst case
+ // c: lhs = rhs
+ // zig: {
+ // zig: const _tmp = rhs;
+ // zig: lhs = _tmp;
+ // zig: _tmp
+ // zig: }
+
+ TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
+
+ // const _tmp = rhs;
+ AstNode *rhs_node = trans_expr(c, ResultUsedYes, &child_scope->base, rhs, TransRValue);
+ if (rhs_node == nullptr) return nullptr;
+ // TODO: avoid name collisions with generated variable names
+ Buf* tmp_var_name = buf_create_from_str("_tmp");
+ AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, rhs_node);
+ child_scope->node->data.block.statements.append(tmp_var_decl);
+
+ // lhs = _tmp;
+ AstNode *lhs_node = trans_expr(c, ResultUsedYes, &child_scope->base, lhs, TransLValue);
+ if (lhs_node == nullptr) return nullptr;
+ child_scope->node->data.block.statements.append(
+ trans_create_node_bin_op(c, lhs_node, BinOpTypeAssign,
+ trans_create_node_symbol(c, tmp_var_name)));
+
+ // _tmp
+ child_scope->node->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name));
+ child_scope->node->data.block.last_statement_is_result_expression = true;
+
+ return child_scope->node;
+ }
+}
+
+static AstNode *trans_create_shift_op(Context *c, TransScope *scope, QualType result_type,
+ Expr *lhs_expr, BinOpType bin_op, Expr *rhs_expr)
+{
+ const SourceLocation &rhs_location = rhs_expr->getLocStart();
+ AstNode *rhs_type = qual_type_to_log2_int_ref(c, result_type, rhs_location);
+ // lhs >> u5(rh)
+
+ AstNode *lhs = trans_expr(c, ResultUsedYes, scope, lhs_expr, TransLValue);
+ if (lhs == nullptr) return nullptr;
+
+ AstNode *rhs = trans_expr(c, ResultUsedYes, scope, rhs_expr, TransRValue);
+ if (rhs == nullptr) return nullptr;
+ AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs);
+
+ return trans_create_node_bin_op(c, lhs, bin_op, coerced_rhs);
+}
+
+static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransScope *scope, const BinaryOperator *stmt) {
+ switch (stmt->getOpcode()) {
+ case BO_PtrMemD:
+ emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_PtrMemD");
+ return nullptr;
+ case BO_PtrMemI:
+ emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_PtrMemI");
+ return nullptr;
+ case BO_Mul:
+ return trans_create_bin_op(c, scope, stmt->getLHS(),
+ qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeMultWrap : BinOpTypeMult,
+ stmt->getRHS());
+ case BO_Div:
+ if (qual_type_has_wrapping_overflow(c, stmt->getType())) {
+ // unsigned/float division uses the operator
+ return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeDiv, stmt->getRHS());
+ } else {
+ // signed integer division uses @divTrunc
+ AstNode *fn_call = trans_create_node_builtin_fn_call_str(c, "divTrunc");
+ AstNode *lhs = trans_expr(c, ResultUsedYes, scope, stmt->getLHS(), TransLValue);
+ if (lhs == nullptr) return nullptr;
+ fn_call->data.fn_call_expr.params.append(lhs);
+ AstNode *rhs = trans_expr(c, ResultUsedYes, scope, stmt->getRHS(), TransLValue);
+ if (rhs == nullptr) return nullptr;
+ fn_call->data.fn_call_expr.params.append(rhs);
+ return fn_call;
+ }
+ case BO_Rem:
+ if (qual_type_has_wrapping_overflow(c, stmt->getType())) {
+ // unsigned/float division uses the operator
+ return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeMod, stmt->getRHS());
+ } else {
+ // signed integer division uses @rem
+ AstNode *fn_call = trans_create_node_builtin_fn_call_str(c, "rem");
+ AstNode *lhs = trans_expr(c, ResultUsedYes, scope, stmt->getLHS(), TransLValue);
+ if (lhs == nullptr) return nullptr;
+ fn_call->data.fn_call_expr.params.append(lhs);
+ AstNode *rhs = trans_expr(c, ResultUsedYes, scope, stmt->getRHS(), TransLValue);
+ if (rhs == nullptr) return nullptr;
+ fn_call->data.fn_call_expr.params.append(rhs);
+ return fn_call;
+ }
+ case BO_Add:
+ return trans_create_bin_op(c, scope, stmt->getLHS(),
+ qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeAddWrap : BinOpTypeAdd,
+ stmt->getRHS());
+ case BO_Sub:
+ return trans_create_bin_op(c, scope, stmt->getLHS(),
+ qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeSubWrap : BinOpTypeSub,
+ stmt->getRHS());
+ case BO_Shl:
+ return trans_create_shift_op(c, scope, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftLeft, stmt->getRHS());
+ case BO_Shr:
+ return trans_create_shift_op(c, scope, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftRight, stmt->getRHS());
+ case BO_LT:
+ return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpLessThan, stmt->getRHS());
+ case BO_GT:
+ return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpGreaterThan, stmt->getRHS());
+ case BO_LE:
+ return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpLessOrEq, stmt->getRHS());
+ case BO_GE:
+ return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpGreaterOrEq, stmt->getRHS());
+ case BO_EQ:
+ return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpEq, stmt->getRHS());
+ case BO_NE:
+ return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpNotEq, stmt->getRHS());
+ case BO_And:
+ return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinAnd, stmt->getRHS());
+ case BO_Xor:
+ return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinXor, stmt->getRHS());
+ case BO_Or:
+ return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinOr, stmt->getRHS());
+ case BO_LAnd:
+ return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolAnd, stmt->getRHS());
+ case BO_LOr:
+ // TODO: int vs bool
+ return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS());
+ case BO_Assign:
+ return trans_create_assign(c, result_used, scope, stmt->getLHS(), stmt->getRHS());
+ case BO_Comma:
+ {
+ TransScopeBlock *scope_block = trans_scope_block_create(c, scope);
+ AstNode *lhs = trans_expr(c, ResultUsedNo, &scope_block->base, stmt->getLHS(), TransRValue);
+ if (lhs == nullptr)
+ return nullptr;
+ scope_block->node->data.block.statements.append(maybe_suppress_result(c, ResultUsedNo, lhs));
+
+ AstNode *rhs = trans_expr(c, result_used, &scope_block->base, stmt->getRHS(), TransRValue);
+ if (rhs == nullptr)
+ return nullptr;
+ scope_block->node->data.block.statements.append(maybe_suppress_result(c, result_used, rhs));
+
+ scope_block->node->data.block.last_statement_is_result_expression = true;
+ return scope_block->node;
+ }
+ case BO_MulAssign:
+ case BO_DivAssign:
+ case BO_RemAssign:
+ case BO_AddAssign:
+ case BO_SubAssign:
+ case BO_ShlAssign:
+ case BO_ShrAssign:
+ case BO_AndAssign:
+ case BO_XorAssign:
+ case BO_OrAssign:
+ zig_unreachable();
+ }
+
+ zig_unreachable();
+}
+
+static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result_used, TransScope *scope,
+ const CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op)
+{
+ const SourceLocation &rhs_location = stmt->getRHS()->getLocStart();
+ AstNode *rhs_type = qual_type_to_log2_int_ref(c, stmt->getComputationLHSType(), rhs_location);
+
+ bool use_intermediate_casts = stmt->getComputationLHSType().getTypePtr() != stmt->getComputationResultType().getTypePtr();
+ if (!use_intermediate_casts && result_used == ResultUsedNo) {
+ // simple common case, where the C and Zig are identical:
+ // lhs >>= rhs
+ AstNode *lhs = trans_expr(c, ResultUsedYes, scope, stmt->getLHS(), TransLValue);
+ if (lhs == nullptr) return nullptr;
+
+ AstNode *rhs = trans_expr(c, ResultUsedYes, scope, stmt->getRHS(), TransRValue);
+ if (rhs == nullptr) return nullptr;
+ AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs);
+
+ return trans_create_node_bin_op(c, lhs, assign_op, coerced_rhs);
+ } else {
+ // need more complexity. worst case, this looks like this:
+ // c: lhs >>= rhs
+ // zig: {
+ // zig: const _ref = &lhs;
+ // zig: *_ref = result_type(operation_type(*_ref) >> u5(rhs));
+ // zig: *_ref
+ // zig: }
+ // where u5 is the appropriate type
+
+ TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
+
+ // const _ref = &lhs;
+ AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getLHS(), TransLValue);
+ if (lhs == nullptr) return nullptr;
+ AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs);
+ // TODO: avoid name collisions with generated variable names
+ Buf* tmp_var_name = buf_create_from_str("_ref");
+ AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs);
+ child_scope->node->data.block.statements.append(tmp_var_decl);
+
+ // *_ref = result_type(operation_type(*_ref) >> u5(rhs));
+
+ AstNode *rhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getRHS(), TransRValue);
+ if (rhs == nullptr) return nullptr;
+ AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs);
+
+ // operation_type(*_ref)
+ AstNode *operation_type_cast = trans_c_cast(c, rhs_location,
+ stmt->getComputationLHSType(),
+ stmt->getLHS()->getType(),
+ trans_create_node_prefix_op(c, PrefixOpDereference,
+ trans_create_node_symbol(c, tmp_var_name)));
+
+ // result_type(... >> u5(rhs))
+ AstNode *result_type_cast = trans_c_cast(c, rhs_location,
+ stmt->getComputationResultType(),
+ stmt->getComputationLHSType(),
+ trans_create_node_bin_op(c,
+ operation_type_cast,
+ bin_op,
+ coerced_rhs));
+
+ // *_ref = ...
+ AstNode *assign_statement = trans_create_node_bin_op(c,
+ trans_create_node_prefix_op(c, PrefixOpDereference,
+ trans_create_node_symbol(c, tmp_var_name)),
+ BinOpTypeAssign, result_type_cast);
+
+ child_scope->node->data.block.statements.append(assign_statement);
+
+ if (result_used == ResultUsedYes) {
+ // *_ref
+ child_scope->node->data.block.statements.append(
+ trans_create_node_prefix_op(c, PrefixOpDereference,
+ trans_create_node_symbol(c, tmp_var_name)));
+ child_scope->node->data.block.last_statement_is_result_expression = true;
+ }
+
+ return child_scope->node;
+ }
+}
+
+static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used, TransScope *scope,
+ const CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op)
+{
+ if (result_used == ResultUsedNo) {
+ // simple common case, where the C and Zig are identical:
+ // lhs += rhs
+ AstNode *lhs = trans_expr(c, ResultUsedYes, scope, stmt->getLHS(), TransLValue);
+ if (lhs == nullptr) return nullptr;
+ AstNode *rhs = trans_expr(c, ResultUsedYes, scope, stmt->getRHS(), TransRValue);
+ if (rhs == nullptr) return nullptr;
+ return trans_create_node_bin_op(c, lhs, assign_op, rhs);
+ } else {
+ // need more complexity. worst case, this looks like this:
+ // c: lhs += rhs
+ // zig: {
+ // zig: const _ref = &lhs;
+ // zig: *_ref = *_ref + rhs;
+ // zig: *_ref
+ // zig: }
+
+ TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
+
+ // const _ref = &lhs;
+ AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getLHS(), TransLValue);
+ if (lhs == nullptr) return nullptr;
+ AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs);
+ // TODO: avoid name collisions with generated variable names
+ Buf* tmp_var_name = buf_create_from_str("_ref");
+ AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs);
+ child_scope->node->data.block.statements.append(tmp_var_decl);
+
+ // *_ref = *_ref + rhs;
+
+ AstNode *rhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getRHS(), TransRValue);
+ if (rhs == nullptr) return nullptr;
+
+ AstNode *assign_statement = trans_create_node_bin_op(c,
+ trans_create_node_prefix_op(c, PrefixOpDereference,
+ trans_create_node_symbol(c, tmp_var_name)),
+ BinOpTypeAssign,
+ trans_create_node_bin_op(c,
+ trans_create_node_prefix_op(c, PrefixOpDereference,
+ trans_create_node_symbol(c, tmp_var_name)),
+ bin_op,
+ rhs));
+ child_scope->node->data.block.statements.append(assign_statement);
+
+ // *_ref
+ child_scope->node->data.block.statements.append(
+ trans_create_node_prefix_op(c, PrefixOpDereference,
+ trans_create_node_symbol(c, tmp_var_name)));
+ child_scope->node->data.block.last_statement_is_result_expression = true;
+
+ return child_scope->node;
+ }
+}
+
+
+static AstNode *trans_compound_assign_operator(Context *c, ResultUsed result_used, TransScope *scope,
+ const CompoundAssignOperator *stmt)
+{
+ switch (stmt->getOpcode()) {
+ case BO_MulAssign:
+ if (qual_type_has_wrapping_overflow(c, stmt->getType()))
+ return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignTimesWrap, BinOpTypeMultWrap);
+ else
+ return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignTimes, BinOpTypeMult);
+ case BO_DivAssign:
+ emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_DivAssign");
+ return nullptr;
+ case BO_RemAssign:
+ emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_RemAssign");
+ return nullptr;
+ case BO_AddAssign:
+ if (qual_type_has_wrapping_overflow(c, stmt->getType()))
+ return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap, BinOpTypeAddWrap);
+ else
+ return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignPlus, BinOpTypeAdd);
+ case BO_SubAssign:
+ if (qual_type_has_wrapping_overflow(c, stmt->getType()))
+ return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap, BinOpTypeSubWrap);
+ else
+ return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignMinus, BinOpTypeSub);
+ case BO_ShlAssign:
+ return trans_create_compound_assign_shift(c, result_used, scope, stmt, BinOpTypeAssignBitShiftLeft, BinOpTypeBitShiftLeft);
+ case BO_ShrAssign:
+ return trans_create_compound_assign_shift(c, result_used, scope, stmt, BinOpTypeAssignBitShiftRight, BinOpTypeBitShiftRight);
+ case BO_AndAssign:
+ return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitAnd, BinOpTypeBinAnd);
+ case BO_XorAssign:
+ return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitXor, BinOpTypeBinXor);
+ case BO_OrAssign:
+ return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitOr, BinOpTypeBinOr);
+ case BO_PtrMemD:
+ case BO_PtrMemI:
+ case BO_Assign:
+ case BO_Mul:
+ case BO_Div:
+ case BO_Rem:
+ case BO_Add:
+ case BO_Sub:
+ case BO_Shl:
+ case BO_Shr:
+ case BO_LT:
+ case BO_GT:
+ case BO_LE:
+ case BO_GE:
+ case BO_EQ:
+ case BO_NE:
+ case BO_And:
+ case BO_Xor:
+ case BO_Or:
+ case BO_LAnd:
+ case BO_LOr:
+ case BO_Comma:
+ zig_unreachable();
+ }
+
+ zig_unreachable();
+}
+
+static AstNode *trans_implicit_cast_expr(Context *c, TransScope *scope, const ImplicitCastExpr *stmt) {
+ switch (stmt->getCastKind()) {
+ case CK_LValueToRValue:
+ return trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue);
+ case CK_IntegralCast:
+ {
+ AstNode *target_node = trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue);
+ if (target_node == nullptr)
+ return nullptr;
+ return trans_c_cast(c, stmt->getExprLoc(), stmt->getType(),
+ stmt->getSubExpr()->getType(), target_node);
+ }
+ case CK_FunctionToPointerDecay:
+ case CK_ArrayToPointerDecay:
+ {
+ AstNode *target_node = trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue);
+ if (target_node == nullptr)
+ return nullptr;
+ return target_node;
+ }
+ case CK_BitCast:
+ {
+ AstNode *target_node = trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue);
+ if (target_node == nullptr)
+ return nullptr;
+
+ if (expr_types_equal(c, stmt, stmt->getSubExpr())) {
+ return target_node;
+ }
+
+ AstNode *dest_type_node = get_expr_type(c, stmt);
+
+ AstNode *node = trans_create_node_builtin_fn_call_str(c, "ptrCast");
+ node->data.fn_call_expr.params.append(dest_type_node);
+ node->data.fn_call_expr.params.append(target_node);
+ return node;
+ }
+ case CK_NullToPointer:
+ return trans_create_node(c, NodeTypeNullLiteral);
+ case CK_Dependent:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_Dependent");
+ return nullptr;
+ case CK_LValueBitCast:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_LValueBitCast");
+ return nullptr;
+ case CK_NoOp:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NoOp");
+ return nullptr;
+ case CK_BaseToDerived:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BaseToDerived");
+ return nullptr;
+ case CK_DerivedToBase:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_DerivedToBase");
+ return nullptr;
+ case CK_UncheckedDerivedToBase:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_UncheckedDerivedToBase");
+ return nullptr;
+ case CK_Dynamic:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_Dynamic");
+ return nullptr;
+ case CK_ToUnion:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ToUnion");
+ return nullptr;
+ case CK_NullToMemberPointer:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NullToMemberPointer");
+ return nullptr;
+ case CK_BaseToDerivedMemberPointer:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BaseToDerivedMemberPointer");
+ return nullptr;
+ case CK_DerivedToBaseMemberPointer:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_DerivedToBaseMemberPointer");
+ return nullptr;
+ case CK_MemberPointerToBoolean:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_MemberPointerToBoolean");
+ return nullptr;
+ case CK_ReinterpretMemberPointer:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ReinterpretMemberPointer");
+ return nullptr;
+ case CK_UserDefinedConversion:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_UserDefinedConversion");
+ return nullptr;
+ case CK_ConstructorConversion:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ConstructorConversion");
+ return nullptr;
+ case CK_IntegralToPointer:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralToPointer");
+ return nullptr;
+ case CK_PointerToIntegral:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_PointerToIntegral");
+ return nullptr;
+ case CK_PointerToBoolean:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_PointerToBoolean");
+ return nullptr;
+ case CK_ToVoid:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ToVoid");
+ return nullptr;
+ case CK_VectorSplat:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_VectorSplat");
+ return nullptr;
+ case CK_IntegralToBoolean:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralToBoolean");
+ return nullptr;
+ case CK_IntegralToFloating:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralToFloating");
+ return nullptr;
+ case CK_FloatingToIntegral:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingToIntegral");
+ return nullptr;
+ case CK_FloatingToBoolean:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingToBoolean");
+ return nullptr;
+ case CK_BooleanToSignedIntegral:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BooleanToSignedIntegral");
+ return nullptr;
+ case CK_FloatingCast:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingCast");
+ return nullptr;
+ case CK_CPointerToObjCPointerCast:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_CPointerToObjCPointerCast");
+ return nullptr;
+ case CK_BlockPointerToObjCPointerCast:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BlockPointerToObjCPointerCast");
+ return nullptr;
+ case CK_AnyPointerToBlockPointerCast:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_AnyPointerToBlockPointerCast");
+ return nullptr;
+ case CK_ObjCObjectLValueCast:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ObjCObjectLValueCast");
+ return nullptr;
+ case CK_FloatingRealToComplex:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingRealToComplex");
+ return nullptr;
+ case CK_FloatingComplexToReal:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingComplexToReal");
+ return nullptr;
+ case CK_FloatingComplexToBoolean:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingComplexToBoolean");
+ return nullptr;
+ case CK_FloatingComplexCast:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingComplexCast");
+ return nullptr;
+ case CK_FloatingComplexToIntegralComplex:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingComplexToIntegralComplex");
+ return nullptr;
+ case CK_IntegralRealToComplex:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralRealToComplex");
+ return nullptr;
+ case CK_IntegralComplexToReal:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralComplexToReal");
+ return nullptr;
+ case CK_IntegralComplexToBoolean:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralComplexToBoolean");
+ return nullptr;
+ case CK_IntegralComplexCast:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralComplexCast");
+ return nullptr;
+ case CK_IntegralComplexToFloatingComplex:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralComplexToFloatingComplex");
+ return nullptr;
+ case CK_ARCProduceObject:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ARCProduceObject");
+ return nullptr;
+ case CK_ARCConsumeObject:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ARCConsumeObject");
+ return nullptr;
+ case CK_ARCReclaimReturnedObject:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ARCReclaimReturnedObject");
+ return nullptr;
+ case CK_ARCExtendBlockObject:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ARCExtendBlockObject");
+ return nullptr;
+ case CK_AtomicToNonAtomic:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_AtomicToNonAtomic");
+ return nullptr;
+ case CK_NonAtomicToAtomic:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NonAtomicToAtomic");
+ return nullptr;
+ case CK_CopyAndAutoreleaseBlockObject:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_CopyAndAutoreleaseBlockObject");
+ return nullptr;
+ case CK_BuiltinFnToFnPtr:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BuiltinFnToFnPtr");
+ return nullptr;
+ case CK_ZeroToOCLEvent:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ZeroToOCLEvent");
+ return nullptr;
+ case CK_ZeroToOCLQueue:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ZeroToOCLQueue");
+ return nullptr;
+ case CK_AddressSpaceConversion:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_AddressSpaceConversion");
+ return nullptr;
+ case CK_IntToOCLSampler:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntToOCLSampler");
+ return nullptr;
+ }
+ zig_unreachable();
+}
+
+static AstNode *trans_decl_ref_expr(Context *c, TransScope *scope, const DeclRefExpr *stmt, TransLRValue lrval) {
+ const ValueDecl *value_decl = stmt->getDecl();
+ Buf *c_symbol_name = buf_create_from_str(decl_name(value_decl));
+ Buf *zig_symbol_name = trans_lookup_zig_symbol(c, scope, c_symbol_name);
+ if (lrval == TransLValue) {
+ c->ptr_params.put(zig_symbol_name, true);
+ }
+ return trans_create_node_symbol(c, zig_symbol_name);
+}
+
+static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, TransScope *scope,
+ const UnaryOperator *stmt, BinOpType assign_op)
+{
+ Expr *op_expr = stmt->getSubExpr();
+
+ if (result_used == ResultUsedNo) {
+ // common case
+ // c: expr++
+ // zig: expr += 1
+ return trans_create_node_bin_op(c,
+ trans_expr(c, ResultUsedYes, scope, op_expr, TransLValue),
+ assign_op,
+ trans_create_node_unsigned(c, 1));
+ }
+ // worst case
+ // c: expr++
+ // zig: {
+ // zig: const _ref = &expr;
+ // zig: const _tmp = *_ref;
+ // zig: *_ref += 1;
+ // zig: _tmp
+ // zig: }
+ TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
+
+ // const _ref = &expr;
+ AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, op_expr, TransLValue);
+ if (expr == nullptr) return nullptr;
+ AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr);
+ // TODO: avoid name collisions with generated variable names
+ Buf* ref_var_name = buf_create_from_str("_ref");
+ AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr);
+ child_scope->node->data.block.statements.append(ref_var_decl);
+
+ // const _tmp = *_ref;
+ Buf* tmp_var_name = buf_create_from_str("_tmp");
+ AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr,
+ trans_create_node_prefix_op(c, PrefixOpDereference,
+ trans_create_node_symbol(c, ref_var_name)));
+ child_scope->node->data.block.statements.append(tmp_var_decl);
+
+ // *_ref += 1;
+ AstNode *assign_statement = trans_create_node_bin_op(c,
+ trans_create_node_prefix_op(c, PrefixOpDereference,
+ trans_create_node_symbol(c, ref_var_name)),
+ assign_op,
+ trans_create_node_unsigned(c, 1));
+ child_scope->node->data.block.statements.append(assign_statement);
+
+ // _tmp
+ child_scope->node->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name));
+ child_scope->node->data.block.last_statement_is_result_expression = true;
+
+ return child_scope->node;
+}
+
+static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, TransScope *scope,
+ const UnaryOperator *stmt, BinOpType assign_op)
+{
+ Expr *op_expr = stmt->getSubExpr();
+
+ if (result_used == ResultUsedNo) {
+ // common case
+ // c: ++expr
+ // zig: expr += 1
+ return trans_create_node_bin_op(c,
+ trans_expr(c, ResultUsedYes, scope, op_expr, TransLValue),
+ assign_op,
+ trans_create_node_unsigned(c, 1));
+ }
+ // worst case
+ // c: ++expr
+ // zig: {
+ // zig: const _ref = &expr;
+ // zig: *_ref += 1;
+ // zig: *_ref
+ // zig: }
+ TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
+
+ // const _ref = &expr;
+ AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, op_expr, TransLValue);
+ if (expr == nullptr) return nullptr;
+ AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr);
+ // TODO: avoid name collisions with generated variable names
+ Buf* ref_var_name = buf_create_from_str("_ref");
+ AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr);
+ child_scope->node->data.block.statements.append(ref_var_decl);
+
+ // *_ref += 1;
+ AstNode *assign_statement = trans_create_node_bin_op(c,
+ trans_create_node_prefix_op(c, PrefixOpDereference,
+ trans_create_node_symbol(c, ref_var_name)),
+ assign_op,
+ trans_create_node_unsigned(c, 1));
+ child_scope->node->data.block.statements.append(assign_statement);
+
+ // *_ref
+ AstNode *deref_expr = trans_create_node_prefix_op(c, PrefixOpDereference,
+ trans_create_node_symbol(c, ref_var_name));
+ child_scope->node->data.block.statements.append(deref_expr);
+ child_scope->node->data.block.last_statement_is_result_expression = true;
+
+ return child_scope->node;
+}
+
+static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransScope *scope, const UnaryOperator *stmt) {
+ switch (stmt->getOpcode()) {
+ case UO_PostInc:
+ if (qual_type_has_wrapping_overflow(c, stmt->getType()))
+ return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap);
+ else
+ return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignPlus);
+ case UO_PostDec:
+ if (qual_type_has_wrapping_overflow(c, stmt->getType()))
+ return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap);
+ else
+ return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignMinus);
+ case UO_PreInc:
+ if (qual_type_has_wrapping_overflow(c, stmt->getType()))
+ return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap);
+ else
+ return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignPlus);
+ case UO_PreDec:
+ if (qual_type_has_wrapping_overflow(c, stmt->getType()))
+ return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap);
+ else
+ return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignMinus);
+ case UO_AddrOf:
+ {
+ AstNode *value_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), TransLValue);
+ if (value_node == nullptr)
+ return value_node;
+ return trans_create_node_addr_of(c, false, false, value_node);
+ }
+ case UO_Deref:
+ {
+ AstNode *value_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), TransRValue);
+ if (value_node == nullptr)
+ return nullptr;
+ bool is_fn_ptr = qual_type_is_fn_ptr(c, stmt->getSubExpr()->getType());
+ if (is_fn_ptr)
+ return value_node;
+ AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, value_node);
+ return trans_create_node_prefix_op(c, PrefixOpDereference, unwrapped);
+ }
+ case UO_Plus:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Plus");
+ return nullptr;
+ case UO_Minus:
+ {
+ Expr *op_expr = stmt->getSubExpr();
+ if (!qual_type_has_wrapping_overflow(c, op_expr->getType())) {
+ AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr);
+ node->data.prefix_op_expr.prefix_op = PrefixOpNegation;
+
+ node->data.prefix_op_expr.primary_expr = trans_expr(c, ResultUsedYes, scope, op_expr, TransRValue);
+ if (node->data.prefix_op_expr.primary_expr == nullptr)
+ return nullptr;
+
+ return node;
+ } else if (c_is_unsigned_integer(c, op_expr->getType())) {
+ // we gotta emit 0 -% x
+ AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
+ node->data.bin_op_expr.op1 = trans_create_node_unsigned(c, 0);
+
+ node->data.bin_op_expr.op2 = trans_expr(c, ResultUsedYes, scope, op_expr, TransRValue);
+ if (node->data.bin_op_expr.op2 == nullptr)
+ return nullptr;
+
+ node->data.bin_op_expr.bin_op = BinOpTypeSubWrap;
+ return node;
+ } else {
+ emit_warning(c, stmt->getLocStart(), "C negation with non float non integer");
+ return nullptr;
+ }
+ }
+ case UO_Not:
+ {
+ Expr *op_expr = stmt->getSubExpr();
+ AstNode *sub_node = trans_expr(c, ResultUsedYes, scope, op_expr, TransRValue);
+ if (sub_node == nullptr)
+ return nullptr;
+ return trans_create_node_prefix_op(c, PrefixOpBinNot, sub_node);
+ }
+ case UO_LNot:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_LNot");
+ return nullptr;
+ case UO_Real:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Real");
+ return nullptr;
+ case UO_Imag:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Imag");
+ return nullptr;
+ case UO_Extension:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Extension");
+ return nullptr;
+ case UO_Coawait:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Coawait");
+ return nullptr;
+ }
+ zig_unreachable();
+}
+
+static int trans_local_declaration(Context *c, TransScope *scope, const DeclStmt *stmt,
+ AstNode **out_node, TransScope **out_scope)
+{
+ // declarations are added via the scope
+ *out_node = nullptr;
+
+ TransScopeBlock *scope_block = trans_scope_block_find(scope);
+ assert(scope_block != nullptr);
+
+ for (auto iter = stmt->decl_begin(); iter != stmt->decl_end(); iter++) {
+ Decl *decl = *iter;
+ switch (decl->getKind()) {
+ case Decl::Var: {
+ VarDecl *var_decl = (VarDecl *)decl;
+ QualType qual_type = var_decl->getTypeSourceInfo()->getType();
+ AstNode *init_node = nullptr;
+ if (var_decl->hasInit()) {
+ init_node = trans_expr(c, ResultUsedYes, scope, var_decl->getInit(), TransRValue);
+ if (init_node == nullptr)
+ return ErrorUnexpected;
+
+ }
+ AstNode *type_node = trans_qual_type(c, qual_type, stmt->getLocStart());
+ if (type_node == nullptr)
+ return ErrorUnexpected;
+
+ Buf *c_symbol_name = buf_create_from_str(decl_name(var_decl));
+
+ TransScopeVar *var_scope = trans_scope_var_create(c, scope, c_symbol_name);
+ scope = &var_scope->base;
+
+ AstNode *node = trans_create_node_var_decl_local(c, qual_type.isConstQualified(),
+ var_scope->zig_name, type_node, init_node);
+
+ scope_block->node->data.block.statements.append(node);
+ continue;
+ }
+ case Decl::AccessSpec:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind AccessSpec");
+ return ErrorUnexpected;
+ case Decl::Block:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Block");
+ return ErrorUnexpected;
+ case Decl::Captured:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Captured");
+ return ErrorUnexpected;
+ case Decl::ClassScopeFunctionSpecialization:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassScopeFunctionSpecialization");
+ return ErrorUnexpected;
+ case Decl::Empty:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Empty");
+ return ErrorUnexpected;
+ case Decl::Export:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Export");
+ return ErrorUnexpected;
+ case Decl::ExternCContext:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ExternCContext");
+ return ErrorUnexpected;
+ case Decl::FileScopeAsm:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind FileScopeAsm");
+ return ErrorUnexpected;
+ case Decl::Friend:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Friend");
+ return ErrorUnexpected;
+ case Decl::FriendTemplate:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind FriendTemplate");
+ return ErrorUnexpected;
+ case Decl::Import:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Import");
+ return ErrorUnexpected;
+ case Decl::LinkageSpec:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind LinkageSpec");
+ return ErrorUnexpected;
+ case Decl::Label:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Label");
+ return ErrorUnexpected;
+ case Decl::Namespace:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Namespace");
+ return ErrorUnexpected;
+ case Decl::NamespaceAlias:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind NamespaceAlias");
+ return ErrorUnexpected;
+ case Decl::ObjCCompatibleAlias:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCCompatibleAlias");
+ return ErrorUnexpected;
+ case Decl::ObjCCategory:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCCategory");
+ return ErrorUnexpected;
+ case Decl::ObjCCategoryImpl:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCCategoryImpl");
+ return ErrorUnexpected;
+ case Decl::ObjCImplementation:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCImplementation");
+ return ErrorUnexpected;
+ case Decl::ObjCInterface:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCInterface");
+ return ErrorUnexpected;
+ case Decl::ObjCProtocol:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCProtocol");
+ return ErrorUnexpected;
+ case Decl::ObjCMethod:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCMethod");
+ return ErrorUnexpected;
+ case Decl::ObjCProperty:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCProperty");
+ return ErrorUnexpected;
+ case Decl::BuiltinTemplate:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind BuiltinTemplate");
+ return ErrorUnexpected;
+ case Decl::ClassTemplate:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassTemplate");
+ return ErrorUnexpected;
+ case Decl::FunctionTemplate:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind FunctionTemplate");
+ return ErrorUnexpected;
+ case Decl::TypeAliasTemplate:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TypeAliasTemplate");
+ return ErrorUnexpected;
+ case Decl::VarTemplate:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind VarTemplate");
+ return ErrorUnexpected;
+ case Decl::TemplateTemplateParm:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TemplateTemplateParm");
+ return ErrorUnexpected;
+ case Decl::Enum:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Enum");
+ return ErrorUnexpected;
+ case Decl::Record:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Record");
+ return ErrorUnexpected;
+ case Decl::CXXRecord:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXRecord");
+ return ErrorUnexpected;
+ case Decl::ClassTemplateSpecialization:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassTemplateSpecialization");
+ return ErrorUnexpected;
+ case Decl::ClassTemplatePartialSpecialization:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassTemplatePartialSpecialization");
+ return ErrorUnexpected;
+ case Decl::TemplateTypeParm:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TemplateTypeParm");
+ return ErrorUnexpected;
+ case Decl::ObjCTypeParam:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCTypeParam");
+ return ErrorUnexpected;
+ case Decl::TypeAlias:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TypeAlias");
+ return ErrorUnexpected;
+ case Decl::Typedef:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Typedef");
+ return ErrorUnexpected;
+ case Decl::UnresolvedUsingTypename:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UnresolvedUsingTypename");
+ return ErrorUnexpected;
+ case Decl::Using:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Using");
+ return ErrorUnexpected;
+ case Decl::UsingDirective:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UsingDirective");
+ return ErrorUnexpected;
+ case Decl::UsingPack:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UsingPack");
+ return ErrorUnexpected;
+ case Decl::UsingShadow:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UsingShadow");
+ return ErrorUnexpected;
+ case Decl::ConstructorUsingShadow:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ConstructorUsingShadow");
+ return ErrorUnexpected;
+ case Decl::Binding:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Binding");
+ return ErrorUnexpected;
+ case Decl::Field:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Field");
+ return ErrorUnexpected;
+ case Decl::ObjCAtDefsField:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCAtDefsField");
+ return ErrorUnexpected;
+ case Decl::ObjCIvar:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCIvar");
+ return ErrorUnexpected;
+ case Decl::Function:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Function");
+ return ErrorUnexpected;
+ case Decl::CXXDeductionGuide:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXDeductionGuide");
+ return ErrorUnexpected;
+ case Decl::CXXMethod:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXMethod");
+ return ErrorUnexpected;
+ case Decl::CXXConstructor:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXConstructor");
+ return ErrorUnexpected;
+ case Decl::CXXConversion:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXConversion");
+ return ErrorUnexpected;
+ case Decl::CXXDestructor:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXDestructor");
+ return ErrorUnexpected;
+ case Decl::MSProperty:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind MSProperty");
+ return ErrorUnexpected;
+ case Decl::NonTypeTemplateParm:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind NonTypeTemplateParm");
+ return ErrorUnexpected;
+ case Decl::Decomposition:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Decomposition");
+ return ErrorUnexpected;
+ case Decl::ImplicitParam:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ImplicitParam");
+ return ErrorUnexpected;
+ case Decl::OMPCapturedExpr:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind OMPCapturedExpr");
+ return ErrorUnexpected;
+ case Decl::ParmVar:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ParmVar");
+ return ErrorUnexpected;
+ case Decl::VarTemplateSpecialization:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind VarTemplateSpecialization");
+ return ErrorUnexpected;
+ case Decl::VarTemplatePartialSpecialization:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind VarTemplatePartialSpecialization");
+ return ErrorUnexpected;
+ case Decl::EnumConstant:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind EnumConstant");
+ return ErrorUnexpected;
+ case Decl::IndirectField:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind IndirectField");
+ return ErrorUnexpected;
+ case Decl::OMPDeclareReduction:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind OMPDeclareReduction");
+ return ErrorUnexpected;
+ case Decl::UnresolvedUsingValue:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UnresolvedUsingValue");
+ return ErrorUnexpected;
+ case Decl::OMPThreadPrivate:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind OMPThreadPrivate");
+ return ErrorUnexpected;
+ case Decl::ObjCPropertyImpl:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCPropertyImpl");
+ return ErrorUnexpected;
+ case Decl::PragmaComment:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind PragmaComment");
+ return ErrorUnexpected;
+ case Decl::PragmaDetectMismatch:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind PragmaDetectMismatch");
+ return ErrorUnexpected;
+ case Decl::StaticAssert:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind StaticAssert");
+ return ErrorUnexpected;
+ case Decl::TranslationUnit:
+ emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TranslationUnit");
+ return ErrorUnexpected;
+ }
+ zig_unreachable();
+ }
+
+ *out_scope = scope;
+ return ErrorNone;
+}
+
+static AstNode *trans_while_loop(Context *c, TransScope *scope, const WhileStmt *stmt) {
+ TransScopeWhile *while_scope = trans_scope_while_create(c, scope);
+
+ while_scope->node->data.while_expr.condition = trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue);
+ if (while_scope->node->data.while_expr.condition == nullptr)
+ return nullptr;
+
+ TransScope *body_scope = trans_stmt(c, &while_scope->base, stmt->getBody(),
+ &while_scope->node->data.while_expr.body);
+ if (body_scope == nullptr)
+ return nullptr;
+
+ return while_scope->node;
+}
+
+static AstNode *trans_if_statement(Context *c, TransScope *scope, const IfStmt *stmt) {
+ // if (c) t
+ // if (c) t else e
+ AstNode *if_node = trans_create_node(c, NodeTypeIfBoolExpr);
+
+ // TODO: condition != 0
+ AstNode *condition_node = trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue);
+ if (condition_node == nullptr)
+ return nullptr;
+ if_node->data.if_bool_expr.condition = condition_node;
+
+ TransScope *then_scope = trans_stmt(c, scope, stmt->getThen(), &if_node->data.if_bool_expr.then_block);
+ if (then_scope == nullptr)
+ return nullptr;
+
+ if (stmt->getElse() != nullptr) {
+ TransScope *else_scope = trans_stmt(c, scope, stmt->getElse(), &if_node->data.if_bool_expr.else_node);
+ if (else_scope == nullptr)
+ return nullptr;
+ }
+
+ return if_node;
+}
+
+static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope *scope, const CallExpr *stmt) {
+ AstNode *node = trans_create_node(c, NodeTypeFnCallExpr);
+
+ AstNode *callee_raw_node = trans_expr(c, ResultUsedYes, scope, stmt->getCallee(), TransRValue);
+ if (callee_raw_node == nullptr)
+ return nullptr;
+
+ AstNode *callee_node = nullptr;
+ if (qual_type_is_fn_ptr(c, stmt->getCallee()->getType())) {
+ if (stmt->getCallee()->getStmtClass() == Stmt::ImplicitCastExprClass) {
+ const ImplicitCastExpr *implicit_cast = static_cast<const ImplicitCastExpr *>(stmt->getCallee());
+ if (implicit_cast->getCastKind() == CK_FunctionToPointerDecay) {
+ if (implicit_cast->getSubExpr()->getStmtClass() == Stmt::DeclRefExprClass) {
+ const DeclRefExpr *decl_ref = static_cast<const DeclRefExpr *>(implicit_cast->getSubExpr());
+ const Decl *decl = decl_ref->getFoundDecl();
+ if (decl->getKind() == Decl::Function) {
+ callee_node = callee_raw_node;
+ }
+ }
+ }
+ }
+ if (callee_node == nullptr) {
+ callee_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, callee_raw_node);
+ }
+ } else {
+ callee_node = callee_raw_node;
+ }
+
+ node->data.fn_call_expr.fn_ref_expr = callee_node;
+
+ unsigned num_args = stmt->getNumArgs();
+ const Expr * const* args = stmt->getArgs();
+ for (unsigned i = 0; i < num_args; i += 1) {
+ AstNode *arg_node = trans_expr(c, ResultUsedYes, scope, args[i], TransRValue);
+ if (arg_node == nullptr)
+ return nullptr;
+
+ node->data.fn_call_expr.params.append(arg_node);
+ }
+
+ return node;
+}
+
+static AstNode *trans_member_expr(Context *c, TransScope *scope, const MemberExpr *stmt) {
+ AstNode *container_node = trans_expr(c, ResultUsedYes, scope, stmt->getBase(), TransRValue);
+ if (container_node == nullptr)
+ return nullptr;
+
+ if (stmt->isArrow()) {
+ container_node = trans_create_node_unwrap_null(c, container_node);
+ }
+
+ const char *name = decl_name(stmt->getMemberDecl());
+
+ AstNode *node = trans_create_node_field_access_str(c, container_node, name);
+ return node;
+}
+
+static AstNode *trans_array_subscript_expr(Context *c, TransScope *scope, const ArraySubscriptExpr *stmt) {
+ AstNode *container_node = trans_expr(c, ResultUsedYes, scope, stmt->getBase(), TransRValue);
+ if (container_node == nullptr)
+ return nullptr;
+
+ AstNode *idx_node = trans_expr(c, ResultUsedYes, scope, stmt->getIdx(), TransRValue);
+ if (idx_node == nullptr)
+ return nullptr;
+
+
+ AstNode *node = trans_create_node(c, NodeTypeArrayAccessExpr);
+ node->data.array_access_expr.array_ref_expr = container_node;
+ node->data.array_access_expr.subscript = idx_node;
+ return node;
+}
+
+static AstNode *trans_c_style_cast_expr(Context *c, ResultUsed result_used, TransScope *scope,
+ const CStyleCastExpr *stmt, TransLRValue lrvalue)
+{
+ AstNode *sub_expr_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), lrvalue);
+ if (sub_expr_node == nullptr)
+ return nullptr;
+
+ return trans_c_cast(c, stmt->getLocStart(), stmt->getType(), stmt->getSubExpr()->getType(), sub_expr_node);
+}
+
+static AstNode *trans_unary_expr_or_type_trait_expr(Context *c, TransScope *scope,
+ const UnaryExprOrTypeTraitExpr *stmt)
+{
+ AstNode *type_node = trans_qual_type(c, stmt->getTypeOfArgument(), stmt->getLocStart());
+ if (type_node == nullptr)
+ return nullptr;
+
+ AstNode *node = trans_create_node_builtin_fn_call_str(c, "sizeOf");
+ node->data.fn_call_expr.params.append(type_node);
+ return node;
+}
+
+static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt *stmt) {
+ TransScopeWhile *while_scope = trans_scope_while_create(c, parent_scope);
+
+ while_scope->node->data.while_expr.condition = trans_create_node_bool(c, true);
+
+ AstNode *body_node;
+ TransScope *child_scope;
+ if (stmt->getBody()->getStmtClass() == Stmt::CompoundStmtClass) {
+ // 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: }
+
+ // We call the low level function so that we can set child_scope to the scope of the generated block.
+ if (trans_stmt_extra(c, &while_scope->base, stmt->getBody(), ResultUsedNo, TransRValue, &body_node,
+ nullptr, &child_scope))
+ {
+ return nullptr;
+ }
+ assert(body_node->type == NodeTypeBlock);
+ } else {
+ // 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: }
+ TransScopeBlock *child_block_scope = trans_scope_block_create(c, &while_scope->base);
+ body_node = child_block_scope->node;
+ AstNode *child_statement;
+ child_scope = trans_stmt(c, &child_block_scope->base, stmt->getBody(), &child_statement);
+ if (child_scope == nullptr) return nullptr;
+ body_node->data.block.statements.append(child_statement);
+ }
+
+ // if (!cond) break;
+ AstNode *condition_node = trans_expr(c, ResultUsedYes, child_scope, stmt->getCond(), TransRValue);
+ if (condition_node == nullptr) return nullptr;
+ AstNode *terminator_node = trans_create_node(c, NodeTypeIfBoolExpr);
+ terminator_node->data.if_bool_expr.condition = trans_create_node_prefix_op(c, PrefixOpBoolNot, condition_node);
+ terminator_node->data.if_bool_expr.then_block = trans_create_node(c, NodeTypeBreak);
+
+ body_node->data.block.statements.append(terminator_node);
+
+ while_scope->node->data.while_expr.body = body_node;
+
+ return while_scope->node;
+}
+
+static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const SwitchStmt *stmt) {
+ TransScopeBlock *block_scope = trans_scope_block_create(c, parent_scope);
+
+ TransScopeSwitch *switch_scope;
+
+ const DeclStmt *var_decl_stmt = stmt->getConditionVariableDeclStmt();
+ if (var_decl_stmt == nullptr) {
+ switch_scope = trans_scope_switch_create(c, &block_scope->base);
+ } else {
+ AstNode *vars_node;
+ TransScope *var_scope = trans_stmt(c, &block_scope->base, var_decl_stmt, &vars_node);
+ if (var_scope == nullptr)
+ return nullptr;
+ if (vars_node != nullptr)
+ block_scope->node->data.block.statements.append(vars_node);
+ switch_scope = trans_scope_switch_create(c, var_scope);
+ }
+ block_scope->node->data.block.statements.append(switch_scope->switch_node);
+
+ // TODO avoid name collisions
+ Buf *end_label_name = buf_create_from_str("end");
+ switch_scope->end_label_name = end_label_name;
+
+ const Expr *cond_expr = stmt->getCond();
+ assert(cond_expr != nullptr);
+
+ AstNode *expr_node = trans_expr(c, ResultUsedYes, &block_scope->base, cond_expr, TransRValue);
+ if (expr_node == nullptr)
+ return nullptr;
+ switch_scope->switch_node->data.switch_expr.expr = expr_node;
+
+ AstNode *body_node;
+ const Stmt *body_stmt = stmt->getBody();
+ if (body_stmt->getStmtClass() == Stmt::CompoundStmtClass) {
+ if (trans_compound_stmt_inline(c, &switch_scope->base, (const CompoundStmt *)body_stmt,
+ block_scope->node, nullptr))
+ {
+ return nullptr;
+ }
+ } else {
+ TransScope *body_scope = trans_stmt(c, &switch_scope->base, body_stmt, &body_node);
+ if (body_scope == nullptr)
+ return nullptr;
+ if (body_node != nullptr)
+ block_scope->node->data.block.statements.append(body_node);
+ }
+
+ if (!switch_scope->found_default && !stmt->isAllEnumCasesCovered()) {
+ AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
+ prong_node->data.switch_prong.expr = trans_create_node_goto(c, end_label_name);
+ switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
+ }
+
+ // This is necessary if the last switch case "falls through" the end of the switch block
+ block_scope->node->data.block.statements.append(trans_create_node_goto(c, end_label_name));
+
+ block_scope->node->data.block.statements.append(trans_create_node_label(c, end_label_name));
+
+ return block_scope->node;
+}
+
+static int trans_switch_case(Context *c, TransScope *parent_scope, const CaseStmt *stmt, AstNode **out_node,
+ TransScope **out_scope)
+{
+ *out_node = nullptr;
+
+ if (stmt->getRHS() != nullptr) {
+ emit_warning(c, stmt->getLocStart(), "TODO support GNU switch case a ... b extension");
+ return ErrorUnexpected;
+ }
+
+ TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope);
+ assert(switch_scope != nullptr);
+
+ Buf *label_name = buf_sprintf("case_%" PRIu32, switch_scope->case_index);
+ switch_scope->case_index += 1;
+
+ {
+ // Add the prong
+ AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
+ AstNode *item_node = trans_expr(c, ResultUsedYes, &switch_scope->base, stmt->getLHS(), TransRValue);
+ if (item_node == nullptr)
+ return ErrorUnexpected;
+ prong_node->data.switch_prong.items.append(item_node);
+
+ prong_node->data.switch_prong.expr = trans_create_node_goto(c, label_name);
+
+ switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
+ }
+
+ TransScopeBlock *scope_block = trans_scope_block_find(parent_scope);
+ scope_block->node->data.block.statements.append(trans_create_node_label(c, label_name));
+
+ AstNode *sub_stmt_node;
+ TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node);
+ if (new_scope == nullptr)
+ return ErrorUnexpected;
+ if (sub_stmt_node != nullptr)
+ scope_block->node->data.block.statements.append(sub_stmt_node);
+
+ *out_scope = new_scope;
+ return ErrorNone;
+}
+
+static int trans_switch_default(Context *c, TransScope *parent_scope, const DefaultStmt *stmt, AstNode **out_node,
+ TransScope **out_scope)
+{
+ *out_node = nullptr;
+
+ TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope);
+ assert(switch_scope != nullptr);
+
+ Buf *label_name = buf_sprintf("default");
+
+ {
+ // Add the prong
+ AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
+
+ prong_node->data.switch_prong.expr = trans_create_node_goto(c, label_name);
+
+ switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
+ switch_scope->found_default = true;
+ }
+
+ TransScopeBlock *scope_block = trans_scope_block_find(parent_scope);
+ scope_block->node->data.block.statements.append(trans_create_node_label(c, label_name));
+
+
+ AstNode *sub_stmt_node;
+ TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node);
+ if (new_scope == nullptr)
+ return ErrorUnexpected;
+ if (sub_stmt_node != nullptr)
+ scope_block->node->data.block.statements.append(sub_stmt_node);
+
+ *out_scope = new_scope;
+ return ErrorNone;
+}
+
+static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForStmt *stmt) {
+ AstNode *loop_block_node;
+ TransScopeWhile *while_scope;
+ TransScope *cond_scope;
+ const Stmt *init_stmt = stmt->getInit();
+ if (init_stmt == nullptr) {
+ while_scope = trans_scope_while_create(c, parent_scope);
+ loop_block_node = while_scope->node;
+ cond_scope = parent_scope;
+ } else {
+ TransScopeBlock *child_scope = trans_scope_block_create(c, parent_scope);
+ loop_block_node = child_scope->node;
+
+ AstNode *vars_node;
+ cond_scope = trans_stmt(c, &child_scope->base, init_stmt, &vars_node);
+ if (cond_scope == nullptr)
+ return nullptr;
+ if (vars_node != nullptr)
+ child_scope->node->data.block.statements.append(vars_node);
+
+ while_scope = trans_scope_while_create(c, cond_scope);
+
+ child_scope->node->data.block.statements.append(while_scope->node);
+ }
+
+ const Stmt *cond_stmt = stmt->getCond();
+ if (cond_stmt == nullptr) {
+ while_scope->node->data.while_expr.condition = trans_create_node_bool(c, true);
+ } else {
+ TransScope *end_cond_scope = trans_stmt(c, cond_scope, cond_stmt,
+ &while_scope->node->data.while_expr.condition);
+ if (end_cond_scope == nullptr)
+ return nullptr;
+ }
+
+ const Stmt *inc_stmt = stmt->getInc();
+ if (inc_stmt != nullptr) {
+ AstNode *inc_node;
+ TransScope *inc_scope = trans_stmt(c, cond_scope, inc_stmt, &inc_node);
+ if (inc_scope == nullptr)
+ return nullptr;
+ while_scope->node->data.while_expr.continue_expr = inc_node;
+ }
+
+ AstNode *body_statement;
+ TransScope *body_scope = trans_stmt(c, &while_scope->base, stmt->getBody(), &body_statement);
+ if (body_scope == nullptr)
+ return nullptr;
+ while_scope->node->data.while_expr.body = body_statement;
+
+ return loop_block_node;
+}
+
+static AstNode *trans_string_literal(Context *c, TransScope *scope, const StringLiteral *stmt) {
+ switch (stmt->getKind()) {
+ case StringLiteral::Ascii:
+ case StringLiteral::UTF8:
+ return trans_create_node_str_lit_c(c, string_ref_to_buf(stmt->getString()));
+ case StringLiteral::UTF16:
+ emit_warning(c, stmt->getLocStart(), "TODO support UTF16 string literals");
+ return nullptr;
+ case StringLiteral::UTF32:
+ emit_warning(c, stmt->getLocStart(), "TODO support UTF32 string literals");
+ return nullptr;
+ case StringLiteral::Wide:
+ emit_warning(c, stmt->getLocStart(), "TODO support wide string literals");
+ return nullptr;
+ }
+ zig_unreachable();
+}
+
+static AstNode *trans_break_stmt(Context *c, TransScope *scope, const BreakStmt *stmt) {
+ TransScope *cur_scope = scope;
+ while (cur_scope != nullptr) {
+ if (cur_scope->id == TransScopeIdWhile) {
+ return trans_create_node(c, NodeTypeBreak);
+ } else if (cur_scope->id == TransScopeIdSwitch) {
+ TransScopeSwitch *switch_scope = (TransScopeSwitch *)cur_scope;
+ return trans_create_node_goto(c, switch_scope->end_label_name);
+ }
+ cur_scope = cur_scope->parent;
+ }
+ zig_unreachable();
+}
+
+static AstNode *trans_continue_stmt(Context *c, TransScope *scope, const ContinueStmt *stmt) {
+ return trans_create_node(c, NodeTypeContinue);
+}
+
+static int wrap_stmt(AstNode **out_node, TransScope **out_scope, TransScope *in_scope, AstNode *result_node) {
+ if (result_node == nullptr)
+ return ErrorUnexpected;
+ *out_node = result_node;
+ if (out_scope != nullptr)
+ *out_scope = in_scope;
+ return ErrorNone;
+}
+
+static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
+ ResultUsed result_used, TransLRValue lrvalue,
+ AstNode **out_node, TransScope **out_child_scope,
+ TransScope **out_node_scope)
+{
+ Stmt::StmtClass sc = stmt->getStmtClass();
+ switch (sc) {
+ case Stmt::ReturnStmtClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_return_stmt(c, scope, (const ReturnStmt *)stmt));
+ case Stmt::CompoundStmtClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_compound_stmt(c, scope, (const CompoundStmt *)stmt, out_node_scope));
+ case Stmt::IntegerLiteralClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_integer_literal(c, (const IntegerLiteral *)stmt));
+ case Stmt::ConditionalOperatorClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_conditional_operator(c, result_used, scope, (const ConditionalOperator *)stmt));
+ case Stmt::BinaryOperatorClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_binary_operator(c, result_used, scope, (const BinaryOperator *)stmt));
+ case Stmt::CompoundAssignOperatorClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_compound_assign_operator(c, result_used, scope, (const CompoundAssignOperator *)stmt));
+ case Stmt::ImplicitCastExprClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_implicit_cast_expr(c, scope, (const ImplicitCastExpr *)stmt));
+ case Stmt::DeclRefExprClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_decl_ref_expr(c, scope, (const DeclRefExpr *)stmt, lrvalue));
+ case Stmt::UnaryOperatorClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_unary_operator(c, result_used, scope, (const UnaryOperator *)stmt));
+ case Stmt::DeclStmtClass:
+ return trans_local_declaration(c, scope, (const DeclStmt *)stmt, out_node, out_child_scope);
+ case Stmt::WhileStmtClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_while_loop(c, scope, (const WhileStmt *)stmt));
+ case Stmt::IfStmtClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_if_statement(c, scope, (const IfStmt *)stmt));
+ case Stmt::CallExprClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_call_expr(c, result_used, scope, (const CallExpr *)stmt));
+ case Stmt::NullStmtClass:
+ *out_node = nullptr;
+ *out_child_scope = scope;
+ return ErrorNone;
+ case Stmt::MemberExprClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_member_expr(c, scope, (const MemberExpr *)stmt));
+ case Stmt::ArraySubscriptExprClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_array_subscript_expr(c, scope, (const ArraySubscriptExpr *)stmt));
+ case Stmt::CStyleCastExprClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_c_style_cast_expr(c, result_used, scope, (const CStyleCastExpr *)stmt, lrvalue));
+ case Stmt::UnaryExprOrTypeTraitExprClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt));
+ case Stmt::DoStmtClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_do_loop(c, scope, (const DoStmt *)stmt));
+ case Stmt::ForStmtClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_for_loop(c, scope, (const ForStmt *)stmt));
+ case Stmt::StringLiteralClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_string_literal(c, scope, (const StringLiteral *)stmt));
+ case Stmt::BreakStmtClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_break_stmt(c, scope, (const BreakStmt *)stmt));
+ case Stmt::ContinueStmtClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_continue_stmt(c, scope, (const ContinueStmt *)stmt));
+ case Stmt::ParenExprClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_expr(c, result_used, scope, ((const ParenExpr*)stmt)->getSubExpr(), lrvalue));
+ case Stmt::SwitchStmtClass:
+ return wrap_stmt(out_node, out_child_scope, scope,
+ trans_switch_stmt(c, scope, (const SwitchStmt *)stmt));
+ case Stmt::CaseStmtClass:
+ return trans_switch_case(c, scope, (const CaseStmt *)stmt, out_node, out_child_scope);
+ case Stmt::DefaultStmtClass:
+ return trans_switch_default(c, scope, (const DefaultStmt *)stmt, out_node, out_child_scope);
+ case Stmt::NoStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C NoStmtClass");
+ return ErrorUnexpected;
+ case Stmt::GCCAsmStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C GCCAsmStmtClass");
+ return ErrorUnexpected;
+ case Stmt::MSAsmStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C MSAsmStmtClass");
+ return ErrorUnexpected;
+ case Stmt::AttributedStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C AttributedStmtClass");
+ return ErrorUnexpected;
+ case Stmt::CXXCatchStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXCatchStmtClass");
+ return ErrorUnexpected;
+ case Stmt::CXXForRangeStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXForRangeStmtClass");
+ return ErrorUnexpected;
+ case Stmt::CXXTryStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTryStmtClass");
+ return ErrorUnexpected;
+ case Stmt::CapturedStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CapturedStmtClass");
+ return ErrorUnexpected;
+ case Stmt::CoreturnStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CoreturnStmtClass");
+ return ErrorUnexpected;
+ case Stmt::CoroutineBodyStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CoroutineBodyStmtClass");
+ return ErrorUnexpected;
+ case Stmt::BinaryConditionalOperatorClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C BinaryConditionalOperatorClass");
+ return ErrorUnexpected;
+ case Stmt::AddrLabelExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C AddrLabelExprClass");
+ return ErrorUnexpected;
+ case Stmt::ArrayInitIndexExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayInitIndexExprClass");
+ return ErrorUnexpected;
+ case Stmt::ArrayInitLoopExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayInitLoopExprClass");
+ return ErrorUnexpected;
+ case Stmt::ArrayTypeTraitExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayTypeTraitExprClass");
+ return ErrorUnexpected;
+ case Stmt::AsTypeExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C AsTypeExprClass");
+ return ErrorUnexpected;
+ case Stmt::AtomicExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C AtomicExprClass");
+ return ErrorUnexpected;
+ case Stmt::BlockExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C BlockExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXBindTemporaryExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXBindTemporaryExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXBoolLiteralExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXBoolLiteralExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXConstructExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXConstructExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXTemporaryObjectExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTemporaryObjectExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXDefaultArgExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDefaultArgExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXDefaultInitExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDefaultInitExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXDeleteExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDeleteExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXDependentScopeMemberExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDependentScopeMemberExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXFoldExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXFoldExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXInheritedCtorInitExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXInheritedCtorInitExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXNewExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNewExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXNoexceptExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNoexceptExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXNullPtrLiteralExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNullPtrLiteralExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXPseudoDestructorExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXPseudoDestructorExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXScalarValueInitExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXScalarValueInitExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXStdInitializerListExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXStdInitializerListExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXThisExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXThisExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXThrowExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXThrowExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXTypeidExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTypeidExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXUnresolvedConstructExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXUnresolvedConstructExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXUuidofExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXUuidofExprClass");
+ return ErrorUnexpected;
+ case Stmt::CUDAKernelCallExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CUDAKernelCallExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXMemberCallExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXMemberCallExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXOperatorCallExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXOperatorCallExprClass");
+ return ErrorUnexpected;
+ case Stmt::UserDefinedLiteralClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C UserDefinedLiteralClass");
+ return ErrorUnexpected;
+ case Stmt::CXXFunctionalCastExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXFunctionalCastExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXConstCastExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXConstCastExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXDynamicCastExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDynamicCastExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXReinterpretCastExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXReinterpretCastExprClass");
+ return ErrorUnexpected;
+ case Stmt::CXXStaticCastExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CXXStaticCastExprClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCBridgedCastExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBridgedCastExprClass");
+ return ErrorUnexpected;
+ case Stmt::CharacterLiteralClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CharacterLiteralClass");
+ return ErrorUnexpected;
+ case Stmt::ChooseExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ChooseExprClass");
+ return ErrorUnexpected;
+ case Stmt::CompoundLiteralExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CompoundLiteralExprClass");
+ return ErrorUnexpected;
+ case Stmt::ConvertVectorExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ConvertVectorExprClass");
+ return ErrorUnexpected;
+ case Stmt::CoawaitExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CoawaitExprClass");
+ return ErrorUnexpected;
+ case Stmt::CoyieldExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CoyieldExprClass");
+ return ErrorUnexpected;
+ case Stmt::DependentCoawaitExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C DependentCoawaitExprClass");
+ return ErrorUnexpected;
+ case Stmt::DependentScopeDeclRefExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C DependentScopeDeclRefExprClass");
+ return ErrorUnexpected;
+ case Stmt::DesignatedInitExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C DesignatedInitExprClass");
+ return ErrorUnexpected;
+ case Stmt::DesignatedInitUpdateExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C DesignatedInitUpdateExprClass");
+ return ErrorUnexpected;
+ case Stmt::ExprWithCleanupsClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ExprWithCleanupsClass");
+ return ErrorUnexpected;
+ case Stmt::ExpressionTraitExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ExpressionTraitExprClass");
+ return ErrorUnexpected;
+ case Stmt::ExtVectorElementExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ExtVectorElementExprClass");
+ return ErrorUnexpected;
+ case Stmt::FloatingLiteralClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C FloatingLiteralClass");
+ return ErrorUnexpected;
+ case Stmt::FunctionParmPackExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C FunctionParmPackExprClass");
+ return ErrorUnexpected;
+ case Stmt::GNUNullExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C GNUNullExprClass");
+ return ErrorUnexpected;
+ case Stmt::GenericSelectionExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C GenericSelectionExprClass");
+ return ErrorUnexpected;
+ case Stmt::ImaginaryLiteralClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ImaginaryLiteralClass");
+ return ErrorUnexpected;
+ case Stmt::ImplicitValueInitExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ImplicitValueInitExprClass");
+ return ErrorUnexpected;
+ case Stmt::InitListExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C InitListExprClass");
+ return ErrorUnexpected;
+ case Stmt::LambdaExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C LambdaExprClass");
+ return ErrorUnexpected;
+ case Stmt::MSPropertyRefExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C MSPropertyRefExprClass");
+ return ErrorUnexpected;
+ case Stmt::MSPropertySubscriptExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C MSPropertySubscriptExprClass");
+ return ErrorUnexpected;
+ case Stmt::MaterializeTemporaryExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C MaterializeTemporaryExprClass");
+ return ErrorUnexpected;
+ case Stmt::NoInitExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C NoInitExprClass");
+ return ErrorUnexpected;
+ case Stmt::OMPArraySectionExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPArraySectionExprClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCArrayLiteralClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCArrayLiteralClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCAvailabilityCheckExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAvailabilityCheckExprClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCBoolLiteralExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBoolLiteralExprClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCBoxedExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBoxedExprClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCDictionaryLiteralClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCDictionaryLiteralClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCEncodeExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCEncodeExprClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCIndirectCopyRestoreExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIndirectCopyRestoreExprClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCIsaExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIsaExprClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCIvarRefExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIvarRefExprClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCMessageExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCMessageExprClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCPropertyRefExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCPropertyRefExprClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCProtocolExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCProtocolExprClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCSelectorExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCSelectorExprClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCStringLiteralClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCStringLiteralClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCSubscriptRefExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCSubscriptRefExprClass");
+ return ErrorUnexpected;
+ case Stmt::OffsetOfExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OffsetOfExprClass");
+ return ErrorUnexpected;
+ case Stmt::OpaqueValueExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OpaqueValueExprClass");
+ return ErrorUnexpected;
+ case Stmt::UnresolvedLookupExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C UnresolvedLookupExprClass");
+ return ErrorUnexpected;
+ case Stmt::UnresolvedMemberExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C UnresolvedMemberExprClass");
+ return ErrorUnexpected;
+ case Stmt::PackExpansionExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C PackExpansionExprClass");
+ return ErrorUnexpected;
+ case Stmt::ParenListExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ParenListExprClass");
+ return ErrorUnexpected;
+ case Stmt::PredefinedExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C PredefinedExprClass");
+ return ErrorUnexpected;
+ case Stmt::PseudoObjectExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C PseudoObjectExprClass");
+ return ErrorUnexpected;
+ case Stmt::ShuffleVectorExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ShuffleVectorExprClass");
+ return ErrorUnexpected;
+ case Stmt::SizeOfPackExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C SizeOfPackExprClass");
+ return ErrorUnexpected;
+ case Stmt::StmtExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C StmtExprClass");
+ return ErrorUnexpected;
+ case Stmt::SubstNonTypeTemplateParmExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C SubstNonTypeTemplateParmExprClass");
+ return ErrorUnexpected;
+ case Stmt::SubstNonTypeTemplateParmPackExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C SubstNonTypeTemplateParmPackExprClass");
+ return ErrorUnexpected;
+ case Stmt::TypeTraitExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C TypeTraitExprClass");
+ return ErrorUnexpected;
+ case Stmt::TypoExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C TypoExprClass");
+ return ErrorUnexpected;
+ case Stmt::VAArgExprClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C VAArgExprClass");
+ return ErrorUnexpected;
+ case Stmt::GotoStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C GotoStmtClass");
+ return ErrorUnexpected;
+ case Stmt::IndirectGotoStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C IndirectGotoStmtClass");
+ return ErrorUnexpected;
+ case Stmt::LabelStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C LabelStmtClass");
+ return ErrorUnexpected;
+ case Stmt::MSDependentExistsStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C MSDependentExistsStmtClass");
+ return ErrorUnexpected;
+ case Stmt::OMPAtomicDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPAtomicDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPBarrierDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPBarrierDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPCancelDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCancelDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPCancellationPointDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCancellationPointDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPCriticalDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCriticalDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPFlushDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPFlushDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPDistributeDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPDistributeParallelForDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeParallelForDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPDistributeParallelForSimdDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeParallelForSimdDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPDistributeSimdDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeSimdDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPForDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPForDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPForSimdDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPForSimdDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPParallelForDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelForDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPParallelForSimdDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelForSimdDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPSimdDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSimdDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTargetParallelForSimdDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelForSimdDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTargetSimdDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetSimdDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTargetTeamsDistributeDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeParallelForDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeParallelForSimdDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeSimdDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTaskLoopDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskLoopDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTaskLoopSimdDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskLoopSimdDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTeamsDistributeDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTeamsDistributeParallelForDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeParallelForDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeParallelForSimdDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTeamsDistributeSimdDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeSimdDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPMasterDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPMasterDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPOrderedDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPOrderedDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPParallelDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPParallelSectionsDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelSectionsDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPSectionDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSectionDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPSectionsDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSectionsDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPSingleDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSingleDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTargetDataDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetDataDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTargetDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTargetEnterDataDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetEnterDataDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTargetExitDataDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetExitDataDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTargetParallelDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTargetParallelForDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelForDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTargetTeamsDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTargetUpdateDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetUpdateDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTaskDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTaskgroupDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskgroupDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTaskwaitDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskwaitDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTaskyieldDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskyieldDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::OMPTeamsDirectiveClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDirectiveClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCAtCatchStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtCatchStmtClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCAtFinallyStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtFinallyStmtClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCAtSynchronizedStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtSynchronizedStmtClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCAtThrowStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtThrowStmtClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCAtTryStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtTryStmtClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCAutoreleasePoolStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAutoreleasePoolStmtClass");
+ return ErrorUnexpected;
+ case Stmt::ObjCForCollectionStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCForCollectionStmtClass");
+ return ErrorUnexpected;
+ case Stmt::SEHExceptStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C SEHExceptStmtClass");
+ return ErrorUnexpected;
+ case Stmt::SEHFinallyStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C SEHFinallyStmtClass");
+ return ErrorUnexpected;
+ case Stmt::SEHLeaveStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C SEHLeaveStmtClass");
+ return ErrorUnexpected;
+ case Stmt::SEHTryStmtClass:
+ emit_warning(c, stmt->getLocStart(), "TODO handle C SEHTryStmtClass");
+ return ErrorUnexpected;
+ }
+ zig_unreachable();
+}
+
+// Returns null if there was an error
+static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr,
+ TransLRValue lrval)
+{
+ AstNode *result_node;
+ TransScope *result_scope;
+ if (trans_stmt_extra(c, scope, expr, result_used, lrval, &result_node, &result_scope, nullptr)) {
+ return nullptr;
+ }
+ return result_node;
+}
+
+// Statements have no result and no concept of L or R value.
+// Returns child scope, or null if there was an error
+static TransScope *trans_stmt(Context *c, TransScope *scope, const Stmt *stmt, AstNode **out_node) {
+ TransScope *child_scope;
+ if (trans_stmt_extra(c, scope, stmt, ResultUsedNo, TransRValue, out_node, &child_scope, nullptr)) {
+ return nullptr;
+ }
+ return child_scope;
+}
+
+static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
+ Buf *fn_name = buf_create_from_str(decl_name(fn_decl));
+
+ if (get_global(c, fn_name)) {
+ // we already saw this function
+ return;
+ }
+
+ AstNode *proto_node = trans_qual_type(c, fn_decl->getType(), fn_decl->getLocation());
+ if (proto_node == nullptr) {
+ emit_warning(c, fn_decl->getLocation(), "unable to resolve prototype of function '%s'", buf_ptr(fn_name));
+ return;
+ }
+
+ proto_node->data.fn_proto.name = fn_name;
+ proto_node->data.fn_proto.is_extern = !fn_decl->hasBody();
+
+ StorageClass sc = fn_decl->getStorageClass();
+ if (sc == SC_None) {
+ proto_node->data.fn_proto.visib_mod = fn_decl->hasBody() ? c->export_visib_mod : c->visib_mod;
+ } else if (sc == SC_Extern || sc == SC_Static) {
+ proto_node->data.fn_proto.visib_mod = c->visib_mod;
+ } else if (sc == SC_PrivateExtern) {
+ emit_warning(c, fn_decl->getLocation(), "unsupported storage class: private extern");
+ return;
+ } else {
+ emit_warning(c, fn_decl->getLocation(), "unsupported storage class: unknown");
+ return;
+ }
+
+ TransScope *scope = &c->global_scope->base;
+
+ for (size_t i = 0; i < proto_node->data.fn_proto.params.length; i += 1) {
+ AstNode *param_node = proto_node->data.fn_proto.params.at(i);
+ const ParmVarDecl *param = fn_decl->getParamDecl(i);
+ const char *name = decl_name(param);
+
+ Buf *proto_param_name;
+ if (strlen(name) != 0) {
+ proto_param_name = buf_create_from_str(name);
+ } else {
+ proto_param_name = param_node->data.param_decl.name;
+ if (proto_param_name == nullptr) {
+ proto_param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i);
+ }
+ }
+
+ TransScopeVar *scope_var = trans_scope_var_create(c, scope, proto_param_name);
+ scope = &scope_var->base;
+
+ param_node->data.param_decl.name = scope_var->zig_name;
+ }
+
+ if (!fn_decl->hasBody()) {
+ // just a prototype
+ add_top_level_decl(c, proto_node->data.fn_proto.name, proto_node);
+ return;
+ }
+
+ // actual function definition with body
+ c->ptr_params.clear();
+ Stmt *body = fn_decl->getBody();
+ AstNode *actual_body_node;
+ TransScope *result_scope = trans_stmt(c, scope, body, &actual_body_node);
+ if (result_scope == nullptr) {
+ emit_warning(c, fn_decl->getLocation(), "unable to translate function");
+ return;
+ }
+ assert(actual_body_node != nullptr);
+ assert(actual_body_node->type == NodeTypeBlock);
+
+ // it worked
+
+ AstNode *body_node_with_param_inits = trans_create_node(c, NodeTypeBlock);
+
+ for (size_t i = 0; i < proto_node->data.fn_proto.params.length; i += 1) {
+ AstNode *param_node = proto_node->data.fn_proto.params.at(i);
+ Buf *good_name = param_node->data.param_decl.name;
+
+ if (c->ptr_params.maybe_get(good_name) != nullptr) {
+ // TODO: avoid name collisions
+ Buf *mangled_name = buf_sprintf("_arg_%s", buf_ptr(good_name));
+ param_node->data.param_decl.name = mangled_name;
+
+ // var c_name = _mangled_name;
+ AstNode *parameter_init = trans_create_node_var_decl_local(c, false, good_name, nullptr, trans_create_node_symbol(c, mangled_name));
+
+ body_node_with_param_inits->data.block.statements.append(parameter_init);
+ }
+ }
+
+ for (size_t i = 0; i < actual_body_node->data.block.statements.length; i += 1) {
+ body_node_with_param_inits->data.block.statements.append(actual_body_node->data.block.statements.at(i));
+ }
+
+ AstNode *fn_def_node = trans_create_node(c, NodeTypeFnDef);
+ fn_def_node->data.fn_def.fn_proto = proto_node;
+ fn_def_node->data.fn_def.body = body_node_with_param_inits;
+
+ proto_node->data.fn_proto.fn_def_node = fn_def_node;
+ add_top_level_decl(c, fn_def_node->data.fn_def.fn_proto->data.fn_proto.name, fn_def_node);
+}
+
+static AstNode *resolve_typdef_as_builtin(Context *c, const TypedefNameDecl *typedef_decl, const char *primitive_name) {
+ AstNode *node = trans_create_node_symbol_str(c, primitive_name);
+ c->decl_table.put(typedef_decl, node);
+ return node;
+}
+
+static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_decl) {
+ auto existing_entry = c->decl_table.maybe_get((void*)typedef_decl->getCanonicalDecl());
+ if (existing_entry) {
+ return existing_entry->value;
+ }
+ QualType child_qt = typedef_decl->getUnderlyingType();
+ Buf *type_name = buf_create_from_str(decl_name(typedef_decl));
+
+ if (buf_eql_str(type_name, "uint8_t")) {
+ return resolve_typdef_as_builtin(c, typedef_decl, "u8");
+ } else if (buf_eql_str(type_name, "int8_t")) {
+ return resolve_typdef_as_builtin(c, typedef_decl, "i8");
+ } else if (buf_eql_str(type_name, "uint16_t")) {
+ return resolve_typdef_as_builtin(c, typedef_decl, "u16");
+ } else if (buf_eql_str(type_name, "int16_t")) {
+ return resolve_typdef_as_builtin(c, typedef_decl, "i16");
+ } else if (buf_eql_str(type_name, "uint32_t")) {
+ return resolve_typdef_as_builtin(c, typedef_decl, "u32");
+ } else if (buf_eql_str(type_name, "int32_t")) {
+ return resolve_typdef_as_builtin(c, typedef_decl, "i32");
+ } else if (buf_eql_str(type_name, "uint64_t")) {
+ return resolve_typdef_as_builtin(c, typedef_decl, "u64");
+ } else if (buf_eql_str(type_name, "int64_t")) {
+ return resolve_typdef_as_builtin(c, typedef_decl, "i64");
+ } else if (buf_eql_str(type_name, "intptr_t")) {
+ return resolve_typdef_as_builtin(c, typedef_decl, "isize");
+ } else if (buf_eql_str(type_name, "uintptr_t")) {
+ return resolve_typdef_as_builtin(c, typedef_decl, "usize");
+ } else if (buf_eql_str(type_name, "ssize_t")) {
+ return resolve_typdef_as_builtin(c, typedef_decl, "isize");
+ } else if (buf_eql_str(type_name, "size_t")) {
+ return resolve_typdef_as_builtin(c, typedef_decl, "usize");
+ }
+
+ // if the underlying type is anonymous, we can special case it to just
+ // use the name of this typedef
+ // TODO
+
+ AstNode *type_node = trans_qual_type(c, child_qt, typedef_decl->getLocation());
+ if (type_node == nullptr) {
+ emit_warning(c, typedef_decl->getLocation(), "typedef %s - unresolved child type", buf_ptr(type_name));
+ c->decl_table.put(typedef_decl, nullptr);
+ return nullptr;
+ }
+ add_global_var(c, type_name, type_node);
+
+ AstNode *symbol_node = trans_create_node_symbol(c, type_name);
+ c->decl_table.put(typedef_decl->getCanonicalDecl(), symbol_node);
+ return symbol_node;
+}
+
+struct AstNode *demote_enum_to_opaque(Context *c, const EnumDecl *enum_decl,
+ Buf *full_type_name, Buf *bare_name)
+{
+ AstNode *opaque_node = trans_create_node_opaque(c);
+ if (full_type_name == nullptr) {
+ c->decl_table.put(enum_decl->getCanonicalDecl(), opaque_node);
+ return opaque_node;
+ }
+ AstNode *symbol_node = trans_create_node_symbol(c, full_type_name);
+ add_global_weak_alias(c, bare_name, full_type_name);
+ add_global_var(c, full_type_name, opaque_node);
+ c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node);
+ return symbol_node;
+}
+
+static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {
+ auto existing_entry = c->decl_table.maybe_get((void*)enum_decl->getCanonicalDecl());
+ if (existing_entry) {
+ return existing_entry->value;
+ }
+
+ const char *raw_name = decl_name(enum_decl);
+ bool is_anonymous = (raw_name[0] == 0);
+ Buf *bare_name = is_anonymous ? nullptr : buf_create_from_str(raw_name);
+ Buf *full_type_name = is_anonymous ? nullptr : buf_sprintf("enum_%s", buf_ptr(bare_name));
+
+ const EnumDecl *enum_def = enum_decl->getDefinition();
+ if (!enum_def) {
+ return demote_enum_to_opaque(c, enum_decl, full_type_name, bare_name);
+ }
+
+ bool pure_enum = true;
+ uint32_t field_count = 0;
+ for (auto it = enum_def->enumerator_begin(),
+ it_end = enum_def->enumerator_end();
+ it != it_end; ++it, field_count += 1)
+ {
+ const EnumConstantDecl *enum_const = *it;
+ if (enum_const->getInitExpr()) {
+ pure_enum = false;
+ }
+ }
+
+ AstNode *tag_int_type = trans_qual_type(c, enum_decl->getIntegerType(), enum_decl->getLocation());
+ assert(tag_int_type);
+
+ if (pure_enum) {
+ AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl);
+ enum_node->data.container_decl.kind = ContainerKindEnum;
+ enum_node->data.container_decl.layout = ContainerLayoutExtern;
+ enum_node->data.container_decl.init_arg_expr = tag_int_type;
+
+ enum_node->data.container_decl.fields.resize(field_count);
+ uint32_t i = 0;
+ for (auto it = enum_def->enumerator_begin(),
+ it_end = enum_def->enumerator_end();
+ it != it_end; ++it, i += 1)
+ {
+ const EnumConstantDecl *enum_const = *it;
+
+ Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
+ Buf *field_name;
+ if (bare_name != nullptr && buf_starts_with_buf(enum_val_name, bare_name)) {
+ field_name = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name));
+ } else {
+ field_name = enum_val_name;
+ }
+
+ AstNode *field_node = trans_create_node(c, NodeTypeStructField);
+ field_node->data.struct_field.name = field_name;
+ field_node->data.struct_field.type = nullptr;
+ enum_node->data.container_decl.fields.items[i] = field_node;
+
+ // 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
+ if (is_anonymous) {
+ AstNode *lit_node = trans_create_node_unsigned(c, i);
+ add_global_var(c, enum_val_name, lit_node);
+ } else {
+ AstNode *field_access_node = trans_create_node_field_access(c,
+ trans_create_node_symbol(c, full_type_name), field_name);
+ add_global_var(c, enum_val_name, field_access_node);
+ }
+ }
+
+ if (is_anonymous) {
+ c->decl_table.put(enum_decl->getCanonicalDecl(), enum_node);
+ return enum_node;
+ } else {
+ AstNode *symbol_node = trans_create_node_symbol(c, full_type_name);
+ add_global_weak_alias(c, bare_name, full_type_name);
+ add_global_var(c, full_type_name, enum_node);
+ c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node);
+ return enum_node;
+ }
+ }
+
+ // TODO after issue #305 is solved, make this be an enum with tag_int_type
+ // as the integer type and set the custom enum values
+ AstNode *enum_node = tag_int_type;
+
+
+ // add variables for all the values with enum_node
+ for (auto it = enum_def->enumerator_begin(),
+ it_end = enum_def->enumerator_end();
+ it != it_end; ++it)
+ {
+ const EnumConstantDecl *enum_const = *it;
+
+ Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
+ AstNode *int_node = trans_create_node_apint(c, enum_const->getInitVal());
+ AstNode *var_node = add_global_var(c, enum_val_name, int_node);
+ var_node->data.variable_declaration.type = tag_int_type;
+ }
+
+ if (is_anonymous) {
+ c->decl_table.put(enum_decl->getCanonicalDecl(), enum_node);
+ return enum_node;
+ } else {
+ AstNode *symbol_node = trans_create_node_symbol(c, full_type_name);
+ add_global_weak_alias(c, bare_name, full_type_name);
+ add_global_var(c, full_type_name, enum_node);
+ c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node);
+ return symbol_node;
+ }
+}
+
+static AstNode *demote_struct_to_opaque(Context *c, const RecordDecl *record_decl,
+ Buf *full_type_name, Buf *bare_name)
+{
+ AstNode *opaque_node = trans_create_node_opaque(c);
+ if (full_type_name == nullptr) {
+ c->decl_table.put(record_decl->getCanonicalDecl(), opaque_node);
+ return opaque_node;
+ }
+ AstNode *symbol_node = trans_create_node_symbol(c, full_type_name);
+ add_global_weak_alias(c, bare_name, full_type_name);
+ add_global_var(c, full_type_name, opaque_node);
+ c->decl_table.put(record_decl->getCanonicalDecl(), symbol_node);
+ return symbol_node;
+}
+
+static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) {
+ auto existing_entry = c->decl_table.maybe_get((void*)record_decl->getCanonicalDecl());
+ if (existing_entry) {
+ return existing_entry->value;
+ }
+
+ const char *raw_name = decl_name(record_decl);
+ const char *container_kind_name;
+ ContainerKind container_kind;
+ if (record_decl->isUnion()) {
+ container_kind_name = "union";
+ container_kind = ContainerKindUnion;
+ } else if (record_decl->isStruct()) {
+ container_kind_name = "struct";
+ container_kind = ContainerKindStruct;
+ } else {
+ emit_warning(c, record_decl->getLocation(), "skipping record %s, not a struct or union", raw_name);
+ c->decl_table.put(record_decl->getCanonicalDecl(), nullptr);
+ return nullptr;
+ }
+
+ bool is_anonymous = record_decl->isAnonymousStructOrUnion() || raw_name[0] == 0;
+ Buf *bare_name = is_anonymous ? nullptr : buf_create_from_str(raw_name);
+ Buf *full_type_name = (bare_name == nullptr) ?
+ nullptr : buf_sprintf("%s_%s", container_kind_name, buf_ptr(bare_name));
+
+ RecordDecl *record_def = record_decl->getDefinition();
+ if (record_def == nullptr) {
+ return demote_struct_to_opaque(c, record_decl, full_type_name, bare_name);
+ }
+
+ // count fields and validate
+ uint32_t field_count = 0;
+ for (auto it = record_def->field_begin(),
+ it_end = record_def->field_end();
+ it != it_end; ++it, field_count += 1)
+ {
+ const FieldDecl *field_decl = *it;
+
+ if (field_decl->isBitField()) {
+ emit_warning(c, field_decl->getLocation(), "%s %s demoted to opaque type - has bitfield",
+ container_kind_name,
+ is_anonymous ? "(anon)" : buf_ptr(bare_name));
+ return demote_struct_to_opaque(c, record_decl, full_type_name, bare_name);
+ }
+ }
+
+ AstNode *struct_node = trans_create_node(c, NodeTypeContainerDecl);
+ struct_node->data.container_decl.kind = container_kind;
+ struct_node->data.container_decl.layout = ContainerLayoutExtern;
+
+ // TODO handle attribute packed
+
+ struct_node->data.container_decl.fields.resize(field_count);
+
+ // must be before fields in case a circular reference happens
+ if (is_anonymous) {
+ c->decl_table.put(record_decl->getCanonicalDecl(), struct_node);
+ } else {
+ c->decl_table.put(record_decl->getCanonicalDecl(), trans_create_node_symbol(c, full_type_name));
+ }
+
+ uint32_t i = 0;
+ for (auto it = record_def->field_begin(),
+ it_end = record_def->field_end();
+ it != it_end; ++it, i += 1)
+ {
+ const FieldDecl *field_decl = *it;
+
+ AstNode *field_node = trans_create_node(c, NodeTypeStructField);
+ field_node->data.struct_field.name = buf_create_from_str(decl_name(field_decl));
+ field_node->data.struct_field.type = trans_qual_type(c, field_decl->getType(), field_decl->getLocation());
+
+ if (field_node->data.struct_field.type == nullptr) {
+ emit_warning(c, field_decl->getLocation(),
+ "%s %s demoted to opaque type - unresolved type",
+ container_kind_name,
+ is_anonymous ? "(anon)" : buf_ptr(bare_name));
+
+ return demote_struct_to_opaque(c, record_decl, full_type_name, bare_name);
+ }
+
+ struct_node->data.container_decl.fields.items[i] = field_node;
+ }
+
+ if (is_anonymous) {
+ return struct_node;
+ } else {
+ add_global_weak_alias(c, bare_name, full_type_name);
+ add_global_var(c, full_type_name, struct_node);
+ return trans_create_node_symbol(c, full_type_name);
+ }
+}
+
+static AstNode *trans_ap_value(Context *c, APValue *ap_value, QualType qt, const SourceLocation &source_loc) {
+ switch (ap_value->getKind()) {
+ case APValue::Int:
+ return trans_create_node_apint(c, ap_value->getInt());
+ case APValue::Uninitialized:
+ return trans_create_node(c, NodeTypeUndefinedLiteral);
+ case APValue::Array: {
+ emit_warning(c, source_loc, "TODO add a test case for this code");
+
+ unsigned init_count = ap_value->getArrayInitializedElts();
+ unsigned all_count = ap_value->getArraySize();
+ unsigned leftover_count = all_count - init_count;
+ AstNode *init_node = trans_create_node(c, NodeTypeContainerInitExpr);
+ AstNode *arr_type_node = trans_qual_type(c, qt, source_loc);
+ init_node->data.container_init_expr.type = arr_type_node;
+ init_node->data.container_init_expr.kind = ContainerInitKindArray;
+
+ QualType child_qt = qt.getTypePtr()->getLocallyUnqualifiedSingleStepDesugaredType();
+
+ for (size_t i = 0; i < init_count; i += 1) {
+ APValue &elem_ap_val = ap_value->getArrayInitializedElt(i);
+ AstNode *elem_node = trans_ap_value(c, &elem_ap_val, child_qt, source_loc);
+ if (elem_node == nullptr)
+ return nullptr;
+ init_node->data.container_init_expr.entries.append(elem_node);
+ }
+ if (leftover_count == 0) {
+ return init_node;
+ }
+
+ APValue &filler_ap_val = ap_value->getArrayFiller();
+ AstNode *filler_node = trans_ap_value(c, &filler_ap_val, child_qt, source_loc);
+ if (filler_node == nullptr)
+ return nullptr;
+
+ AstNode *filler_arr_1 = trans_create_node(c, NodeTypeContainerInitExpr);
+ init_node->data.container_init_expr.type = arr_type_node;
+ init_node->data.container_init_expr.kind = ContainerInitKindArray;
+ init_node->data.container_init_expr.entries.append(filler_node);
+
+ AstNode *rhs_node;
+ if (leftover_count == 1) {
+ rhs_node = filler_arr_1;
+ } else {
+ AstNode *amt_node = trans_create_node_unsigned(c, leftover_count);
+ rhs_node = trans_create_node_bin_op(c, filler_arr_1, BinOpTypeArrayMult, amt_node);
+ }
+
+ return trans_create_node_bin_op(c, init_node, BinOpTypeArrayCat, rhs_node);
+ }
+ case APValue::LValue: {
+ const APValue::LValueBase lval_base = ap_value->getLValueBase();
+ if (const Expr *expr = lval_base.dyn_cast<const Expr *>()) {
+ return trans_expr(c, ResultUsedYes, &c->global_scope->base, expr, TransRValue);
+ }
+ //const ValueDecl *value_decl = lval_base.get<const ValueDecl *>();
+ emit_warning(c, source_loc, "TODO handle initializer LValue ValueDecl");
+ return nullptr;
+ }
+ case APValue::Float:
+ emit_warning(c, source_loc, "unsupported initializer value kind: Float");
+ return nullptr;
+ case APValue::ComplexInt:
+ emit_warning(c, source_loc, "unsupported initializer value kind: ComplexInt");
+ return nullptr;
+ case APValue::ComplexFloat:
+ emit_warning(c, source_loc, "unsupported initializer value kind: ComplexFloat");
+ return nullptr;
+ case APValue::Vector:
+ emit_warning(c, source_loc, "unsupported initializer value kind: Vector");
+ return nullptr;
+ case APValue::Struct:
+ emit_warning(c, source_loc, "unsupported initializer value kind: Struct");
+ return nullptr;
+ case APValue::Union:
+ emit_warning(c, source_loc, "unsupported initializer value kind: Union");
+ return nullptr;
+ case APValue::MemberPointer:
+ emit_warning(c, source_loc, "unsupported initializer value kind: MemberPointer");
+ return nullptr;
+ case APValue::AddrLabelDiff:
+ emit_warning(c, source_loc, "unsupported initializer value kind: AddrLabelDiff");
+ return nullptr;
+ }
+ zig_unreachable();
+}
+
+static void visit_var_decl(Context *c, const VarDecl *var_decl) {
+ Buf *name = buf_create_from_str(decl_name(var_decl));
+
+ switch (var_decl->getTLSKind()) {
+ case VarDecl::TLS_None:
+ break;
+ case VarDecl::TLS_Static:
+ emit_warning(c, var_decl->getLocation(),
+ "ignoring variable '%s' - static thread local storage", buf_ptr(name));
+ return;
+ case VarDecl::TLS_Dynamic:
+ emit_warning(c, var_decl->getLocation(),
+ "ignoring variable '%s' - dynamic thread local storage", buf_ptr(name));
+ return;
+ }
+
+ QualType qt = var_decl->getType();
+ AstNode *var_type = trans_qual_type(c, qt, var_decl->getLocation());
+ if (var_type == nullptr) {
+ emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - unresolved type", buf_ptr(name));
+ return;
+ }
+
+ bool is_extern = var_decl->hasExternalStorage();
+ bool is_static = var_decl->isFileVarDecl();
+ bool is_const = qt.isConstQualified();
+
+ if (is_static && !is_extern) {
+ AstNode *init_node;
+ if (var_decl->hasInit()) {
+ APValue *ap_value = var_decl->evaluateValue();
+ if (ap_value == nullptr) {
+ emit_warning(c, var_decl->getLocation(),
+ "ignoring variable '%s' - unable to evaluate initializer", buf_ptr(name));
+ return;
+ }
+ init_node = trans_ap_value(c, ap_value, qt, var_decl->getLocation());
+ if (init_node == nullptr)
+ return;
+ } else {
+ init_node = trans_create_node(c, NodeTypeUndefinedLiteral);
+ }
+
+ AstNode *var_node = trans_create_node_var_decl_global(c, is_const, name, var_type, init_node);
+ add_top_level_decl(c, name, var_node);
+ return;
+ }
+
+ if (is_extern) {
+ AstNode *var_node = trans_create_node_var_decl_global(c, is_const, name, var_type, nullptr);
+ var_node->data.variable_declaration.is_extern = true;
+ add_top_level_decl(c, name, var_node);
+ return;
+ }
+
+ emit_warning(c, var_decl->getLocation(),
+ "ignoring variable '%s' - non-extern, non-static variable", buf_ptr(name));
+ return;
+}
+
+static bool decl_visitor(void *context, const Decl *decl) {
+ Context *c = (Context*)context;
+
+ switch (decl->getKind()) {
+ case Decl::Function:
+ visit_fn_decl(c, static_cast<const FunctionDecl*>(decl));
+ break;
+ case Decl::Typedef:
+ resolve_typedef_decl(c, static_cast<const TypedefNameDecl *>(decl));
+ break;
+ case Decl::Enum:
+ resolve_enum_decl(c, static_cast<const EnumDecl *>(decl));
+ break;
+ case Decl::Record:
+ resolve_record_decl(c, static_cast<const RecordDecl *>(decl));
+ break;
+ case Decl::Var:
+ visit_var_decl(c, static_cast<const VarDecl *>(decl));
+ break;
+ default:
+ emit_warning(c, decl->getLocation(), "ignoring %s decl", decl->getDeclKindName());
+ }
+
+ return true;
+}
+
+static bool name_exists_global(Context *c, Buf *name) {
+ return get_global(c, name) != nullptr;
+}
+
+static bool name_exists_scope(Context *c, Buf *name, TransScope *scope) {
+ while (scope != nullptr) {
+ if (scope->id == TransScopeIdVar) {
+ TransScopeVar *var_scope = (TransScopeVar *)scope;
+ if (buf_eql_buf(name, var_scope->zig_name)) {
+ return true;
+ }
+ }
+ scope = scope->parent;
+ }
+ return name_exists_global(c, name);
+}
+
+static Buf *get_unique_name(Context *c, Buf *name, TransScope *scope) {
+ Buf *proposed_name = name;
+ int count = 0;
+ while (name_exists_scope(c, proposed_name, scope)) {
+ if (proposed_name == name) {
+ proposed_name = buf_alloc();
+ }
+ buf_resize(proposed_name, 0);
+ buf_appendf(proposed_name, "%s_%d", buf_ptr(name), count);
+ count += 1;
+ }
+ return proposed_name;
+}
+
+static TransScopeRoot *trans_scope_root_create(Context *c) {
+ TransScopeRoot *result = allocate<TransScopeRoot>(1);
+ result->base.id = TransScopeIdRoot;
+ return result;
+}
+
+static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope) {
+ TransScopeWhile *result = allocate<TransScopeWhile>(1);
+ result->base.id = TransScopeIdWhile;
+ result->base.parent = parent_scope;
+ result->node = trans_create_node(c, NodeTypeWhileExpr);
+ return result;
+}
+
+static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope) {
+ TransScopeBlock *result = allocate<TransScopeBlock>(1);
+ result->base.id = TransScopeIdBlock;
+ result->base.parent = parent_scope;
+ result->node = trans_create_node(c, NodeTypeBlock);
+ return result;
+}
+
+static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name) {
+ TransScopeVar *result = allocate<TransScopeVar>(1);
+ result->base.id = TransScopeIdVar;
+ result->base.parent = parent_scope;
+ result->c_name = wanted_name;
+ result->zig_name = get_unique_name(c, wanted_name, parent_scope);
+ return result;
+}
+
+static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope) {
+ TransScopeSwitch *result = allocate<TransScopeSwitch>(1);
+ result->base.id = TransScopeIdSwitch;
+ result->base.parent = parent_scope;
+ result->switch_node = trans_create_node(c, NodeTypeSwitchExpr);
+ return result;
+}
+
+static TransScopeBlock *trans_scope_block_find(TransScope *scope) {
+ while (scope != nullptr) {
+ if (scope->id == TransScopeIdBlock) {
+ return (TransScopeBlock *)scope;
+ }
+ scope = scope->parent;
+ }
+ return nullptr;
+}
+
+static TransScopeSwitch *trans_scope_switch_find(TransScope *scope) {
+ while (scope != nullptr) {
+ if (scope->id == TransScopeIdSwitch) {
+ return (TransScopeSwitch *)scope;
+ }
+ scope = scope->parent;
+ }
+ return nullptr;
+}
+
+static void render_aliases(Context *c) {
+ for (size_t i = 0; i < c->aliases.length; i += 1) {
+ Alias *alias = &c->aliases.at(i);
+ if (name_exists_global(c, alias->new_name))
+ continue;
+
+ add_global_var(c, alias->new_name, trans_create_node_symbol(c, alias->canon_name));
+ }
+}
+
+static AstNode *trans_lookup_ast_container_typeof(Context *c, AstNode *ref_node);
+
+static AstNode *trans_lookup_ast_container(Context *c, AstNode *type_node) {
+ if (type_node == nullptr) {
+ return nullptr;
+ } else if (type_node->type == NodeTypeContainerDecl) {
+ return type_node;
+ } else if (type_node->type == NodeTypePrefixOpExpr) {
+ return type_node;
+ } else if (type_node->type == NodeTypeSymbol) {
+ AstNode *existing_node = get_global(c, type_node->data.symbol_expr.symbol);
+ if (existing_node == nullptr)
+ return nullptr;
+ if (existing_node->type != NodeTypeVariableDeclaration)
+ return nullptr;
+ return trans_lookup_ast_container(c, existing_node->data.variable_declaration.expr);
+ } else if (type_node->type == NodeTypeFieldAccessExpr) {
+ AstNode *container_node = trans_lookup_ast_container_typeof(c, type_node->data.field_access_expr.struct_expr);
+ if (container_node == nullptr)
+ return nullptr;
+ if (container_node->type != NodeTypeContainerDecl)
+ return container_node;
+
+ for (size_t i = 0; i < container_node->data.container_decl.fields.length; i += 1) {
+ AstNode *field_node = container_node->data.container_decl.fields.items[i];
+ if (buf_eql_buf(field_node->data.struct_field.name, type_node->data.field_access_expr.field_name)) {
+ return trans_lookup_ast_container(c, field_node->data.struct_field.type);
+ }
+ }
+ return nullptr;
+ } else {
+ return nullptr;
+ }
+}
+
+static AstNode *trans_lookup_ast_container_typeof(Context *c, AstNode *ref_node) {
+ if (ref_node->type == NodeTypeSymbol) {
+ AstNode *existing_node = get_global(c, ref_node->data.symbol_expr.symbol);
+ if (existing_node == nullptr)
+ return nullptr;
+ if (existing_node->type != NodeTypeVariableDeclaration)
+ return nullptr;
+ return trans_lookup_ast_container(c, existing_node->data.variable_declaration.type);
+ } else if (ref_node->type == NodeTypeFieldAccessExpr) {
+ AstNode *container_node = trans_lookup_ast_container_typeof(c, ref_node->data.field_access_expr.struct_expr);
+ if (container_node == nullptr)
+ return nullptr;
+ if (container_node->type != NodeTypeContainerDecl)
+ return container_node;
+ for (size_t i = 0; i < container_node->data.container_decl.fields.length; i += 1) {
+ AstNode *field_node = container_node->data.container_decl.fields.items[i];
+ if (buf_eql_buf(field_node->data.struct_field.name, ref_node->data.field_access_expr.field_name)) {
+ return trans_lookup_ast_container(c, field_node->data.struct_field.type);
+ }
+ }
+ return nullptr;
+ } else {
+ return nullptr;
+ }
+}
+
+static AstNode *trans_lookup_ast_maybe_fn(Context *c, AstNode *ref_node) {
+ AstNode *prefix_node = trans_lookup_ast_container_typeof(c, ref_node);
+ if (prefix_node == nullptr)
+ return nullptr;
+ if (prefix_node->type != NodeTypePrefixOpExpr)
+ return nullptr;
+ if (prefix_node->data.prefix_op_expr.prefix_op != PrefixOpMaybe)
+ return nullptr;
+
+ AstNode *fn_proto_node = prefix_node->data.prefix_op_expr.primary_expr;
+ if (fn_proto_node->type != NodeTypeFnProto)
+ return nullptr;
+
+ return fn_proto_node;
+}
+
+static void render_macros(Context *c) {
+ auto it = c->macro_table.entry_iterator();
+ for (;;) {
+ auto *entry = it.next();
+ if (!entry)
+ break;
+
+ AstNode *proto_node;
+ AstNode *value_node = entry->value;
+ if (value_node->type == NodeTypeFnDef) {
+ add_top_level_decl(c, value_node->data.fn_def.fn_proto->data.fn_proto.name, value_node);
+ } else if ((proto_node = trans_lookup_ast_maybe_fn(c, value_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.
+ AstNode *inline_fn_node = trans_create_node_inline_fn(c, entry->key, value_node, proto_node);
+ add_top_level_decl(c, entry->key, inline_fn_node);
+ } else {
+ add_global_var(c, entry->key, value_node);
+ }
+ }
+}
+
+static AstNode *parse_ctok_num_lit(Context *c, CTokenize *ctok, size_t *tok_i, bool negate) {
+ CTok *tok = &ctok->tokens.at(*tok_i);
+ if (tok->id == CTokIdNumLitInt) {
+ *tok_i += 1;
+ switch (tok->data.num_lit_int.suffix) {
+ case CNumLitSuffixNone:
+ return trans_create_node_unsigned_negative(c, tok->data.num_lit_int.x, negate);
+ case CNumLitSuffixL:
+ return trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate, "c_long");
+ case CNumLitSuffixU:
+ return trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate, "c_uint");
+ case CNumLitSuffixLU:
+ return trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate, "c_ulong");
+ case CNumLitSuffixLL:
+ return trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate, "c_longlong");
+ case CNumLitSuffixLLU:
+ return trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate, "c_ulonglong");
+ }
+ zig_unreachable();
+ } else if (tok->id == CTokIdNumLitFloat) {
+ *tok_i += 1;
+ double value = negate ? -tok->data.num_lit_float : tok->data.num_lit_float;
+ return trans_create_node_float_lit(c, value);
+ }
+ return nullptr;
+}
+
+static AstNode *parse_ctok(Context *c, CTokenize *ctok, size_t *tok_i) {
+ CTok *tok = &ctok->tokens.at(*tok_i);
+ switch (tok->id) {
+ case CTokIdCharLit:
+ *tok_i += 1;
+ return trans_create_node_unsigned(c, tok->data.char_lit);
+ case CTokIdStrLit:
+ *tok_i += 1;
+ return trans_create_node_str_lit_c(c, buf_create_from_buf(&tok->data.str_lit));
+ case CTokIdMinus:
+ *tok_i += 1;
+ return parse_ctok_num_lit(c, ctok, tok_i, true);
+ case CTokIdNumLitInt:
+ case CTokIdNumLitFloat:
+ return parse_ctok_num_lit(c, ctok, tok_i, false);
+ case CTokIdSymbol:
+ {
+ bool need_symbol = false;
+ CTokId curr_id = CTokIdSymbol;
+ Buf *symbol_name = buf_create_from_buf(&tok->data.symbol);
+ AstNode *curr_node = trans_create_node_symbol(c, symbol_name);
+ AstNode *parent_node = curr_node;
+ do {
+ *tok_i += 1;
+ CTok* curr_tok = &ctok->tokens.at(*tok_i);
+ if (need_symbol) {
+ if (curr_tok->id == CTokIdSymbol) {
+ symbol_name = buf_create_from_buf(&curr_tok->data.symbol);
+ curr_node = trans_create_node_field_access(c, parent_node, buf_create_from_buf(symbol_name));
+ parent_node = curr_node;
+ need_symbol = false;
+ } else {
+ return nullptr;
+ }
+ } else {
+ if (curr_tok->id == CTokIdDot) {
+ need_symbol = true;
+ continue;
+ } else {
+ break;
+ }
+ }
+ } while (curr_id != CTokIdEOF);
+ return curr_node;
+ }
+ case CTokIdLParen:
+ {
+ *tok_i += 1;
+ AstNode *inner_node = parse_ctok(c, ctok, tok_i);
+
+ CTok *next_tok = &ctok->tokens.at(*tok_i);
+ if (next_tok->id != CTokIdRParen) {
+ return nullptr;
+ }
+ *tok_i += 1;
+ return inner_node;
+ }
+ case CTokIdDot:
+ case CTokIdEOF:
+ case CTokIdRParen:
+ // not able to make sense of this
+ return nullptr;
+ }
+ zig_unreachable();
+}
+
+static void process_macro(Context *c, CTokenize *ctok, Buf *name, const char *char_ptr) {
+ tokenize_c_macro(ctok, (const uint8_t *)char_ptr);
+
+ if (ctok->error) {
+ return;
+ }
+
+ size_t tok_i = 0;
+ CTok *name_tok = &ctok->tokens.at(tok_i);
+ assert(name_tok->id == CTokIdSymbol && buf_eql_buf(&name_tok->data.symbol, name));
+ tok_i += 1;
+
+ AstNode *result_node = parse_ctok(c, ctok, &tok_i);
+ if (result_node == nullptr) {
+ return;
+ }
+ CTok *eof_tok = &ctok->tokens.at(tok_i);
+ if (eof_tok->id != CTokIdEOF) {
+ return;
+ }
+ if (result_node->type == NodeTypeSymbol) {
+ // if it equals itself, ignore. for example, from stdio.h:
+ // #define stdin stdin
+ Buf *symbol_name = result_node->data.symbol_expr.symbol;
+ if (buf_eql_buf(name, symbol_name)) {
+ return;
+ }
+ }
+ c->macro_table.put(name, result_node);
+}
+
+static void process_preprocessor_entities(Context *c, ASTUnit &unit) {
+ CTokenize ctok = {{0}};
+
+ // TODO if we see #undef, delete it from the table
+
+ for (PreprocessedEntity *entity : unit.getLocalPreprocessingEntities()) {
+ switch (entity->getKind()) {
+ case PreprocessedEntity::InvalidKind:
+ case PreprocessedEntity::InclusionDirectiveKind:
+ case PreprocessedEntity::MacroExpansionKind:
+ continue;
+ case PreprocessedEntity::MacroDefinitionKind:
+ {
+ MacroDefinitionRecord *macro = static_cast<MacroDefinitionRecord *>(entity);
+ const char *raw_name = macro->getName()->getNameStart();
+ SourceRange range = macro->getSourceRange();
+ SourceLocation begin_loc = range.getBegin();
+ SourceLocation end_loc = range.getEnd();
+
+ if (begin_loc == end_loc) {
+ // this means it is a macro without a value
+ // we don't care about such things
+ continue;
+ }
+ Buf *name = buf_create_from_str(raw_name);
+ if (name_exists_global(c, name)) {
+ continue;
+ }
+
+ const char *begin_c = c->source_manager->getCharacterData(begin_loc);
+ process_macro(c, &ctok, name, begin_c);
+ }
+ }
+ }
+}
+
+int parse_h_buf(ImportTableEntry *import, ZigList<ErrorMsg *> *errors, Buf *source,
+ CodeGen *codegen, AstNode *source_node)
+{
+ int err;
+ Buf tmp_file_path = BUF_INIT;
+ if ((err = os_buf_to_tmp_file(source, buf_create_from_str(".h"), &tmp_file_path))) {
+ return err;
+ }
+
+ err = parse_h_file(import, errors, buf_ptr(&tmp_file_path), codegen, source_node);
+
+ os_delete_file(&tmp_file_path);
+
+ return err;
+}
+
+int parse_h_file(ImportTableEntry *import, ZigList<ErrorMsg *> *errors, const char *target_file,
+ CodeGen *codegen, AstNode *source_node)
+{
+ Context context = {0};
+ Context *c = &context;
+ c->warnings_on = codegen->verbose_cimport;
+ c->import = import;
+ c->errors = errors;
+ if (buf_ends_with_str(buf_create_from_str(target_file), ".h")) {
+ c->visib_mod = VisibModPub;
+ c->export_visib_mod = VisibModPub;
+ } else {
+ c->visib_mod = VisibModPub;
+ c->export_visib_mod = VisibModExport;
+ }
+ c->decl_table.init(8);
+ c->macro_table.init(8);
+ c->global_table.init(8);
+ c->ptr_params.init(8);
+ c->codegen = codegen;
+ c->source_node = source_node;
+ c->global_scope = trans_scope_root_create(c);
+
+ ZigList<const char *> clang_argv = {0};
+
+ clang_argv.append("-x");
+ clang_argv.append("c");
+
+ if (c->codegen->is_native_target) {
+ char *ZIG_PARSEC_CFLAGS = getenv("ZIG_NATIVE_PARSEC_CFLAGS");
+ if (ZIG_PARSEC_CFLAGS) {
+ Buf tmp_buf = BUF_INIT;
+ char *start = ZIG_PARSEC_CFLAGS;
+ char *space = strstr(start, " ");
+ while (space) {
+ if (space - start > 0) {
+ buf_init_from_mem(&tmp_buf, start, space - start);
+ clang_argv.append(buf_ptr(buf_create_from_buf(&tmp_buf)));
+ }
+ start = space + 1;
+ space = strstr(start, " ");
+ }
+ buf_init_from_str(&tmp_buf, start);
+ clang_argv.append(buf_ptr(buf_create_from_buf(&tmp_buf)));
+ }
+ }
+
+ clang_argv.append("-isystem");
+ clang_argv.append(buf_ptr(codegen->zig_c_headers_dir));
+
+ clang_argv.append("-isystem");
+ clang_argv.append(buf_ptr(codegen->libc_include_dir));
+
+ // windows c runtime requires -D_DEBUG if using debug libraries
+ if (codegen->build_mode == BuildModeDebug) {
+ clang_argv.append("-D_DEBUG");
+ }
+
+ for (size_t i = 0; i < codegen->clang_argv_len; i += 1) {
+ clang_argv.append(codegen->clang_argv[i]);
+ }
+
+ // we don't need spell checking and it slows things down
+ clang_argv.append("-fno-spell-checking");
+
+ // this gives us access to preprocessing entities, presumably at
+ // the cost of performance
+ clang_argv.append("-Xclang");
+ clang_argv.append("-detailed-preprocessing-record");
+
+ if (!c->codegen->is_native_target) {
+ clang_argv.append("-target");
+ clang_argv.append(buf_ptr(&c->codegen->triple_str));
+ }
+
+ clang_argv.append(target_file);
+
+ // to make the [start...end] argument work
+ clang_argv.append(nullptr);
+
+ IntrusiveRefCntPtr<DiagnosticsEngine> diags(CompilerInstance::createDiagnostics(new DiagnosticOptions));
+
+ std::shared_ptr<PCHContainerOperations> pch_container_ops = std::make_shared<PCHContainerOperations>();
+
+ bool skip_function_bodies = false;
+ bool only_local_decls = true;
+ bool capture_diagnostics = true;
+ bool user_files_are_volatile = true;
+ bool allow_pch_with_compiler_errors = false;
+ bool single_file_parse = false;
+ bool for_serialization = false;
+ const char *resources_path = buf_ptr(codegen->zig_c_headers_dir);
+ std::unique_ptr<ASTUnit> err_unit;
+ std::unique_ptr<ASTUnit> ast_unit(ASTUnit::LoadFromCommandLine(
+ &clang_argv.at(0), &clang_argv.last(),
+ pch_container_ops, diags, resources_path,
+ only_local_decls, capture_diagnostics, None, true, 0, TU_Complete,
+ false, false, allow_pch_with_compiler_errors, skip_function_bodies,
+ single_file_parse, user_files_are_volatile, for_serialization, None, &err_unit,
+ nullptr));
+
+ // Early failures in LoadFromCommandLine may return with ErrUnit unset.
+ if (!ast_unit && !err_unit) {
+ return ErrorFileSystem;
+ }
+
+ if (diags->getClient()->getNumErrors() > 0) {
+ if (ast_unit) {
+ err_unit = std::move(ast_unit);
+ }
+
+ for (ASTUnit::stored_diag_iterator it = err_unit->stored_diag_begin(),
+ it_end = err_unit->stored_diag_end();
+ it != it_end; ++it)
+ {
+ switch (it->getLevel()) {
+ case DiagnosticsEngine::Ignored:
+ case DiagnosticsEngine::Note:
+ case DiagnosticsEngine::Remark:
+ case DiagnosticsEngine::Warning:
+ continue;
+ case DiagnosticsEngine::Error:
+ case DiagnosticsEngine::Fatal:
+ break;
+ }
+ StringRef msg_str_ref = it->getMessage();
+ Buf *msg = string_ref_to_buf(msg_str_ref);
+ FullSourceLoc fsl = it->getLocation();
+ if (fsl.hasManager()) {
+ FileID file_id = fsl.getFileID();
+ StringRef filename = fsl.getManager().getFilename(fsl);
+ unsigned line = fsl.getSpellingLineNumber() - 1;
+ unsigned column = fsl.getSpellingColumnNumber() - 1;
+ unsigned offset = fsl.getManager().getFileOffset(fsl);
+ const char *source = (const char *)fsl.getManager().getBufferData(file_id).bytes_begin();
+ Buf *path;
+ if (filename.empty()) {
+ path = buf_alloc();
+ } else {
+ path = string_ref_to_buf(filename);
+ }
+
+ ErrorMsg *err_msg = err_msg_create_with_offset(path, line, column, offset, source, msg);
+
+ c->errors->append(err_msg);
+ } else {
+ // NOTE the only known way this gets triggered right now is if you have a lot of errors
+ // clang emits "too many errors emitted, stopping now"
+ fprintf(stderr, "unexpected error from clang: %s\n", buf_ptr(msg));
+ }
+ }
+
+ return 0;
+ }
+
+ c->ctx = &ast_unit->getASTContext();
+ c->source_manager = &ast_unit->getSourceManager();
+ c->root = trans_create_node(c, NodeTypeRoot);
+
+ ast_unit->visitLocalTopLevelDecls(c, decl_visitor);
+
+ process_preprocessor_entities(c, *ast_unit);
+
+ render_macros(c);
+ render_aliases(c);
+
+ import->root = c->root;
+
+ return 0;
+}
diff --git a/src/parsec.hpp b/src/translate_c.hpp
diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp
@@ -403,6 +403,10 @@ unsigned ZigLLVMTag_DW_structure_type(void) {
return dwarf::DW_TAG_structure_type;
}
+unsigned ZigLLVMTag_DW_union_type(void) {
+ return dwarf::DW_TAG_union_type;
+}
+
ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved) {
DIBuilder *di_builder = new DIBuilder(*unwrap(module), allow_unresolved);
return reinterpret_cast<ZigLLVMDIBuilder *>(di_builder);
diff --git a/src/zig_llvm.hpp b/src/zig_llvm.hpp
@@ -117,6 +117,7 @@ unsigned ZigLLVMEncoding_DW_ATE_signed_char(void);
unsigned ZigLLVMLang_DW_LANG_C99(void);
unsigned ZigLLVMTag_DW_variable(void);
unsigned ZigLLVMTag_DW_structure_type(void);
+unsigned ZigLLVMTag_DW_union_type(void);
ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved);
void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module);
diff --git a/std/base64.zig b/std/base64.zig
@@ -1,186 +1,485 @@
const assert = @import("debug.zig").assert;
const mem = @import("mem.zig");
-pub const standard_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
-
-pub fn encode(dest: []u8, source: []const u8) -> []u8 {
- return encodeWithAlphabet(dest, source, standard_alphabet);
-}
-
-/// invalid characters in source are allowed, but they cause the value of dest to be undefined.
-pub fn decode(dest: []u8, source: []const u8) -> []u8 {
- return decodeWithAlphabet(dest, source, standard_alphabet);
-}
-
-pub fn encodeWithAlphabet(dest: []u8, source: []const u8, alphabet: []const u8) -> []u8 {
- assert(alphabet.len == 65);
- assert(dest.len >= calcEncodedSize(source.len));
-
- var i: usize = 0;
- var out_index: usize = 0;
- while (i + 2 < source.len) : (i += 3) {
- dest[out_index] = alphabet[(source[i] >> 2) & 0x3f];
- out_index += 1;
-
- dest[out_index] = alphabet[((source[i] & 0x3) << 4) |
- ((source[i + 1] & 0xf0) >> 4)];
- out_index += 1;
+pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+pub const standard_pad_char = '=';
+pub const standard_encoder = Base64Encoder.init(standard_alphabet_chars, standard_pad_char);
+
+pub const Base64Encoder = struct {
+ alphabet_chars: []const u8,
+ pad_char: u8,
+
+ /// a bunch of assertions, then simply pass the data right through.
+ pub fn init(alphabet_chars: []const u8, pad_char: u8) -> Base64Encoder {
+ assert(alphabet_chars.len == 64);
+ var char_in_alphabet = []bool{false} ** 256;
+ for (alphabet_chars) |c| {
+ assert(!char_in_alphabet[c]);
+ assert(c != pad_char);
+ char_in_alphabet[c] = true;
+ }
- dest[out_index] = alphabet[((source[i + 1] & 0xf) << 2) |
- ((source[i + 2] & 0xc0) >> 6)];
- out_index += 1;
+ return Base64Encoder{
+ .alphabet_chars = alphabet_chars,
+ .pad_char = pad_char,
+ };
+ }
- dest[out_index] = alphabet[source[i + 2] & 0x3f];
- out_index += 1;
+ /// ceil(source_len * 4/3)
+ pub fn calcSize(source_len: usize) -> usize {
+ return @divTrunc(source_len + 2, 3) * 4;
}
- if (i < source.len) {
- dest[out_index] = alphabet[(source[i] >> 2) & 0x3f];
- out_index += 1;
+ /// dest.len must be what you get from ::calcSize.
+ pub fn encode(encoder: &const Base64Encoder, dest: []u8, source: []const u8) {
+ assert(dest.len == Base64Encoder.calcSize(source.len));
- if (i + 1 == source.len) {
- dest[out_index] = alphabet[(source[i] & 0x3) << 4];
+ var i: usize = 0;
+ var out_index: usize = 0;
+ while (i + 2 < source.len) : (i += 3) {
+ dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];
out_index += 1;
- dest[out_index] = alphabet[64];
- out_index += 1;
- } else {
- dest[out_index] = alphabet[((source[i] & 0x3) << 4) |
+ dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) |
((source[i + 1] & 0xf0) >> 4)];
out_index += 1;
- dest[out_index] = alphabet[(source[i + 1] & 0xf) << 2];
+ dest[out_index] = encoder.alphabet_chars[((source[i + 1] & 0xf) << 2) |
+ ((source[i + 2] & 0xc0) >> 6)];
+ out_index += 1;
+
+ dest[out_index] = encoder.alphabet_chars[source[i + 2] & 0x3f];
out_index += 1;
}
- dest[out_index] = alphabet[64];
- out_index += 1;
- }
+ if (i < source.len) {
+ dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];
+ out_index += 1;
- return dest[0..out_index];
-}
+ if (i + 1 == source.len) {
+ dest[out_index] = encoder.alphabet_chars[(source[i] & 0x3) << 4];
+ out_index += 1;
+
+ dest[out_index] = encoder.pad_char;
+ out_index += 1;
+ } else {
+ dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) |
+ ((source[i + 1] & 0xf0) >> 4)];
+ out_index += 1;
-/// invalid characters in source are allowed, but they cause the value of dest to be undefined.
-pub fn decodeWithAlphabet(dest: []u8, source: []const u8, alphabet: []const u8) -> []u8 {
- assert(alphabet.len == 65);
+ dest[out_index] = encoder.alphabet_chars[(source[i + 1] & 0xf) << 2];
+ out_index += 1;
+ }
- var ascii6 = []u8{64} ** 256;
- for (alphabet) |c, i| {
- ascii6[c] = u8(i);
+ dest[out_index] = encoder.pad_char;
+ out_index += 1;
+ }
}
+};
+
+pub const standard_decoder = Base64Decoder.init(standard_alphabet_chars, standard_pad_char);
+error InvalidPadding;
+error InvalidCharacter;
+
+pub const Base64Decoder = struct {
+ /// e.g. 'A' => 0.
+ /// undefined for any value not in the 64 alphabet chars.
+ char_to_index: [256]u8,
+ /// true only for the 64 chars in the alphabet, not the pad char.
+ char_in_alphabet: [256]bool,
+ pad_char: u8,
+
+ pub fn init(alphabet_chars: []const u8, pad_char: u8) -> Base64Decoder {
+ assert(alphabet_chars.len == 64);
+
+ var result = Base64Decoder{
+ .char_to_index = undefined,
+ .char_in_alphabet = []bool{false} ** 256,
+ .pad_char = pad_char,
+ };
+
+ for (alphabet_chars) |c, i| {
+ assert(!result.char_in_alphabet[c]);
+ assert(c != pad_char);
+
+ result.char_to_index[c] = u8(i);
+ result.char_in_alphabet[c] = true;
+ }
- return decodeWithAscii6BitMap(dest, source, ascii6[0..], alphabet[64]);
-}
+ return result;
+ }
-pub fn decodeWithAscii6BitMap(dest: []u8, source: []const u8, ascii6: []const u8, pad_char: u8) -> []u8 {
- assert(ascii6.len == 256);
- assert(dest.len >= calcExactDecodedSizeWithPadChar(source, pad_char));
+ /// If the encoded buffer is detected to be invalid, returns error.InvalidPadding.
+ pub fn calcSize(decoder: &const Base64Decoder, source: []const u8) -> %usize {
+ if (source.len % 4 != 0) return error.InvalidPadding;
+ return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
+ }
- var src_index: usize = 0;
- var dest_index: usize = 0;
- var in_buf_len: usize = source.len;
+ /// dest.len must be what you get from ::calcSize.
+ /// invalid characters result in error.InvalidCharacter.
+ /// invalid padding results in error.InvalidPadding.
+ pub fn decode(decoder: &const Base64Decoder, dest: []u8, source: []const u8) -> %void {
+ assert(dest.len == %%decoder.calcSize(source));
+ assert(source.len % 4 == 0);
+
+ var src_cursor: usize = 0;
+ var dest_cursor: usize = 0;
+
+ while (src_cursor < source.len) : (src_cursor += 4) {
+ if (!decoder.char_in_alphabet[source[src_cursor + 0]]) return error.InvalidCharacter;
+ if (!decoder.char_in_alphabet[source[src_cursor + 1]]) return error.InvalidCharacter;
+ if (src_cursor < source.len - 4 or source[src_cursor + 3] != decoder.pad_char) {
+ // common case
+ if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
+ if (!decoder.char_in_alphabet[source[src_cursor + 3]]) return error.InvalidCharacter;
+ dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
+ decoder.char_to_index[source[src_cursor + 1]] >> 4;
+ dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 |
+ decoder.char_to_index[source[src_cursor + 2]] >> 2;
+ dest[dest_cursor + 2] = decoder.char_to_index[source[src_cursor + 2]] << 6 |
+ decoder.char_to_index[source[src_cursor + 3]];
+ dest_cursor += 3;
+ } else if (source[src_cursor + 2] != decoder.pad_char) {
+ // one pad char
+ if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
+ dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
+ decoder.char_to_index[source[src_cursor + 1]] >> 4;
+ dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 |
+ decoder.char_to_index[source[src_cursor + 2]] >> 2;
+ if (decoder.char_to_index[source[src_cursor + 2]] << 6 != 0) return error.InvalidPadding;
+ dest_cursor += 2;
+ } else {
+ // two pad chars
+ dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
+ decoder.char_to_index[source[src_cursor + 1]] >> 4;
+ if (decoder.char_to_index[source[src_cursor + 1]] << 4 != 0) return error.InvalidPadding;
+ dest_cursor += 1;
+ }
+ }
- while (in_buf_len > 0 and source[in_buf_len - 1] == pad_char) {
- in_buf_len -= 1;
+ assert(src_cursor == source.len);
+ assert(dest_cursor == dest.len);
}
+};
+
+error OutputTooSmall;
+
+pub const Base64DecoderWithIgnore = struct {
+ decoder: Base64Decoder,
+ char_is_ignored: [256]bool,
+ pub fn init(alphabet_chars: []const u8, pad_char: u8, ignore_chars: []const u8) -> Base64DecoderWithIgnore {
+ var result = Base64DecoderWithIgnore {
+ .decoder = Base64Decoder.init(alphabet_chars, pad_char),
+ .char_is_ignored = []bool{false} ** 256,
+ };
+
+ for (ignore_chars) |c| {
+ assert(!result.decoder.char_in_alphabet[c]);
+ assert(!result.char_is_ignored[c]);
+ assert(result.decoder.pad_char != c);
+ result.char_is_ignored[c] = true;
+ }
- while (in_buf_len > 4) {
- dest[dest_index] = ascii6[source[src_index + 0]] << 2 |
- ascii6[source[src_index + 1]] >> 4;
- dest_index += 1;
+ return result;
+ }
- dest[dest_index] = ascii6[source[src_index + 1]] << 4 |
- ascii6[source[src_index + 2]] >> 2;
- dest_index += 1;
+ /// If no characters end up being ignored or padding, this will be the exact decoded size.
+ pub fn calcSizeUpperBound(encoded_len: usize) -> %usize {
+ return @divTrunc(encoded_len, 4) * 3;
+ }
- dest[dest_index] = ascii6[source[src_index + 2]] << 6 |
- ascii6[source[src_index + 3]];
- dest_index += 1;
+ /// Invalid characters that are not ignored result in error.InvalidCharacter.
+ /// Invalid padding results in error.InvalidPadding.
+ /// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound.
+ /// Returns the number of bytes writen to dest.
+ pub fn decode(decoder_with_ignore: &const Base64DecoderWithIgnore, dest: []u8, source: []const u8) -> %usize {
+ const decoder = &const decoder_with_ignore.decoder;
+
+ var src_cursor: usize = 0;
+ var dest_cursor: usize = 0;
+
+ while (true) {
+ // get the next 4 chars, if available
+ var next_4_chars: [4]u8 = undefined;
+ var available_chars: usize = 0;
+ var pad_char_count: usize = 0;
+ while (available_chars < 4 and src_cursor < source.len) {
+ var c = source[src_cursor];
+ src_cursor += 1;
+
+ if (decoder.char_in_alphabet[c]) {
+ // normal char
+ next_4_chars[available_chars] = c;
+ available_chars += 1;
+ } else if (decoder_with_ignore.char_is_ignored[c]) {
+ // we're told to skip this one
+ continue;
+ } else if (c == decoder.pad_char) {
+ // the padding has begun. count the pad chars.
+ pad_char_count += 1;
+ while (src_cursor < source.len) {
+ c = source[src_cursor];
+ src_cursor += 1;
+ if (c == decoder.pad_char) {
+ pad_char_count += 1;
+ if (pad_char_count > 2) return error.InvalidCharacter;
+ } else if (decoder_with_ignore.char_is_ignored[c]) {
+ // we can even ignore chars during the padding
+ continue;
+ } else return error.InvalidCharacter;
+ }
+ break;
+ } else return error.InvalidCharacter;
+ }
+
+ switch (available_chars) {
+ 4 => {
+ // common case
+ if (dest_cursor + 3 > dest.len) return error.OutputTooSmall;
+ assert(pad_char_count == 0);
+ dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 |
+ decoder.char_to_index[next_4_chars[1]] >> 4;
+ dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 |
+ decoder.char_to_index[next_4_chars[2]] >> 2;
+ dest[dest_cursor + 2] = decoder.char_to_index[next_4_chars[2]] << 6 |
+ decoder.char_to_index[next_4_chars[3]];
+ dest_cursor += 3;
+ continue;
+ },
+ 3 => {
+ if (dest_cursor + 2 > dest.len) return error.OutputTooSmall;
+ if (pad_char_count != 1) return error.InvalidPadding;
+ dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 |
+ decoder.char_to_index[next_4_chars[1]] >> 4;
+ dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 |
+ decoder.char_to_index[next_4_chars[2]] >> 2;
+ if (decoder.char_to_index[next_4_chars[2]] << 6 != 0) return error.InvalidPadding;
+ dest_cursor += 2;
+ break;
+ },
+ 2 => {
+ if (dest_cursor + 1 > dest.len) return error.OutputTooSmall;
+ if (pad_char_count != 2) return error.InvalidPadding;
+ dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 |
+ decoder.char_to_index[next_4_chars[1]] >> 4;
+ if (decoder.char_to_index[next_4_chars[1]] << 4 != 0) return error.InvalidPadding;
+ dest_cursor += 1;
+ break;
+ },
+ 1 => {
+ return error.InvalidPadding;
+ },
+ 0 => {
+ if (pad_char_count != 0) return error.InvalidPadding;
+ break;
+ },
+ else => unreachable,
+ }
+ }
- src_index += 4;
- in_buf_len -= 4;
- }
+ assert(src_cursor == source.len);
- if (in_buf_len > 1) {
- dest[dest_index] = ascii6[source[src_index + 0]] << 2 |
- ascii6[source[src_index + 1]] >> 4;
- dest_index += 1;
+ return dest_cursor;
}
- if (in_buf_len > 2) {
- dest[dest_index] = ascii6[source[src_index + 1]] << 4 |
- ascii6[source[src_index + 2]] >> 2;
- dest_index += 1;
+};
+
+
+pub const standard_decoder_unsafe = Base64DecoderUnsafe.init(standard_alphabet_chars, standard_pad_char);
+
+pub const Base64DecoderUnsafe = struct {
+ /// e.g. 'A' => 0.
+ /// undefined for any value not in the 64 alphabet chars.
+ char_to_index: [256]u8,
+ pad_char: u8,
+
+ pub fn init(alphabet_chars: []const u8, pad_char: u8) -> Base64DecoderUnsafe {
+ assert(alphabet_chars.len == 64);
+ var result = Base64DecoderUnsafe {
+ .char_to_index = undefined,
+ .pad_char = pad_char,
+ };
+ for (alphabet_chars) |c, i| {
+ assert(c != pad_char);
+ result.char_to_index[c] = u8(i);
+ }
+ return result;
}
- if (in_buf_len > 3) {
- dest[dest_index] = ascii6[source[src_index + 2]] << 6 |
- ascii6[source[src_index + 3]];
- dest_index += 1;
+
+ /// The source buffer must be valid.
+ pub fn calcSize(decoder: &const Base64DecoderUnsafe, source: []const u8) -> usize {
+ return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
}
- return dest[0..dest_index];
-}
+ /// dest.len must be what you get from ::calcDecodedSizeExactUnsafe.
+ /// invalid characters or padding will result in undefined values.
+ pub fn decode(decoder: &const Base64DecoderUnsafe, dest: []u8, source: []const u8) {
+ assert(dest.len == decoder.calcSize(source));
-pub fn calcEncodedSize(source_len: usize) -> usize {
- return (((source_len * 4) / 3 + 3) / 4) * 4;
-}
+ var src_index: usize = 0;
+ var dest_index: usize = 0;
+ var in_buf_len: usize = source.len;
-/// Computes the upper bound of the decoded size based only on the encoded length.
-/// To compute the exact decoded size, see ::calcExactDecodedSize
-pub fn calcMaxDecodedSize(encoded_len: usize) -> usize {
- return @divExact(encoded_len * 3, 4);
-}
+ while (in_buf_len > 0 and source[in_buf_len - 1] == decoder.pad_char) {
+ in_buf_len -= 1;
+ }
-/// Computes the number of decoded bytes there will be. This function must
-/// be given the encoded buffer because there might be padding
-/// bytes at the end ('=' in the standard alphabet)
-pub fn calcExactDecodedSize(encoded: []const u8) -> usize {
- return calcExactDecodedSizeWithAlphabet(encoded, standard_alphabet);
-}
+ while (in_buf_len > 4) {
+ dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 |
+ decoder.char_to_index[source[src_index + 1]] >> 4;
+ dest_index += 1;
-pub fn calcExactDecodedSizeWithAlphabet(encoded: []const u8, alphabet: []const u8) -> usize {
- assert(alphabet.len == 65);
- return calcExactDecodedSizeWithPadChar(encoded, alphabet[64]);
-}
+ dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 |
+ decoder.char_to_index[source[src_index + 2]] >> 2;
+ dest_index += 1;
-pub fn calcExactDecodedSizeWithPadChar(encoded: []const u8, pad_char: u8) -> usize {
- var buf_len = encoded.len;
+ dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 |
+ decoder.char_to_index[source[src_index + 3]];
+ dest_index += 1;
- while (buf_len > 0 and encoded[buf_len - 1] == pad_char) {
- buf_len -= 1;
- }
+ src_index += 4;
+ in_buf_len -= 4;
+ }
- return (buf_len * 3) / 4;
+ if (in_buf_len > 1) {
+ dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 |
+ decoder.char_to_index[source[src_index + 1]] >> 4;
+ dest_index += 1;
+ }
+ if (in_buf_len > 2) {
+ dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 |
+ decoder.char_to_index[source[src_index + 2]] >> 2;
+ dest_index += 1;
+ }
+ if (in_buf_len > 3) {
+ dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 |
+ decoder.char_to_index[source[src_index + 3]];
+ dest_index += 1;
+ }
+ }
+};
+
+fn calcDecodedSizeExactUnsafe(source: []const u8, pad_char: u8) -> usize {
+ if (source.len == 0) return 0;
+ var result = @divExact(source.len, 4) * 3;
+ if (source[source.len - 1] == pad_char) {
+ result -= 1;
+ if (source[source.len - 2] == pad_char) {
+ result -= 1;
+ }
+ }
+ return result;
}
+
test "base64" {
- testBase64();
- comptime testBase64();
+ @setEvalBranchQuota(5000);
+ %%testBase64();
+ comptime %%testBase64();
}
-fn testBase64() {
- testBase64Case("", "");
- testBase64Case("f", "Zg==");
- testBase64Case("fo", "Zm8=");
- testBase64Case("foo", "Zm9v");
- testBase64Case("foob", "Zm9vYg==");
- testBase64Case("fooba", "Zm9vYmE=");
- testBase64Case("foobar", "Zm9vYmFy");
+fn testBase64() -> %void {
+ %return testAllApis("", "");
+ %return testAllApis("f", "Zg==");
+ %return testAllApis("fo", "Zm8=");
+ %return testAllApis("foo", "Zm9v");
+ %return testAllApis("foob", "Zm9vYg==");
+ %return testAllApis("fooba", "Zm9vYmE=");
+ %return testAllApis("foobar", "Zm9vYmFy");
+
+ %return testDecodeIgnoreSpace("", " ");
+ %return testDecodeIgnoreSpace("f", "Z g= =");
+ %return testDecodeIgnoreSpace("fo", " Zm8=");
+ %return testDecodeIgnoreSpace("foo", "Zm9v ");
+ %return testDecodeIgnoreSpace("foob", "Zm9vYg = = ");
+ %return testDecodeIgnoreSpace("fooba", "Zm9v YmE=");
+ %return testDecodeIgnoreSpace("foobar", " Z m 9 v Y m F y ");
+
+ // test getting some api errors
+ %return testError("A", error.InvalidPadding);
+ %return testError("AA", error.InvalidPadding);
+ %return testError("AAA", error.InvalidPadding);
+ %return testError("A..A", error.InvalidCharacter);
+ %return testError("AA=A", error.InvalidCharacter);
+ %return testError("AA/=", error.InvalidPadding);
+ %return testError("A/==", error.InvalidPadding);
+ %return testError("A===", error.InvalidCharacter);
+ %return testError("====", error.InvalidCharacter);
+
+ %return testOutputTooSmallError("AA==");
+ %return testOutputTooSmallError("AAA=");
+ %return testOutputTooSmallError("AAAA");
+ %return testOutputTooSmallError("AAAAAA==");
}
-fn testBase64Case(expected_decoded: []const u8, expected_encoded: []const u8) {
- const calculated_decoded_len = calcExactDecodedSize(expected_encoded);
- assert(calculated_decoded_len == expected_decoded.len);
+fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) -> %void {
+ // Base64Encoder
+ {
+ var buffer: [0x100]u8 = undefined;
+ var encoded = buffer[0..Base64Encoder.calcSize(expected_decoded.len)];
+ standard_encoder.encode(encoded, expected_decoded);
+ assert(mem.eql(u8, encoded, expected_encoded));
+ }
- const calculated_encoded_len = calcEncodedSize(expected_decoded.len);
- assert(calculated_encoded_len == expected_encoded.len);
+ // Base64Decoder
+ {
+ var buffer: [0x100]u8 = undefined;
+ var decoded = buffer[0..%return standard_decoder.calcSize(expected_encoded)];
+ %return standard_decoder.decode(decoded, expected_encoded);
+ assert(mem.eql(u8, decoded, expected_decoded));
+ }
- var buf: [100]u8 = undefined;
+ // Base64DecoderWithIgnore
+ {
+ const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(
+ standard_alphabet_chars, standard_pad_char, "");
+ var buffer: [0x100]u8 = undefined;
+ var decoded = buffer[0..%return Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)];
+ var written = %return standard_decoder_ignore_nothing.decode(decoded, expected_encoded);
+ assert(written <= decoded.len);
+ assert(mem.eql(u8, decoded[0..written], expected_decoded));
+ }
- const actual_decoded = decode(buf[0..], expected_encoded);
- assert(actual_decoded.len == expected_decoded.len);
- assert(mem.eql(u8, expected_decoded, actual_decoded));
+ // Base64DecoderUnsafe
+ {
+ var buffer: [0x100]u8 = undefined;
+ var decoded = buffer[0..standard_decoder_unsafe.calcSize(expected_encoded)];
+ standard_decoder_unsafe.decode(decoded, expected_encoded);
+ assert(mem.eql(u8, decoded, expected_decoded));
+ }
+}
+
+fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) -> %void {
+ const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
+ standard_alphabet_chars, standard_pad_char, " ");
+ var buffer: [0x100]u8 = undefined;
+ var decoded = buffer[0..%return Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)];
+ var written = %return standard_decoder_ignore_space.decode(decoded, encoded);
+ assert(mem.eql(u8, decoded[0..written], expected_decoded));
+}
+
+error ExpectedError;
+fn testError(encoded: []const u8, expected_err: error) -> %void {
+ const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
+ standard_alphabet_chars, standard_pad_char, " ");
+ var buffer: [0x100]u8 = undefined;
+ if (standard_decoder.calcSize(encoded)) |decoded_size| {
+ var decoded = buffer[0..decoded_size];
+ if (standard_decoder.decode(decoded, encoded)) |_| {
+ return error.ExpectedError;
+ } else |err| if (err != expected_err) return err;
+ } else |err| if (err != expected_err) return err;
+
+ if (standard_decoder_ignore_space.decode(buffer[0..], encoded)) |_| {
+ return error.ExpectedError;
+ } else |err| if (err != expected_err) return err;
+}
- const actual_encoded = encode(buf[0..], expected_decoded);
- assert(actual_encoded.len == expected_encoded.len);
- assert(mem.eql(u8, expected_encoded, actual_encoded));
+fn testOutputTooSmallError(encoded: []const u8) -> %void {
+ const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
+ standard_alphabet_chars, standard_pad_char, " ");
+ var buffer: [0x100]u8 = undefined;
+ var decoded = buffer[0..calcDecodedSizeExactUnsafe(encoded, standard_pad_char) - 1];
+ if (standard_decoder_ignore_space.decode(decoded, encoded)) |_| {
+ return error.ExpectedError;
+ } else |err| if (err != error.OutputTooSmall) return err;
}
diff --git a/std/os/index.zig b/std/os/index.zig
@@ -31,6 +31,8 @@ pub const windowsWaitSingle = windows_util.windowsWaitSingle;
pub const windowsWrite = windows_util.windowsWrite;
pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
pub const windowsOpen = windows_util.windowsOpen;
+pub const windowsLoadDll = windows_util.windowsLoadDll;
+pub const windowsUnloadDll = windows_util.windowsUnloadDll;
pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
pub const FileHandle = if (is_windows) windows.HANDLE else i32;
@@ -620,7 +622,9 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:
}
// here we replace the standard +/ with -_ so that it can be used in a file name
-const b64_fs_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=";
+const b64_fs_encoder = base64.Base64Encoder.init(
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
+ base64.standard_pad_char);
pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) -> %void {
if (symLink(allocator, existing_path, new_path)) {
@@ -632,12 +636,12 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:
}
var rand_buf: [12]u8 = undefined;
- const tmp_path = %return allocator.alloc(u8, new_path.len + base64.calcEncodedSize(rand_buf.len));
+ const tmp_path = %return allocator.alloc(u8, new_path.len + base64.Base64Encoder.calcSize(rand_buf.len));
defer allocator.free(tmp_path);
mem.copy(u8, tmp_path[0..], new_path);
while (true) {
%return getRandomBytes(rand_buf[0..]);
- _ = base64.encodeWithAlphabet(tmp_path[new_path.len..], rand_buf, b64_fs_alphabet);
+ b64_fs_encoder.encode(tmp_path[new_path.len..], rand_buf);
if (symLink(allocator, existing_path, tmp_path)) {
return rename(allocator, tmp_path, new_path);
} else |err| {
@@ -715,11 +719,11 @@ pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []con
/// Guaranteed to be atomic.
pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: usize) -> %void {
var rand_buf: [12]u8 = undefined;
- const tmp_path = %return allocator.alloc(u8, dest_path.len + base64.calcEncodedSize(rand_buf.len));
+ const tmp_path = %return allocator.alloc(u8, dest_path.len + base64.Base64Encoder.calcSize(rand_buf.len));
defer allocator.free(tmp_path);
mem.copy(u8, tmp_path[0..], dest_path);
%return getRandomBytes(rand_buf[0..]);
- _ = base64.encodeWithAlphabet(tmp_path[dest_path.len..], rand_buf, b64_fs_alphabet);
+ b64_fs_encoder.encode(tmp_path[dest_path.len..], rand_buf);
var out_file = %return io.File.openWriteMode(tmp_path, mode, allocator);
defer out_file.close();
diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig
@@ -84,6 +84,11 @@ pub extern "kernel32" stdcallcc fn WriteFile(in_hFile: HANDLE, in_lpBuffer: &con
in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?&DWORD,
in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL;
+//TODO: call unicode versions instead of relying on ANSI code page
+pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) -> ?HMODULE;
+
+pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) -> BOOL;
+
pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) -> c_int;
pub const PROV_RSA_FULL = 1;
@@ -97,6 +102,7 @@ pub const FLOAT = f32;
pub const HANDLE = &c_void;
pub const HCRYPTPROV = ULONG_PTR;
pub const HINSTANCE = &@OpaqueType();
+pub const HMODULE = &@OpaqueType();
pub const INT = c_int;
pub const LPBYTE = &BYTE;
pub const LPCH = &CHAR;
diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig
@@ -4,6 +4,7 @@ const windows = std.os.windows;
const assert = std.debug.assert;
const mem = std.mem;
const BufMap = std.BufMap;
+const cstr = std.cstr;
error WaitAbandoned;
error WaitTimeOut;
@@ -149,3 +150,25 @@ pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap)
result[i] = 0;
return result;
}
+
+error DllNotFound;
+pub fn windowsLoadDll(allocator: &mem.Allocator, dll_path: []const u8) -> %windows.HMODULE {
+ const padded_buff = %return cstr.addNullByte(allocator, dll_path);
+ defer allocator.free(padded_buff);
+ return windows.LoadLibraryA(padded_buff.ptr) ?? error.DllNotFound;
+}
+
+pub fn windowsUnloadDll(hModule: windows.HMODULE) {
+ assert(windows.FreeLibrary(hModule)!= 0);
+}
+
+
+test "InvalidDll" {
+ const DllName = "asdf.dll";
+ const allocator = std.debug.global_allocator;
+ const handle = os.windowsLoadDll(allocator, DllName) %% |err| {
+ assert(err == error.DllNotFound);
+ return;
+ };
+}
+
diff --git a/test/cases/union.zig b/test/cases/union.zig
@@ -31,3 +31,47 @@ test "unions embedded in aggregate types" {
else => unreachable,
}
}
+
+
+const Foo = union {
+ float: f64,
+ int: i32,
+};
+
+test "basic unions" {
+ var foo = Foo { .int = 1 };
+ assert(foo.int == 1);
+ foo = Foo {.float = 12.34};
+ assert(foo.float == 12.34);
+}
+
+test "init union with runtime value" {
+ var foo: Foo = undefined;
+
+ setFloat(&foo, 12.34);
+ assert(foo.float == 12.34);
+
+ setInt(&foo, 42);
+ assert(foo.int == 42);
+}
+
+fn setFloat(foo: &Foo, x: f64) {
+ *foo = Foo { .float = x };
+}
+
+fn setInt(foo: &Foo, x: i32) {
+ *foo = Foo { .int = x };
+}
+
+const FooExtern = extern union {
+ float: f64,
+ int: i32,
+};
+
+test "basic extern unions" {
+ var foo = FooExtern { .int = 1 };
+ assert(foo.int == 1);
+ foo.float = 12.34;
+ assert(foo.float == 12.34);
+}
+
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
@@ -389,8 +389,8 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ const y = a.bar;
\\}
,
- ".tmp_source.zig:4:6: error: no member named 'foo' in 'A'",
- ".tmp_source.zig:5:16: error: no member named 'bar' in 'A'");
+ ".tmp_source.zig:4:6: error: no member named 'foo' in struct 'A'",
+ ".tmp_source.zig:5:16: error: no member named 'bar' in struct 'A'");
cases.add("redefinition of struct",
\\const A = struct { x : i32, };
@@ -454,7 +454,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ .foo = 42,
\\ };
\\}
- , ".tmp_source.zig:10:9: error: no member named 'foo' in 'A'");
+ , ".tmp_source.zig:10:9: error: no member named 'foo' in struct 'A'");
cases.add("invalid break expression",
\\export fn f() {
@@ -2343,4 +2343,23 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\pub extern fn foo(format: &const u8, ...);
,
".tmp_source.zig:2:9: error: expected type '&const u8', found '[5]u8'");
+
+ cases.add("constant inside comptime function has compile error",
+ \\const ContextAllocator = MemoryPool(usize);
+ \\
+ \\pub fn MemoryPool(comptime T: type) -> type {
+ \\ const free_list_t = @compileError("aoeu");
+ \\
+ \\ struct {
+ \\ free_list: free_list_t,
+ \\ }
+ \\}
+ \\
+ \\export fn entry() {
+ \\ var allocator: ContextAllocator = undefined;
+ \\}
+ ,
+ ".tmp_source.zig:4:25: error: aoeu",
+ ".tmp_source.zig:1:36: note: called from here",
+ ".tmp_source.zig:12:20: note: referenced here");
}
diff --git a/test/debug_safety.zig b/test/debug_safety.zig
@@ -260,4 +260,24 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ return int_slice[0];
\\}
);
+
+ cases.addDebugSafety("bad union field access",
+ \\pub fn panic(message: []const u8) -> noreturn {
+ \\ @import("std").os.exit(126);
+ \\}
+ \\
+ \\const Foo = union {
+ \\ float: f32,
+ \\ int: u32,
+ \\};
+ \\
+ \\pub fn main() -> %void {
+ \\ var f = Foo { .int = 42 };
+ \\ bar(&f);
+ \\}
+ \\
+ \\fn bar(f: &Foo) {
+ \\ f.float = 12.34;
+ \\}
+ );
}
diff --git a/test/parsec.zig b/test/parsec.zig
@@ -1,871 +0,0 @@
-const tests = @import("tests.zig");
-
-pub fn addCases(cases: &tests.ParseCContext) {
- 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);
- ,
- \\pub extern fn foo(a: u8, b: u8, c: i8) -> c_int;
- ,
- \\pub extern fn bar(a: u8, b: u16, c: u32, d: u64);
- ,
- \\pub extern fn baz(a: i8, b: i16, c: i32, d: i64);
- );
-
- cases.add("noreturn attribute",
- \\void foo(void) __attribute__((noreturn));
- ,
- \\pub extern fn foo() -> noreturn;
- );
-
- cases.addC("simple function",
- \\int abs(int a) {
- \\ return a < 0 ? -a : a;
- \\}
- ,
- \\export fn abs(a: c_int) -> c_int {
- \\ return if (a < 0) -a else a;
- \\}
- );
-
- cases.add("enums",
- \\enum Foo {
- \\ FooA,
- \\ FooB,
- \\ Foo1,
- \\};
- ,
- \\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("restrict -> noalias",
- \\void foo(void *restrict bar, void *restrict);
- ,
- \\pub extern fn foo(noalias bar: ?&c_void, noalias arg1: ?&c_void);
- );
-
- cases.add("simple struct",
- \\struct Foo {
- \\ int x;
- \\ char *y;
- \\};
- ,
- \\const struct_Foo = extern struct {
- \\ x: c_int,
- \\ y: ?&u8,
- \\};
- ,
- \\pub const Foo = struct_Foo;
- );
-
- cases.add("qualified struct and enum",
- \\struct Foo {
- \\ int x;
- \\ int y;
- \\};
- \\enum Bar {
- \\ BarA,
- \\ BarB,
- \\};
- \\void func(struct Foo *a, enum Bar **b);
- ,
- \\pub const struct_Foo = extern struct {
- \\ x: c_int,
- \\ y: c_int,
- \\};
- ,
- \\pub const enum_Bar = extern enum {
- \\ A,
- \\ B,
- \\};
- ,
- \\pub const BarA = enum_Bar.A;
- ,
- \\pub const BarB = enum_Bar.B;
- ,
- \\pub extern fn func(a: ?&struct_Foo, b: ?&(?&enum_Bar));
- ,
- \\pub const Foo = struct_Foo;
- ,
- \\pub const Bar = enum_Bar;
- );
-
- cases.add("constant size array",
- \\void func(int array[20]);
- ,
- \\pub extern fn func(array: ?&c_int);
- );
-
- cases.add("self referential struct with function pointer",
- \\struct Foo {
- \\ void (*derp)(struct Foo *foo);
- \\};
- ,
- \\pub const struct_Foo = extern struct {
- \\ derp: ?extern fn(?&struct_Foo),
- \\};
- ,
- \\pub const Foo = struct_Foo;
- );
-
- cases.add("struct prototype used in func",
- \\struct Foo;
- \\struct Foo *some_func(struct Foo *foo, int x);
- ,
- \\pub const struct_Foo = @OpaqueType();
- ,
- \\pub extern fn some_func(foo: ?&struct_Foo, x: c_int) -> ?&struct_Foo;
- ,
- \\pub const Foo = struct_Foo;
- );
-
- cases.add("#define a char literal",
- \\#define A_CHAR 'a'
- ,
- \\pub const A_CHAR = 97;
- );
-
- cases.add("#define an unsigned integer literal",
- \\#define CHANNEL_COUNT 24
- ,
- \\pub const CHANNEL_COUNT = 24;
- );
-
- cases.add("#define referencing another #define",
- \\#define THING2 THING1
- \\#define THING1 1234
- ,
- \\pub const THING1 = 1234;
- ,
- \\pub const THING2 = THING1;
- );
-
- cases.add("variables",
- \\extern int extern_var;
- \\static const int int_var = 13;
- ,
- \\pub extern var extern_var: c_int;
- ,
- \\pub const int_var: c_int = 13;
- );
-
- cases.add("circular struct definitions",
- \\struct Bar;
- \\
- \\struct Foo {
- \\ struct Bar *next;
- \\};
- \\
- \\struct Bar {
- \\ struct Foo *next;
- \\};
- ,
- \\pub const struct_Bar = extern struct {
- \\ next: ?&struct_Foo,
- \\};
- ,
- \\pub const struct_Foo = extern struct {
- \\ next: ?&struct_Bar,
- \\};
- );
-
- cases.add("typedef void",
- \\typedef void Foo;
- \\Foo fun(Foo *a);
- ,
- \\pub const Foo = c_void;
- ,
- \\pub extern fn fun(a: ?&Foo) -> Foo;
- );
-
- 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
- ,
- \\pub extern var fn_ptr: ?extern fn();
- ,
- \\pub inline fn foo() {
- \\ (??fn_ptr)()
- \\}
- ,
- \\pub extern var fn_ptr2: ?extern fn(c_int, f32) -> u8;
- ,
- \\pub inline fn bar(arg0: c_int, arg1: f32) -> u8 {
- \\ (??fn_ptr2)(arg0, arg1)
- \\}
- );
-
- cases.add("#define string",
- \\#define foo "a string"
- ,
- \\pub const foo = c"a string";
- );
-
- cases.add("__cdecl doesn't mess up function pointers",
- \\void foo(void (__cdecl *fn_ptr)(void));
- ,
- \\pub extern fn foo(fn_ptr: ?extern fn());
- );
-
- cases.add("comment after integer literal",
- \\#define SDL_INIT_VIDEO 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- ,
- \\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 */
- ,
- \\pub const SDL_INIT_VIDEO = c_uint(32);
- );
-
- cases.add("l integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020l /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- ,
- \\pub const SDL_INIT_VIDEO = c_long(32);
- );
-
- cases.add("ul integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020ul /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- ,
- \\pub const SDL_INIT_VIDEO = c_ulong(32);
- );
-
- cases.add("lu integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020lu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- ,
- \\pub const SDL_INIT_VIDEO = c_ulong(32);
- );
-
- cases.add("ll integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020ll /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- ,
- \\pub const SDL_INIT_VIDEO = c_longlong(32);
- );
-
- cases.add("ull integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020ull /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- ,
- \\pub const SDL_INIT_VIDEO = c_ulonglong(32);
- );
-
- cases.add("llu integer suffix after hex literal",
- \\#define SDL_INIT_VIDEO 0x00000020llu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
- ,
- \\pub const SDL_INIT_VIDEO = c_ulonglong(32);
- );
-
- cases.add("zig keywords in C code",
- \\struct comptime {
- \\ int defer;
- \\};
- ,
- \\pub const struct_comptime = extern struct {
- \\ @"defer": c_int,
- \\};
- ,
- \\pub const @"comptime" = struct_comptime;
- );
-
- cases.add("macro defines string literal with hex",
- \\#define FOO "aoeu\xab derp"
- \\#define FOO2 "aoeu\x0007a derp"
- \\#define FOO_CHAR '\xfF'
- ,
- \\pub const FOO = c"aoeu\xab derp";
- ,
- \\pub const FOO2 = c"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'
- ,
- \\pub const FOO = c"aoeu\x13 derp";
- ,
- \\pub const FOO2 = c"aoeu\x134 derp";
- ,
- \\pub const FOO_CHAR = 63;
- );
-
- cases.add("macro with parens around negative number",
- \\#define LUA_GLOBALSINDEX (-10002)
- ,
- \\pub const LUA_GLOBALSINDEX = -10002;
- );
-
- cases.addC("post increment",
- \\unsigned foo1(unsigned a) {
- \\ a++;
- \\ return a;
- \\}
- \\int foo2(int a) {
- \\ a++;
- \\ return a;
- \\}
- ,
- \\export fn foo1(_arg_a: c_uint) -> c_uint {
- \\ var a = _arg_a;
- \\ a +%= 1;
- \\ return a;
- \\}
- \\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;
- \\}
- ,
- \\export fn log2(_arg_a: c_uint) -> c_int {
- \\ var a = _arg_a;
- \\ var i: c_int = 0;
- \\ while (a > c_uint(0)) {
- \\ a >>= @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;
- \\}
- ,
- \\export fn max(a: c_int, b: c_int) -> c_int {
- \\ if (a < b) return b;
- \\ if (a < b) return b else return a;
- \\}
- );
-
- cases.addC("==, !=",
- \\int max(int a, int b) {
- \\ if (a == b)
- \\ return a;
- \\ if (a != b)
- \\ return b;
- \\ return a;
- \\}
- ,
- \\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("add, sub, mul, div, rem",
- \\int s(int a, int b) {
- \\ int c;
- \\ c = a + b;
- \\ c = a - b;
- \\ c = a * b;
- \\ c = a / b;
- \\ c = a % b;
- \\}
- \\unsigned u(unsigned a, unsigned b) {
- \\ unsigned c;
- \\ c = a + b;
- \\ c = a - b;
- \\ c = a * b;
- \\ c = a / b;
- \\ c = a % b;
- \\}
- ,
- \\export fn s(a: c_int, b: c_int) -> c_int {
- \\ var c: c_int;
- \\ c = (a + b);
- \\ c = (a - b);
- \\ c = (a * b);
- \\ c = @divTrunc(a, b);
- \\ c = @rem(a, b);
- \\}
- \\export fn u(a: c_uint, b: c_uint) -> c_uint {
- \\ var c: c_uint;
- \\ c = (a +% b);
- \\ c = (a -% b);
- \\ c = (a *% b);
- \\ c = (a / b);
- \\ c = (a % b);
- \\}
- );
-
- cases.addC("bitwise binary operators",
- \\int max(int a, int b) {
- \\ return (a & b) ^ (a | b);
- \\}
- ,
- \\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;
- \\}
- ,
- \\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("assign",
- \\int max(int a) {
- \\ int tmp;
- \\ tmp = a;
- \\ a = tmp;
- \\}
- ,
- \\export fn max(_arg_a: c_int) -> c_int {
- \\ var a = _arg_a;
- \\ var tmp: c_int;
- \\ tmp = a;
- \\ a = tmp;
- \\}
- );
-
- cases.addC("chaining assign",
- \\void max(int a) {
- \\ int b, c;
- \\ c = b = a;
- \\}
- ,
- \\export fn max(a: c_int) {
- \\ var b: c_int;
- \\ var c: c_int;
- \\ c = {
- \\ const _tmp = a;
- \\ b = _tmp;
- \\ _tmp
- \\ };
- \\}
- );
-
- 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;
- \\}
- ,
- \\export fn log2(_arg_a: u32) -> c_int {
- \\ var a = _arg_a;
- \\ var i: c_int = 0;
- \\ while (a > c_uint(0)) {
- \\ a >>= u5(1);
- \\ };
- \\ return i;
- \\}
- );
-
- cases.add("anonymous enum",
- \\enum {
- \\ One,
- \\ Two,
- \\};
- ,
- \\pub const One = 0;
- \\pub const Two = 1;
- );
-
- cases.addC("function call",
- \\static void bar(void) { }
- \\void foo(void) { bar(); }
- ,
- \\pub fn bar() {}
- \\export fn foo() {
- \\ bar();
- \\}
- );
-
- cases.addC("field access expression",
- \\struct Foo {
- \\ int field;
- \\};
- \\int read_field(struct Foo *foo) {
- \\ return foo->field;
- \\}
- ,
- \\pub const struct_Foo = extern struct {
- \\ field: c_int,
- \\};
- \\export fn read_field(foo: ?&struct_Foo) -> c_int {
- \\ return (??foo).field;
- \\}
- );
-
- cases.addC("null statements",
- \\void foo(void) {
- \\ ;;;;;
- \\}
- ,
- \\export fn foo() {}
- );
-
- cases.add("undefined array global",
- \\int array[100];
- ,
- \\pub var array: [100]c_int = undefined;
- );
-
- cases.addC("array access",
- \\int array[100];
- \\int foo(int index) {
- \\ return array[index];
- \\}
- ,
- \\pub var array: [100]c_int = undefined;
- \\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;
- \\}
- ,
- \\export fn float_to_int(a: f32) -> c_int {
- \\ return c_int(a);
- \\}
- );
-
- cases.addC("implicit cast to void *",
- \\void *foo(unsigned short *x) {
- \\ return x;
- \\}
- ,
- \\export fn foo(x: ?&c_ushort) -> ?&c_void {
- \\ return @ptrCast(?&c_void, x);
- \\}
- );
-
- cases.addC("sizeof",
- \\#include <stddef.h>
- \\size_t size_of(void) {
- \\ return sizeof(int);
- \\}
- ,
- \\export fn size_of() -> usize {
- \\ return @sizeOf(c_int);
- \\}
- );
-
- cases.addC("null pointer implicit cast",
- \\int* foo(void) {
- \\ return 0;
- \\}
- ,
- \\export fn foo() -> ?&c_int {
- \\ return null;
- \\}
- );
-
- cases.addC("comma operator",
- \\int foo(void) {
- \\ return 1, 2;
- \\}
- ,
- \\export fn foo() -> c_int {
- \\ return {
- \\ _ = 1;
- \\ 2
- \\ };
- \\}
- );
-
- cases.addC("bitshift",
- \\int foo(void) {
- \\ return (1 << 2) >> 1;
- \\}
- ,
- \\export fn foo() -> c_int {
- \\ return (1 << @import("std").math.Log2Int(c_int)(2)) >> @import("std").math.Log2Int(c_int)(1);
- \\}
- );
-
- cases.addC("compound assignment operators",
- \\void foo(void) {
- \\ int a = 0;
- \\ a += (a += 1);
- \\ a -= (a -= 1);
- \\ a *= (a *= 1);
- \\ a &= (a &= 1);
- \\ a |= (a |= 1);
- \\ a ^= (a ^= 1);
- \\ a >>= (a >>= 1);
- \\ a <<= (a <<= 1);
- \\}
- ,
- \\export fn foo() {
- \\ var a: c_int = 0;
- \\ a += {
- \\ const _ref = &a;
- \\ (*_ref) = ((*_ref) + 1);
- \\ *_ref
- \\ };
- \\ a -= {
- \\ const _ref = &a;
- \\ (*_ref) = ((*_ref) - 1);
- \\ *_ref
- \\ };
- \\ a *= {
- \\ const _ref = &a;
- \\ (*_ref) = ((*_ref) * 1);
- \\ *_ref
- \\ };
- \\ a &= {
- \\ const _ref = &a;
- \\ (*_ref) = ((*_ref) & 1);
- \\ *_ref
- \\ };
- \\ a |= {
- \\ const _ref = &a;
- \\ (*_ref) = ((*_ref) | 1);
- \\ *_ref
- \\ };
- \\ a ^= {
- \\ const _ref = &a;
- \\ (*_ref) = ((*_ref) ^ 1);
- \\ *_ref
- \\ };
- \\ a >>= @import("std").math.Log2Int(c_int)({
- \\ const _ref = &a;
- \\ (*_ref) = c_int(c_int(*_ref) >> @import("std").math.Log2Int(c_int)(1));
- \\ *_ref
- \\ });
- \\ a <<= @import("std").math.Log2Int(c_int)({
- \\ const _ref = &a;
- \\ (*_ref) = c_int(c_int(*_ref) << @import("std").math.Log2Int(c_int)(1));
- \\ *_ref
- \\ });
- \\}
- );
-
- cases.addC("compound assignment operators unsigned",
- \\void foo(void) {
- \\ unsigned a = 0;
- \\ a += (a += 1);
- \\ a -= (a -= 1);
- \\ a *= (a *= 1);
- \\ a &= (a &= 1);
- \\ a |= (a |= 1);
- \\ a ^= (a ^= 1);
- \\ a >>= (a >>= 1);
- \\ a <<= (a <<= 1);
- \\}
- ,
- \\export fn foo() {
- \\ var a: c_uint = c_uint(0);
- \\ a +%= {
- \\ const _ref = &a;
- \\ (*_ref) = ((*_ref) +% c_uint(1));
- \\ *_ref
- \\ };
- \\ a -%= {
- \\ const _ref = &a;
- \\ (*_ref) = ((*_ref) -% c_uint(1));
- \\ *_ref
- \\ };
- \\ a *%= {
- \\ const _ref = &a;
- \\ (*_ref) = ((*_ref) *% c_uint(1));
- \\ *_ref
- \\ };
- \\ a &= {
- \\ const _ref = &a;
- \\ (*_ref) = ((*_ref) & c_uint(1));
- \\ *_ref
- \\ };
- \\ a |= {
- \\ const _ref = &a;
- \\ (*_ref) = ((*_ref) | c_uint(1));
- \\ *_ref
- \\ };
- \\ a ^= {
- \\ const _ref = &a;
- \\ (*_ref) = ((*_ref) ^ c_uint(1));
- \\ *_ref
- \\ };
- \\ a >>= @import("std").math.Log2Int(c_uint)({
- \\ const _ref = &a;
- \\ (*_ref) = c_uint(c_uint(*_ref) >> @import("std").math.Log2Int(c_uint)(1));
- \\ *_ref
- \\ });
- \\ a <<= @import("std").math.Log2Int(c_uint)({
- \\ const _ref = &a;
- \\ (*_ref) = c_uint(c_uint(*_ref) << @import("std").math.Log2Int(c_uint)(1));
- \\ *_ref
- \\ });
- \\}
- );
-
- cases.addC("duplicate typedef",
- \\typedef long foo;
- \\typedef int bar;
- \\typedef long foo;
- \\typedef int baz;
- ,
- \\pub const foo = c_long;
- \\pub const bar = c_int;
- \\pub const baz = c_int;
- );
-
- cases.addC("post increment/decrement",
- \\void foo(void) {
- \\ int i = 0;
- \\ unsigned u = 0;
- \\ i++;
- \\ i--;
- \\ u++;
- \\ u--;
- \\ i = i++;
- \\ i = i--;
- \\ u = u++;
- \\ u = u--;
- \\}
- ,
- \\export fn foo() {
- \\ var i: c_int = 0;
- \\ var u: c_uint = c_uint(0);
- \\ i += 1;
- \\ i -= 1;
- \\ u +%= 1;
- \\ u -%= 1;
- \\ i = {
- \\ const _ref = &i;
- \\ const _tmp = *_ref;
- \\ (*_ref) += 1;
- \\ _tmp
- \\ };
- \\ i = {
- \\ const _ref = &i;
- \\ const _tmp = *_ref;
- \\ (*_ref) -= 1;
- \\ _tmp
- \\ };
- \\ u = {
- \\ const _ref = &u;
- \\ const _tmp = *_ref;
- \\ (*_ref) +%= 1;
- \\ _tmp
- \\ };
- \\ u = {
- \\ const _ref = &u;
- \\ const _tmp = *_ref;
- \\ (*_ref) -%= 1;
- \\ _tmp
- \\ };
- \\}
- );
-
- cases.addC("do loop",
- \\void foo(void) {
- \\ int a = 2;
- \\ do {
- \\ a--;
- \\ } while (a != 0);
- \\
- \\ int b = 2;
- \\ do
- \\ b--;
- \\ while (b != 0);
- \\}
- ,
- \\export fn foo() {
- \\ 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) {}
- \\void bar(void) {
- \\ void(*f)(void) = foo;
- \\ f();
- \\ (*(f))();
- \\}
- ,
- \\export fn foo() {}
- \\export fn bar() {
- \\ var f: ?extern fn() = foo;
- \\ (??f)();
- \\ (??f)();
- \\}
- );
-
- cases.addC("normal deref",
- \\void foo(int *x) {
- \\ *x = 1;
- \\}
- ,
- \\export fn foo(x: ?&c_int) {
- \\ (*(??x)) = 1;
- \\}
- );
-}
-
-
-
-// TODO
-//float *ptrcast(int *a) {
-// return (float *)a;
-//}
-// should translate to
-// fn ptrcast(a: ?&c_int) -> ?&f32 {
-// return @ptrCast(?&f32, a);
-// }
diff --git a/test/tests.zig b/test/tests.zig
@@ -18,7 +18,7 @@ const build_examples = @import("build_examples.zig");
const compile_errors = @import("compile_errors.zig");
const assemble_and_link = @import("assemble_and_link.zig");
const debug_safety = @import("debug_safety.zig");
-const parsec = @import("parsec.zig");
+const translate_c = @import("translate_c.zig");
const TestTarget = struct {
os: builtin.Os,
@@ -123,16 +123,16 @@ pub fn addAssembleAndLinkTests(b: &build.Builder, test_filter: ?[]const u8) -> &
return cases.step;
}
-pub fn addParseCTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
- const cases = %%b.allocator.create(ParseCContext);
- *cases = ParseCContext {
+pub fn addTranslateCTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
+ const cases = %%b.allocator.create(TranslateCContext);
+ *cases = TranslateCContext {
.b = b,
- .step = b.step("test-parsec", "Run the C header file parsing tests"),
+ .step = b.step("test-translate-c", "Run the C header file parsing tests"),
.test_index = 0,
.test_filter = test_filter,
};
- parsec.addCases(cases);
+ translate_c.addCases(cases);
return cases.step;
}
@@ -770,7 +770,7 @@ pub const BuildExamplesContext = struct {
}
};
-pub const ParseCContext = struct {
+pub const TranslateCContext = struct {
b: &build.Builder,
step: &build.Step,
test_index: usize,
@@ -799,17 +799,17 @@ pub const ParseCContext = struct {
}
};
- const ParseCCmpOutputStep = struct {
+ const TranslateCCmpOutputStep = struct {
step: build.Step,
- context: &ParseCContext,
+ context: &TranslateCContext,
name: []const u8,
test_index: usize,
case: &const TestCase,
- pub fn create(context: &ParseCContext, name: []const u8, case: &const TestCase) -> &ParseCCmpOutputStep {
+ pub fn create(context: &TranslateCContext, name: []const u8, case: &const TestCase) -> &TranslateCCmpOutputStep {
const allocator = context.b.allocator;
- const ptr = %%allocator.create(ParseCCmpOutputStep);
- *ptr = ParseCCmpOutputStep {
+ const ptr = %%allocator.create(TranslateCCmpOutputStep);
+ *ptr = TranslateCCmpOutputStep {
.step = build.Step.init("ParseCCmpOutput", allocator, make),
.context = context,
.name = name,
@@ -821,7 +821,7 @@ pub const ParseCContext = struct {
}
fn make(step: &build.Step) -> %void {
- const self = @fieldParentPtr(ParseCCmpOutputStep, "step", step);
+ const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step);
const b = self.context.b;
const root_src = %%os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename);
@@ -829,7 +829,7 @@ pub const ParseCContext = struct {
var zig_args = ArrayList([]const u8).init(b.allocator);
%%zig_args.append(b.zig_exe);
- %%zig_args.append("parsec");
+ %%zig_args.append("translate-c");
%%zig_args.append(b.pathFromRoot(root_src));
warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
@@ -882,7 +882,7 @@ pub const ParseCContext = struct {
if (stderr.len != 0 and !self.case.allow_warnings) {
warn(
- \\====== parsec emitted warnings: ============
+ \\====== translate-c emitted warnings: =======
\\{}
\\============================================
\\
@@ -914,7 +914,7 @@ pub const ParseCContext = struct {
warn("\n");
}
- pub fn create(self: &ParseCContext, allow_warnings: bool, filename: []const u8, name: []const u8,
+ pub fn create(self: &TranslateCContext, allow_warnings: bool, filename: []const u8, name: []const u8,
source: []const u8, expected_lines: ...) -> &TestCase
{
const tc = %%self.b.allocator.create(TestCase);
@@ -932,37 +932,37 @@ pub const ParseCContext = struct {
return tc;
}
- pub fn add(self: &ParseCContext, name: []const u8, source: []const u8, expected_lines: ...) {
+ pub fn add(self: &TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) {
const tc = self.create(false, "source.h", name, source, expected_lines);
self.addCase(tc);
}
- pub fn addC(self: &ParseCContext, name: []const u8, source: []const u8, expected_lines: ...) {
+ pub fn addC(self: &TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) {
const tc = self.create(false, "source.c", name, source, expected_lines);
self.addCase(tc);
}
- pub fn addAllowWarnings(self: &ParseCContext, name: []const u8, source: []const u8, expected_lines: ...) {
+ pub fn addAllowWarnings(self: &TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) {
const tc = self.create(true, "source.h", name, source, expected_lines);
self.addCase(tc);
}
- pub fn addCase(self: &ParseCContext, case: &const TestCase) {
+ pub fn addCase(self: &TranslateCContext, case: &const TestCase) {
const b = self.b;
- const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "parsec {}", case.name);
+ const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "translate-c {}", case.name);
if (self.test_filter) |filter| {
if (mem.indexOf(u8, annotated_case_name, filter) == null)
return;
}
- const parsec_and_cmp = ParseCCmpOutputStep.create(self, annotated_case_name, case);
- self.step.dependOn(&parsec_and_cmp.step);
+ const translate_c_and_cmp = TranslateCCmpOutputStep.create(self, annotated_case_name, case);
+ self.step.dependOn(&translate_c_and_cmp.step);
for (case.sources.toSliceConst()) |src_file| {
const expanded_src_path = %%os.path.join(b.allocator, b.cache_root, src_file.filename);
const write_src = b.addWriteFile(expanded_src_path, src_file.source);
- parsec_and_cmp.step.dependOn(&write_src.step);
+ translate_c_and_cmp.step.dependOn(&write_src.step);
}
}
};
diff --git a/test/translate_c.zig b/test/translate_c.zig
@@ -0,0 +1,1181 @@
+const tests = @import("tests.zig");
+
+pub fn addCases(cases: &tests.TranslateCContext) {
+ 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);
+ ,
+ \\pub extern fn foo(a: u8, b: u8, c: i8) -> c_int;
+ ,
+ \\pub extern fn bar(a: u8, b: u16, c: u32, d: u64);
+ ,
+ \\pub extern fn baz(a: i8, b: i16, c: i32, d: i64);
+ );
+
+ cases.add("noreturn attribute",
+ \\void foo(void) __attribute__((noreturn));
+ ,
+ \\pub extern fn foo() -> noreturn;
+ );
+
+ cases.addC("simple function",
+ \\int abs(int a) {
+ \\ return a < 0 ? -a : a;
+ \\}
+ ,
+ \\export fn abs(a: c_int) -> c_int {
+ \\ return if (a < 0) -a else a;
+ \\}
+ );
+
+ cases.add("enums",
+ \\enum Foo {
+ \\ FooA,
+ \\ FooB,
+ \\ Foo1,
+ \\};
+ ,
+ \\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("restrict -> noalias",
+ \\void foo(void *restrict bar, void *restrict);
+ ,
+ \\pub extern fn foo(noalias bar: ?&c_void, noalias arg1: ?&c_void);
+ );
+
+ cases.add("simple struct",
+ \\struct Foo {
+ \\ int x;
+ \\ char *y;
+ \\};
+ ,
+ \\const struct_Foo = extern struct {
+ \\ x: c_int,
+ \\ y: ?&u8,
+ \\};
+ ,
+ \\pub const Foo = struct_Foo;
+ );
+
+ cases.add("qualified struct and enum",
+ \\struct Foo {
+ \\ int x;
+ \\ int y;
+ \\};
+ \\enum Bar {
+ \\ BarA,
+ \\ BarB,
+ \\};
+ \\void func(struct Foo *a, enum Bar **b);
+ ,
+ \\pub const struct_Foo = extern struct {
+ \\ x: c_int,
+ \\ y: c_int,
+ \\};
+ ,
+ \\pub const enum_Bar = extern enum {
+ \\ A,
+ \\ B,
+ \\};
+ ,
+ \\pub const BarA = enum_Bar.A;
+ ,
+ \\pub const BarB = enum_Bar.B;
+ ,
+ \\pub extern fn func(a: ?&struct_Foo, b: ?&(?&enum_Bar));
+ ,
+ \\pub const Foo = struct_Foo;
+ ,
+ \\pub const Bar = enum_Bar;
+ );
+
+ cases.add("constant size array",
+ \\void func(int array[20]);
+ ,
+ \\pub extern fn func(array: ?&c_int);
+ );
+
+ cases.add("self referential struct with function pointer",
+ \\struct Foo {
+ \\ void (*derp)(struct Foo *foo);
+ \\};
+ ,
+ \\pub const struct_Foo = extern struct {
+ \\ derp: ?extern fn(?&struct_Foo),
+ \\};
+ ,
+ \\pub const Foo = struct_Foo;
+ );
+
+ cases.add("struct prototype used in func",
+ \\struct Foo;
+ \\struct Foo *some_func(struct Foo *foo, int x);
+ ,
+ \\pub const struct_Foo = @OpaqueType();
+ ,
+ \\pub extern fn some_func(foo: ?&struct_Foo, x: c_int) -> ?&struct_Foo;
+ ,
+ \\pub const Foo = struct_Foo;
+ );
+
+ cases.add("#define a char literal",
+ \\#define A_CHAR 'a'
+ ,
+ \\pub const A_CHAR = 97;
+ );
+
+ cases.add("#define an unsigned integer literal",
+ \\#define CHANNEL_COUNT 24
+ ,
+ \\pub const CHANNEL_COUNT = 24;
+ );
+
+ cases.add("#define referencing another #define",
+ \\#define THING2 THING1
+ \\#define THING1 1234
+ ,
+ \\pub const THING1 = 1234;
+ ,
+ \\pub const THING2 = THING1;
+ );
+
+ cases.add("variables",
+ \\extern int extern_var;
+ \\static const int int_var = 13;
+ ,
+ \\pub extern var extern_var: c_int;
+ ,
+ \\pub const int_var: c_int = 13;
+ );
+
+ cases.add("circular struct definitions",
+ \\struct Bar;
+ \\
+ \\struct Foo {
+ \\ struct Bar *next;
+ \\};
+ \\
+ \\struct Bar {
+ \\ struct Foo *next;
+ \\};
+ ,
+ \\pub const struct_Bar = extern struct {
+ \\ next: ?&struct_Foo,
+ \\};
+ ,
+ \\pub const struct_Foo = extern struct {
+ \\ next: ?&struct_Bar,
+ \\};
+ );
+
+ cases.add("typedef void",
+ \\typedef void Foo;
+ \\Foo fun(Foo *a);
+ ,
+ \\pub const Foo = c_void;
+ ,
+ \\pub extern fn fun(a: ?&Foo) -> Foo;
+ );
+
+ 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
+ ,
+ \\pub extern var fn_ptr: ?extern fn();
+ ,
+ \\pub inline fn foo() {
+ \\ (??fn_ptr)()
+ \\}
+ ,
+ \\pub extern var fn_ptr2: ?extern fn(c_int, f32) -> u8;
+ ,
+ \\pub inline fn bar(arg0: c_int, arg1: f32) -> u8 {
+ \\ (??fn_ptr2)(arg0, arg1)
+ \\}
+ );
+
+ cases.add("#define string",
+ \\#define foo "a string"
+ ,
+ \\pub const foo = c"a string";
+ );
+
+ cases.add("__cdecl doesn't mess up function pointers",
+ \\void foo(void (__cdecl *fn_ptr)(void));
+ ,
+ \\pub extern fn foo(fn_ptr: ?extern fn());
+ );
+
+ cases.add("comment after integer literal",
+ \\#define SDL_INIT_VIDEO 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ ,
+ \\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 */
+ ,
+ \\pub const SDL_INIT_VIDEO = c_uint(32);
+ );
+
+ cases.add("l integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020l /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ ,
+ \\pub const SDL_INIT_VIDEO = c_long(32);
+ );
+
+ cases.add("ul integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020ul /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ ,
+ \\pub const SDL_INIT_VIDEO = c_ulong(32);
+ );
+
+ cases.add("lu integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020lu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ ,
+ \\pub const SDL_INIT_VIDEO = c_ulong(32);
+ );
+
+ cases.add("ll integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020ll /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ ,
+ \\pub const SDL_INIT_VIDEO = c_longlong(32);
+ );
+
+ cases.add("ull integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020ull /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ ,
+ \\pub const SDL_INIT_VIDEO = c_ulonglong(32);
+ );
+
+ cases.add("llu integer suffix after hex literal",
+ \\#define SDL_INIT_VIDEO 0x00000020llu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+ ,
+ \\pub const SDL_INIT_VIDEO = c_ulonglong(32);
+ );
+
+ cases.add("zig keywords in C code",
+ \\struct comptime {
+ \\ int defer;
+ \\};
+ ,
+ \\pub const struct_comptime = extern struct {
+ \\ @"defer": c_int,
+ \\};
+ ,
+ \\pub const @"comptime" = struct_comptime;
+ );
+
+ cases.add("macro defines string literal with hex",
+ \\#define FOO "aoeu\xab derp"
+ \\#define FOO2 "aoeu\x0007a derp"
+ \\#define FOO_CHAR '\xfF'
+ ,
+ \\pub const FOO = c"aoeu\xab derp";
+ ,
+ \\pub const FOO2 = c"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'
+ ,
+ \\pub const FOO = c"aoeu\x13 derp";
+ ,
+ \\pub const FOO2 = c"aoeu\x134 derp";
+ ,
+ \\pub const FOO_CHAR = 63;
+ );
+
+ cases.add("macro with parens around negative number",
+ \\#define LUA_GLOBALSINDEX (-10002)
+ ,
+ \\pub const LUA_GLOBALSINDEX = -10002;
+ );
+
+ cases.addC("post increment",
+ \\unsigned foo1(unsigned a) {
+ \\ a++;
+ \\ return a;
+ \\}
+ \\int foo2(int a) {
+ \\ a++;
+ \\ return a;
+ \\}
+ ,
+ \\export fn foo1(_arg_a: c_uint) -> c_uint {
+ \\ var a = _arg_a;
+ \\ a +%= 1;
+ \\ return a;
+ \\}
+ \\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;
+ \\}
+ ,
+ \\export fn log2(_arg_a: c_uint) -> c_int {
+ \\ var a = _arg_a;
+ \\ var i: c_int = 0;
+ \\ while (a > c_uint(0)) {
+ \\ a >>= @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;
+ \\}
+ ,
+ \\export fn max(a: c_int, b: c_int) -> c_int {
+ \\ if (a < b) return b;
+ \\ if (a < b) return b else return a;
+ \\}
+ );
+
+ cases.addC("==, !=",
+ \\int max(int a, int b) {
+ \\ if (a == b)
+ \\ return a;
+ \\ if (a != b)
+ \\ return b;
+ \\ return a;
+ \\}
+ ,
+ \\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("add, sub, mul, div, rem",
+ \\int s(int a, int b) {
+ \\ int c;
+ \\ c = a + b;
+ \\ c = a - b;
+ \\ c = a * b;
+ \\ c = a / b;
+ \\ c = a % b;
+ \\}
+ \\unsigned u(unsigned a, unsigned b) {
+ \\ unsigned c;
+ \\ c = a + b;
+ \\ c = a - b;
+ \\ c = a * b;
+ \\ c = a / b;
+ \\ c = a % b;
+ \\}
+ ,
+ \\export fn s(a: c_int, b: c_int) -> c_int {
+ \\ var c: c_int;
+ \\ c = (a + b);
+ \\ c = (a - b);
+ \\ c = (a * b);
+ \\ c = @divTrunc(a, b);
+ \\ c = @rem(a, b);
+ \\}
+ \\export fn u(a: c_uint, b: c_uint) -> c_uint {
+ \\ var c: c_uint;
+ \\ c = (a +% b);
+ \\ c = (a -% b);
+ \\ c = (a *% b);
+ \\ c = (a / b);
+ \\ c = (a % b);
+ \\}
+ );
+
+ cases.addC("bitwise binary operators",
+ \\int max(int a, int b) {
+ \\ return (a & b) ^ (a | b);
+ \\}
+ ,
+ \\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;
+ \\}
+ ,
+ \\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("assign",
+ \\int max(int a) {
+ \\ int tmp;
+ \\ tmp = a;
+ \\ a = tmp;
+ \\}
+ ,
+ \\export fn max(_arg_a: c_int) -> c_int {
+ \\ var a = _arg_a;
+ \\ var tmp: c_int;
+ \\ tmp = a;
+ \\ a = tmp;
+ \\}
+ );
+
+ cases.addC("chaining assign",
+ \\void max(int a) {
+ \\ int b, c;
+ \\ c = b = a;
+ \\}
+ ,
+ \\export fn max(a: c_int) {
+ \\ var b: c_int;
+ \\ var c: c_int;
+ \\ c = {
+ \\ const _tmp = a;
+ \\ b = _tmp;
+ \\ _tmp
+ \\ };
+ \\}
+ );
+
+ 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;
+ \\}
+ ,
+ \\export fn log2(_arg_a: u32) -> c_int {
+ \\ var a = _arg_a;
+ \\ var i: c_int = 0;
+ \\ while (a > c_uint(0)) {
+ \\ a >>= u5(1);
+ \\ };
+ \\ return i;
+ \\}
+ );
+
+ cases.add("anonymous enum",
+ \\enum {
+ \\ One,
+ \\ Two,
+ \\};
+ ,
+ \\pub const One = 0;
+ \\pub const Two = 1;
+ );
+
+ cases.addC("function call",
+ \\static void bar(void) { }
+ \\void foo(void) { bar(); }
+ ,
+ \\pub fn bar() {}
+ \\export fn foo() {
+ \\ bar();
+ \\}
+ );
+
+ cases.addC("field access expression",
+ \\struct Foo {
+ \\ int field;
+ \\};
+ \\int read_field(struct Foo *foo) {
+ \\ return foo->field;
+ \\}
+ ,
+ \\pub const struct_Foo = extern struct {
+ \\ field: c_int,
+ \\};
+ \\export fn read_field(foo: ?&struct_Foo) -> c_int {
+ \\ return (??foo).field;
+ \\}
+ );
+
+ cases.addC("null statements",
+ \\void foo(void) {
+ \\ ;;;;;
+ \\}
+ ,
+ \\export fn foo() {}
+ );
+
+ cases.add("undefined array global",
+ \\int array[100];
+ ,
+ \\pub var array: [100]c_int = undefined;
+ );
+
+ cases.addC("array access",
+ \\int array[100];
+ \\int foo(int index) {
+ \\ return array[index];
+ \\}
+ ,
+ \\pub var array: [100]c_int = undefined;
+ \\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;
+ \\}
+ ,
+ \\export fn float_to_int(a: f32) -> c_int {
+ \\ return c_int(a);
+ \\}
+ );
+
+ cases.addC("implicit cast to void *",
+ \\void *foo(unsigned short *x) {
+ \\ return x;
+ \\}
+ ,
+ \\export fn foo(x: ?&c_ushort) -> ?&c_void {
+ \\ return @ptrCast(?&c_void, x);
+ \\}
+ );
+
+ cases.addC("sizeof",
+ \\#include <stddef.h>
+ \\size_t size_of(void) {
+ \\ return sizeof(int);
+ \\}
+ ,
+ \\export fn size_of() -> usize {
+ \\ return @sizeOf(c_int);
+ \\}
+ );
+
+ cases.addC("null pointer implicit cast",
+ \\int* foo(void) {
+ \\ return 0;
+ \\}
+ ,
+ \\export fn foo() -> ?&c_int {
+ \\ return null;
+ \\}
+ );
+
+ cases.addC("comma operator",
+ \\int foo(void) {
+ \\ return 1, 2;
+ \\}
+ ,
+ \\export fn foo() -> c_int {
+ \\ return {
+ \\ _ = 1;
+ \\ 2
+ \\ };
+ \\}
+ );
+
+ cases.addC("bitshift",
+ \\int foo(void) {
+ \\ return (1 << 2) >> 1;
+ \\}
+ ,
+ \\export fn foo() -> c_int {
+ \\ return (1 << @import("std").math.Log2Int(c_int)(2)) >> @import("std").math.Log2Int(c_int)(1);
+ \\}
+ );
+
+ cases.addC("compound assignment operators",
+ \\void foo(void) {
+ \\ int a = 0;
+ \\ a += (a += 1);
+ \\ a -= (a -= 1);
+ \\ a *= (a *= 1);
+ \\ a &= (a &= 1);
+ \\ a |= (a |= 1);
+ \\ a ^= (a ^= 1);
+ \\ a >>= (a >>= 1);
+ \\ a <<= (a <<= 1);
+ \\}
+ ,
+ \\export fn foo() {
+ \\ var a: c_int = 0;
+ \\ a += {
+ \\ const _ref = &a;
+ \\ (*_ref) = ((*_ref) + 1);
+ \\ *_ref
+ \\ };
+ \\ a -= {
+ \\ const _ref = &a;
+ \\ (*_ref) = ((*_ref) - 1);
+ \\ *_ref
+ \\ };
+ \\ a *= {
+ \\ const _ref = &a;
+ \\ (*_ref) = ((*_ref) * 1);
+ \\ *_ref
+ \\ };
+ \\ a &= {
+ \\ const _ref = &a;
+ \\ (*_ref) = ((*_ref) & 1);
+ \\ *_ref
+ \\ };
+ \\ a |= {
+ \\ const _ref = &a;
+ \\ (*_ref) = ((*_ref) | 1);
+ \\ *_ref
+ \\ };
+ \\ a ^= {
+ \\ const _ref = &a;
+ \\ (*_ref) = ((*_ref) ^ 1);
+ \\ *_ref
+ \\ };
+ \\ a >>= @import("std").math.Log2Int(c_int)({
+ \\ const _ref = &a;
+ \\ (*_ref) = ((*_ref) >> @import("std").math.Log2Int(c_int)(1));
+ \\ *_ref
+ \\ });
+ \\ a <<= @import("std").math.Log2Int(c_int)({
+ \\ const _ref = &a;
+ \\ (*_ref) = ((*_ref) << @import("std").math.Log2Int(c_int)(1));
+ \\ *_ref
+ \\ });
+ \\}
+ );
+
+ cases.addC("compound assignment operators unsigned",
+ \\void foo(void) {
+ \\ unsigned a = 0;
+ \\ a += (a += 1);
+ \\ a -= (a -= 1);
+ \\ a *= (a *= 1);
+ \\ a &= (a &= 1);
+ \\ a |= (a |= 1);
+ \\ a ^= (a ^= 1);
+ \\ a >>= (a >>= 1);
+ \\ a <<= (a <<= 1);
+ \\}
+ ,
+ \\export fn foo() {
+ \\ var a: c_uint = c_uint(0);
+ \\ a +%= {
+ \\ const _ref = &a;
+ \\ (*_ref) = ((*_ref) +% c_uint(1));
+ \\ *_ref
+ \\ };
+ \\ a -%= {
+ \\ const _ref = &a;
+ \\ (*_ref) = ((*_ref) -% c_uint(1));
+ \\ *_ref
+ \\ };
+ \\ a *%= {
+ \\ const _ref = &a;
+ \\ (*_ref) = ((*_ref) *% c_uint(1));
+ \\ *_ref
+ \\ };
+ \\ a &= {
+ \\ const _ref = &a;
+ \\ (*_ref) = ((*_ref) & c_uint(1));
+ \\ *_ref
+ \\ };
+ \\ a |= {
+ \\ const _ref = &a;
+ \\ (*_ref) = ((*_ref) | c_uint(1));
+ \\ *_ref
+ \\ };
+ \\ a ^= {
+ \\ const _ref = &a;
+ \\ (*_ref) = ((*_ref) ^ c_uint(1));
+ \\ *_ref
+ \\ };
+ \\ a >>= @import("std").math.Log2Int(c_uint)({
+ \\ const _ref = &a;
+ \\ (*_ref) = ((*_ref) >> @import("std").math.Log2Int(c_uint)(1));
+ \\ *_ref
+ \\ });
+ \\ a <<= @import("std").math.Log2Int(c_uint)({
+ \\ const _ref = &a;
+ \\ (*_ref) = ((*_ref) << @import("std").math.Log2Int(c_uint)(1));
+ \\ *_ref
+ \\ });
+ \\}
+ );
+
+ cases.addC("duplicate typedef",
+ \\typedef long foo;
+ \\typedef int bar;
+ \\typedef long foo;
+ \\typedef int baz;
+ ,
+ \\pub const foo = c_long;
+ \\pub const bar = c_int;
+ \\pub const baz = c_int;
+ );
+
+ cases.addC("post increment/decrement",
+ \\void foo(void) {
+ \\ int i = 0;
+ \\ unsigned u = 0;
+ \\ i++;
+ \\ i--;
+ \\ u++;
+ \\ u--;
+ \\ i = i++;
+ \\ i = i--;
+ \\ u = u++;
+ \\ u = u--;
+ \\}
+ ,
+ \\export fn foo() {
+ \\ var i: c_int = 0;
+ \\ var u: c_uint = c_uint(0);
+ \\ i += 1;
+ \\ i -= 1;
+ \\ u +%= 1;
+ \\ u -%= 1;
+ \\ i = {
+ \\ const _ref = &i;
+ \\ const _tmp = *_ref;
+ \\ (*_ref) += 1;
+ \\ _tmp
+ \\ };
+ \\ i = {
+ \\ const _ref = &i;
+ \\ const _tmp = *_ref;
+ \\ (*_ref) -= 1;
+ \\ _tmp
+ \\ };
+ \\ u = {
+ \\ const _ref = &u;
+ \\ const _tmp = *_ref;
+ \\ (*_ref) +%= 1;
+ \\ _tmp
+ \\ };
+ \\ u = {
+ \\ const _ref = &u;
+ \\ const _tmp = *_ref;
+ \\ (*_ref) -%= 1;
+ \\ _tmp
+ \\ };
+ \\}
+ );
+
+ cases.addC("pre increment/decrement",
+ \\void foo(void) {
+ \\ int i = 0;
+ \\ unsigned u = 0;
+ \\ ++i;
+ \\ --i;
+ \\ ++u;
+ \\ --u;
+ \\ i = ++i;
+ \\ i = --i;
+ \\ u = ++u;
+ \\ u = --u;
+ \\}
+ ,
+ \\export fn foo() {
+ \\ var i: c_int = 0;
+ \\ var u: c_uint = c_uint(0);
+ \\ i += 1;
+ \\ i -= 1;
+ \\ u +%= 1;
+ \\ u -%= 1;
+ \\ i = {
+ \\ const _ref = &i;
+ \\ (*_ref) += 1;
+ \\ *_ref
+ \\ };
+ \\ i = {
+ \\ const _ref = &i;
+ \\ (*_ref) -= 1;
+ \\ *_ref
+ \\ };
+ \\ u = {
+ \\ const _ref = &u;
+ \\ (*_ref) +%= 1;
+ \\ *_ref
+ \\ };
+ \\ u = {
+ \\ const _ref = &u;
+ \\ (*_ref) -%= 1;
+ \\ *_ref
+ \\ };
+ \\}
+ );
+
+ cases.addC("do loop",
+ \\void foo(void) {
+ \\ int a = 2;
+ \\ do {
+ \\ a--;
+ \\ } while (a != 0);
+ \\
+ \\ int b = 2;
+ \\ do
+ \\ b--;
+ \\ while (b != 0);
+ \\}
+ ,
+ \\export fn foo() {
+ \\ 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) {}
+ \\void baz(void) {}
+ \\void bar(void) {
+ \\ void(*f)(void) = foo;
+ \\ f();
+ \\ (*(f))();
+ \\ baz();
+ \\}
+ ,
+ \\export fn foo() {}
+ \\export fn baz() {}
+ \\export fn bar() {
+ \\ var f: ?extern fn() = foo;
+ \\ (??f)();
+ \\ (??f)();
+ \\ baz();
+ \\}
+ );
+
+ cases.addC("normal deref",
+ \\void foo(int *x) {
+ \\ *x = 1;
+ \\}
+ ,
+ \\export fn foo(x: ?&c_int) {
+ \\ (*(??x)) = 1;
+ \\}
+ );
+
+ cases.add("simple union",
+ \\union Foo {
+ \\ int x;
+ \\ double y;
+ \\};
+ ,
+ \\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;
+ \\}
+ ,
+ \\pub fn foo() -> c_int {
+ \\ var x: c_int = 1234;
+ \\ var ptr: ?&c_int = &x;
+ \\ return *(??ptr);
+ \\}
+ );
+
+ cases.add("string literal",
+ \\const char *foo(void) {
+ \\ return "bar";
+ \\}
+ ,
+ \\pub fn foo() -> ?&const u8 {
+ \\ return c"bar";
+ \\}
+ );
+
+ cases.add("return void",
+ \\void foo(void) {
+ \\ return;
+ \\}
+ ,
+ \\pub fn foo() {
+ \\ return;
+ \\}
+ );
+
+ cases.add("for loop",
+ \\void foo(void) {
+ \\ for (int i = 0; i < 10; i += 1) { }
+ \\}
+ ,
+ \\pub fn foo() {
+ \\ {
+ \\ var i: c_int = 0;
+ \\ while (i < 10) : (i += 1) {};
+ \\ };
+ \\}
+ );
+
+ cases.add("empty for loop",
+ \\void foo(void) {
+ \\ for (;;) { }
+ \\}
+ ,
+ \\pub fn foo() {
+ \\ while (true) {};
+ \\}
+ );
+
+ cases.add("break statement",
+ \\void foo(void) {
+ \\ for (;;) {
+ \\ break;
+ \\ }
+ \\}
+ ,
+ \\pub fn foo() {
+ \\ while (true) {
+ \\ break;
+ \\ };
+ \\}
+ );
+
+ cases.add("continue statement",
+ \\void foo(void) {
+ \\ for (;;) {
+ \\ continue;
+ \\ }
+ \\}
+ ,
+ \\pub fn foo() {
+ \\ while (true) {
+ \\ continue;
+ \\ };
+ \\}
+ );
+
+ cases.add("switch statement",
+ \\int foo(int x) {
+ \\ switch (x) {
+ \\ case 1:
+ \\ x += 1;
+ \\ case 2:
+ \\ break;
+ \\ case 3:
+ \\ case 4:
+ \\ return x + 1;
+ \\ default:
+ \\ return 10;
+ \\ }
+ \\ return x + 13;
+ \\}
+ ,
+ \\fn foo(_arg_x: c_int) -> c_int {
+ \\ var x = _arg_x;
+ \\ {
+ \\ switch (x) {
+ \\ 1 => goto case_0,
+ \\ 2 => goto case_1,
+ \\ 3 => goto case_2,
+ \\ 4 => goto case_3,
+ \\ else => goto default,
+ \\ };
+ \\ case_0:
+ \\ x += 1;
+ \\ case_1:
+ \\ goto end;
+ \\ case_2:
+ \\ case_3:
+ \\ return x + 1;
+ \\ default:
+ \\ return 10;
+ \\ goto end;
+ \\ end:
+ \\ };
+ \\ return x + 13;
+ \\}
+ );
+
+ 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
+ ,
+ \\pub const GLbitfield = c_uint;
+ ,
+ \\pub const PFNGLCLEARPROC = ?extern fn(GLbitfield);
+ ,
+ \\pub const OpenGLProc = ?extern fn();
+ ,
+ \\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) {
+ \\ (??glProcs.gl.Clear)(arg0)
+ \\}
+ ,
+ \\pub const OpenGLProcs = union_OpenGLProcs;
+ );
+
+ cases.add("switch statement with no default",
+ \\int foo(int x) {
+ \\ switch (x) {
+ \\ case 1:
+ \\ x += 1;
+ \\ case 2:
+ \\ break;
+ \\ case 3:
+ \\ case 4:
+ \\ return x + 1;
+ \\ }
+ \\ return x + 13;
+ \\}
+ ,
+ \\fn foo(_arg_x: c_int) -> c_int {
+ \\ var x = _arg_x;
+ \\ {
+ \\ switch (x) {
+ \\ 1 => goto case_0,
+ \\ 2 => goto case_1,
+ \\ 3 => goto case_2,
+ \\ 4 => goto case_3,
+ \\ else => goto end,
+ \\ };
+ \\ case_0:
+ \\ x += 1;
+ \\ case_1:
+ \\ goto end;
+ \\ case_2:
+ \\ case_3:
+ \\ return x + 1;
+ \\ goto end;
+ \\ end:
+ \\ };
+ \\ return x + 13;
+ \\}
+ );
+
+ cases.add("variable name shadowing",
+ \\int foo(void) {
+ \\ int x = 1;
+ \\ {
+ \\ int x = 2;
+ \\ x += 1;
+ \\ }
+ \\ return x;
+ \\}
+ ,
+ \\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;
+ \\}
+ ,
+ \\fn ptrcast(a: ?&c_int) -> ?&f32 {
+ \\ return @ptrCast(?&f32, a);
+ \\}
+ );
+
+ cases.add("bin not",
+ \\int foo(int x) {
+ \\ return ~x;
+ \\}
+ ,
+ \\pub fn foo(x: c_int) -> c_int {
+ \\ return ~x;
+ \\}
+ );
+
+ cases.add("primitive types included in defined symbols",
+ \\int foo(int u32) {
+ \\ return u32;
+ \\}
+ ,
+ \\pub fn foo(u32_0: c_int) -> c_int {
+ \\ return u32_0;
+ \\}
+ );
+
+ cases.add("const ptr initializer",
+ \\static const char *v0 = "0.0.0";
+ ,
+ \\pub var v0: ?&const u8 = c"0.0.0";
+ );
+}