| author | |
| committer | |
| log | 31aefa6a2179dfae752020195fb193c6333bae7e |
| tree | 3bf77a35c39424e736da020a0451d6fe32d7648b |
| parent | b11c5d8f8256fc19e08eacfc50be209878f00e73 |
Now, if a struct has any fields which require comptime,
such as `type`, then the struct is marked as requiring
comptime as well. Same goes for unions.
This means that a function will implicitly be called
at comptime if the return type is a struct which contains
a field of type `type`.
closes #5865 files changed, 77 insertions(+), 81 deletions(-)
src/all_types.hpp+8| ... | @@ -1037,6 +1037,10 @@ struct TypeTableEntryStruct { | ... | @@ -1037,6 +1037,10 @@ struct TypeTableEntryStruct { |
| 1037 | // whether we've finished resolving it | 1037 | // whether we've finished resolving it |
| 1038 | bool complete; | 1038 | bool complete; |
| 1039 | 1039 | ||
| 1040 | // whether any of the fields require comptime | ||
| 1041 | // the value is not valid until zero_bits_known == true | ||
| 1042 | bool requires_comptime; | ||
| 1043 | |||
| 1040 | bool zero_bits_loop_flag; | 1044 | bool zero_bits_loop_flag; |
| 1041 | bool zero_bits_known; | 1045 | bool zero_bits_known; |
| 1042 | uint32_t abi_alignment; // also figured out with zero_bits pass | 1046 | uint32_t abi_alignment; // also figured out with zero_bits pass |
| ... | @@ -1105,6 +1109,10 @@ struct TypeTableEntryUnion { | ... | @@ -1105,6 +1109,10 @@ struct TypeTableEntryUnion { |
| 1105 | // whether we've finished resolving it | 1109 | // whether we've finished resolving it |
| 1106 | bool complete; | 1110 | bool complete; |
| 1107 | 1111 | ||
| 1112 | // whether any of the fields require comptime | ||
| 1113 | // the value is not valid until zero_bits_known == true | ||
| 1114 | bool requires_comptime; | ||
| 1115 | |||
| 1108 | bool zero_bits_loop_flag; | 1116 | bool zero_bits_loop_flag; |
| 1109 | bool zero_bits_known; | 1117 | bool zero_bits_known; |
| 1110 | uint32_t abi_alignment; // also figured out with zero_bits pass | 1118 | uint32_t abi_alignment; // also figured out with zero_bits pass |
src/analyze.cpp+22-1| ... | @@ -2533,6 +2533,10 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { | ... | @@ -2533,6 +2533,10 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { |
| 2533 | continue; | 2533 | continue; |
| 2534 | } | 2534 | } |
| 2535 | 2535 | ||
| 2536 | if (type_requires_comptime(field_type)) { | ||
| 2537 | struct_type->data.structure.requires_comptime = true; | ||
| 2538 | } | ||
| 2539 | |||
| 2536 | if (!type_has_bits(field_type)) | 2540 | if (!type_has_bits(field_type)) |
| 2537 | continue; | 2541 | continue; |
| 2538 | 2542 | ||
| ... | @@ -2724,6 +2728,11 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { | ... | @@ -2724,6 +2728,11 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { |
| 2724 | } | 2728 | } |
| 2725 | union_field->type_entry = field_type; | 2729 | union_field->type_entry = field_type; |
| 2726 | 2730 | ||
| 2731 | if (type_requires_comptime(field_type)) { | ||
| 2732 | union_type->data.unionation.requires_comptime = true; | ||
| 2733 | } | ||
| 2734 | |||
| 2735 | |||
| 2727 | if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) { | 2736 | if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) { |
| 2728 | ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value, | 2737 | ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value, |
| 2729 | buf_sprintf("non-enum union field assignment")); | 2738 | buf_sprintf("non-enum union field assignment")); |
| ... | @@ -4944,17 +4953,29 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { | ... | @@ -4944,17 +4953,29 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { |
| 4944 | case TypeTableEntryIdArgTuple: | 4953 | case TypeTableEntryIdArgTuple: |
| 4945 | return true; | 4954 | return true; |
| 4946 | case TypeTableEntryIdArray: | 4955 | case TypeTableEntryIdArray: |
| 4956 | return type_requires_comptime(type_entry->data.array.child_type); | ||
| 4947 | case TypeTableEntryIdStruct: | 4957 | case TypeTableEntryIdStruct: |
| 4958 | assert(type_has_zero_bits_known(type_entry)); | ||
| 4959 | return type_entry->data.structure.requires_comptime; | ||
| 4948 | case TypeTableEntryIdUnion: | 4960 | case TypeTableEntryIdUnion: |
| 4961 | assert(type_has_zero_bits_known(type_entry)); | ||
| 4962 | return type_entry->data.unionation.requires_comptime; | ||
| 4949 | case TypeTableEntryIdMaybe: | 4963 | case TypeTableEntryIdMaybe: |
| 4964 | return type_requires_comptime(type_entry->data.maybe.child_type); | ||
| 4950 | case TypeTableEntryIdErrorUnion: | 4965 | case TypeTableEntryIdErrorUnion: |
| 4966 | return type_requires_comptime(type_entry->data.error_union.payload_type); | ||
| 4967 | case TypeTableEntryIdPointer: | ||
| 4968 | if (type_entry->data.pointer.child_type->id == TypeTableEntryIdOpaque) { | ||
| 4969 | return false; | ||
| 4970 | } else { | ||
| 4971 | return type_requires_comptime(type_entry->data.pointer.child_type); | ||
| 4972 | } | ||
| 4951 | case TypeTableEntryIdEnum: | 4973 | case TypeTableEntryIdEnum: |
| 4952 | case TypeTableEntryIdErrorSet: | 4974 | case TypeTableEntryIdErrorSet: |
| 4953 | case TypeTableEntryIdFn: | 4975 | case TypeTableEntryIdFn: |
| 4954 | case TypeTableEntryIdBool: | 4976 | case TypeTableEntryIdBool: |
| 4955 | case TypeTableEntryIdInt: | 4977 | case TypeTableEntryIdInt: |
| 4956 | case TypeTableEntryIdFloat: | 4978 | case TypeTableEntryIdFloat: |
| 4957 | case TypeTableEntryIdPointer: | ||
| 4958 | case TypeTableEntryIdVoid: | 4979 | case TypeTableEntryIdVoid: |
| 4959 | case TypeTableEntryIdUnreachable: | 4980 | case TypeTableEntryIdUnreachable: |
| 4960 | case TypeTableEntryIdPromise: | 4981 | case TypeTableEntryIdPromise: |
src/ir.cpp+33-79| ... | @@ -11624,61 +11624,6 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi | ... | @@ -11624,61 +11624,6 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi |
| 11624 | zig_unreachable(); | 11624 | zig_unreachable(); |
| 11625 | } | 11625 | } |
| 11626 | 11626 | ||
| 11627 | enum VarClassRequired { | ||
| 11628 | VarClassRequiredAny, | ||
| 11629 | VarClassRequiredConst, | ||
| 11630 | VarClassRequiredIllegal, | ||
| 11631 | }; | ||
| 11632 | |||
| 11633 | static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) { | ||
| 11634 | switch (type_entry->id) { | ||
| 11635 | case TypeTableEntryIdInvalid: | ||
| 11636 | zig_unreachable(); | ||
| 11637 | case TypeTableEntryIdUnreachable: | ||
| 11638 | return VarClassRequiredIllegal; | ||
| 11639 | case TypeTableEntryIdBool: | ||
| 11640 | case TypeTableEntryIdInt: | ||
| 11641 | case TypeTableEntryIdFloat: | ||
| 11642 | case TypeTableEntryIdVoid: | ||
| 11643 | case TypeTableEntryIdErrorSet: | ||
| 11644 | case TypeTableEntryIdFn: | ||
| 11645 | case TypeTableEntryIdPromise: | ||
| 11646 | return VarClassRequiredAny; | ||
| 11647 | case TypeTableEntryIdComptimeFloat: | ||
| 11648 | case TypeTableEntryIdComptimeInt: | ||
| 11649 | case TypeTableEntryIdUndefined: | ||
| 11650 | case TypeTableEntryIdBlock: | ||
| 11651 | case TypeTableEntryIdNull: | ||
| 11652 | case TypeTableEntryIdOpaque: | ||
| 11653 | case TypeTableEntryIdMetaType: | ||
| 11654 | case TypeTableEntryIdNamespace: | ||
| 11655 | case TypeTableEntryIdBoundFn: | ||
| 11656 | case TypeTableEntryIdArgTuple: | ||
| 11657 | return VarClassRequiredConst; | ||
| 11658 | |||
| 11659 | case TypeTableEntryIdPointer: | ||
| 11660 | if (type_entry->data.pointer.child_type->id == TypeTableEntryIdOpaque) { | ||
| 11661 | return VarClassRequiredAny; | ||
| 11662 | } else { | ||
| 11663 | return get_var_class_required(type_entry->data.pointer.child_type); | ||
| 11664 | } | ||
| 11665 | case TypeTableEntryIdArray: | ||
| 11666 | return get_var_class_required(type_entry->data.array.child_type); | ||
| 11667 | case TypeTableEntryIdMaybe: | ||
| 11668 | return get_var_class_required(type_entry->data.maybe.child_type); | ||
| 11669 | case TypeTableEntryIdErrorUnion: | ||
| 11670 | return get_var_class_required(type_entry->data.error_union.payload_type); | ||
| 11671 | |||
| 11672 | case TypeTableEntryIdStruct: | ||
| 11673 | case TypeTableEntryIdEnum: | ||
| 11674 | case TypeTableEntryIdUnion: | ||
| 11675 | // TODO check the fields of these things and make sure that they don't recursively | ||
| 11676 | // contain any of the other variable classes | ||
| 11677 | return VarClassRequiredAny; | ||
| 11678 | } | ||
| 11679 | zig_unreachable(); | ||
| 11680 | } | ||
| 11681 | |||
| 11682 | static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) { | 11627 | static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) { |
| 11683 | VariableTableEntry *var = decl_var_instruction->var; | 11628 | VariableTableEntry *var = decl_var_instruction->var; |
| 11684 | 11629 | ||
| ... | @@ -11713,36 +11658,41 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc | ... | @@ -11713,36 +11658,41 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc |
| 11713 | if (type_is_invalid(result_type)) { | 11658 | if (type_is_invalid(result_type)) { |
| 11714 | result_type = ira->codegen->builtin_types.entry_invalid; | 11659 | result_type = ira->codegen->builtin_types.entry_invalid; |
| 11715 | } else { | 11660 | } else { |
| 11716 | switch (get_var_class_required(result_type)) { | 11661 | type_ensure_zero_bits_known(ira->codegen, result_type); |
| 11717 | case VarClassRequiredIllegal: | 11662 | if (type_is_invalid(result_type)) { |
| 11663 | result_type = ira->codegen->builtin_types.entry_invalid; | ||
| 11664 | } | ||
| 11665 | } | ||
| 11666 | |||
| 11667 | if (!type_is_invalid(result_type)) { | ||
| 11668 | if (result_type->id == TypeTableEntryIdUnreachable || | ||
| 11669 | result_type->id == TypeTableEntryIdOpaque) | ||
| 11670 | { | ||
| 11671 | ir_add_error_node(ira, source_node, | ||
| 11672 | buf_sprintf("variable of type '%s' not allowed", buf_ptr(&result_type->name))); | ||
| 11673 | result_type = ira->codegen->builtin_types.entry_invalid; | ||
| 11674 | } else if (type_requires_comptime(result_type)) { | ||
| 11675 | var_class_requires_const = true; | ||
| 11676 | if (!var->src_is_const && !is_comptime_var) { | ||
| 11718 | ir_add_error_node(ira, source_node, | 11677 | ir_add_error_node(ira, source_node, |
| 11719 | buf_sprintf("variable of type '%s' not allowed", buf_ptr(&result_type->name))); | 11678 | buf_sprintf("variable of type '%s' must be const or comptime", |
| 11679 | buf_ptr(&result_type->name))); | ||
| 11720 | result_type = ira->codegen->builtin_types.entry_invalid; | 11680 | result_type = ira->codegen->builtin_types.entry_invalid; |
| 11721 | break; | 11681 | } |
| 11722 | case VarClassRequiredConst: | 11682 | } else { |
| 11683 | if (casted_init_value->value.special == ConstValSpecialStatic && | ||
| 11684 | casted_init_value->value.type->id == TypeTableEntryIdFn && | ||
| 11685 | casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) | ||
| 11686 | { | ||
| 11723 | var_class_requires_const = true; | 11687 | var_class_requires_const = true; |
| 11724 | if (!var->src_is_const && !is_comptime_var) { | 11688 | if (!var->src_is_const && !is_comptime_var) { |
| 11725 | ir_add_error_node(ira, source_node, | 11689 | ErrorMsg *msg = ir_add_error_node(ira, source_node, |
| 11726 | buf_sprintf("variable of type '%s' must be const or comptime", | 11690 | buf_sprintf("functions marked inline must be stored in const or comptime var")); |
| 11727 | buf_ptr(&result_type->name))); | 11691 | AstNode *proto_node = casted_init_value->value.data.x_ptr.data.fn.fn_entry->proto_node; |
| 11692 | add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here")); | ||
| 11728 | result_type = ira->codegen->builtin_types.entry_invalid; | 11693 | result_type = ira->codegen->builtin_types.entry_invalid; |
| 11729 | } | 11694 | } |
| 11730 | break; | 11695 | } |
| 11731 | case VarClassRequiredAny: | ||
| 11732 | if (casted_init_value->value.special == ConstValSpecialStatic && | ||
| 11733 | casted_init_value->value.type->id == TypeTableEntryIdFn && | ||
| 11734 | casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) | ||
| 11735 | { | ||
| 11736 | var_class_requires_const = true; | ||
| 11737 | if (!var->src_is_const && !is_comptime_var) { | ||
| 11738 | ErrorMsg *msg = ir_add_error_node(ira, source_node, | ||
| 11739 | buf_sprintf("functions marked inline must be stored in const or comptime var")); | ||
| 11740 | AstNode *proto_node = casted_init_value->value.data.x_ptr.data.fn.fn_entry->proto_node; | ||
| 11741 | add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here")); | ||
| 11742 | result_type = ira->codegen->builtin_types.entry_invalid; | ||
| 11743 | } | ||
| 11744 | } | ||
| 11745 | break; | ||
| 11746 | } | 11696 | } |
| 11747 | } | 11697 | } |
| 11748 | 11698 | ||
| ... | @@ -12623,6 +12573,10 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal | ... | @@ -12623,6 +12573,10 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal |
| 12623 | inst_fn_type_id.return_type = specified_return_type; | 12573 | inst_fn_type_id.return_type = specified_return_type; |
| 12624 | } | 12574 | } |
| 12625 | 12575 | ||
| 12576 | type_ensure_zero_bits_known(ira->codegen, specified_return_type); | ||
| 12577 | if (type_is_invalid(specified_return_type)) | ||
| 12578 | return ira->codegen->builtin_types.entry_invalid; | ||
| 12579 | |||
| 12626 | if (type_requires_comptime(specified_return_type)) { | 12580 | if (type_requires_comptime(specified_return_type)) { |
| 12627 | // Throw out our work and call the function as if it were comptime. | 12581 | // Throw out our work and call the function as if it were comptime. |
| 12628 | return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, FnInlineAuto); | 12582 | return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, FnInlineAuto); |
test/cases/eval.zig+13| ... | @@ -610,3 +610,16 @@ test "slice of type" { | ... | @@ -610,3 +610,16 @@ test "slice of type" { |
| 610 | } | 610 | } |
| 611 | } | 611 | } |
| 612 | } | 612 | } |
| 613 | |||
| 614 | const Wrapper = struct { | ||
| 615 | T: type, | ||
| 616 | }; | ||
| 617 | |||
| 618 | fn wrap(comptime T: type) Wrapper { | ||
| 619 | return Wrapper{ .T = T }; | ||
| 620 | } | ||
| 621 | |||
| 622 | test "function which returns struct with type field causes implicit comptime" { | ||
| 623 | const ty = wrap(i32).T; | ||
| 624 | assert(ty == i32); | ||
| 625 | } |
test/compile_errors.zig+1-1| ... | @@ -3329,7 +3329,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -3329,7 +3329,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3329 | ".tmp_source.zig:9:4: error: variable of type 'comptime_float' must be const or comptime", | 3329 | ".tmp_source.zig:9:4: error: variable of type 'comptime_float' must be const or comptime", |
| 3330 | ".tmp_source.zig:10:4: error: variable of type '(block)' must be const or comptime", | 3330 | ".tmp_source.zig:10:4: error: variable of type '(block)' must be const or comptime", |
| 3331 | ".tmp_source.zig:11:4: error: variable of type '(null)' must be const or comptime", | 3331 | ".tmp_source.zig:11:4: error: variable of type '(null)' must be const or comptime", |
| 3332 | ".tmp_source.zig:12:4: error: variable of type 'Opaque' must be const or comptime", | 3332 | ".tmp_source.zig:12:4: error: variable of type 'Opaque' not allowed", |
| 3333 | ".tmp_source.zig:13:4: error: variable of type 'type' must be const or comptime", | 3333 | ".tmp_source.zig:13:4: error: variable of type 'type' must be const or comptime", |
| 3334 | ".tmp_source.zig:14:4: error: variable of type '(namespace)' must be const or comptime", | 3334 | ".tmp_source.zig:14:4: error: variable of type '(namespace)' must be const or comptime", |
| 3335 | ".tmp_source.zig:15:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime", | 3335 | ".tmp_source.zig:15:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime", |