authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-08-07 20:08:37-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-08-07 20:08:37-07:00
log0d5ecc4312f45f9288c4a349837f04b733405960
treee5b61cab7f98f36025b4bbb85d9bef7239162aa5
parent275410dc33d3f040be9213d5f8175e1ced6c6a7d

ability to have a return type of 'type'


9 files changed, 267 insertions(+), 147 deletions(-)

src/all_types.hpp+9-2
......@@ -843,7 +843,6 @@ struct FnTypeId {
843843 bool is_naked;
844844 bool is_cold;
845845 bool is_extern;
846 bool is_inline;
847846 FnTypeParamInfo prealloc_param_info[fn_type_id_prealloc_param_info_count];
848847};
849848
......@@ -1055,6 +1054,12 @@ enum WantPure {
10551054 WantPureTrue,
10561055};
10571056
1057enum FnInline {
1058 FnInlineAuto,
1059 FnInlineAlways,
1060 FnInlineNever,
1061};
1062
10581063struct FnTableEntry {
10591064 LLVMValueRef fn_value;
10601065 AstNode *proto_node;
......@@ -1070,8 +1075,10 @@ struct FnTableEntry {
10701075 bool is_test;
10711076 bool is_pure;
10721077 WantPure want_pure;
1078 AstNode *want_pure_attr_node;
1079 AstNode *want_pure_return_type;
10731080 bool safety_off;
1074 bool is_noinline;
1081 FnInline fn_inline;
10751082 BlockContext *parent_block_context;
10761083 FnAnalState anal_state;
10771084
src/analyze.cpp+205-138
......@@ -113,9 +113,30 @@ static AstNode *first_executing_node(AstNode *node) {
113113 zig_unreachable();
114114}
115115
116static void mark_impure_fn(BlockContext *context) {
117 if (context->fn_entry) {
118 context->fn_entry->is_pure = false;
116static void mark_impure_fn(CodeGen *g, BlockContext *context, AstNode *node) {
117 if (!context->fn_entry) return;
118 if (!context->fn_entry->is_pure) return;
119
120 context->fn_entry->is_pure = false;
121 if (context->fn_entry->want_pure == WantPureTrue) {
122 context->fn_entry->proto_node->data.fn_proto.skip = true;
123
124 ErrorMsg *msg = add_node_error(g, context->fn_entry->proto_node,
125 buf_sprintf("failed to evaluate function at compile time"));
126
127 add_error_note(g, msg, node,
128 buf_sprintf("unable to evaluate this expression at compile time"));
129
130 if (context->fn_entry->want_pure_attr_node) {
131 add_error_note(g, msg, context->fn_entry->want_pure_attr_node,
132 buf_sprintf("required to be compile-time function here"));
133 }
134
135 if (context->fn_entry->want_pure_return_type) {
136 add_error_note(g, msg, context->fn_entry->want_pure_return_type,
137 buf_sprintf("required to be compile-time function because of return type '%s'",
138 buf_ptr(&context->fn_entry->type_entry->data.fn.fn_type_id.return_type->name)));
139 }
119140 }
120141}
121142
......@@ -659,7 +680,7 @@ TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *
659680}
660681
661682// accepts ownership of fn_type_id memory
662TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
683TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id, bool gen_debug_info) {
663684 auto table_entry = g->fn_type_table.maybe_get(fn_type_id);
664685 if (table_entry) {
665686 return table_entry->value;
......@@ -704,65 +725,67 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
704725 buf_appendf(&fn_type->name, " -> %s", buf_ptr(&fn_type_id->return_type->name));
705726 }
706727
707 // next, loop over the parameters again and compute debug information
708 // and codegen information
709 bool first_arg_return = !fn_type_id->is_extern && handle_is_ptr(fn_type_id->return_type);
710 // +1 for maybe making the first argument the return value
711 LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(1 + fn_type_id->param_count);
712 // +1 because 0 is the return type and +1 for maybe making first arg ret val
713 LLVMZigDIType **param_di_types = allocate<LLVMZigDIType*>(2 + fn_type_id->param_count);
714 param_di_types[0] = fn_type_id->return_type->di_type;
715 int gen_param_index = 0;
716 TypeTableEntry *gen_return_type;
717 if (!type_has_bits(fn_type_id->return_type)) {
718 gen_return_type = g->builtin_types.entry_void;
719 } else if (first_arg_return) {
720 TypeTableEntry *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false);
721 gen_param_types[gen_param_index] = gen_type->type_ref;
722 gen_param_index += 1;
723 // after the gen_param_index += 1 because 0 is the return type
724 param_di_types[gen_param_index] = gen_type->di_type;
725 gen_return_type = g->builtin_types.entry_void;
726 } else {
727 gen_return_type = fn_type_id->return_type;
728 }
729 fn_type->data.fn.gen_return_type = gen_return_type;
730
731 fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(fn_type_id->param_count);
732 for (int i = 0; i < fn_type_id->param_count; i += 1) {
733 FnTypeParamInfo *src_param_info = &fn_type->data.fn.fn_type_id.param_info[i];
734 TypeTableEntry *type_entry = src_param_info->type;
735 FnGenParamInfo *gen_param_info = &fn_type->data.fn.gen_param_info[i];
736
737 gen_param_info->src_index = i;
738 gen_param_info->gen_index = -1;
739
740 assert(type_is_complete(type_entry));
741 if (type_has_bits(type_entry)) {
742 TypeTableEntry *gen_type;
743 if (handle_is_ptr(type_entry)) {
744 gen_type = get_pointer_to_type(g, type_entry, true);
745 gen_param_info->is_byval = true;
746 } else {
747 gen_type = type_entry;
748 }
728 if (gen_debug_info) {
729 // next, loop over the parameters again and compute debug information
730 // and codegen information
731 bool first_arg_return = !fn_type_id->is_extern && handle_is_ptr(fn_type_id->return_type);
732 // +1 for maybe making the first argument the return value
733 LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(1 + fn_type_id->param_count);
734 // +1 because 0 is the return type and +1 for maybe making first arg ret val
735 LLVMZigDIType **param_di_types = allocate<LLVMZigDIType*>(2 + fn_type_id->param_count);
736 param_di_types[0] = fn_type_id->return_type->di_type;
737 int gen_param_index = 0;
738 TypeTableEntry *gen_return_type;
739 if (!type_has_bits(fn_type_id->return_type)) {
740 gen_return_type = g->builtin_types.entry_void;
741 } else if (first_arg_return) {
742 TypeTableEntry *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false);
749743 gen_param_types[gen_param_index] = gen_type->type_ref;
750 gen_param_info->gen_index = gen_param_index;
751 gen_param_info->type = gen_type;
752
753744 gen_param_index += 1;
754
755745 // after the gen_param_index += 1 because 0 is the return type
756746 param_di_types[gen_param_index] = gen_type->di_type;
747 gen_return_type = g->builtin_types.entry_void;
748 } else {
749 gen_return_type = fn_type_id->return_type;
757750 }
758 }
751 fn_type->data.fn.gen_return_type = gen_return_type;
752
753 fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(fn_type_id->param_count);
754 for (int i = 0; i < fn_type_id->param_count; i += 1) {
755 FnTypeParamInfo *src_param_info = &fn_type->data.fn.fn_type_id.param_info[i];
756 TypeTableEntry *type_entry = src_param_info->type;
757 FnGenParamInfo *gen_param_info = &fn_type->data.fn.gen_param_info[i];
758
759 gen_param_info->src_index = i;
760 gen_param_info->gen_index = -1;
761
762 assert(type_is_complete(type_entry));
763 if (type_has_bits(type_entry)) {
764 TypeTableEntry *gen_type;
765 if (handle_is_ptr(type_entry)) {
766 gen_type = get_pointer_to_type(g, type_entry, true);
767 gen_param_info->is_byval = true;
768 } else {
769 gen_type = type_entry;
770 }
771 gen_param_types[gen_param_index] = gen_type->type_ref;
772 gen_param_info->gen_index = gen_param_index;
773 gen_param_info->type = gen_type;
774
775 gen_param_index += 1;
759776
760 fn_type->data.fn.gen_param_count = gen_param_index;
777 // after the gen_param_index += 1 because 0 is the return type
778 param_di_types[gen_param_index] = gen_type->di_type;
779 }
780 }
761781
762 fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref,
763 gen_param_types, gen_param_index, fn_type_id->is_var_args);
764 fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0);
765 fn_type->di_type = LLVMZigCreateSubroutineType(g->dbuilder, param_di_types, gen_param_index + 1, 0);
782 fn_type->data.fn.gen_param_count = gen_param_index;
783
784 fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref,
785 gen_param_types, gen_param_index, fn_type_id->is_var_args);
786 fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0);
787 fn_type->di_type = LLVMZigCreateSubroutineType(g->dbuilder, param_di_types, gen_param_index + 1, 0);
788 }
766789
767790 g->fn_type_table.put(&fn_type->data.fn.fn_type_id, fn_type);
768791
......@@ -862,8 +885,15 @@ static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, B
862885 return analyze_type_expr_pointer_only(g, import, context, node, false);
863886}
864887
888static bool fn_wants_full_static_eval(FnTableEntry *fn_table_entry) {
889 assert(fn_table_entry);
890 AstNodeFnProto *fn_proto = &fn_table_entry->proto_node->data.fn_proto;
891 return fn_proto->inline_arg_count == fn_proto->params.length && fn_table_entry->want_pure == WantPureTrue;
892}
893
894// fn_table_entry is populated if and only if there is a function definition for this prototype
865895static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *import, BlockContext *context,
866 TypeTableEntry *expected_type, AstNode *node, bool is_naked, bool is_cold)
896 TypeTableEntry *expected_type, AstNode *node, bool is_naked, bool is_cold, FnTableEntry *fn_table_entry)
867897{
868898 assert(node->type == NodeTypeFnProto);
869899 AstNodeFnProto *fn_proto = &node->data.fn_proto;
......@@ -876,7 +906,6 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
876906 fn_type_id.is_extern = fn_proto->is_extern || (fn_proto->top_level_decl.visib_mod == VisibModExport);
877907 fn_type_id.is_naked = is_naked;
878908 fn_type_id.is_cold = is_cold;
879 fn_type_id.is_inline = fn_proto->is_inline;
880909 fn_type_id.param_count = fn_proto->params.length;
881910
882911 if (fn_type_id.param_count > fn_type_id_prealloc_param_info_count) {
......@@ -902,14 +931,6 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
902931 buf_sprintf("return type '%s' not allowed", buf_ptr(&fn_type_id.return_type->name)));
903932 break;
904933 case TypeTableEntryIdMetaType:
905 if (!fn_proto->is_inline) {
906 fn_proto->skip = true;
907 add_node_error(g, fn_proto->return_type,
908 buf_sprintf("function with return type '%s' must be declared inline",
909 buf_ptr(&fn_type_id.return_type->name)));
910 return g->builtin_types.entry_invalid;
911 }
912 break;
913934 case TypeTableEntryIdUnreachable:
914935 case TypeTableEntryIdVoid:
915936 case TypeTableEntryIdBool:
......@@ -984,7 +1005,33 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
9841005 return g->builtin_types.entry_invalid;
9851006 }
9861007
987 return get_fn_type(g, &fn_type_id);
1008 if (fn_table_entry && fn_type_id.return_type->id == TypeTableEntryIdMetaType) {
1009 fn_table_entry->want_pure = WantPureTrue;
1010 fn_table_entry->want_pure_return_type = fn_proto->return_type;
1011
1012 ErrorMsg *err_msg = nullptr;
1013 for (int i = 0; i < fn_proto->params.length; i += 1) {
1014 AstNode *param_decl_node = fn_proto->params.at(i);
1015 assert(param_decl_node->type == NodeTypeParamDecl);
1016 if (!param_decl_node->data.param_decl.is_inline) {
1017 if (!err_msg) {
1018 err_msg = add_node_error(g, fn_proto->return_type,
1019 buf_sprintf("function with return type '%s' must declare all parameters inline",
1020 buf_ptr(&fn_type_id.return_type->name)));
1021 }
1022 add_error_note(g, err_msg, param_decl_node,
1023 buf_sprintf("non-inline parameter here"));
1024 }
1025 }
1026 if (err_msg) {
1027 fn_proto->skip = true;
1028 return g->builtin_types.entry_invalid;
1029 }
1030 }
1031
1032
1033 bool gen_debug_info = !(fn_table_entry && fn_wants_full_static_eval(fn_table_entry));
1034 return get_fn_type(g, &fn_type_id, gen_debug_info);
9881035}
9891036
9901037static Buf *resolve_const_expr_str(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode **node) {
......@@ -1110,10 +1157,12 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
11101157 bool enable;
11111158 bool ok = resolve_const_expr_bool(g, import, import->block_context,
11121159 &directive_node->data.directive.expr, &enable);
1113 if (!enable || !ok) {
1160 if (!ok || !enable) {
11141161 fn_table_entry->want_pure = WantPureFalse;
1162 } else if (ok && enable) {
1163 fn_table_entry->want_pure = WantPureTrue;
1164 fn_table_entry->want_pure_attr_node = directive_node->data.directive.expr;
11151165 }
1116 // TODO cause compile error if enable is true and impure fn
11171166 }
11181167 } else {
11191168 add_node_error(g, directive_node,
......@@ -1129,21 +1178,24 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
11291178
11301179
11311180 TypeTableEntry *fn_type = analyze_fn_proto_type(g, import, containing_context, nullptr, node,
1132 is_naked, is_cold);
1181 is_naked, is_cold, fn_table_entry);
11331182
11341183 fn_table_entry->type_entry = fn_type;
11351184 fn_table_entry->is_test = is_test;
1136 fn_table_entry->is_noinline = is_noinline;
11371185
11381186 if (fn_type->id == TypeTableEntryIdInvalid) {
11391187 fn_proto->skip = true;
11401188 return;
11411189 }
11421190
1143 if (fn_proto->is_inline && fn_table_entry->is_noinline) {
1191 if (fn_proto->is_inline && is_noinline) {
11441192 add_node_error(g, node, buf_sprintf("function is both inline and noinline"));
11451193 fn_proto->skip = true;
11461194 return;
1195 } else if (fn_proto->is_inline) {
1196 fn_table_entry->fn_inline = FnInlineAlways;
1197 } else if (is_noinline) {
1198 fn_table_entry->fn_inline = FnInlineNever;
11471199 }
11481200
11491201
......@@ -1159,48 +1211,54 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
11591211 fn_table_entry->fn_def_node->data.fn_def.block_context = context;
11601212 }
11611213
1162 fn_table_entry->fn_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_type->data.fn.raw_type_ref);
1163
1164 if (fn_proto->is_inline) {
1165 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMAlwaysInlineAttribute);
1166 }
1167 if (fn_table_entry->is_noinline) {
1168 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoInlineAttribute);
1169 }
1170 if (fn_type->data.fn.fn_type_id.is_naked) {
1171 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNakedAttribute);
1172 }
1214 if (!fn_wants_full_static_eval(fn_table_entry)) {
1215 fn_table_entry->fn_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_type->data.fn.raw_type_ref);
11731216
1174 LLVMSetLinkage(fn_table_entry->fn_value, fn_table_entry->internal_linkage ?
1175 LLVMInternalLinkage : LLVMExternalLinkage);
1217 switch (fn_table_entry->fn_inline) {
1218 case FnInlineAlways:
1219 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMAlwaysInlineAttribute);
1220 break;
1221 case FnInlineNever:
1222 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoInlineAttribute);
1223 break;
1224 case FnInlineAuto:
1225 break;
1226 }
1227 if (fn_type->data.fn.fn_type_id.is_naked) {
1228 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNakedAttribute);
1229 }
11761230
1177 if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdUnreachable) {
1178 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoReturnAttribute);
1179 }
1180 LLVMSetFunctionCallConv(fn_table_entry->fn_value, fn_type->data.fn.calling_convention);
1181 if (!fn_table_entry->is_extern) {
1182 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoUnwindAttribute);
1183 }
1184 if (!g->is_release_build && !fn_proto->is_inline) {
1185 ZigLLVMAddFunctionAttr(fn_table_entry->fn_value, "no-frame-pointer-elim", "true");
1186 ZigLLVMAddFunctionAttr(fn_table_entry->fn_value, "no-frame-pointer-elim-non-leaf", nullptr);
1187 }
1231 LLVMSetLinkage(fn_table_entry->fn_value, fn_table_entry->internal_linkage ?
1232 LLVMInternalLinkage : LLVMExternalLinkage);
11881233
1189 if (fn_table_entry->fn_def_node) {
1190 // Add debug info.
1191 unsigned line_number = node->line + 1;
1192 unsigned scope_line = line_number;
1193 bool is_definition = fn_table_entry->fn_def_node != nullptr;
1194 unsigned flags = 0;
1195 bool is_optimized = g->is_release_build;
1196 LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder,
1197 containing_context->di_scope, buf_ptr(&fn_table_entry->symbol_name), "",
1198 import->di_file, line_number,
1199 fn_type->di_type, fn_table_entry->internal_linkage,
1200 is_definition, scope_line, flags, is_optimized, nullptr);
1234 if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdUnreachable) {
1235 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoReturnAttribute);
1236 }
1237 LLVMSetFunctionCallConv(fn_table_entry->fn_value, fn_type->data.fn.calling_convention);
1238 if (!fn_table_entry->is_extern) {
1239 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoUnwindAttribute);
1240 }
1241 if (!g->is_release_build && !fn_proto->is_inline) {
1242 ZigLLVMAddFunctionAttr(fn_table_entry->fn_value, "no-frame-pointer-elim", "true");
1243 ZigLLVMAddFunctionAttr(fn_table_entry->fn_value, "no-frame-pointer-elim-non-leaf", nullptr);
1244 }
12011245
1202 fn_table_entry->fn_def_node->data.fn_def.block_context->di_scope = LLVMZigSubprogramToScope(subprogram);
1203 ZigLLVMFnSetSubprogram(fn_table_entry->fn_value, subprogram);
1246 if (fn_table_entry->fn_def_node) {
1247 // Add debug info.
1248 unsigned line_number = node->line + 1;
1249 unsigned scope_line = line_number;
1250 bool is_definition = fn_table_entry->fn_def_node != nullptr;
1251 unsigned flags = 0;
1252 bool is_optimized = g->is_release_build;
1253 LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder,
1254 containing_context->di_scope, buf_ptr(&fn_table_entry->symbol_name), "",
1255 import->di_file, line_number,
1256 fn_type->di_type, fn_table_entry->internal_linkage,
1257 is_definition, scope_line, flags, is_optimized, nullptr);
1258
1259 fn_table_entry->fn_def_node->data.fn_def.block_context->di_scope = LLVMZigSubprogramToScope(subprogram);
1260 ZigLLVMFnSetSubprogram(fn_table_entry->fn_value, subprogram);
1261 }
12041262 }
12051263}
12061264
......@@ -1609,29 +1667,31 @@ static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstN
16091667
16101668
16111669 } else {
1612 g->fn_protos.append(fn_table_entry);
1670 resolve_function_proto(g, proto_node, fn_table_entry, import, containing_context);
16131671
1614 if (fn_def_node) {
1615 g->fn_defs.append(fn_table_entry);
1616 }
1672 if (!fn_wants_full_static_eval(fn_table_entry)) {
1673 g->fn_protos.append(fn_table_entry);
16171674
1618 bool is_main_fn = !is_generic_instance &&
1619 !parent_decl && (import == g->root_import) &&
1620 buf_eql_str(proto_name, "main");
1621 if (is_main_fn) {
1622 g->main_fn = fn_table_entry;
1623 }
1675 if (fn_def_node) {
1676 g->fn_defs.append(fn_table_entry);
1677 }
16241678
1625 resolve_function_proto(g, proto_node, fn_table_entry, import, containing_context);
1679 bool is_main_fn = !is_generic_instance &&
1680 !parent_decl && (import == g->root_import) &&
1681 buf_eql_str(proto_name, "main");
1682 if (is_main_fn) {
1683 g->main_fn = fn_table_entry;
1684 }
16261685
1627 if (is_main_fn && !g->link_libc) {
1628 TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void);
1629 TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
1630 if (actual_return_type != err_void) {
1631 AstNode *return_type_node = fn_table_entry->proto_node->data.fn_proto.return_type;
1632 add_node_error(g, return_type_node,
1633 buf_sprintf("expected return type of main to be '%%void', instead is '%s'",
1634 buf_ptr(&actual_return_type->name)));
1686 if (is_main_fn && !g->link_libc) {
1687 TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void);
1688 TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
1689 if (actual_return_type != err_void) {
1690 AstNode *return_type_node = fn_table_entry->proto_node->data.fn_proto.return_type;
1691 add_node_error(g, return_type_node,
1692 buf_sprintf("expected return type of main to be '%%void', instead is '%s'",
1693 buf_ptr(&actual_return_type->name)));
1694 }
16351695 }
16361696 }
16371697 }
......@@ -3022,7 +3082,7 @@ static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, Variabl
30223082{
30233083 get_resolved_expr(source_node)->variable = var;
30243084 if (!var_is_pure(var, context)) {
3025 mark_impure_fn(context);
3085 mark_impure_fn(g, context, source_node);
30263086 }
30273087 if (var->is_const && var->val_node) {
30283088 ConstExprValue *other_const_val = &get_resolved_expr(var->val_node)->const_val;
......@@ -3102,7 +3162,7 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import,
31023162 return g->builtin_types.entry_invalid;
31033163 }
31043164
3105 mark_impure_fn(context);
3165 mark_impure_fn(g, context, node);
31063166 add_node_error(g, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
31073167 return g->builtin_types.entry_invalid;
31083168}
......@@ -3943,7 +4003,8 @@ static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import,
39434003static TypeTableEntry *analyze_fn_proto_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
39444004 TypeTableEntry *expected_type, AstNode *node)
39454005{
3946 TypeTableEntry *type_entry = analyze_fn_proto_type(g, import, context, expected_type, node, false, false);
4006 TypeTableEntry *type_entry = analyze_fn_proto_type(g, import, context, expected_type, node,
4007 false, false, nullptr);
39474008
39484009 if (type_entry->id == TypeTableEntryIdInvalid) {
39494010 return type_entry;
......@@ -4386,7 +4447,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
43864447 (wanted_type->data.structure.fields[0].type_entry->data.pointer.is_const ||
43874448 !actual_type->data.structure.fields[0].type_entry->data.pointer.is_const))
43884449 {
4389 mark_impure_fn(context);
4450 mark_impure_fn(g, context, node);
43904451 return resolve_cast(g, context, node, expr_node, wanted_type, CastOpResizeSlice, true);
43914452 }
43924453
......@@ -4395,7 +4456,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
43954456 actual_type->id == TypeTableEntryIdArray &&
43964457 is_u8(actual_type->data.array.child_type))
43974458 {
4398 mark_impure_fn(context);
4459 mark_impure_fn(g, context, node);
43994460 uint64_t child_type_size = type_size(g,
44004461 wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type);
44014462 if (actual_type->data.array.len % child_type_size == 0) {
......@@ -5276,11 +5337,11 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
52765337 case BuiltinFnIdErrName:
52775338 return analyze_err_name(g, import, context, node);
52785339 case BuiltinFnIdBreakpoint:
5279 mark_impure_fn(context);
5340 mark_impure_fn(g, context, node);
52805341 return g->builtin_types.entry_void;
52815342 case BuiltinFnIdReturnAddress:
52825343 case BuiltinFnIdFrameAddress:
5283 mark_impure_fn(context);
5344 mark_impure_fn(g, context, node);
52845345 return builtin_fn->return_type;
52855346 case BuiltinFnIdEmbedFile:
52865347 return analyze_embed_file(g, import, context, node);
......@@ -5388,6 +5449,9 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,
53885449 if (ok_invocation && fn_table_entry && fn_table_entry->is_pure && fn_table_entry->want_pure != WantPureFalse) {
53895450 if (fn_table_entry->anal_state == FnAnalStateReady) {
53905451 analyze_fn_body(g, fn_table_entry);
5452 if (fn_table_entry->proto_node->data.fn_proto.skip) {
5453 return g->builtin_types.entry_invalid;
5454 }
53915455 }
53925456 if (all_args_const_expr) {
53935457 if (fn_table_entry->is_pure && fn_table_entry->anal_state == FnAnalStateComplete) {
......@@ -5401,7 +5465,10 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,
54015465 }
54025466 if (!ok_invocation || !fn_table_entry || !fn_table_entry->is_pure || fn_table_entry->want_pure == WantPureFalse) {
54035467 // calling an impure fn is impure
5404 mark_impure_fn(context);
5468 mark_impure_fn(g, context, node);
5469 if (fn_table_entry && fn_table_entry->want_pure == WantPureTrue) {
5470 return g->builtin_types.entry_invalid;
5471 }
54055472 }
54065473
54075474 if (handle_is_ptr(return_type)) {
......@@ -6298,7 +6365,7 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import,
62986365static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
62996366 TypeTableEntry *expected_type, AstNode *node)
63006367{
6301 mark_impure_fn(context);
6368 mark_impure_fn(g, context, node);
63026369
63036370 node->data.asm_expr.return_count = 0;
63046371 TypeTableEntry *return_type = g->builtin_types.entry_void;
src/analyze.hpp+1-1
......@@ -24,7 +24,7 @@ TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits);
2424TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type);
2525TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type);
2626TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *child_type);
27TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id);
27TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id, bool gen_debug_info);
2828TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type);
2929TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size);
3030TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_const);
src/parseh.cpp+1-1
......@@ -600,7 +600,7 @@ static TypeTableEntry *resolve_type_with_table(Context *c, const Type *ty, const
600600 param_info->is_noalias = qt.isRestrictQualified();
601601 }
602602
603 return get_fn_type(c->codegen, &fn_type_id);
603 return get_fn_type(c->codegen, &fn_type_id, true);
604604 }
605605 case Type::Record:
606606 {
std/hash_map.zig+3-3
......@@ -6,8 +6,8 @@ const Allocator = mem.Allocator;
66const want_modification_safety = !@compile_var("is_release");
77const debug_u32 = if (want_modification_safety) u32 else void;
88
9pub inline fn HashMap(inline K: type, inline V: type,
10 inline hash: fn(key: K)->u32, inline eql: fn(a: K, b: K)->bool)
9pub fn HashMap(inline K: type, inline V: type, inline hash: fn(key: K)->u32,
10 inline eql: fn(a: K, b: K)->bool) -> type
1111{
1212 SmallHashMap(K, V, hash, eql, 8)
1313}
......@@ -258,7 +258,7 @@ fn global_free(self: &Allocator, old_mem: []u8) {
258258
259259#attribute("test")
260260fn basic_hash_map_test() {
261 var map: SmallHashMap(i32, i32, hash_i32, eql_i32, 4) = undefined;
261 var map: HashMap(i32, i32, hash_i32, eql_i32) = undefined;
262262 map.init(&global_allocator);
263263 defer map.deinit();
264264
std/list.zig+2-2
......@@ -2,7 +2,7 @@ const assert = @import("debug.zig").assert;
22const mem = @import("mem.zig");
33const Allocator = mem.Allocator;
44
5pub inline fn List(inline T: type) -> type {
5pub fn List(inline T: type) -> type {
66 SmallList(T, 8)
77}
88
......@@ -77,7 +77,7 @@ fn global_free(self: &Allocator, old_mem: []u8) {
7777
7878#attribute("test")
7979fn basic_list_test() {
80 var list: SmallList(i32, 4) = undefined;
80 var list: List(i32) = undefined;
8181 list.init(&global_allocator);
8282 defer list.deinit();
8383
test/cases/return_type_type.zig created+21
......@@ -0,0 +1,21 @@
1const assert = @import("std").debug.assert;
2
3pub fn List(inline T: type) -> type {
4 SmallList(T, 8)
5}
6
7pub struct SmallList(inline T: type, inline STATIC_SIZE: usize) {
8 items: []T,
9 length: usize,
10 prealloc_items: [STATIC_SIZE]T,
11}
12
13#attribute("test")
14fn function_with_return_type_type() {
15 var list: List(i32) = undefined;
16 var list2: List(i32) = undefined;
17 list.length = 10;
18 list2.length = 10;
19 assert(list.prealloc_items.len == 8);
20 assert(list2.prealloc_items.len == 8);
21}
test/run_tests.cpp+24
......@@ -1427,6 +1427,30 @@ fn f() {
14271427 var foo = ([]u32)(array)[0];
14281428}
14291429 )SOURCE", 1, ".tmp_source.zig:4:22: error: unable to convert [5]u8 to []u32: size mismatch");
1430
1431 add_compile_fail_case("non-pure function returns type", R"SOURCE(
1432var a: u32 = 0;
1433pub fn List(inline T: type) -> type {
1434 a += 1;
1435 SmallList(T, 8)
1436}
1437
1438pub struct SmallList(inline T: type, inline STATIC_SIZE: usize) {
1439 items: []T,
1440 length: usize,
1441 prealloc_items: [STATIC_SIZE]T,
1442}
1443
1444#attribute("test")
1445fn function_with_return_type_type() {
1446 var list: List(i32) = undefined;
1447 list.length = 10;
1448}
1449
1450 )SOURCE", 3,
1451 ".tmp_source.zig:3:5: error: failed to evaluate function at compile time",
1452 ".tmp_source.zig:4:5: note: unable to evaluate this expression at compile time",
1453 ".tmp_source.zig:3:32: note: required to be compile-time function because of return type 'type'");
14301454}
14311455
14321456//////////////////////////////////////////////////////////////////////////////
test/self_hosted.zig+1
......@@ -3,6 +3,7 @@ const assert = std.debug.assert;
33const str = std.str;
44const cstr = std.cstr;
55const other = @import("other.zig");
6const cases_return_type_type = @import("cases/return_type_type.zig");
67
78// normal comment
89/// this is a documentation comment