authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-26 16:21:03-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-26 16:21:03-04:00
log269a53b6afb052ec09f7b26db68219cfeaa0460e
tree6bb7bc0571670de5b09b736ac5f42172259ce576
parent21ed9391179a9e472b25c80eaec5eebc9d08aca8
signaturelock-open Commit is signed but in an unrecognized format.

introduce @hasDecl builtin function

closes #1439

9 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#}
67366736
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#}
6744const std = @import("std");
6745const assert = std.debug.assert;
6746
6747const Foo = struct {
6748 nope: i32,
6749
6750 pub var blah = "xxx";
6751 const hi = 1;
6752};
6753
6754test "@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};
14751476
1476struct BuiltinFnEntry {1477struct 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};
23012303
2302struct IrInstruction {2304struct IrInstruction {
...@@ -3503,6 +3505,13 @@ struct IrInstructionAssertNonNull {...@@ -3503,6 +3505,13 @@ struct IrInstructionAssertNonNull {
3503 IrInstruction *target;3505 IrInstruction *target;
3504};3506};
35053507
3508struct IrInstructionHasDecl {
3509 IrInstruction base;
3510
3511 IrInstruction *container;
3512 IrInstruction *name;
3513};
3514
3506static const size_t slice_ptr_index = 0;3515static const size_t slice_ptr_index = 0;
3507static const size_t slice_len_index = 1;3516static const size_t slice_len_index = 1;
35083517
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();
56205621
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}
74137415
7414static const char *bool_to_str(bool b) {7416static 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}
10131013
1014static constexpr IrInstructionId ir_instruction_id(IrInstructionHasDecl *) {
1015 return IrInstructionIdHasDecl;
1016}
1017
1014template<typename T>1018template<typename T>
1015static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {1019static 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}
30163020
3021static 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
3017static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *scope_is_comptime, IrInstruction *is_comptime) {3034static 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}
2317523207
23208static 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
23176static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {23235static 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;
2370723769
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}
14551455
1456static 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
1456static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {1464static 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");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub 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 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const Foo = @import("hasdecl/foo.zig");
5
6const Bar = struct {
7 nope: i32,
8
9 const hi = 1;
10 pub var blah = "xxx";
11};
12
13test "@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 @@
1pub const public_thing = 42;
2const private_thing = 666;