diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index a4337faef3..816ff7c24a 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -3,24 +3,27 @@ const io = @import("std").io; const os = @import("std").os; const heap = @import("std").mem; -// TODO: OutSteam and InStream interface -// TODO: move allocator to heap namespace // TODO: sync up CLI with c++ code +// TODO: concurrency error InvalidArgument; error MissingArg0; var arg0: []u8 = undefined; +var stderr_file: io.File = undefined; +const stderr = &stderr_file.out_stream; + pub fn main() -> %void { + stderr_file = %return io.getStdErr(); if (internal_main()) |_| { return; } else |err| { if (err == error.InvalidArgument) { - io.stderr.printf("\n") %% return err; - printUsage(&io.stderr) %% return err; + stderr.print("\n") %% return err; + printUsage(stderr) %% return err; } else { - io.stderr.printf("{}\n", err) %% return err; + stderr.print("{}\n", err) %% return err; } return err; } @@ -266,7 +269,6 @@ fn printUsage(outstream: &io.OutStream) -> %void { \\ --test-cmd-bin appends test binary path to test cmd args \\ ); - %return outstream.flush(); } const ZIG_ZEN = diff --git a/src/all_types.hpp b/src/all_types.hpp index c5ab12db35..797897e425 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1211,6 +1211,8 @@ enum BuiltinFnId { BuiltinFnIdMaxValue, BuiltinFnIdMinValue, BuiltinFnIdMemberCount, + BuiltinFnIdMemberType, + BuiltinFnIdMemberName, BuiltinFnIdTypeof, BuiltinFnIdAddWithOverflow, BuiltinFnIdSubWithOverflow, @@ -1261,6 +1263,7 @@ enum BuiltinFnId { BuiltinFnIdAlignCast, BuiltinFnIdOpaqueType, BuiltinFnIdSetAlignStack, + BuiltinFnIdArgType, }; struct BuiltinFnEntry { @@ -1366,6 +1369,12 @@ enum BuildMode { BuildModeSafeRelease, }; +enum EmitFileType { + EmitFileTypeBinary, + EmitFileTypeAssembly, + EmitFileTypeLLVMIr, +}; + struct LinkLib { Buf *name; Buf *path; @@ -1449,6 +1458,7 @@ struct CodeGen { TypeTableEntry *entry_arg_tuple; } builtin_types; + EmitFileType emit_file_type; ZigTarget zig_target; LLVMTargetDataRef target_data_ref; unsigned pointer_size_bytes; @@ -1837,6 +1847,8 @@ enum IrInstructionId { IrInstructionIdMemcpy, IrInstructionIdSlice, IrInstructionIdMemberCount, + IrInstructionIdMemberType, + IrInstructionIdMemberName, IrInstructionIdBreakpoint, IrInstructionIdReturnAddress, IrInstructionIdFrameAddress, @@ -1875,6 +1887,7 @@ enum IrInstructionId { IrInstructionIdAlignCast, IrInstructionIdOpaqueType, IrInstructionIdSetAlignStack, + IrInstructionIdArgType, }; struct IrInstruction { @@ -2399,6 +2412,20 @@ struct IrInstructionMemberCount { IrInstruction *container; }; +struct IrInstructionMemberType { + IrInstruction base; + + IrInstruction *container_type; + IrInstruction *member_index; +}; + +struct IrInstructionMemberName { + IrInstruction base; + + IrInstruction *container_type; + IrInstruction *member_index; +}; + struct IrInstructionBreakpoint { IrInstruction base; }; @@ -2675,6 +2702,13 @@ struct IrInstructionSetAlignStack { IrInstruction *align_bytes; }; +struct IrInstructionArgType { + IrInstruction base; + + IrInstruction *fn_type; + IrInstruction *arg_index; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/analyze.cpp b/src/analyze.cpp index 4258a4d7d6..0dc221408d 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1366,119 +1366,140 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { enum_type->data.enumeration.union_size_bytes = biggest_size_in_bits / 8; enum_type->data.enumeration.most_aligned_union_member = most_aligned_union_member; - if (!enum_type->data.enumeration.is_invalid) { - TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count); - TypeTableEntry *tag_type_entry = create_enum_tag_type(g, enum_type, tag_int_type); - enum_type->data.enumeration.tag_type = tag_type_entry; + if (enum_type->data.enumeration.is_invalid) + return; - uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); + if (enum_type->zero_bits) { + enum_type->type_ref = LLVMVoidType(); - if (most_aligned_union_member) { - // create llvm type for union - uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits; - LLVMTypeRef union_type_ref; - if (padding_in_bits > 0) { - TypeTableEntry *u8_type = get_int_type(g, false, 8); - TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8); - LLVMTypeRef union_element_types[] = { - most_aligned_union_member->type_ref, - padding_array->type_ref, - }; - union_type_ref = LLVMStructType(union_element_types, 2, false); - } else { - union_type_ref = most_aligned_union_member->type_ref; - } - enum_type->data.enumeration.union_type_ref = union_type_ref; + uint64_t debug_size_in_bits = 0; + uint64_t debug_align_in_bits = 0; + ZigLLVMDIType **di_root_members = nullptr; + size_t debug_member_count = 0; + ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, + ZigLLVMFileToScope(import->di_file), + buf_ptr(&enum_type->name), + import->di_file, (unsigned)(decl_node->line + 1), + debug_size_in_bits, + debug_align_in_bits, + 0, nullptr, di_root_members, (int)debug_member_count, 0, nullptr, ""); - 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); + ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type); + enum_type->di_type = replacement_di_type; + return; + } - if (align_of_tag_in_bits >= biggest_align_in_bits) { - enum_type->data.enumeration.gen_tag_index = 0; - enum_type->data.enumeration.gen_union_index = 1; - } else { - enum_type->data.enumeration.gen_union_index = 0; - enum_type->data.enumeration.gen_tag_index = 1; - } + TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count); + TypeTableEntry *tag_type_entry = create_enum_tag_type(g, enum_type, tag_int_type); + enum_type->data.enumeration.tag_type = tag_type_entry; - // create llvm type for root struct - LLVMTypeRef root_struct_element_types[2]; - root_struct_element_types[enum_type->data.enumeration.gen_tag_index] = tag_type_entry->type_ref; - root_struct_element_types[enum_type->data.enumeration.gen_union_index] = union_type_ref; - LLVMStructSetBody(enum_type->type_ref, root_struct_element_types, 2, false); + uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); - // 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(enum_type->di_type), "AnonEnum", - import->di_file, (unsigned)(decl_node->line + 1), - tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, - tag_type_entry->di_type, ""); - - // create debug type for union - ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, - ZigLLVMTypeToScope(enum_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, ""); - - // create debug types for members of root struct - uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, - enum_type->data.enumeration.gen_tag_index); - ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, - ZigLLVMTypeToScope(enum_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); - - uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, - enum_type->data.enumeration.gen_union_index); - ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, - ZigLLVMTypeToScope(enum_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); - - // create debug type for root struct - ZigLLVMDIType *di_root_members[2]; - di_root_members[enum_type->data.enumeration.gen_tag_index] = tag_member_di_type; - di_root_members[enum_type->data.enumeration.gen_union_index] = union_member_di_type; - - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, enum_type->type_ref); - uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, enum_type->type_ref); - ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, - ZigLLVMFileToScope(import->di_file), - buf_ptr(&enum_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, enum_type->di_type, replacement_di_type); - enum_type->di_type = replacement_di_type; + if (most_aligned_union_member) { + // create llvm type for union + uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits; + LLVMTypeRef union_type_ref; + if (padding_in_bits > 0) { + TypeTableEntry *u8_type = get_int_type(g, false, 8); + TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8); + LLVMTypeRef union_element_types[] = { + most_aligned_union_member->type_ref, + padding_array->type_ref, + }; + union_type_ref = LLVMStructType(union_element_types, 2, false); } else { - // create llvm type for root struct - enum_type->type_ref = tag_type_entry->type_ref; - - // 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, - ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_type->name), - import->di_file, (unsigned)(decl_node->line + 1), - tag_debug_size_in_bits, - tag_debug_align_in_bits, - di_enumerators, field_count, - tag_type_entry->di_type, ""); - - ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type); - enum_type->di_type = tag_di_type; + union_type_ref = most_aligned_union_member->type_ref; } + enum_type->data.enumeration.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); + + if (align_of_tag_in_bits >= biggest_align_in_bits) { + enum_type->data.enumeration.gen_tag_index = 0; + enum_type->data.enumeration.gen_union_index = 1; + } else { + enum_type->data.enumeration.gen_union_index = 0; + enum_type->data.enumeration.gen_tag_index = 1; + } + + // create llvm type for root struct + LLVMTypeRef root_struct_element_types[2]; + root_struct_element_types[enum_type->data.enumeration.gen_tag_index] = tag_type_entry->type_ref; + root_struct_element_types[enum_type->data.enumeration.gen_union_index] = union_type_ref; + LLVMStructSetBody(enum_type->type_ref, root_struct_element_types, 2, false); + + // 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(enum_type->di_type), "AnonEnum", + import->di_file, (unsigned)(decl_node->line + 1), + tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, + tag_type_entry->di_type, ""); + + // create debug type for union + ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, + ZigLLVMTypeToScope(enum_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, ""); + + // create debug types for members of root struct + uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, + enum_type->data.enumeration.gen_tag_index); + ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(enum_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); + + uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, + enum_type->data.enumeration.gen_union_index); + ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(enum_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); + + // create debug type for root struct + ZigLLVMDIType *di_root_members[2]; + di_root_members[enum_type->data.enumeration.gen_tag_index] = tag_member_di_type; + di_root_members[enum_type->data.enumeration.gen_union_index] = union_member_di_type; + + uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, enum_type->type_ref); + uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, enum_type->type_ref); + ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, + ZigLLVMFileToScope(import->di_file), + buf_ptr(&enum_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, enum_type->di_type, replacement_di_type); + enum_type->di_type = replacement_di_type; + } else { + // create llvm type for root struct + enum_type->type_ref = tag_type_entry->type_ref; + + // 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, + ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_type->name), + import->di_file, (unsigned)(decl_node->line + 1), + tag_debug_size_in_bits, + tag_debug_align_in_bits, + di_enumerators, field_count, + tag_type_entry->di_type, ""); + + ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type); + enum_type->di_type = tag_di_type; } } @@ -1875,9 +1896,11 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { enum_type->data.enumeration.zero_bits_known = true; // also compute abi_alignment - TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count); - uint32_t align_of_tag_in_bytes = LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); - enum_type->data.enumeration.abi_alignment = max(align_of_tag_in_bytes, biggest_align_bytes); + if (!enum_type->zero_bits) { + TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count); + uint32_t align_of_tag_in_bytes = LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); + enum_type->data.enumeration.abi_alignment = max(align_of_tag_in_bytes, biggest_align_bytes); + } } static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { @@ -3440,8 +3463,14 @@ void find_libc_lib_path(CodeGen *g) { zig_panic("Unable to determine libc lib path."); } } + if (!g->libc_static_lib_dir || buf_len(g->libc_static_lib_dir) == 0) { - zig_panic("Unable to determine libc static lib path."); + if ((g->zig_target.os == ZigLLVM_Win32) && (g->msvc_lib_dir != NULL)) { + return; + } + else { + zig_panic("Unable to determine libc static lib path."); + } } } diff --git a/src/c_tokenizer.cpp b/src/c_tokenizer.cpp index 00977e0f32..044831f72e 100644 --- a/src/c_tokenizer.cpp +++ b/src/c_tokenizer.cpp @@ -91,6 +91,9 @@ IDENT_START: \ case DIGIT +#define LINE_ENDING \ + '\r': \ + case '\n' static void begin_token(CTokenize *ctok, CTokId id) { assert(ctok->cur_tok == nullptr); @@ -191,7 +194,7 @@ void tokenize_c_macro(CTokenize *ctok, const uint8_t *c) { case '\\': ctok->state = CTokStateBackslash; break; - case '\n': + case LINE_ENDING: goto found_end_of_macro; case IDENT_START: ctok->state = CTokStateIdentifier; diff --git a/src/codegen.cpp b/src/codegen.cpp index 4852846af0..976b20405e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -189,6 +189,10 @@ void codegen_set_is_test(CodeGen *g, bool is_test_build) { g->is_test_build = is_test_build; } +void codegen_set_emit_file_type(CodeGen *g, EmitFileType emit_file_type) { + g->emit_file_type = emit_file_type; +} + void codegen_set_is_static(CodeGen *g, bool is_static) { g->is_static = is_static; } @@ -3380,6 +3384,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdEmbedFile: case IrInstructionIdIntType: case IrInstructionIdMemberCount: + case IrInstructionIdMemberType: + case IrInstructionIdMemberName: case IrInstructionIdAlignOf: case IrInstructionIdFnProto: case IrInstructionIdTestComptime: @@ -3397,6 +3403,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdPtrTypeOf: case IrInstructionIdOpaqueType: case IrInstructionIdSetAlignStack: + case IrInstructionIdArgType: zig_unreachable(); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); @@ -4493,24 +4500,70 @@ static void do_code_gen(CodeGen *g) { LLVMVerifyModule(g->module, LLVMAbortProcessAction, &error); #endif - codegen_add_time_event(g, "LLVM Emit Object"); + codegen_add_time_event(g, "LLVM Emit Output"); char *err_msg = nullptr; Buf *o_basename = buf_create_from_buf(g->root_out_name); - const char *o_ext = target_o_file_ext(&g->zig_target); - buf_append_str(o_basename, o_ext); + + switch (g->emit_file_type) { + case EmitFileTypeBinary: + { + const char *o_ext = target_o_file_ext(&g->zig_target); + buf_append_str(o_basename, o_ext); + break; + } + case EmitFileTypeAssembly: + { + const char *asm_ext = target_asm_file_ext(&g->zig_target); + buf_append_str(o_basename, asm_ext); + break; + } + case EmitFileTypeLLVMIr: + { + const char *llvm_ir_ext = target_llvm_ir_file_ext(&g->zig_target); + buf_append_str(o_basename, llvm_ir_ext); + break; + } + default: + zig_unreachable(); + } + Buf *output_path = buf_alloc(); os_path_join(g->cache_dir, o_basename, output_path); ensure_cache_dir(g); - if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path), - LLVMObjectFile, &err_msg, g->build_mode == BuildModeDebug)) - { - zig_panic("unable to write object file %s: %s", buf_ptr(output_path), err_msg); + + switch (g->emit_file_type) { + case EmitFileTypeBinary: + if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path), + ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug)) + { + zig_panic("unable to write object file %s: %s", buf_ptr(output_path), err_msg); + } + validate_inline_fns(g); + g->link_objects.append(output_path); + break; + + case EmitFileTypeAssembly: + if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path), + ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug)) + { + zig_panic("unable to write assembly file %s: %s", buf_ptr(output_path), err_msg); + } + validate_inline_fns(g); + break; + + case EmitFileTypeLLVMIr: + if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path), + ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug)) + { + zig_panic("unable to write llvm-ir file %s: %s", buf_ptr(output_path), err_msg); + } + validate_inline_fns(g); + break; + + default: + zig_unreachable(); } - - validate_inline_fns(g); - - g->link_objects.append(output_path); } static const uint8_t int_sizes_in_bits[] = { @@ -4816,7 +4869,9 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdMaxValue, "maxValue", 1); create_builtin_fn(g, BuiltinFnIdMinValue, "minValue", 1); create_builtin_fn(g, BuiltinFnIdMemberCount, "memberCount", 1); - create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); + create_builtin_fn(g, BuiltinFnIdMemberType, "memberType", 2); + create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2); + create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4); create_builtin_fn(g, BuiltinFnIdMulWithOverflow, "mulWithOverflow", 4); @@ -4863,6 +4918,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdAlignCast, "alignCast", 2); create_builtin_fn(g, BuiltinFnIdOpaqueType, "OpaqueType", 0); create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1); + create_builtin_fn(g, BuiltinFnIdArgType, "ArgType", 2); } static const char *bool_to_str(bool b) { diff --git a/src/codegen.hpp b/src/codegen.hpp index 622edb82d8..b71a7fa651 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -23,6 +23,7 @@ void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len); void codegen_set_is_test(CodeGen *codegen, bool is_test); void codegen_set_each_lib_rpath(CodeGen *codegen, bool each_lib_rpath); +void codegen_set_emit_file_type(CodeGen *g, EmitFileType emit_file_type); void codegen_set_is_static(CodeGen *codegen, bool is_static); void codegen_set_strip(CodeGen *codegen, bool strip); void codegen_set_errmsg_color(CodeGen *codegen, ErrColor err_color); diff --git a/src/ir.cpp b/src/ir.cpp index 6c6ce676f6..ae48c1d369 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -411,6 +411,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberCount *) { return IrInstructionIdMemberCount; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberType *) { + return IrInstructionIdMemberType; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberName *) { + return IrInstructionIdMemberName; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionBreakpoint *) { return IrInstructionIdBreakpoint; } @@ -567,6 +575,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSetAlignStack *) return IrInstructionIdSetAlignStack; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionArgType *) { + return IrInstructionIdArgType; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -1779,6 +1791,32 @@ static IrInstruction *ir_build_member_count(IrBuilder *irb, Scope *scope, AstNod return &instruction->base; } +static IrInstruction *ir_build_member_type(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *container_type, IrInstruction *member_index) +{ + IrInstructionMemberType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->container_type = container_type; + instruction->member_index = member_index; + + ir_ref_instruction(container_type, irb->current_basic_block); + ir_ref_instruction(member_index, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_member_name(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *container_type, IrInstruction *member_index) +{ + IrInstructionMemberName *instruction = ir_build_instruction(irb, scope, source_node); + instruction->container_type = container_type; + instruction->member_index = member_index; + + ir_ref_instruction(container_type, irb->current_basic_block); + ir_ref_instruction(member_index, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_breakpoint(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionBreakpoint *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; @@ -2263,6 +2301,19 @@ static IrInstruction *ir_build_set_align_stack(IrBuilder *irb, Scope *scope, Ast return &instruction->base; } +static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *fn_type, IrInstruction *arg_index) +{ + IrInstructionArgType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->fn_type = fn_type; + instruction->arg_index = arg_index; + + ir_ref_instruction(fn_type, irb->current_basic_block); + ir_ref_instruction(arg_index, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) { return nullptr; } @@ -2710,6 +2761,22 @@ static IrInstruction *ir_instruction_membercount_get_dep(IrInstructionMemberCoun } } +static IrInstruction *ir_instruction_membertype_get_dep(IrInstructionMemberType *instruction, size_t index) { + switch (index) { + case 0: return instruction->container_type; + case 1: return instruction->member_index; + default: return nullptr; + } +} + +static IrInstruction *ir_instruction_membername_get_dep(IrInstructionMemberName *instruction, size_t index) { + switch (index) { + case 0: return instruction->container_type; + case 1: return instruction->member_index; + default: return nullptr; + } +} + static IrInstruction *ir_instruction_breakpoint_get_dep(IrInstructionBreakpoint *instruction, size_t index) { return nullptr; } @@ -2992,6 +3059,14 @@ static IrInstruction *ir_instruction_setalignstack_get_dep(IrInstructionSetAlign } } +static IrInstruction *ir_instruction_argtype_get_dep(IrInstructionArgType *instruction, size_t index) { + switch (index) { + case 0: return instruction->fn_type; + case 1: return instruction->arg_index; + default: return nullptr; + } +} + static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -3118,6 +3193,10 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_slice_get_dep((IrInstructionSlice *) instruction, index); case IrInstructionIdMemberCount: return ir_instruction_membercount_get_dep((IrInstructionMemberCount *) instruction, index); + case IrInstructionIdMemberType: + return ir_instruction_membertype_get_dep((IrInstructionMemberType *) instruction, index); + case IrInstructionIdMemberName: + return ir_instruction_membername_get_dep((IrInstructionMemberName *) instruction, index); case IrInstructionIdBreakpoint: return ir_instruction_breakpoint_get_dep((IrInstructionBreakpoint *) instruction, index); case IrInstructionIdReturnAddress: @@ -3194,6 +3273,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_opaquetype_get_dep((IrInstructionOpaqueType *) instruction, index); case IrInstructionIdSetAlignStack: return ir_instruction_setalignstack_get_dep((IrInstructionSetAlignStack *) instruction, index); + case IrInstructionIdArgType: + return ir_instruction_argtype_get_dep((IrInstructionArgType *) instruction, index); } zig_unreachable(); } @@ -4352,6 +4433,36 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_member_count(irb, scope, node, arg0_value); } + case BuiltinFnIdMemberType: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + + return ir_build_member_type(irb, scope, node, arg0_value, arg1_value); + } + case BuiltinFnIdMemberName: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + + return ir_build_member_name(irb, scope, node, arg0_value, arg1_value); + } case BuiltinFnIdBreakpoint: return ir_build_breakpoint(irb, scope, node); case BuiltinFnIdReturnAddress: @@ -4629,6 +4740,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_set_align_stack(irb, scope, node, arg0_value); } + case BuiltinFnIdArgType: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + return ir_build_arg_type(irb, scope, node, arg0_value, arg1_value); + } } zig_unreachable(); } @@ -11643,6 +11768,62 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->builtin_types.entry_invalid; } + } else if (child_type->id == TypeTableEntryIdErrorUnion) { + if (buf_eql_str(field_name, "Child")) { + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, + create_const_type(ira->codegen, child_type->data.error.child_type), + ira->codegen->builtin_types.entry_type, + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); + } else { + ir_add_error(ira, &field_ptr_instruction->base, + buf_sprintf("type '%s' has no member called '%s'", + buf_ptr(&child_type->name), buf_ptr(field_name))); + return ira->codegen->builtin_types.entry_invalid; + } + } else if (child_type->id == TypeTableEntryIdMaybe) { + if (buf_eql_str(field_name, "Child")) { + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, + create_const_type(ira->codegen, child_type->data.maybe.child_type), + ira->codegen->builtin_types.entry_type, + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); + } else { + ir_add_error(ira, &field_ptr_instruction->base, + buf_sprintf("type '%s' has no member called '%s'", + buf_ptr(&child_type->name), buf_ptr(field_name))); + return ira->codegen->builtin_types.entry_invalid; + } + } else if (child_type->id == TypeTableEntryIdFn) { + if (buf_eql_str(field_name, "ReturnType")) { + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, + create_const_type(ira->codegen, child_type->data.fn.fn_type_id.return_type), + ira->codegen->builtin_types.entry_type, + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); + } else if (buf_eql_str(field_name, "is_var_args")) { + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, + create_const_bool(ira->codegen, child_type->data.fn.fn_type_id.is_var_args), + ira->codegen->builtin_types.entry_bool, + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); + } else if (buf_eql_str(field_name, "arg_count")) { + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, + create_const_usize(ira->codegen, child_type->data.fn.fn_type_id.param_count), + ira->codegen->builtin_types.entry_usize, + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); + } else { + ir_add_error(ira, &field_ptr_instruction->base, + buf_sprintf("type '%s' has no member called '%s'", + buf_ptr(&child_type->name), buf_ptr(field_name))); + return ira->codegen->builtin_types.entry_invalid; + } } else { ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("type '%s' does not support field access", buf_ptr(&child_type->name))); @@ -14240,6 +14421,90 @@ static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrIns return ira->codegen->builtin_types.entry_num_lit_int; } +static TypeTableEntry *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInstructionMemberType *instruction) { + IrInstruction *container_type_value = instruction->container_type->other; + TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value); + if (type_is_invalid(container_type)) + return ira->codegen->builtin_types.entry_invalid; + + uint64_t member_index; + IrInstruction *index_value = instruction->member_index->other; + if (!ir_resolve_usize(ira, index_value, &member_index)) + return ira->codegen->builtin_types.entry_invalid; + + if (container_type->id == TypeTableEntryIdStruct) { + if (member_index >= container_type->data.structure.src_field_count) { + ir_add_error(ira, index_value, + buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members", + member_index, buf_ptr(&container_type->name), container_type->data.structure.src_field_count)); + return ira->codegen->builtin_types.entry_invalid; + } + TypeStructField *field = &container_type->data.structure.fields[member_index]; + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_type = field->type_entry; + return ira->codegen->builtin_types.entry_type; + } else if (container_type->id == TypeTableEntryIdEnum) { + if (member_index >= container_type->data.enumeration.src_field_count) { + ir_add_error(ira, index_value, + buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members", + member_index, buf_ptr(&container_type->name), container_type->data.enumeration.src_field_count)); + return ira->codegen->builtin_types.entry_invalid; + } + TypeEnumField *field = &container_type->data.enumeration.fields[member_index]; + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_type = field->type_entry; + return ira->codegen->builtin_types.entry_type; + } else { + ir_add_error(ira, container_type_value, + buf_sprintf("type '%s' does not support @memberType", buf_ptr(&container_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } +} + +static TypeTableEntry *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstructionMemberName *instruction) { + IrInstruction *container_type_value = instruction->container_type->other; + TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value); + if (type_is_invalid(container_type)) + return ira->codegen->builtin_types.entry_invalid; + + uint64_t member_index; + IrInstruction *index_value = instruction->member_index->other; + if (!ir_resolve_usize(ira, index_value, &member_index)) + return ira->codegen->builtin_types.entry_invalid; + + if (container_type->id == TypeTableEntryIdStruct) { + if (member_index >= container_type->data.structure.src_field_count) { + ir_add_error(ira, index_value, + buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members", + member_index, buf_ptr(&container_type->name), container_type->data.structure.src_field_count)); + return ira->codegen->builtin_types.entry_invalid; + } + TypeStructField *field = &container_type->data.structure.fields[member_index]; + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + init_const_str_lit(ira->codegen, out_val, field->name); + return out_val->type; + } else if (container_type->id == TypeTableEntryIdEnum) { + if (member_index >= container_type->data.enumeration.src_field_count) { + ir_add_error(ira, index_value, + buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members", + member_index, buf_ptr(&container_type->name), container_type->data.enumeration.src_field_count)); + return ira->codegen->builtin_types.entry_invalid; + } + TypeEnumField *field = &container_type->data.enumeration.fields[member_index]; + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + init_const_str_lit(ira->codegen, out_val, field->name); + return out_val->type; + } else { + ir_add_error(ira, container_type_value, + buf_sprintf("type '%s' does not support @memberName", buf_ptr(&container_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } +} + static TypeTableEntry *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstructionBreakpoint *instruction) { ir_build_breakpoint_from(&ira->new_irb, &instruction->base); return ira->codegen->builtin_types.entry_void; @@ -15346,6 +15611,35 @@ static TypeTableEntry *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, Ir return ira->codegen->builtin_types.entry_void; } +static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstructionArgType *instruction) { + IrInstruction *fn_type_inst = instruction->fn_type->other; + TypeTableEntry *fn_type = ir_resolve_type(ira, fn_type_inst); + if (type_is_invalid(fn_type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *arg_index_inst = instruction->arg_index->other; + uint64_t arg_index; + if (!ir_resolve_usize(ira, arg_index_inst, &arg_index)) + return ira->codegen->builtin_types.entry_invalid; + + if (fn_type->id != TypeTableEntryIdFn) { + ir_add_error(ira, fn_type_inst, buf_sprintf("expected function, found '%s'", buf_ptr(&fn_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; + if (arg_index >= fn_type_id->param_count) { + ir_add_error(ira, arg_index_inst, + buf_sprintf("arg index %" ZIG_PRI_u64 " out of bounds; '%s' has %" ZIG_PRI_usize " arguments", + arg_index, buf_ptr(&fn_type->name), fn_type_id->param_count)); + return ira->codegen->builtin_types.entry_invalid; + } + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_type = fn_type_id->param_info[arg_index].type; + return ira->codegen->builtin_types.entry_type; +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -15480,6 +15774,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_slice(ira, (IrInstructionSlice *)instruction); case IrInstructionIdMemberCount: return ir_analyze_instruction_member_count(ira, (IrInstructionMemberCount *)instruction); + case IrInstructionIdMemberType: + return ir_analyze_instruction_member_type(ira, (IrInstructionMemberType *)instruction); + case IrInstructionIdMemberName: + return ir_analyze_instruction_member_name(ira, (IrInstructionMemberName *)instruction); case IrInstructionIdBreakpoint: return ir_analyze_instruction_breakpoint(ira, (IrInstructionBreakpoint *)instruction); case IrInstructionIdReturnAddress: @@ -15536,6 +15834,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_opaque_type(ira, (IrInstructionOpaqueType *)instruction); case IrInstructionIdSetAlignStack: return ir_analyze_instruction_set_align_stack(ira, (IrInstructionSetAlignStack *)instruction); + case IrInstructionIdArgType: + return ir_analyze_instruction_arg_type(ira, (IrInstructionArgType *)instruction); } zig_unreachable(); } @@ -15687,6 +15987,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdBoolNot: case IrInstructionIdSlice: case IrInstructionIdMemberCount: + case IrInstructionIdMemberType: + case IrInstructionIdMemberName: case IrInstructionIdAlignOf: case IrInstructionIdReturnAddress: case IrInstructionIdFrameAddress: @@ -15716,6 +16018,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdTypeId: case IrInstructionIdAlignCast: case IrInstructionIdOpaqueType: + case IrInstructionIdArgType: return false; case IrInstructionIdAsm: { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 63ccd76ef0..1c60d68628 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -658,6 +658,22 @@ static void ir_print_member_count(IrPrint *irp, IrInstructionMemberCount *instru fprintf(irp->f, ")"); } +static void ir_print_member_type(IrPrint *irp, IrInstructionMemberType *instruction) { + fprintf(irp->f, "@memberType("); + ir_print_other_instruction(irp, instruction->container_type); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->member_index); + fprintf(irp->f, ")"); +} + +static void ir_print_member_name(IrPrint *irp, IrInstructionMemberName *instruction) { + fprintf(irp->f, "@memberName("); + ir_print_other_instruction(irp, instruction->container_type); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->member_index); + fprintf(irp->f, ")"); +} + static void ir_print_breakpoint(IrPrint *irp, IrInstructionBreakpoint *instruction) { fprintf(irp->f, "@breakpoint()"); } @@ -954,6 +970,15 @@ static void ir_print_set_align_stack(IrPrint *irp, IrInstructionSetAlignStack *i fprintf(irp->f, ")"); } +static void ir_print_arg_type(IrPrint *irp, IrInstructionArgType *instruction) { + fprintf(irp->f, "@ArgType("); + ir_print_other_instruction(irp, instruction->fn_type); + fprintf(irp->f, ","); + ir_print_other_instruction(irp, instruction->arg_index); + fprintf(irp->f, ")"); +} + + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1139,6 +1164,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdMemberCount: ir_print_member_count(irp, (IrInstructionMemberCount *)instruction); break; + case IrInstructionIdMemberType: + ir_print_member_type(irp, (IrInstructionMemberType *)instruction); + break; + case IrInstructionIdMemberName: + ir_print_member_name(irp, (IrInstructionMemberName *)instruction); + break; case IrInstructionIdBreakpoint: ir_print_breakpoint(irp, (IrInstructionBreakpoint *)instruction); break; @@ -1256,6 +1287,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdSetAlignStack: ir_print_set_align_stack(irp, (IrInstructionSetAlignStack *)instruction); break; + case IrInstructionIdArgType: + ir_print_arg_type(irp, (IrInstructionArgType *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/src/link.cpp b/src/link.cpp index d73ee4ea96..1a166a444f 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -894,7 +894,7 @@ void codegen_link(CodeGen *g, const char *out_file) { Buf *o_file_path = g->link_objects.at(0); int err; if ((err = os_rename(o_file_path, &lj.out_file))) { - zig_panic("unable to rename object file into final output: %s", err_str(err)); + zig_panic("unable to rename object file %s into final output %s: %s", buf_ptr(o_file_path), buf_ptr(&lj.out_file), err_str(err)); } } return; diff --git a/src/main.cpp b/src/main.cpp index 358f1cf255..db463ac92b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,6 +32,7 @@ static int usage(const char *arg0) { " --assembly $source add assembly file to build\n" " --cache-dir $path override the cache directory\n" " --color $auto|off|on enable or disable colored error messages\n" + " --emit $filetype emit a specific file format as compilation output\n" " --enable-timing-info print timing diagnostics\n" " --libc-include-dir $path directory where libc stdlib.h resides\n" " --name $name override output name\n" @@ -269,6 +270,7 @@ int main(int argc, char **argv) { char *arg0 = argv[0]; Cmd cmd = CmdInvalid; + EmitFileType emit_file_type = EmitFileTypeBinary; const char *in_file = nullptr; const char *out_file = nullptr; const char *out_file_h = nullptr; @@ -535,6 +537,17 @@ int main(int argc, char **argv) { fprintf(stderr, "--color options are 'auto', 'on', or 'off'\n"); return usage(arg0); } + } else if (strcmp(arg, "--emit") == 0) { + if (strcmp(argv[i], "asm") == 0) { + emit_file_type = EmitFileTypeAssembly; + } else if (strcmp(argv[i], "bin") == 0) { + emit_file_type = EmitFileTypeBinary; + } else if (strcmp(argv[i], "llvm-ir") == 0) { + emit_file_type = EmitFileTypeLLVMIr; + } else { + fprintf(stderr, "--emit options are 'asm', 'bin', or 'llvm-ir'\n"); + return usage(arg0); + } } else if (strcmp(arg, "--name") == 0) { out_name = argv[i]; } else if (strcmp(arg, "--libc-lib-dir") == 0) { @@ -815,6 +828,8 @@ int main(int argc, char **argv) { add_package(g, cur_pkg, g->root_package); if (cmd == CmdBuild) { + codegen_set_emit_file_type(g, emit_file_type); + for (size_t i = 0; i < objects.length; i += 1) { codegen_add_object(g, buf_create_from_str(objects.at(i))); } diff --git a/src/target.cpp b/src/target.cpp index eb14330e22..d760f230bf 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -581,6 +581,14 @@ const char *target_o_file_ext(ZigTarget *target) { } } +const char *target_asm_file_ext(ZigTarget *target) { + return ".s"; +} + +const char *target_llvm_ir_file_ext(ZigTarget *target) { + return ".ll"; +} + const char *target_exe_file_ext(ZigTarget *target) { if (target->os == ZigLLVM_Win32) { return ".exe"; diff --git a/src/target.hpp b/src/target.hpp index 528e42d687..2b678b313d 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -73,6 +73,8 @@ void resolve_target_object_format(ZigTarget *target); uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id); const char *target_o_file_ext(ZigTarget *target); +const char *target_asm_file_ext(ZigTarget *target); +const char *target_llvm_ir_file_ext(ZigTarget *target); const char *target_exe_file_ext(ZigTarget *target); Buf *target_dynamic_linker(ZigTarget *target); diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 074fa4c712..46ac45caff 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -77,7 +77,7 @@ static const bool assertions_on = false; #endif bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, - const char *filename, LLVMCodeGenFileType file_type, char **error_message, bool is_debug) + const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug) { std::error_code EC; raw_fd_ostream dest(filename, EC, sys::fs::F_None); @@ -135,18 +135,24 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM MPM.add(createTargetTransformInfoWrapperPass(target_machine->getTargetIRAnalysis())); PMBuilder->populateModulePassManager(MPM); + // Set output pass. TargetMachine::CodeGenFileType ft; - switch (file_type) { - case LLVMAssemblyFile: - ft = TargetMachine::CGFT_AssemblyFile; - break; - default: - ft = TargetMachine::CGFT_ObjectFile; - break; - } - if (target_machine->addPassesToEmitFile(MPM, dest, ft)) { - *error_message = strdup("TargetMachine can't emit a file of this type"); - return true; + if (output_type != ZigLLVM_EmitLLVMIr) { + switch (output_type) { + case ZigLLVM_EmitAssembly: + ft = TargetMachine::CGFT_AssemblyFile; + break; + case ZigLLVM_EmitBinary: + ft = TargetMachine::CGFT_ObjectFile; + break; + default: + abort(); + } + + if (target_machine->addPassesToEmitFile(MPM, dest, ft)) { + *error_message = strdup("TargetMachine can't emit a file of this type"); + return true; + } } // run per function optimization passes @@ -158,7 +164,11 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM MPM.run(*module); - dest.close(); + if (output_type == ZigLLVM_EmitLLVMIr) { + if (LLVMPrintModuleToFile(module_ref, filename, error_message)) { + return true; + } + } return false; } diff --git a/src/zig_llvm.hpp b/src/zig_llvm.hpp index 93ae462a28..c7bb211960 100644 --- a/src/zig_llvm.hpp +++ b/src/zig_llvm.hpp @@ -34,8 +34,16 @@ void ZigLLVMInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R); char *ZigLLVMGetHostCPUName(void); char *ZigLLVMGetNativeFeatures(void); +// We use a custom enum here since LLVM does not expose LLVMIr as an emit +// output through the same mechanism as assembly/binary. +enum ZigLLVM_EmitOutputType { + ZigLLVM_EmitAssembly, + ZigLLVM_EmitBinary, + ZigLLVM_EmitLLVMIr, +}; + bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, - const char *filename, LLVMCodeGenFileType file_type, char **error_message, bool is_debug); + const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug); LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args, unsigned NumArgs, unsigned CC, bool always_inline, const char *Name); diff --git a/std/heap.zig b/std/heap.zig index b654f28a74..d0bf8ab871 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -17,8 +17,8 @@ pub var c_allocator = Allocator { }; fn cAlloc(self: &Allocator, n: usize, alignment: usize) -> %[]u8 { - if (c.malloc(usize(n))) |mem| { - @ptrCast(&u8, mem)[0..n] + if (c.malloc(usize(n))) |buf| { + @ptrCast(&u8, buf)[0..n] } else { error.OutOfMemory } @@ -29,8 +29,8 @@ fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) old_mem[0..new_size] } else { const old_ptr = @ptrCast(&c_void, old_mem.ptr); - if (c.realloc(old_ptr, usize(new_size))) |mem| { - @ptrCast(&u8, mem)[0..new_size] + if (c.realloc(old_ptr, usize(new_size))) |buf| { + @ptrCast(&u8, buf)[0..new_size] } else { error.OutOfMemory } @@ -136,6 +136,14 @@ pub const IncrementingAllocator = struct { } }; +test "c_allocator" { + if (builtin.link_libc) { + var slice = c_allocator.alloc(u8, 50) %% return; + defer c_allocator.free(slice); + slice = c_allocator.realloc(u8, slice, 100) %% return; + } +} + test "IncrementingAllocator" { const total_bytes = 100 * 1024 * 1024; var inc_allocator = %%IncrementingAllocator.init(total_bytes); diff --git a/std/io.zig b/std/io.zig index 9778f38239..499ae95da9 100644 --- a/std/io.zig +++ b/std/io.zig @@ -308,7 +308,7 @@ pub const InStream = struct { readFn: fn(self: &InStream, buffer: []u8) -> %usize, /// Replaces `buffer` contents by reading from the stream until it is finished. - /// If `buffer.len()` woould exceed `max_size`, `error.StreamTooLong` is returned and + /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and /// the contents read from the stream are lost. pub fn readAllBuffer(self: &InStream, buffer: &Buffer, max_size: usize) -> %void { %return buffer.resize(0); @@ -339,7 +339,7 @@ pub const InStream = struct { var buf = Buffer.initNull(allocator); defer buf.deinit(); - %return self.readAllBuffer(self, &buf, max_size); + %return self.readAllBuffer(&buf, max_size); return buf.toOwnedSlice(); } diff --git a/test/behavior.zig b/test/behavior.zig index 952c725e8c..bdd428074b 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -29,6 +29,7 @@ comptime { _ = @import("cases/null.zig"); _ = @import("cases/pub_enum/index.zig"); _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig"); + _ = @import("cases/reflection.zig"); _ = @import("cases/sizeof_and_typeof.zig"); _ = @import("cases/slice.zig"); _ = @import("cases/struct.zig"); diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig new file mode 100644 index 0000000000..4227f79a04 --- /dev/null +++ b/test/cases/reflection.zig @@ -0,0 +1,70 @@ +const assert = @import("std").debug.assert; +const mem = @import("std").mem; + +test "reflection: array, pointer, nullable, error union type child" { + comptime { + assert(([10]u8).Child == u8); + assert((&u8).Child == u8); + assert((%u8).Child == u8); + assert((?u8).Child == u8); + } +} + +test "reflection: function return type, var args, and param types" { + comptime { + assert(@typeOf(dummy).ReturnType == i32); + assert(!@typeOf(dummy).is_var_args); + assert(@typeOf(dummy_varargs).is_var_args); + assert(@typeOf(dummy).arg_count == 3); + assert(@ArgType(@typeOf(dummy), 0) == bool); + assert(@ArgType(@typeOf(dummy), 1) == i32); + assert(@ArgType(@typeOf(dummy), 2) == f32); + } +} + +fn dummy(a: bool, b: i32, c: f32) -> i32 { 1234 } +fn dummy_varargs(args: ...) {} + +test "reflection: struct member types and names" { + comptime { + assert(@memberCount(Foo) == 3); + + assert(@memberType(Foo, 0) == i32); + assert(@memberType(Foo, 1) == bool); + assert(@memberType(Foo, 2) == void); + + assert(mem.eql(u8, @memberName(Foo, 0), "one")); + assert(mem.eql(u8, @memberName(Foo, 1), "two")); + assert(mem.eql(u8, @memberName(Foo, 2), "three")); + } +} + +test "reflection: enum member types and names" { + comptime { + assert(@memberCount(Bar) == 4); + + assert(@memberType(Bar, 0) == void); + assert(@memberType(Bar, 1) == i32); + assert(@memberType(Bar, 2) == bool); + assert(@memberType(Bar, 3) == f64); + + assert(mem.eql(u8, @memberName(Bar, 0), "One")); + assert(mem.eql(u8, @memberName(Bar, 1), "Two")); + assert(mem.eql(u8, @memberName(Bar, 2), "Three")); + assert(mem.eql(u8, @memberName(Bar, 3), "Four")); + } + +} + +const Foo = struct { + one: i32, + two: bool, + three: void, +}; + +const Bar = enum { + One, + Two: i32, + Three: bool, + Four: f64, +}; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f8e08d599f..b2bfb9b8e4 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2275,4 +2275,64 @@ pub fn addCases(cases: &tests.CompileErrorContext) { , ".tmp_source.zig:2:1: error: invalid character: '\\t'"); + cases.add("@ArgType given non function parameter", + \\comptime { + \\ _ = @ArgType(i32, 3); + \\} + , + ".tmp_source.zig:2:18: error: expected function, found 'i32'"); + + cases.add("@ArgType arg index out of bounds", + \\comptime { + \\ _ = @ArgType(@typeOf(add), 2); + \\} + \\fn add(a: i32, b: i32) -> i32 { return a + b; } + , + ".tmp_source.zig:2:32: error: arg index 2 out of bounds; 'fn(i32, i32) -> i32' has 2 arguments"); + + cases.add("@memberType on unsupported type", + \\comptime { + \\ _ = @memberType(i32, 0); + \\} + , + ".tmp_source.zig:2:21: error: type 'i32' does not support @memberType"); + + cases.add("@memberType struct out of bounds", + \\comptime { + \\ _ = @memberType(Foo, 0); + \\} + \\const Foo = struct {}; + , + ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members"); + + cases.add("@memberType enum out of bounds", + \\comptime { + \\ _ = @memberType(Foo, 0); + \\} + \\const Foo = enum {}; + , + ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members"); + + cases.add("@memberName on unsupported type", + \\comptime { + \\ _ = @memberName(i32, 0); + \\} + , + ".tmp_source.zig:2:21: error: type 'i32' does not support @memberName"); + + cases.add("@memberName struct out of bounds", + \\comptime { + \\ _ = @memberName(Foo, 0); + \\} + \\const Foo = struct {}; + , + ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members"); + + cases.add("@memberName enum out of bounds", + \\comptime { + \\ _ = @memberName(Foo, 0); + \\} + \\const Foo = enum {}; + , + ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members"); }