| author | |
| committer | |
| log | 269a53b6afb052ec09f7b26db68219cfeaa0460e |
| tree | 6bb7bc0571670de5b09b736ac5f42172259ce576 |
| parent | 21ed9391179a9e472b25c80eaec5eebc9d08aca8 |
| signature |
closes #14399 files changed, 149 insertions(+), 0 deletions(-)
doc/langref.html.in+32| ... | @@ -6734,6 +6734,38 @@ export fn @"A function name that is a complete sentence."() void {} | ... | @@ -6734,6 +6734,38 @@ export fn @"A function name that is a complete sentence."() void {} |
| 6734 | </p> | 6734 | </p> |
| 6735 | {#header_close#} | 6735 | {#header_close#} |
| 6736 | 6736 | ||
| 6737 | {#header_open|@hasDecl#} | ||
| 6738 | <pre>{#syntax#}@hasDecl(comptime container: type, comptime name: []const u8) bool{#endsyntax#}</pre> | ||
| 6739 | <p> | ||
| 6740 | Returns whether or not a {#link|struct#}, {#link|enum#}, or {#link|union#} has a declaration | ||
| 6741 | matching {#syntax#}name{#endsyntax#}. | ||
| 6742 | </p> | ||
| 6743 | {#code_begin|test#} | ||
| 6744 | const std = @import("std"); | ||
| 6745 | const assert = std.debug.assert; | ||
| 6746 | |||
| 6747 | const Foo = struct { | ||
| 6748 | nope: i32, | ||
| 6749 | |||
| 6750 | pub var blah = "xxx"; | ||
| 6751 | const hi = 1; | ||
| 6752 | }; | ||
| 6753 | |||
| 6754 | test "@hasDecl" { | ||
| 6755 | assert(@hasDecl(Foo, "blah")); | ||
| 6756 | |||
| 6757 | // Even though `hi` is private, @hasDecl returns true because this test is | ||
| 6758 | // in the same file scope as Foo. It would return false if Foo was declared | ||
| 6759 | // in a different file. | ||
| 6760 | assert(@hasDecl(Foo, "hi")); | ||
| 6761 | |||
| 6762 | // @hasDecl is for declarations; not fields. | ||
| 6763 | assert(!@hasDecl(Foo, "nope")); | ||
| 6764 | assert(!@hasDecl(Foo, "nope1234")); | ||
| 6765 | } | ||
| 6766 | {#code_end#} | ||
| 6767 | {#header_close#} | ||
| 6768 | |||
| 6737 | {#header_open|@import#} | 6769 | {#header_open|@import#} |
| 6738 | <pre>{#syntax#}@import(comptime path: []u8) type{#endsyntax#}</pre> | 6770 | <pre>{#syntax#}@import(comptime path: []u8) type{#endsyntax#}</pre> |
| 6739 | <p> | 6771 | <p> |
src/all_types.hpp+9| ... | @@ -1471,6 +1471,7 @@ enum BuiltinFnId { | ... | @@ -1471,6 +1471,7 @@ enum BuiltinFnId { |
| 1471 | BuiltinFnIdErrorReturnTrace, | 1471 | BuiltinFnIdErrorReturnTrace, |
| 1472 | BuiltinFnIdAtomicRmw, | 1472 | BuiltinFnIdAtomicRmw, |
| 1473 | BuiltinFnIdAtomicLoad, | 1473 | BuiltinFnIdAtomicLoad, |
| 1474 | BuiltinFnIdHasDecl, | ||
| 1474 | }; | 1475 | }; |
| 1475 | 1476 | ||
| 1476 | struct BuiltinFnEntry { | 1477 | struct BuiltinFnEntry { |
| ... | @@ -2297,6 +2298,7 @@ enum IrInstructionId { | ... | @@ -2297,6 +2298,7 @@ enum IrInstructionId { |
| 2297 | IrInstructionIdArrayToVector, | 2298 | IrInstructionIdArrayToVector, |
| 2298 | IrInstructionIdAssertZero, | 2299 | IrInstructionIdAssertZero, |
| 2299 | IrInstructionIdAssertNonNull, | 2300 | IrInstructionIdAssertNonNull, |
| 2301 | IrInstructionIdHasDecl, | ||
| 2300 | }; | 2302 | }; |
| 2301 | 2303 | ||
| 2302 | struct IrInstruction { | 2304 | struct IrInstruction { |
| ... | @@ -3503,6 +3505,13 @@ struct IrInstructionAssertNonNull { | ... | @@ -3503,6 +3505,13 @@ struct IrInstructionAssertNonNull { |
| 3503 | IrInstruction *target; | 3505 | IrInstruction *target; |
| 3504 | }; | 3506 | }; |
| 3505 | 3507 | ||
| 3508 | struct IrInstructionHasDecl { | ||
| 3509 | IrInstruction base; | ||
| 3510 | |||
| 3511 | IrInstruction *container; | ||
| 3512 | IrInstruction *name; | ||
| 3513 | }; | ||
| 3514 | |||
| 3506 | static const size_t slice_ptr_index = 0; | 3515 | static const size_t slice_ptr_index = 0; |
| 3507 | static const size_t slice_len_index = 1; | 3516 | static const size_t slice_len_index = 1; |
| 3508 | 3517 |
src/codegen.cpp+2| ... | @@ -5616,6 +5616,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, | ... | @@ -5616,6 +5616,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 5616 | case IrInstructionIdLoadPtr: | 5616 | case IrInstructionIdLoadPtr: |
| 5617 | case IrInstructionIdBitCast: | 5617 | case IrInstructionIdBitCast: |
| 5618 | case IrInstructionIdGlobalAsm: | 5618 | case IrInstructionIdGlobalAsm: |
| 5619 | case IrInstructionIdHasDecl: | ||
| 5619 | zig_unreachable(); | 5620 | zig_unreachable(); |
| 5620 | 5621 | ||
| 5621 | case IrInstructionIdDeclVarGen: | 5622 | case IrInstructionIdDeclVarGen: |
| ... | @@ -7409,6 +7410,7 @@ static void define_builtin_fns(CodeGen *g) { | ... | @@ -7409,6 +7410,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 7409 | create_builtin_fn(g, BuiltinFnIdToBytes, "sliceToBytes", 1); | 7410 | create_builtin_fn(g, BuiltinFnIdToBytes, "sliceToBytes", 1); |
| 7410 | create_builtin_fn(g, BuiltinFnIdFromBytes, "bytesToSlice", 2); | 7411 | create_builtin_fn(g, BuiltinFnIdFromBytes, "bytesToSlice", 2); |
| 7411 | create_builtin_fn(g, BuiltinFnIdThis, "This", 0); | 7412 | create_builtin_fn(g, BuiltinFnIdThis, "This", 0); |
| 7413 | create_builtin_fn(g, BuiltinFnIdHasDecl, "hasDecl", 2); | ||
| 7412 | } | 7414 | } |
| 7413 | 7415 | ||
| 7414 | static const char *bool_to_str(bool b) { | 7416 | static const char *bool_to_str(bool b) { |
src/ir.cpp+62| ... | @@ -1011,6 +1011,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertNonNull *) | ... | @@ -1011,6 +1011,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertNonNull *) |
| 1011 | return IrInstructionIdAssertNonNull; | 1011 | return IrInstructionIdAssertNonNull; |
| 1012 | } | 1012 | } |
| 1013 | 1013 | ||
| 1014 | static constexpr IrInstructionId ir_instruction_id(IrInstructionHasDecl *) { | ||
| 1015 | return IrInstructionIdHasDecl; | ||
| 1016 | } | ||
| 1017 | |||
| 1014 | template<typename T> | 1018 | template<typename T> |
| 1015 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { | 1019 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { |
| 1016 | T *special_instruction = allocate<T>(1); | 1020 | T *special_instruction = allocate<T>(1); |
| ... | @@ -3014,6 +3018,19 @@ static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *sourc | ... | @@ -3014,6 +3018,19 @@ static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *sourc |
| 3014 | return &instruction->base; | 3018 | return &instruction->base; |
| 3015 | } | 3019 | } |
| 3016 | 3020 | ||
| 3021 | static IrInstruction *ir_build_has_decl(IrBuilder *irb, Scope *scope, AstNode *source_node, | ||
| 3022 | IrInstruction *container, IrInstruction *name) | ||
| 3023 | { | ||
| 3024 | IrInstructionHasDecl *instruction = ir_build_instruction<IrInstructionHasDecl>(irb, scope, source_node); | ||
| 3025 | instruction->container = container; | ||
| 3026 | instruction->name = name; | ||
| 3027 | |||
| 3028 | ir_ref_instruction(container, irb->current_basic_block); | ||
| 3029 | ir_ref_instruction(name, irb->current_basic_block); | ||
| 3030 | |||
| 3031 | return &instruction->base; | ||
| 3032 | } | ||
| 3033 | |||
| 3017 | static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *scope_is_comptime, IrInstruction *is_comptime) { | 3034 | static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *scope_is_comptime, IrInstruction *is_comptime) { |
| 3018 | IrInstructionCheckRuntimeScope *instruction = ir_build_instruction<IrInstructionCheckRuntimeScope>(irb, scope, source_node); | 3035 | IrInstructionCheckRuntimeScope *instruction = ir_build_instruction<IrInstructionCheckRuntimeScope>(irb, scope, source_node); |
| 3019 | instruction->scope_is_comptime = scope_is_comptime; | 3036 | instruction->scope_is_comptime = scope_is_comptime; |
| ... | @@ -5098,6 +5115,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo | ... | @@ -5098,6 +5115,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 5098 | } | 5115 | } |
| 5099 | return ir_lval_wrap(irb, scope, result, lval); | 5116 | return ir_lval_wrap(irb, scope, result, lval); |
| 5100 | } | 5117 | } |
| 5118 | case BuiltinFnIdHasDecl: | ||
| 5119 | { | ||
| 5120 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | ||
| 5121 | IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); | ||
| 5122 | if (arg0_value == irb->codegen->invalid_instruction) | ||
| 5123 | return arg0_value; | ||
| 5124 | |||
| 5125 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | ||
| 5126 | IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); | ||
| 5127 | if (arg1_value == irb->codegen->invalid_instruction) | ||
| 5128 | return arg1_value; | ||
| 5129 | |||
| 5130 | IrInstruction *has_decl = ir_build_has_decl(irb, scope, node, arg0_value, arg1_value); | ||
| 5131 | return ir_lval_wrap(irb, scope, has_decl, lval); | ||
| 5132 | } | ||
| 5101 | } | 5133 | } |
| 5102 | zig_unreachable(); | 5134 | zig_unreachable(); |
| 5103 | } | 5135 | } |
| ... | @@ -23173,6 +23205,33 @@ static IrInstruction *ir_analyze_instruction_check_runtime_scope(IrAnalyze *ira, | ... | @@ -23173,6 +23205,33 @@ static IrInstruction *ir_analyze_instruction_check_runtime_scope(IrAnalyze *ira, |
| 23173 | return ir_const_void(ira, &instruction->base); | 23205 | return ir_const_void(ira, &instruction->base); |
| 23174 | } | 23206 | } |
| 23175 | 23207 | ||
| 23208 | static IrInstruction *ir_analyze_instruction_has_decl(IrAnalyze *ira, IrInstructionHasDecl *instruction) { | ||
| 23209 | ZigType *container_type = ir_resolve_type(ira, instruction->container->child); | ||
| 23210 | if (type_is_invalid(container_type)) | ||
| 23211 | return ira->codegen->invalid_instruction; | ||
| 23212 | |||
| 23213 | Buf *name = ir_resolve_str(ira, instruction->name->child); | ||
| 23214 | if (name == nullptr) | ||
| 23215 | return ira->codegen->invalid_instruction; | ||
| 23216 | |||
| 23217 | if (!is_container(container_type)) { | ||
| 23218 | ir_add_error(ira, instruction->container, | ||
| 23219 | buf_sprintf("expected struct, enum, or union; found '%s'", buf_ptr(&container_type->name))); | ||
| 23220 | return ira->codegen->invalid_instruction; | ||
| 23221 | } | ||
| 23222 | |||
| 23223 | ScopeDecls *container_scope = get_container_scope(container_type); | ||
| 23224 | Tld *tld = find_container_decl(ira->codegen, container_scope, name); | ||
| 23225 | if (tld == nullptr) | ||
| 23226 | return ir_const_bool(ira, &instruction->base, false); | ||
| 23227 | |||
| 23228 | if (tld->visib_mod == VisibModPrivate && tld->import != get_scope_import(instruction->base.scope)) { | ||
| 23229 | return ir_const_bool(ira, &instruction->base, false); | ||
| 23230 | } | ||
| 23231 | |||
| 23232 | return ir_const_bool(ira, &instruction->base, true); | ||
| 23233 | } | ||
| 23234 | |||
| 23176 | static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { | 23235 | static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { |
| 23177 | switch (instruction->id) { | 23236 | switch (instruction->id) { |
| 23178 | case IrInstructionIdInvalid: | 23237 | case IrInstructionIdInvalid: |
| ... | @@ -23467,6 +23526,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio | ... | @@ -23467,6 +23526,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio |
| 23467 | return ir_analyze_instruction_enum_to_int(ira, (IrInstructionEnumToInt *)instruction); | 23526 | return ir_analyze_instruction_enum_to_int(ira, (IrInstructionEnumToInt *)instruction); |
| 23468 | case IrInstructionIdCheckRuntimeScope: | 23527 | case IrInstructionIdCheckRuntimeScope: |
| 23469 | return ir_analyze_instruction_check_runtime_scope(ira, (IrInstructionCheckRuntimeScope *)instruction); | 23528 | return ir_analyze_instruction_check_runtime_scope(ira, (IrInstructionCheckRuntimeScope *)instruction); |
| 23529 | case IrInstructionIdHasDecl: | ||
| 23530 | return ir_analyze_instruction_has_decl(ira, (IrInstructionHasDecl *)instruction); | ||
| 23470 | } | 23531 | } |
| 23471 | zig_unreachable(); | 23532 | zig_unreachable(); |
| 23472 | } | 23533 | } |
| ... | @@ -23703,6 +23764,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { | ... | @@ -23703,6 +23764,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 23703 | case IrInstructionIdEnumToInt: | 23764 | case IrInstructionIdEnumToInt: |
| 23704 | case IrInstructionIdVectorToArray: | 23765 | case IrInstructionIdVectorToArray: |
| 23705 | case IrInstructionIdArrayToVector: | 23766 | case IrInstructionIdArrayToVector: |
| 23767 | case IrInstructionIdHasDecl: | ||
| 23706 | return false; | 23768 | return false; |
| 23707 | 23769 | ||
| 23708 | case IrInstructionIdAsm: | 23770 | case IrInstructionIdAsm: |
src/ir_print.cpp+11| ... | @@ -1453,6 +1453,14 @@ static void ir_print_decl_var_gen(IrPrint *irp, IrInstructionDeclVarGen *decl_va | ... | @@ -1453,6 +1453,14 @@ static void ir_print_decl_var_gen(IrPrint *irp, IrInstructionDeclVarGen *decl_va |
| 1453 | } | 1453 | } |
| 1454 | } | 1454 | } |
| 1455 | 1455 | ||
| 1456 | static void ir_print_has_decl(IrPrint *irp, IrInstructionHasDecl *instruction) { | ||
| 1457 | fprintf(irp->f, "@hasDecl("); | ||
| 1458 | ir_print_other_instruction(irp, instruction->container); | ||
| 1459 | fprintf(irp->f, ","); | ||
| 1460 | ir_print_other_instruction(irp, instruction->name); | ||
| 1461 | fprintf(irp->f, ")"); | ||
| 1462 | } | ||
| 1463 | |||
| 1456 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | 1464 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1457 | ir_print_prefix(irp, instruction); | 1465 | ir_print_prefix(irp, instruction); |
| 1458 | switch (instruction->id) { | 1466 | switch (instruction->id) { |
| ... | @@ -1920,6 +1928,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | ... | @@ -1920,6 +1928,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1920 | case IrInstructionIdResizeSlice: | 1928 | case IrInstructionIdResizeSlice: |
| 1921 | ir_print_resize_slice(irp, (IrInstructionResizeSlice *)instruction); | 1929 | ir_print_resize_slice(irp, (IrInstructionResizeSlice *)instruction); |
| 1922 | break; | 1930 | break; |
| 1931 | case IrInstructionIdHasDecl: | ||
| 1932 | ir_print_has_decl(irp, (IrInstructionHasDecl *)instruction); | ||
| 1933 | break; | ||
| 1923 | } | 1934 | } |
| 1924 | fprintf(irp->f, "\n"); | 1935 | fprintf(irp->f, "\n"); |
| 1925 | } | 1936 | } |
test/compile_errors.zig+9| ... | @@ -2,6 +2,15 @@ const tests = @import("tests.zig"); | ... | @@ -2,6 +2,15 @@ const tests = @import("tests.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.add( | ||
| 6 | "@hasDecl with non-container", | ||
| 7 | \\export fn entry() void { | ||
| 8 | \\ _ = @hasDecl(i32, "hi"); | ||
| 9 | \\} | ||
| 10 | , | ||
| 11 | "tmp.zig:2:18: error: expected struct, enum, or union; found 'i32'", | ||
| 12 | ); | ||
| 13 | |||
| 5 | cases.add( | 14 | cases.add( |
| 6 | "field access of slices", | 15 | "field access of slices", |
| 7 | \\export fn entry() void { | 16 | \\export fn entry() void { |
test/stage1/behavior.zig+1| ... | @@ -53,6 +53,7 @@ comptime { | ... | @@ -53,6 +53,7 @@ comptime { |
| 53 | _ = @import("behavior/fn_in_struct_in_comptime.zig"); | 53 | _ = @import("behavior/fn_in_struct_in_comptime.zig"); |
| 54 | _ = @import("behavior/for.zig"); | 54 | _ = @import("behavior/for.zig"); |
| 55 | _ = @import("behavior/generics.zig"); | 55 | _ = @import("behavior/generics.zig"); |
| 56 | _ = @import("behavior/hasdecl.zig"); | ||
| 56 | _ = @import("behavior/if.zig"); | 57 | _ = @import("behavior/if.zig"); |
| 57 | _ = @import("behavior/import.zig"); | 58 | _ = @import("behavior/import.zig"); |
| 58 | _ = @import("behavior/incomplete_struct_param_tld.zig"); | 59 | _ = @import("behavior/incomplete_struct_param_tld.zig"); |
test/stage1/behavior/hasdecl.zig created+21| ... | @@ -0,0 +1,21 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const expect = std.testing.expect; | ||
| 3 | |||
| 4 | const Foo = @import("hasdecl/foo.zig"); | ||
| 5 | |||
| 6 | const Bar = struct { | ||
| 7 | nope: i32, | ||
| 8 | |||
| 9 | const hi = 1; | ||
| 10 | pub var blah = "xxx"; | ||
| 11 | }; | ||
| 12 | |||
| 13 | test "@hasDecl" { | ||
| 14 | expect(@hasDecl(Foo, "public_thing")); | ||
| 15 | expect(!@hasDecl(Foo, "private_thing")); | ||
| 16 | expect(!@hasDecl(Foo, "no_thing")); | ||
| 17 | |||
| 18 | expect(@hasDecl(Bar, "hi")); | ||
| 19 | expect(@hasDecl(Bar, "blah")); | ||
| 20 | expect(!@hasDecl(Bar, "nope")); | ||
| 21 | } | ||
test/stage1/behavior/hasdecl/foo.zig created+2| ... | @@ -0,0 +1,2 @@ | ||
| 1 | pub const public_thing = 42; | ||
| 2 | const private_thing = 666; | ||