| author | |
| committer | |
| log | 57d6724186ead79ac76491f42390c0d581a76b04 |
| tree | d428783b71938e855e079a120a6c65e1251717af |
| parent | b05b5649df341573c0a42de503ceb025c4129473 |
| parent | df11512f85e5228ee2393e9dc5fc42294ecba1c9 |
| signature |
8 files changed, 145 insertions(+), 0 deletions(-)
doc/langref.html.in+13| ... | ... | @@ -6801,6 +6801,19 @@ test "@hasDecl" { |
| 6801 | 6801 | assert(!@hasDecl(Foo, "nope1234")); |
| 6802 | 6802 | } |
| 6803 | 6803 | {#code_end#} |
| 6804 | {#see_also|@hasField#} | |
| 6805 | {#header_close#} | |
| 6806 | ||
| 6807 | {#header_open|@hasField#} | |
| 6808 | <pre>{#syntax#}@hasField(comptime T: type, comptime name: []const u8) bool{#endsyntax#}</pre> | |
| 6809 | <p>Returns whether the field name of a struct, union, or enum exists.</p> | |
| 6810 | <p> | |
| 6811 | The result is a compile time constant. | |
| 6812 | </p> | |
| 6813 | <p> | |
| 6814 | It does not include functions, variables, or constants. | |
| 6815 | </p> | |
| 6816 | {#see_also|@hasDecl#} | |
| 6804 | 6817 | {#header_close#} |
| 6805 | 6818 | |
| 6806 | 6819 | {#header_open|@import#} |
src/all_types.hpp+9| ... | ... | @@ -1416,6 +1416,7 @@ enum BuiltinFnId { |
| 1416 | 1416 | BuiltinFnIdMemberName, |
| 1417 | 1417 | BuiltinFnIdField, |
| 1418 | 1418 | BuiltinFnIdTypeInfo, |
| 1419 | BuiltinFnIdHasField, | |
| 1419 | 1420 | BuiltinFnIdTypeof, |
| 1420 | 1421 | BuiltinFnIdAddWithOverflow, |
| 1421 | 1422 | BuiltinFnIdSubWithOverflow, |
| ... | ... | @@ -2307,6 +2308,7 @@ enum IrInstructionId { |
| 2307 | 2308 | IrInstructionIdByteOffsetOf, |
| 2308 | 2309 | IrInstructionIdBitOffsetOf, |
| 2309 | 2310 | IrInstructionIdTypeInfo, |
| 2311 | IrInstructionIdHasField, | |
| 2310 | 2312 | IrInstructionIdTypeId, |
| 2311 | 2313 | IrInstructionIdSetEvalBranchQuota, |
| 2312 | 2314 | IrInstructionIdPtrType, |
| ... | ... | @@ -3333,6 +3335,13 @@ struct IrInstructionTypeInfo { |
| 3333 | 3335 | IrInstruction *type_value; |
| 3334 | 3336 | }; |
| 3335 | 3337 | |
| 3338 | struct IrInstructionHasField { | |
| 3339 | IrInstruction base; | |
| 3340 | ||
| 3341 | IrInstruction *container_type; | |
| 3342 | IrInstruction *field_name; | |
| 3343 | }; | |
| 3344 | ||
| 3336 | 3345 | struct IrInstructionTypeId { |
| 3337 | 3346 | IrInstruction base; |
| 3338 | 3347 |
src/codegen.cpp+2| ... | ... | @@ -5592,6 +5592,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 5592 | 5592 | case IrInstructionIdByteOffsetOf: |
| 5593 | 5593 | case IrInstructionIdBitOffsetOf: |
| 5594 | 5594 | case IrInstructionIdTypeInfo: |
| 5595 | case IrInstructionIdHasField: | |
| 5595 | 5596 | case IrInstructionIdTypeId: |
| 5596 | 5597 | case IrInstructionIdSetEvalBranchQuota: |
| 5597 | 5598 | case IrInstructionIdPtrType: |
| ... | ... | @@ -7328,6 +7329,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 7328 | 7329 | create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2); |
| 7329 | 7330 | create_builtin_fn(g, BuiltinFnIdField, "field", 2); |
| 7330 | 7331 | create_builtin_fn(g, BuiltinFnIdTypeInfo, "typeInfo", 1); |
| 7332 | create_builtin_fn(g, BuiltinFnIdHasField, "hasField", 2); | |
| 7331 | 7333 | create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf |
| 7332 | 7334 | create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); |
| 7333 | 7335 | create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4); |
src/ir.cpp+63| ... | ... | @@ -897,6 +897,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeInfo *) { |
| 897 | 897 | return IrInstructionIdTypeInfo; |
| 898 | 898 | } |
| 899 | 899 | |
| 900 | static constexpr IrInstructionId ir_instruction_id(IrInstructionHasField *) { | |
| 901 | return IrInstructionIdHasField; | |
| 902 | } | |
| 903 | ||
| 900 | 904 | static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeId *) { |
| 901 | 905 | return IrInstructionIdTypeId; |
| 902 | 906 | } |
| ... | ... | @@ -1375,6 +1379,19 @@ static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode * |
| 1375 | 1379 | return &instruction->base; |
| 1376 | 1380 | } |
| 1377 | 1381 | |
| 1382 | static IrInstruction *ir_build_has_field(IrBuilder *irb, Scope *scope, AstNode *source_node, | |
| 1383 | IrInstruction *container_type, IrInstruction *field_name) | |
| 1384 | { | |
| 1385 | IrInstructionHasField *instruction = ir_build_instruction<IrInstructionHasField>(irb, scope, source_node); | |
| 1386 | instruction->container_type = container_type; | |
| 1387 | instruction->field_name = field_name; | |
| 1388 | ||
| 1389 | ir_ref_instruction(container_type, irb->current_basic_block); | |
| 1390 | ir_ref_instruction(field_name, irb->current_basic_block); | |
| 1391 | ||
| 1392 | return &instruction->base; | |
| 1393 | } | |
| 1394 | ||
| 1378 | 1395 | static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 1379 | 1396 | IrInstruction *struct_ptr, TypeStructField *field) |
| 1380 | 1397 | { |
| ... | ... | @@ -5101,6 +5118,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 5101 | 5118 | IrInstruction *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction); |
| 5102 | 5119 | return ir_expr_wrap(irb, scope, load_ptr, result_loc); |
| 5103 | 5120 | } |
| 5121 | case BuiltinFnIdHasField: | |
| 5122 | { | |
| 5123 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | |
| 5124 | IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); | |
| 5125 | if (arg0_value == irb->codegen->invalid_instruction) | |
| 5126 | return arg0_value; | |
| 5127 | ||
| 5128 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | |
| 5129 | IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); | |
| 5130 | if (arg1_value == irb->codegen->invalid_instruction) | |
| 5131 | return arg1_value; | |
| 5132 | ||
| 5133 | IrInstruction *type_info = ir_build_has_field(irb, scope, node, arg0_value, arg1_value); | |
| 5134 | return ir_lval_wrap(irb, scope, type_info, lval, result_loc); | |
| 5135 | } | |
| 5104 | 5136 | case BuiltinFnIdTypeInfo: |
| 5105 | 5137 | { |
| 5106 | 5138 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| ... | ... | @@ -22620,6 +22652,34 @@ static IrInstruction *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstr |
| 22620 | 22652 | } |
| 22621 | 22653 | } |
| 22622 | 22654 | |
| 22655 | static IrInstruction *ir_analyze_instruction_has_field(IrAnalyze *ira, IrInstructionHasField *instruction) { | |
| 22656 | Error err; | |
| 22657 | ZigType *container_type = ir_resolve_type(ira, instruction->container_type->child); | |
| 22658 | if (type_is_invalid(container_type)) | |
| 22659 | return ira->codegen->invalid_instruction; | |
| 22660 | ||
| 22661 | if ((err = type_resolve(ira->codegen, container_type, ResolveStatusZeroBitsKnown))) | |
| 22662 | return ira->codegen->invalid_instruction; | |
| 22663 | ||
| 22664 | Buf *field_name = ir_resolve_str(ira, instruction->field_name->child); | |
| 22665 | if (field_name == nullptr) | |
| 22666 | return ira->codegen->invalid_instruction; | |
| 22667 | ||
| 22668 | bool result; | |
| 22669 | if (container_type->id == ZigTypeIdStruct) { | |
| 22670 | result = find_struct_type_field(container_type, field_name) != nullptr; | |
| 22671 | } else if (container_type->id == ZigTypeIdEnum) { | |
| 22672 | result = find_enum_type_field(container_type, field_name) != nullptr; | |
| 22673 | } else if (container_type->id == ZigTypeIdUnion) { | |
| 22674 | result = find_union_type_field(container_type, field_name) != nullptr; | |
| 22675 | } else { | |
| 22676 | ir_add_error(ira, instruction->container_type, | |
| 22677 | buf_sprintf("type '%s' does not support @hasField", buf_ptr(&container_type->name))); | |
| 22678 | return ira->codegen->invalid_instruction; | |
| 22679 | } | |
| 22680 | return ir_const_bool(ira, &instruction->base, result); | |
| 22681 | } | |
| 22682 | ||
| 22623 | 22683 | static IrInstruction *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstructionBreakpoint *instruction) { |
| 22624 | 22684 | IrInstruction *result = ir_build_breakpoint(&ira->new_irb, |
| 22625 | 22685 | instruction->base.scope, instruction->base.source_node); |
| ... | ... | @@ -25480,6 +25540,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction |
| 25480 | 25540 | return ir_analyze_instruction_bit_offset_of(ira, (IrInstructionBitOffsetOf *)instruction); |
| 25481 | 25541 | case IrInstructionIdTypeInfo: |
| 25482 | 25542 | return ir_analyze_instruction_type_info(ira, (IrInstructionTypeInfo *) instruction); |
| 25543 | case IrInstructionIdHasField: | |
| 25544 | return ir_analyze_instruction_has_field(ira, (IrInstructionHasField *) instruction); | |
| 25483 | 25545 | case IrInstructionIdTypeId: |
| 25484 | 25546 | return ir_analyze_instruction_type_id(ira, (IrInstructionTypeId *)instruction); |
| 25485 | 25547 | case IrInstructionIdSetEvalBranchQuota: |
| ... | ... | @@ -25788,6 +25850,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 25788 | 25850 | case IrInstructionIdByteOffsetOf: |
| 25789 | 25851 | case IrInstructionIdBitOffsetOf: |
| 25790 | 25852 | case IrInstructionIdTypeInfo: |
| 25853 | case IrInstructionIdHasField: | |
| 25791 | 25854 | case IrInstructionIdTypeId: |
| 25792 | 25855 | case IrInstructionIdAlignCast: |
| 25793 | 25856 | case IrInstructionIdImplicitCast: |
src/ir_print.cpp+11| ... | ... | @@ -1270,6 +1270,14 @@ static void ir_print_type_info(IrPrint *irp, IrInstructionTypeInfo *instruction) |
| 1270 | 1270 | fprintf(irp->f, ")"); |
| 1271 | 1271 | } |
| 1272 | 1272 | |
| 1273 | static void ir_print_has_field(IrPrint *irp, IrInstructionHasField *instruction) { | |
| 1274 | fprintf(irp->f, "@hasField("); | |
| 1275 | ir_print_other_instruction(irp, instruction->container_type); | |
| 1276 | fprintf(irp->f, ","); | |
| 1277 | ir_print_other_instruction(irp, instruction->field_name); | |
| 1278 | fprintf(irp->f, ")"); | |
| 1279 | } | |
| 1280 | ||
| 1273 | 1281 | static void ir_print_type_id(IrPrint *irp, IrInstructionTypeId *instruction) { |
| 1274 | 1282 | fprintf(irp->f, "@typeId("); |
| 1275 | 1283 | ir_print_other_instruction(irp, instruction->type_value); |
| ... | ... | @@ -1965,6 +1973,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1965 | 1973 | case IrInstructionIdTypeInfo: |
| 1966 | 1974 | ir_print_type_info(irp, (IrInstructionTypeInfo *)instruction); |
| 1967 | 1975 | break; |
| 1976 | case IrInstructionIdHasField: | |
| 1977 | ir_print_has_field(irp, (IrInstructionHasField *)instruction); | |
| 1978 | break; | |
| 1968 | 1979 | case IrInstructionIdTypeId: |
| 1969 | 1980 | ir_print_type_id(irp, (IrInstructionTypeId *)instruction); |
| 1970 | 1981 | break; |
test/compile_errors.zig+9| ... | ... | @@ -2,6 +2,15 @@ const tests = @import("tests.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | |
| 4 | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.add( | |
| 6 | "wrong type to @hasField", | |
| 7 | \\export fn entry() bool { | |
| 8 | \\ return @hasField(i32, "hi"); | |
| 9 | \\} | |
| 10 | , | |
| 11 | "tmp.zig:2:22: error: type 'i32' does not support @hasField", | |
| 12 | ); | |
| 13 | ||
| 5 | 14 | cases.add( |
| 6 | 15 | "slice passed as array init type with elems", |
| 7 | 16 | \\export fn entry() void { |
test/stage1/behavior.zig+1| ... | ... | @@ -98,4 +98,5 @@ comptime { |
| 98 | 98 | _ = @import("behavior/void.zig"); |
| 99 | 99 | _ = @import("behavior/while.zig"); |
| 100 | 100 | _ = @import("behavior/widening.zig"); |
| 101 | _ = @import("behavior/hasfield.zig"); | |
| 101 | 102 | } |
test/stage1/behavior/hasfield.zig created+37| ... | ... | @@ -0,0 +1,37 @@ |
| 1 | const expect = @import("std").testing.expect; | |
| 2 | const builtin = @import("builtin"); | |
| 3 | ||
| 4 | test "@hasField" { | |
| 5 | const struc = struct { | |
| 6 | a: i32, | |
| 7 | b: []u8, | |
| 8 | ||
| 9 | pub const nope = 1; | |
| 10 | }; | |
| 11 | expect(@hasField(struc, "a") == true); | |
| 12 | expect(@hasField(struc, "b") == true); | |
| 13 | expect(@hasField(struc, "non-existant") == false); | |
| 14 | expect(@hasField(struc, "nope") == false); | |
| 15 | ||
| 16 | const unin = union { | |
| 17 | a: u64, | |
| 18 | b: []u16, | |
| 19 | ||
| 20 | pub const nope = 1; | |
| 21 | }; | |
| 22 | expect(@hasField(unin, "a") == true); | |
| 23 | expect(@hasField(unin, "b") == true); | |
| 24 | expect(@hasField(unin, "non-existant") == false); | |
| 25 | expect(@hasField(unin, "nope") == false); | |
| 26 | ||
| 27 | const enm = enum { | |
| 28 | a, | |
| 29 | b, | |
| 30 | ||
| 31 | pub const nope = 1; | |
| 32 | }; | |
| 33 | expect(@hasField(enm, "a") == true); | |
| 34 | expect(@hasField(enm, "b") == true); | |
| 35 | expect(@hasField(enm, "non-existant") == false); | |
| 36 | expect(@hasField(enm, "nope") == false); | |
| 37 | } |