| 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,6 +6801,19 @@ test "@hasDecl" { |
| 6801 | assert(!@hasDecl(Foo, "nope1234")); | 6801 | assert(!@hasDecl(Foo, "nope1234")); |
| 6802 | } | 6802 | } |
| 6803 | {#code_end#} | 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 | {#header_close#} | 6817 | {#header_close#} |
| 6805 | 6818 | ||
| 6806 | {#header_open|@import#} | 6819 | {#header_open|@import#} |
src/all_types.hpp+9| ... | @@ -1416,6 +1416,7 @@ enum BuiltinFnId { | ... | @@ -1416,6 +1416,7 @@ enum BuiltinFnId { |
| 1416 | BuiltinFnIdMemberName, | 1416 | BuiltinFnIdMemberName, |
| 1417 | BuiltinFnIdField, | 1417 | BuiltinFnIdField, |
| 1418 | BuiltinFnIdTypeInfo, | 1418 | BuiltinFnIdTypeInfo, |
| 1419 | BuiltinFnIdHasField, | ||
| 1419 | BuiltinFnIdTypeof, | 1420 | BuiltinFnIdTypeof, |
| 1420 | BuiltinFnIdAddWithOverflow, | 1421 | BuiltinFnIdAddWithOverflow, |
| 1421 | BuiltinFnIdSubWithOverflow, | 1422 | BuiltinFnIdSubWithOverflow, |
| ... | @@ -2307,6 +2308,7 @@ enum IrInstructionId { | ... | @@ -2307,6 +2308,7 @@ enum IrInstructionId { |
| 2307 | IrInstructionIdByteOffsetOf, | 2308 | IrInstructionIdByteOffsetOf, |
| 2308 | IrInstructionIdBitOffsetOf, | 2309 | IrInstructionIdBitOffsetOf, |
| 2309 | IrInstructionIdTypeInfo, | 2310 | IrInstructionIdTypeInfo, |
| 2311 | IrInstructionIdHasField, | ||
| 2310 | IrInstructionIdTypeId, | 2312 | IrInstructionIdTypeId, |
| 2311 | IrInstructionIdSetEvalBranchQuota, | 2313 | IrInstructionIdSetEvalBranchQuota, |
| 2312 | IrInstructionIdPtrType, | 2314 | IrInstructionIdPtrType, |
| ... | @@ -3333,6 +3335,13 @@ struct IrInstructionTypeInfo { | ... | @@ -3333,6 +3335,13 @@ struct IrInstructionTypeInfo { |
| 3333 | IrInstruction *type_value; | 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 | struct IrInstructionTypeId { | 3345 | struct IrInstructionTypeId { |
| 3337 | IrInstruction base; | 3346 | IrInstruction base; |
| 3338 | 3347 |
src/codegen.cpp+2| ... | @@ -5592,6 +5592,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, | ... | @@ -5592,6 +5592,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 5592 | case IrInstructionIdByteOffsetOf: | 5592 | case IrInstructionIdByteOffsetOf: |
| 5593 | case IrInstructionIdBitOffsetOf: | 5593 | case IrInstructionIdBitOffsetOf: |
| 5594 | case IrInstructionIdTypeInfo: | 5594 | case IrInstructionIdTypeInfo: |
| 5595 | case IrInstructionIdHasField: | ||
| 5595 | case IrInstructionIdTypeId: | 5596 | case IrInstructionIdTypeId: |
| 5596 | case IrInstructionIdSetEvalBranchQuota: | 5597 | case IrInstructionIdSetEvalBranchQuota: |
| 5597 | case IrInstructionIdPtrType: | 5598 | case IrInstructionIdPtrType: |
| ... | @@ -7328,6 +7329,7 @@ static void define_builtin_fns(CodeGen *g) { | ... | @@ -7328,6 +7329,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 7328 | create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2); | 7329 | create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2); |
| 7329 | create_builtin_fn(g, BuiltinFnIdField, "field", 2); | 7330 | create_builtin_fn(g, BuiltinFnIdField, "field", 2); |
| 7330 | create_builtin_fn(g, BuiltinFnIdTypeInfo, "typeInfo", 1); | 7331 | create_builtin_fn(g, BuiltinFnIdTypeInfo, "typeInfo", 1); |
| 7332 | create_builtin_fn(g, BuiltinFnIdHasField, "hasField", 2); | ||
| 7331 | create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf | 7333 | create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf |
| 7332 | create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); | 7334 | create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); |
| 7333 | create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4); | 7335 | create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4); |
src/ir.cpp+63| ... | @@ -897,6 +897,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeInfo *) { | ... | @@ -897,6 +897,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeInfo *) { |
| 897 | return IrInstructionIdTypeInfo; | 897 | return IrInstructionIdTypeInfo; |
| 898 | } | 898 | } |
| 899 | 899 | ||
| 900 | static constexpr IrInstructionId ir_instruction_id(IrInstructionHasField *) { | ||
| 901 | return IrInstructionIdHasField; | ||
| 902 | } | ||
| 903 | |||
| 900 | static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeId *) { | 904 | static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeId *) { |
| 901 | return IrInstructionIdTypeId; | 905 | return IrInstructionIdTypeId; |
| 902 | } | 906 | } |
| ... | @@ -1375,6 +1379,19 @@ static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode * | ... | @@ -1375,6 +1379,19 @@ static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode * |
| 1375 | return &instruction->base; | 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 | static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, | 1395 | static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 1379 | IrInstruction *struct_ptr, TypeStructField *field) | 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,6 +5118,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 5101 | IrInstruction *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction); | 5118 | IrInstruction *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction); |
| 5102 | return ir_expr_wrap(irb, scope, load_ptr, result_loc); | 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 | case BuiltinFnIdTypeInfo: | 5136 | case BuiltinFnIdTypeInfo: |
| 5105 | { | 5137 | { |
| 5106 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | 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,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 | static IrInstruction *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstructionBreakpoint *instruction) { | 22683 | static IrInstruction *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstructionBreakpoint *instruction) { |
| 22624 | IrInstruction *result = ir_build_breakpoint(&ira->new_irb, | 22684 | IrInstruction *result = ir_build_breakpoint(&ira->new_irb, |
| 22625 | instruction->base.scope, instruction->base.source_node); | 22685 | instruction->base.scope, instruction->base.source_node); |
| ... | @@ -25480,6 +25540,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction | ... | @@ -25480,6 +25540,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction |
| 25480 | return ir_analyze_instruction_bit_offset_of(ira, (IrInstructionBitOffsetOf *)instruction); | 25540 | return ir_analyze_instruction_bit_offset_of(ira, (IrInstructionBitOffsetOf *)instruction); |
| 25481 | case IrInstructionIdTypeInfo: | 25541 | case IrInstructionIdTypeInfo: |
| 25482 | return ir_analyze_instruction_type_info(ira, (IrInstructionTypeInfo *) instruction); | 25542 | return ir_analyze_instruction_type_info(ira, (IrInstructionTypeInfo *) instruction); |
| 25543 | case IrInstructionIdHasField: | ||
| 25544 | return ir_analyze_instruction_has_field(ira, (IrInstructionHasField *) instruction); | ||
| 25483 | case IrInstructionIdTypeId: | 25545 | case IrInstructionIdTypeId: |
| 25484 | return ir_analyze_instruction_type_id(ira, (IrInstructionTypeId *)instruction); | 25546 | return ir_analyze_instruction_type_id(ira, (IrInstructionTypeId *)instruction); |
| 25485 | case IrInstructionIdSetEvalBranchQuota: | 25547 | case IrInstructionIdSetEvalBranchQuota: |
| ... | @@ -25788,6 +25850,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { | ... | @@ -25788,6 +25850,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 25788 | case IrInstructionIdByteOffsetOf: | 25850 | case IrInstructionIdByteOffsetOf: |
| 25789 | case IrInstructionIdBitOffsetOf: | 25851 | case IrInstructionIdBitOffsetOf: |
| 25790 | case IrInstructionIdTypeInfo: | 25852 | case IrInstructionIdTypeInfo: |
| 25853 | case IrInstructionIdHasField: | ||
| 25791 | case IrInstructionIdTypeId: | 25854 | case IrInstructionIdTypeId: |
| 25792 | case IrInstructionIdAlignCast: | 25855 | case IrInstructionIdAlignCast: |
| 25793 | case IrInstructionIdImplicitCast: | 25856 | case IrInstructionIdImplicitCast: |
src/ir_print.cpp+11| ... | @@ -1270,6 +1270,14 @@ static void ir_print_type_info(IrPrint *irp, IrInstructionTypeInfo *instruction) | ... | @@ -1270,6 +1270,14 @@ static void ir_print_type_info(IrPrint *irp, IrInstructionTypeInfo *instruction) |
| 1270 | fprintf(irp->f, ")"); | 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 | static void ir_print_type_id(IrPrint *irp, IrInstructionTypeId *instruction) { | 1281 | static void ir_print_type_id(IrPrint *irp, IrInstructionTypeId *instruction) { |
| 1274 | fprintf(irp->f, "@typeId("); | 1282 | fprintf(irp->f, "@typeId("); |
| 1275 | ir_print_other_instruction(irp, instruction->type_value); | 1283 | ir_print_other_instruction(irp, instruction->type_value); |
| ... | @@ -1965,6 +1973,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | ... | @@ -1965,6 +1973,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1965 | case IrInstructionIdTypeInfo: | 1973 | case IrInstructionIdTypeInfo: |
| 1966 | ir_print_type_info(irp, (IrInstructionTypeInfo *)instruction); | 1974 | ir_print_type_info(irp, (IrInstructionTypeInfo *)instruction); |
| 1967 | break; | 1975 | break; |
| 1976 | case IrInstructionIdHasField: | ||
| 1977 | ir_print_has_field(irp, (IrInstructionHasField *)instruction); | ||
| 1978 | break; | ||
| 1968 | case IrInstructionIdTypeId: | 1979 | case IrInstructionIdTypeId: |
| 1969 | ir_print_type_id(irp, (IrInstructionTypeId *)instruction); | 1980 | ir_print_type_id(irp, (IrInstructionTypeId *)instruction); |
| 1970 | break; | 1981 | break; |
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 | "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 | cases.add( | 14 | cases.add( |
| 6 | "slice passed as array init type with elems", | 15 | "slice passed as array init type with elems", |
| 7 | \\export fn entry() void { | 16 | \\export fn entry() void { |
test/stage1/behavior.zig+1| ... | @@ -98,4 +98,5 @@ comptime { | ... | @@ -98,4 +98,5 @@ comptime { |
| 98 | _ = @import("behavior/void.zig"); | 98 | _ = @import("behavior/void.zig"); |
| 99 | _ = @import("behavior/while.zig"); | 99 | _ = @import("behavior/while.zig"); |
| 100 | _ = @import("behavior/widening.zig"); | 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 | } | ||