From dc8b011d616991da166ad83795bf0b7e90471bd3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Nov 2017 21:53:50 -0400 Subject: [PATCH 01/12] fix incorrect debug info for empty structs closes #579 now all tests pass for llvm master branch --- src/analyze.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 0bc3343f6c..4258a4d7d6 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1712,8 +1712,21 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) { if (struct_type->zero_bits) { struct_type->type_ref = LLVMVoidType(); - ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, g->builtin_types.entry_void->di_type); - struct_type->di_type = g->builtin_types.entry_void->di_type; + + ImportTableEntry *import = get_scope_import(scope); + uint64_t debug_size_in_bits = 0; + uint64_t debug_align_in_bits = 0; + ZigLLVMDIType **di_element_types = nullptr; + size_t debug_field_count = 0; + ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, + ZigLLVMFileToScope(import->di_file), + buf_ptr(&struct_type->name), + import->di_file, (unsigned)(decl_node->line + 1), + debug_size_in_bits, + debug_align_in_bits, + 0, nullptr, di_element_types, (int)debug_field_count, 0, nullptr, ""); + ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type); + struct_type->di_type = replacement_di_type; return; } assert(struct_type->di_type); From a31b23c46ba2a8c28df01adc1aa0b4d878b9a5cf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Nov 2017 00:00:57 -0400 Subject: [PATCH 02/12] more compile-time type reflection See #383 --- src-self-hosted/main.zig | 15 +++++++----- src/ir.cpp | 49 +++++++++++++++++++++++++++++++++++++++ test/behavior.zig | 1 + test/cases/reflection.zig | 22 ++++++++++++++++++ 4 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 test/cases/reflection.zig diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index a4337faef3..020a042571 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -3,24 +3,28 @@ 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 +// TODO: ability to iterate over enums at compile time (for listing targets) 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 +270,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/ir.cpp b/src/ir.cpp index 6c6ce676f6..e2e61fed7d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11643,6 +11643,55 @@ 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 { + 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))); 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..33bad839fb --- /dev/null +++ b/test/cases/reflection.zig @@ -0,0 +1,22 @@ +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 and var args" { + comptime { + assert(@typeOf(dummy).ReturnType == i32); + assert(!@typeOf(dummy).is_var_args); + assert(@typeOf(dummy_varargs).is_var_args); + } +} + +fn dummy() -> i32 { 1234 } +fn dummy_varargs(args: ...) {} From 795703a39cfe0d59fcd9fff7a605a7efe9c5bdb2 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sat, 4 Nov 2017 02:09:33 +1300 Subject: [PATCH 03/12] Add emit command-line option (#580) Add emit command-line option --- src/all_types.hpp | 7 +++++ src/codegen.cpp | 72 +++++++++++++++++++++++++++++++++++++++-------- src/codegen.hpp | 1 + src/main.cpp | 15 ++++++++++ src/target.cpp | 8 ++++++ src/target.hpp | 2 ++ src/zig_llvm.cpp | 36 +++++++++++++++--------- src/zig_llvm.hpp | 10 ++++++- 8 files changed, 126 insertions(+), 25 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index c5ab12db35..af885413f7 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1366,6 +1366,12 @@ enum BuildMode { BuildModeSafeRelease, }; +enum EmitFileType { + EmitFileTypeBinary, + EmitFileTypeAssembly, + EmitFileTypeLLVMIr, +}; + struct LinkLib { Buf *name; Buf *path; @@ -1449,6 +1455,7 @@ struct CodeGen { TypeTableEntry *entry_arg_tuple; } builtin_types; + EmitFileType emit_file_type; ZigTarget zig_target; LLVMTargetDataRef target_data_ref; unsigned pointer_size_bytes; diff --git a/src/codegen.cpp b/src/codegen.cpp index 4852846af0..4a69f40da1 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; } @@ -4493,24 +4497,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[] = { 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/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 de7509f4ae..75cbf0c64f 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -555,6 +555,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 0e1a067bc6..086c1d6f96 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 e8df900a10..d7c9784e79 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); From 1ef6cb1b64e926a74db562401ebef3c673c3d7cf Mon Sep 17 00:00:00 2001 From: dimenus Date: Fri, 3 Nov 2017 16:25:00 -0500 Subject: [PATCH 04/12] Add support for windows line endings with c macros --- src/c_tokenizer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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; From 1890760206daa54af6b99ea052bc1d021004245d Mon Sep 17 00:00:00 2001 From: dimenus Date: Fri, 3 Nov 2017 16:46:43 -0500 Subject: [PATCH 05/12] Windows libc & static libc are located in the same dir which is already covered by msvc_lib_dir --- src/analyze.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 4258a4d7d6..0a408f44ff 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3440,8 +3440,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."); + } } } From 4a6df04f7568dd84245b513bfd8d245f31fa8f04 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Nov 2017 20:07:32 -0400 Subject: [PATCH 06/12] slightly more verbose error message when building object file fails --- src/link.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From f0d755153d90f35da612c8aa6f1853f5112f5cf1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 4 Nov 2017 16:19:43 -0400 Subject: [PATCH 07/12] add compile-time reflection for function arg types See #383 --- src/all_types.hpp | 9 +++++ src/codegen.cpp | 4 +- src/ir.cpp | 80 +++++++++++++++++++++++++++++++++++++++ src/ir_print.cpp | 12 ++++++ test/cases/reflection.zig | 9 ++++- test/compile_errors.zig | 14 +++++++ 6 files changed, 125 insertions(+), 3 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index af885413f7..7f06134c2c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1261,6 +1261,7 @@ enum BuiltinFnId { BuiltinFnIdAlignCast, BuiltinFnIdOpaqueType, BuiltinFnIdSetAlignStack, + BuiltinFnIdArgType, }; struct BuiltinFnEntry { @@ -1882,6 +1883,7 @@ enum IrInstructionId { IrInstructionIdAlignCast, IrInstructionIdOpaqueType, IrInstructionIdSetAlignStack, + IrInstructionIdArgType, }; struct IrInstruction { @@ -2682,6 +2684,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/codegen.cpp b/src/codegen.cpp index 4a69f40da1..2a2cd3ba10 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3401,6 +3401,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); @@ -4866,7 +4867,7 @@ 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, 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); @@ -4913,6 +4914,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/ir.cpp b/src/ir.cpp index e2e61fed7d..49e043a837 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -567,6 +567,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); @@ -2263,6 +2267,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; } @@ -2992,6 +3009,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: @@ -3194,6 +3219,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(); } @@ -4629,6 +4656,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(); } @@ -11686,6 +11727,13 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru 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'", @@ -15395,6 +15443,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_usize " 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: @@ -15585,6 +15662,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(); } @@ -15765,6 +15844,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..c0f3169b3e 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -954,6 +954,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) { @@ -1256,6 +1265,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/test/cases/reflection.zig b/test/cases/reflection.zig index 33bad839fb..13adfd20cd 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -10,13 +10,18 @@ test "reflection: array, pointer, nullable, error union type child" { } } -test "reflection: function return type and var args" { +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() -> i32 { 1234 } +fn dummy(a: bool, b: i32, c: f32) -> i32 { 1234 } fn dummy_varargs(args: ...) {} + diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f8e08d599f..6901bd302c 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2275,4 +2275,18 @@ 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"); } From 4cc9fe90a8a3c0bce803bf9fffd66477da9e37d0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 4 Nov 2017 16:40:55 -0400 Subject: [PATCH 08/12] fix build on MacOS --- src/ir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 49e043a837..556f255a0a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15462,7 +15462,7 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc 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_usize " out of bounds; '%s' has %" ZIG_PRI_usize " arguments", + 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; } From bd6f8d99c57004716333a3371fc6574fee534ecc Mon Sep 17 00:00:00 2001 From: scurest Date: Sun, 5 Nov 2017 11:27:56 -0600 Subject: [PATCH 09/12] add test for c_allocator --- std/heap.zig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/std/heap.zig b/std/heap.zig index b654f28a74..3f67a97926 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -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); From 48c8181886e783aecb32c2c9ca9e2af1e39fd1bf Mon Sep 17 00:00:00 2001 From: scurest Date: Sun, 5 Nov 2017 15:46:21 -0600 Subject: [PATCH 10/12] fix redeclaration of mem (#585) --- std/heap.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/std/heap.zig b/std/heap.zig index 3f67a97926..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 } From f0dafd3f209a342f055850108651545bea9b065b Mon Sep 17 00:00:00 2001 From: scurest Date: Mon, 6 Nov 2017 10:40:58 -0600 Subject: [PATCH 11/12] fix typos in std.io (#589) Fixes a bug that prevented InStream.realAllAlloc from compiling. --- std/io.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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(); } From 634e8713c394bacfe080d03256d1dd4f9a43dd8c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Nov 2017 22:07:19 -0500 Subject: [PATCH 12/12] add @memberType and @memberName builtin functions see #383 there is a plan to unify most of the reflection into 2 builtin functions, as outlined in the above issue, but this gives us needed features for now, and we can iterate on the design in future commits --- src-self-hosted/main.zig | 1 - src/all_types.hpp | 18 +++ src/analyze.cpp | 239 +++++++++++++++++++++----------------- src/codegen.cpp | 4 + src/ir.cpp | 174 +++++++++++++++++++++++++++ src/ir_print.cpp | 22 ++++ test/cases/reflection.zig | 43 +++++++ test/compile_errors.zig | 46 ++++++++ 8 files changed, 438 insertions(+), 109 deletions(-) diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 020a042571..816ff7c24a 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -5,7 +5,6 @@ const heap = @import("std").mem; // TODO: sync up CLI with c++ code // TODO: concurrency -// TODO: ability to iterate over enums at compile time (for listing targets) error InvalidArgument; error MissingArg0; diff --git a/src/all_types.hpp b/src/all_types.hpp index 7f06134c2c..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, @@ -1845,6 +1847,8 @@ enum IrInstructionId { IrInstructionIdMemcpy, IrInstructionIdSlice, IrInstructionIdMemberCount, + IrInstructionIdMemberType, + IrInstructionIdMemberName, IrInstructionIdBreakpoint, IrInstructionIdReturnAddress, IrInstructionIdFrameAddress, @@ -2408,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; }; diff --git a/src/analyze.cpp b/src/analyze.cpp index 0a408f44ff..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) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 2a2cd3ba10..976b20405e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3384,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: @@ -4867,6 +4869,8 @@ 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, 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); diff --git a/src/ir.cpp b/src/ir.cpp index 556f255a0a..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; } @@ -1783,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; @@ -2727,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; } @@ -3143,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: @@ -4379,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: @@ -14337,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; @@ -15606,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: @@ -15815,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: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index c0f3169b3e..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()"); } @@ -1148,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; diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index 13adfd20cd..4227f79a04 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -25,3 +25,46 @@ test "reflection: function return type, var args, and param types" { 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 6901bd302c..b2bfb9b8e4 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2289,4 +2289,50 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\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"); }