From f276fd0f3728bf1a43b185e3e2d33d593309cb2f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Nov 2017 23:53:53 -0500 Subject: basic union support See #144 --- src/codegen.cpp | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 97 insertions(+), 4 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index 17c0ffc653..fc949f2ecd 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2393,6 +2393,27 @@ 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); + 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; +} + 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 +3386,25 @@ 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; + + assert(type_has_bits(type_union_field->type_entry)); + + LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, (unsigned)0, ""); + LLVMValueRef value = ir_llvm_value(g, instruction->init_value); + + 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); + + 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 +3526,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 +3586,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 +3639,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 +3653,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 +3684,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 +3931,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 +3953,41 @@ 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->type_ref; + ConstExprValue *payload_value = const_val->data.x_union.value; + 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.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; + + unsigned field_count; + LLVMValueRef fields[2]; + fields[0] = correctly_typed_value; + if (pad_bytes == 0) { + field_count = 1; + } else { + fields[0] = correctly_typed_value; + fields[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes)); + field_count = 2; + } + + if (make_unnamed_struct) { + return LLVMConstStruct(fields, field_count, false); + } else { + return LLVMConstNamedStruct(type_entry->type_ref, fields, field_count); + } + } case TypeTableEntryIdEnum: { LLVMTypeRef tag_type_ref = type_entry->data.enumeration.tag_type->type_ref; @@ -4376,6 +4466,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; -- cgit v1.2.3 From 018cbff438cedc19d0ad18021619ec7ede997307 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 15 Nov 2017 22:52:47 -0500 Subject: unions have a secret field for the type See #144 --- src/all_types.hpp | 12 ++++- src/analyze.cpp | 140 ++++++++++++++++++++++++++++++++++++++++++++++----- src/codegen.cpp | 57 +++++++++++++++------ test/cases/union.zig | 13 +++++ 4 files changed, 193 insertions(+), 29 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index ca6c214af8..a6a3f4e1e5 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1037,6 +1037,9 @@ 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; @@ -1044,6 +1047,8 @@ struct TypeTableEntryUnion { uint32_t gen_field_count; TypeUnionField *fields; bool is_invalid; // true if any fields are invalid + TypeTableEntry *tag_type; + LLVMTypeRef union_type_ref; ScopeDecls *decls_scope; @@ -1057,8 +1062,13 @@ struct TypeTableEntryUnion { bool zero_bits_known; uint32_t abi_alignment; // also figured out with zero_bits pass - uint32_t size_bytes; + size_t gen_union_index; + size_t gen_tag_index; + + uint32_t union_size_bytes; TypeTableEntry *most_aligned_union_member; + + HashMap distinct_types = {}; }; struct FnGenParamInfo { diff --git a/src/analyze.cpp b/src/analyze.cpp index 2f7eecaff4..0f8414aaf8 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -992,26 +992,23 @@ TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKi TypeTableEntryId type_id = container_to_type(kind); TypeTableEntry *entry = new_container_type_entry(type_id, decl_node, scope); - unsigned dwarf_kind; switch (kind) { case ContainerKindStruct: entry->data.structure.decl_node = decl_node; entry->data.structure.layout = layout; - dwarf_kind = ZigLLVMTag_DW_structure_type(); break; case ContainerKindEnum: entry->data.enumeration.decl_node = decl_node; entry->data.enumeration.layout = layout; - dwarf_kind = ZigLLVMTag_DW_structure_type(); break; case ContainerKindUnion: entry->data.unionation.decl_node = decl_node; entry->data.unionation.layout = layout; - dwarf_kind = ZigLLVMTag_DW_union_type(); break; } 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); @@ -1873,6 +1870,11 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { 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(field_count); + auto distinct_types = &union_type->data.unionation.distinct_types; + distinct_types->init(4); + Scope *scope = &union_type->data.unionation.decls_scope->base; ImportTableEntry *import = get_scope_import(scope); @@ -1893,6 +1895,11 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { if (!type_has_bits(field_type)) continue; + size_t distinct_type_index = distinct_types->size(); + if (distinct_types->put_unique(field_type, distinct_type_index) == nullptr) { + di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(&field_type->name), distinct_type_index); + } + 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); @@ -1919,7 +1926,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { // unset temporary flag union_type->data.unionation.embedded_in_current = false; union_type->data.unionation.complete = true; - union_type->data.unionation.size_bytes = biggest_size_in_bits / 8; + 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) @@ -1947,8 +1954,42 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { assert(most_aligned_union_member != nullptr); - // create llvm type for union + bool want_safety = (distinct_types->size() > 1) && auto_layout; 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); @@ -1956,20 +1997,87 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { most_aligned_union_member->type_ref, padding_array->type_ref, }; - LLVMStructSetBody(union_type->type_ref, union_element_types, 2, false); + 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, distinct_types->size() - 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 { - LLVMStructSetBody(union_type->type_ref, &most_aligned_union_member->type_ref, 1, false); + union_type->data.unionation.gen_union_index = 0; + union_type->data.unionation.gen_tag_index = 1; } - 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); + 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, distinct_types->size(), + tag_type_entry->di_type, ""); // create debug type for union - ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, - ZigLLVMFileToScope(import->di_file), buf_ptr(&union_type->name), + 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; } @@ -5140,3 +5248,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/codegen.cpp b/src/codegen.cpp index fc949f2ecd..26a2590e44 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2408,9 +2408,15 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab LLVMValueRef union_ptr = ir_llvm_value(g, instruction->union_ptr); LLVMTypeRef field_type_ref = LLVMPointerType(field->type_entry->type_ref, 0); - LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, 0, ""); - LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, ""); + 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; + } + + 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; } @@ -3955,7 +3961,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { } case TypeTableEntryIdUnion: { - LLVMTypeRef union_type_ref = type_entry->type_ref; + LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref; ConstExprValue *payload_value = const_val->data.x_union.value; assert(payload_value != nullptr); @@ -3964,29 +3970,48 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { } uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, payload_value->type->type_ref); - uint64_t pad_bytes = type_entry->data.unionation.size_bytes - field_type_bytes; - + 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; - unsigned field_count; - LLVMValueRef fields[2]; - fields[0] = correctly_typed_value; - if (pad_bytes == 0) { - field_count = 1; - } else { + LLVMValueRef union_value_ref; + { + unsigned field_count; + LLVMValueRef fields[2]; fields[0] = correctly_typed_value; - fields[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes)); - field_count = 2; + if (pad_bytes == 0) { + field_count = 1; + } else { + fields[0] = correctly_typed_value; + fields[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes)); + field_count = 2; + } + + if (make_unnamed_struct || type_entry->data.unionation.gen_tag_index != SIZE_MAX) { + union_value_ref = LLVMConstStruct(fields, field_count, false); + } else { + union_value_ref = LLVMConstNamedStruct(union_type_ref, fields, field_count); + } + } + + if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) { + return union_value_ref; } + size_t distinct_type_index = type_entry->data.unionation.distinct_types.get(const_val->data.x_union.value->type); + LLVMValueRef tag_value = LLVMConstInt(type_entry->data.unionation.tag_type->type_ref, distinct_type_index, 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, field_count, false); + return LLVMConstStruct(fields, 2, false); } else { - return LLVMConstNamedStruct(type_entry->type_ref, fields, field_count); + return LLVMConstNamedStruct(type_entry->type_ref, fields, 2); } + } case TypeTableEntryIdEnum: { diff --git a/test/cases/union.zig b/test/cases/union.zig index 74bda8db6a..377374c157 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -44,3 +44,16 @@ test "basic unions" { foo.float = 12.34; assert(foo.float == 12.34); } + + +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); +} -- cgit v1.2.3 From f12d36641f67564d2103f75ed7a5445219197db5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Nov 2017 10:06:58 -0500 Subject: union secret field is the tag index instead of distinct type index See #144 --- src/all_types.hpp | 6 +++--- src/analyze.cpp | 18 ++++++++---------- src/codegen.cpp | 5 ++--- src/ir.cpp | 3 ++- 4 files changed, 15 insertions(+), 17 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index a6a3f4e1e5..86c9720f69 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -105,7 +105,8 @@ struct ConstStructValue { }; struct ConstUnionValue { - ConstExprValue *value; + uint64_t tag; + ConstExprValue *payload; ConstParent parent; }; @@ -349,6 +350,7 @@ struct TypeEnumField { struct TypeUnionField { Buf *name; TypeTableEntry *type_entry; + uint32_t value; uint32_t gen_index; }; @@ -1067,8 +1069,6 @@ struct TypeTableEntryUnion { uint32_t union_size_bytes; TypeTableEntry *most_aligned_union_member; - - HashMap distinct_types = {}; }; struct FnGenParamInfo { diff --git a/src/analyze.cpp b/src/analyze.cpp index 0f8414aaf8..ebad9fe0cb 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1872,8 +1872,6 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto); ZigLLVMDIEnumerator **di_enumerators = allocate(field_count); - auto distinct_types = &union_type->data.unionation.distinct_types; - distinct_types->init(4); Scope *scope = &union_type->data.unionation.decls_scope->base; ImportTableEntry *import = get_scope_import(scope); @@ -1895,10 +1893,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { if (!type_has_bits(field_type)) continue; - size_t distinct_type_index = distinct_types->size(); - if (distinct_types->put_unique(field_type, distinct_type_index) == nullptr) { - di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(&field_type->name), distinct_type_index); - } + 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); @@ -1954,7 +1949,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { assert(most_aligned_union_member != nullptr); - bool want_safety = (distinct_types->size() > 1) && auto_layout; + bool want_safety = auto_layout && (field_count >= 2); uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits; @@ -2007,7 +2002,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { 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, distinct_types->size() - 1); + 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); @@ -2034,7 +2029,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { 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, distinct_types->size(), + tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, tag_type_entry->di_type, ""); // create debug type for union @@ -2257,6 +2252,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { 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)) { @@ -2276,9 +2272,11 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { } } + 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); + 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 diff --git a/src/codegen.cpp b/src/codegen.cpp index 26a2590e44..3777c3a87a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3962,7 +3962,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { case TypeTableEntryIdUnion: { LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref; - ConstExprValue *payload_value = const_val->data.x_union.value; + ConstExprValue *payload_value = const_val->data.x_union.payload; assert(payload_value != nullptr); if (!type_has_bits(payload_value->type)) { @@ -3999,8 +3999,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { return union_value_ref; } - size_t distinct_type_index = type_entry->data.unionation.distinct_types.get(const_val->data.x_union.value->type); - LLVMValueRef tag_value = LLVMConstInt(type_entry->data.unionation.tag_type->type_ref, distinct_type_index, false); + 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; diff --git a/src/ir.cpp b/src/ir.cpp index 6df6b4f828..fa59aa03f2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13158,7 +13158,8 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, instruction); - out_val->data.x_union.value = field_val; + 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) { -- cgit v1.2.3 From e26ccd5166000f81a589c446d04102c21045bff6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Nov 2017 21:15:15 -0500 Subject: debug safety for unions --- src/all_types.hpp | 1 + src/codegen.cpp | 37 ++++++++++++++++++++++++++----------- test/cases/union.zig | 2 +- test/debug_safety.zig | 20 ++++++++++++++++++++ 4 files changed, 48 insertions(+), 12 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index 86c9720f69..2b09131bef 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1317,6 +1317,7 @@ enum PanicMsgId { PanicMsgIdUnwrapMaybeFail, PanicMsgIdInvalidErrorCode, PanicMsgIdIncorrectAlignment, + PanicMsgIdBadUnionField, PanicMsgIdCount, }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 3777c3a87a..eb56d26cae 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -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(); } @@ -2415,6 +2417,23 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab 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; @@ -3977,21 +3996,17 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { LLVMValueRef union_value_ref; { - unsigned field_count; - LLVMValueRef fields[2]; - fields[0] = correctly_typed_value; if (pad_bytes == 0) { - field_count = 1; + union_value_ref = correctly_typed_value; } else { + LLVMValueRef fields[2]; fields[0] = correctly_typed_value; fields[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes)); - field_count = 2; - } - - if (make_unnamed_struct || type_entry->data.unionation.gen_tag_index != SIZE_MAX) { - union_value_ref = LLVMConstStruct(fields, field_count, false); - } else { - union_value_ref = LLVMConstNamedStruct(union_type_ref, fields, field_count); + 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); + } } } diff --git a/test/cases/union.zig b/test/cases/union.zig index 377374c157..1abebb3b30 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -41,7 +41,7 @@ const Foo = union { test "basic unions" { var foo = Foo { .int = 1 }; assert(foo.int == 1); - foo.float = 12.34; + foo = Foo {.float = 12.34}; assert(foo.float == 12.34); } diff --git a/test/debug_safety.zig b/test/debug_safety.zig index 9e9ff98349..36f8d020c3 100644 --- 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; + \\} + ); } -- cgit v1.2.3 From 5d2ba056c801f46a07182a05c07887e06fd197fa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Nov 2017 22:06:08 -0500 Subject: fix codegen for union init with runtime value see #144 --- src/codegen.cpp | 27 ++++++++++++++++++++++----- test/cases/union.zig | 18 ++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index eb56d26cae..680f5a9e35 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3414,17 +3414,34 @@ static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, IrInstructionUnionInit *instruction) { TypeUnionField *type_union_field = instruction->field; - assert(type_has_bits(type_union_field->type_entry)); - - LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, (unsigned)0, ""); - LLVMValueRef value = ir_llvm_value(g, instruction->init_value); + 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; diff --git a/test/cases/union.zig b/test/cases/union.zig index 1abebb3b30..4044721582 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -45,6 +45,23 @@ test "basic unions" { 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, @@ -57,3 +74,4 @@ test "basic extern unions" { foo.float = 12.34; assert(foo.float == 12.34); } + -- cgit v1.2.3 From 5a25505668bac9aed0ad8f3b23fe81c6aff29b71 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 24 Nov 2017 14:56:05 -0500 Subject: rename "parsec" to "translate-c" --- CMakeLists.txt | 2 +- build.zig | 2 +- ci/travis_osx_script | 2 +- src-self-hosted/main.zig | 2 +- src/analyze.cpp | 4 +- src/codegen.cpp | 4 +- src/codegen.hpp | 2 +- src/ir.cpp | 2 +- src/main.cpp | 22 +- src/parsec.cpp | 3527 ---------------------------------------------- src/parsec.hpp | 20 - src/translate_c.cpp | 3527 ++++++++++++++++++++++++++++++++++++++++++++++ src/translate_c.hpp | 20 + test/parsec.zig | 899 ------------ test/tests.zig | 48 +- test/translate_c.zig | 899 ++++++++++++ 16 files changed, 4491 insertions(+), 4491 deletions(-) delete mode 100644 src/parsec.cpp delete mode 100644 src/parsec.hpp create mode 100644 src/translate_c.cpp create mode 100644 src/translate_c.hpp delete mode 100644 test/parsec.zig create mode 100644 test/translate_c.zig (limited to 'src/codegen.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 8906798c69..72c480cd40 100644 --- 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 index 79fefd5c7a..1c1d4f832e 100644 --- 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 index 1f345c9c50..a55132395b 100755 --- 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/src-self-hosted/main.zig b/src-self-hosted/main.zig index 0716ad433b..71180b2001 100644 --- 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/analyze.cpp b/src/analyze.cpp index ebad9fe0cb..7a9df874d4 100644 --- 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"); diff --git a/src/codegen.cpp b/src/codegen.cpp index 680f5a9e35..24d24a91e5 100644 --- 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" @@ -5353,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 index b71a7fa651..b29cadee55 100644 --- 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 index fa59aa03f2..c81de7fa7a 100644 --- 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" diff --git a/src/main.cpp b/src/main.cpp index 13da71f9e8..60d2750bde 100644 --- 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 deleted file mode 100644 index 79ba2ab990..0000000000 --- a/src/parsec.cpp +++ /dev/null @@ -1,3527 +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 -#include -#include - -#include - -using namespace clang; - -struct MacroSymbol { - Buf *name; - Buf *value; -}; - -struct Alias { - Buf *new_name; - Buf *canon_name; -}; - -struct Context { - ImportTableEntry *import; - ZigList *errors; - VisibMod visib_mod; - VisibMod export_visib_mod; - AstNode *root; - HashMap decl_table; - HashMap macro_table; - HashMap global_table; - SourceManager *source_manager; - ZigList aliases; - ZigList macro_symbols; - AstNode *source_node; - bool warnings_on; - - CodeGen *codegen; - ASTContext *ctx; - - HashMap 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(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(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(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(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(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(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(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(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(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(qt.getTypePtr()); - if (paren_type->getInnerType()->getTypeClass() == Type::FunctionProto) { - return true; - } - } else if (qt.getTypePtr()->getTypeClass() == Type::Attributed) { - const AttributedType *attr_type = static_cast(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(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(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(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(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(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::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(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(ty); - const TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); - return resolve_typedef_decl(c, typedef_decl); - } - case Type::Elaborated: - { - const ElaboratedType *elaborated_ty = static_cast(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(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(ty); - return resolve_record_decl(c, record_ty->getDecl()); - } - case Type::Enum: - { - const EnumType *enum_ty = static_cast(ty); - return resolve_enum_decl(c, enum_ty->getDecl()); - } - case Type::ConstantArray: - { - const ConstantArrayType *const_arr_ty = static_cast(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(ty); - return trans_qual_type(c, paren_ty->getInnerType(), source_loc); - } - case Type::Decayed: - { - const DecayedType *decayed_ty = static_cast(ty); - return trans_qual_type(c, decayed_ty->getDecayedType(), source_loc); - } - case Type::Attributed: - { - const AttributedType *attributed_ty = static_cast(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: - 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: - { - AstNode *value_node = trans_expr(c, result_used, block, 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, block, 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, 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); - - 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 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(decl)); - break; - case Decl::Typedef: - resolve_typedef_decl(c, static_cast(decl)); - break; - case Decl::Enum: - resolve_enum_decl(c, static_cast(decl)); - break; - case Decl::Record: - resolve_record_decl(c, static_cast(decl)); - break; - case Decl::Var: - visit_var_decl(c, static_cast(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(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 *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 *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 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 diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); - - std::shared_ptr pch_container_ops = std::make_shared(); - - 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 err_unit; - std::unique_ptr 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/parsec.hpp b/src/parsec.hpp deleted file mode 100644 index 62e150f43c..0000000000 --- a/src/parsec.hpp +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2015 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - - -#ifndef ZIG_PARSEC_HPP -#define ZIG_PARSEC_HPP - -#include "all_types.hpp" - -int parse_h_file(ImportTableEntry *import, ZigList *errors, const char *target_file, - CodeGen *codegen, AstNode *source_node); - -int parse_h_buf(ImportTableEntry *import, ZigList *errors, Buf *source, - CodeGen *codegen, AstNode *source_node); - -#endif diff --git a/src/translate_c.cpp b/src/translate_c.cpp new file mode 100644 index 0000000000..a76432fa57 --- /dev/null +++ b/src/translate_c.cpp @@ -0,0 +1,3527 @@ +/* + * 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 +#include +#include + +#include + +using namespace clang; + +struct MacroSymbol { + Buf *name; + Buf *value; +}; + +struct Alias { + Buf *new_name; + Buf *canon_name; +}; + +struct Context { + ImportTableEntry *import; + ZigList *errors; + VisibMod visib_mod; + VisibMod export_visib_mod; + AstNode *root; + HashMap decl_table; + HashMap macro_table; + HashMap global_table; + SourceManager *source_manager; + ZigList aliases; + ZigList macro_symbols; + AstNode *source_node; + bool warnings_on; + + CodeGen *codegen; + ASTContext *ctx; + + HashMap 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(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(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(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(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(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(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(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(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(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(qt.getTypePtr()); + if (paren_type->getInnerType()->getTypeClass() == Type::FunctionProto) { + return true; + } + } else if (qt.getTypePtr()->getTypeClass() == Type::Attributed) { + const AttributedType *attr_type = static_cast(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(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(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(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(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(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::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(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(ty); + const TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); + return resolve_typedef_decl(c, typedef_decl); + } + case Type::Elaborated: + { + const ElaboratedType *elaborated_ty = static_cast(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(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(ty); + return resolve_record_decl(c, record_ty->getDecl()); + } + case Type::Enum: + { + const EnumType *enum_ty = static_cast(ty); + return resolve_enum_decl(c, enum_ty->getDecl()); + } + case Type::ConstantArray: + { + const ConstantArrayType *const_arr_ty = static_cast(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(ty); + return trans_qual_type(c, paren_ty->getInnerType(), source_loc); + } + case Type::Decayed: + { + const DecayedType *decayed_ty = static_cast(ty); + return trans_qual_type(c, decayed_ty->getDecayedType(), source_loc); + } + case Type::Attributed: + { + const AttributedType *attributed_ty = static_cast(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: + 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: + { + AstNode *value_node = trans_expr(c, result_used, block, 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, block, 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, 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); + + 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 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(decl)); + break; + case Decl::Typedef: + resolve_typedef_decl(c, static_cast(decl)); + break; + case Decl::Enum: + resolve_enum_decl(c, static_cast(decl)); + break; + case Decl::Record: + resolve_record_decl(c, static_cast(decl)); + break; + case Decl::Var: + visit_var_decl(c, static_cast(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(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 *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 *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 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 diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); + + std::shared_ptr pch_container_ops = std::make_shared(); + + 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 err_unit; + std::unique_ptr 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.hpp b/src/translate_c.hpp new file mode 100644 index 0000000000..62e150f43c --- /dev/null +++ b/src/translate_c.hpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + + +#ifndef ZIG_PARSEC_HPP +#define ZIG_PARSEC_HPP + +#include "all_types.hpp" + +int parse_h_file(ImportTableEntry *import, ZigList *errors, const char *target_file, + CodeGen *codegen, AstNode *source_node); + +int parse_h_buf(ImportTableEntry *import, ZigList *errors, Buf *source, + CodeGen *codegen, AstNode *source_node); + +#endif diff --git a/test/parsec.zig b/test/parsec.zig deleted file mode 100644 index f830131262..0000000000 --- a/test/parsec.zig +++ /dev/null @@ -1,899 +0,0 @@ -const tests = @import("tests.zig"); - -pub fn addCases(cases: &tests.ParseCContext) { - cases.addAllowWarnings("simple data types", - \\#include - \\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 - \\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 - \\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; - \\} - ); - - 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); - \\} - ); -} - - - -// 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 index 20b57c7573..73d9646552 100644 --- 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 new file mode 100644 index 0000000000..6394950fd0 --- /dev/null +++ b/test/translate_c.zig @@ -0,0 +1,899 @@ +const tests = @import("tests.zig"); + +pub fn addCases(cases: &tests.TranslateCContext) { + cases.addAllowWarnings("simple data types", + \\#include + \\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 + \\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 + \\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; + \\} + ); + + 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); + \\} + ); +} + + + +// TODO +//float *ptrcast(int *a) { +// return (float *)a; +//} +// should translate to +// fn ptrcast(a: ?&c_int) -> ?&f32 { +// return @ptrCast(?&f32, a); +// } -- cgit v1.2.3