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 {...@@ -843,7 +843,6 @@ struct FnTypeId {
843 bool is_naked;843 bool is_naked;
844 bool is_cold;844 bool is_cold;
845 bool is_extern;845 bool is_extern;
846 bool is_inline;
847 FnTypeParamInfo prealloc_param_info[fn_type_id_prealloc_param_info_count];846 FnTypeParamInfo prealloc_param_info[fn_type_id_prealloc_param_info_count];
848};847};
849848
...@@ -1055,6 +1054,12 @@ enum WantPure {...@@ -1055,6 +1054,12 @@ enum WantPure {
1055 WantPureTrue,1054 WantPureTrue,
1056};1055};
10571056
1057enum FnInline {
1058 FnInlineAuto,
1059 FnInlineAlways,
1060 FnInlineNever,
1061};
1062
1058struct FnTableEntry {1063struct FnTableEntry {
1059 LLVMValueRef fn_value;1064 LLVMValueRef fn_value;
1060 AstNode *proto_node;1065 AstNode *proto_node;
...@@ -1070,8 +1075,10 @@ struct FnTableEntry {...@@ -1070,8 +1075,10 @@ struct FnTableEntry {
1070 bool is_test;1075 bool is_test;
1071 bool is_pure;1076 bool is_pure;
1072 WantPure want_pure;1077 WantPure want_pure;
1078 AstNode *want_pure_attr_node;
1079 AstNode *want_pure_return_type;
1073 bool safety_off;1080 bool safety_off;
1074 bool is_noinline;1081 FnInline fn_inline;
1075 BlockContext *parent_block_context;1082 BlockContext *parent_block_context;
1076 FnAnalState anal_state;1083 FnAnalState anal_state;
10771084
src/analyze.cpp+205-138
...@@ -113,9 +113,30 @@ static AstNode *first_executing_node(AstNode *node) {...@@ -113,9 +113,30 @@ static AstNode *first_executing_node(AstNode *node) {
113 zig_unreachable();113 zig_unreachable();
114}114}
115115
116static void mark_impure_fn(BlockContext *context) {116static void mark_impure_fn(CodeGen *g, BlockContext *context, AstNode *node) {
117 if (context->fn_entry) {117 if (!context->fn_entry) return;
118 context->fn_entry->is_pure = false;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 }
119 }140 }
120}141}
121142
...@@ -659,7 +680,7 @@ TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *...@@ -659,7 +680,7 @@ TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *
659}680}
660681
661// accepts ownership of fn_type_id memory682// 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) {
663 auto table_entry = g->fn_type_table.maybe_get(fn_type_id);684 auto table_entry = g->fn_type_table.maybe_get(fn_type_id);
664 if (table_entry) {685 if (table_entry) {
665 return table_entry->value;686 return table_entry->value;
...@@ -704,65 +725,67 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {...@@ -704,65 +725,67 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
704 buf_appendf(&fn_type->name, " -> %s", buf_ptr(&fn_type_id->return_type->name));725 buf_appendf(&fn_type->name, " -> %s", buf_ptr(&fn_type_id->return_type->name));
705 }726 }
706727
707 // next, loop over the parameters again and compute debug information728 if (gen_debug_info) {
708 // and codegen information729 // next, loop over the parameters again and compute debug information
709 bool first_arg_return = !fn_type_id->is_extern && handle_is_ptr(fn_type_id->return_type);730 // and codegen information
710 // +1 for maybe making the first argument the return value731 bool first_arg_return = !fn_type_id->is_extern && handle_is_ptr(fn_type_id->return_type);
711 LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(1 + fn_type_id->param_count);732 // +1 for maybe making the first argument the return value
712 // +1 because 0 is the return type and +1 for maybe making first arg ret val733 LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(1 + fn_type_id->param_count);
713 LLVMZigDIType **param_di_types = allocate<LLVMZigDIType*>(2 + fn_type_id->param_count);734 // +1 because 0 is the return type and +1 for maybe making first arg ret val
714 param_di_types[0] = fn_type_id->return_type->di_type;735 LLVMZigDIType **param_di_types = allocate<LLVMZigDIType*>(2 + fn_type_id->param_count);
715 int gen_param_index = 0;736 param_di_types[0] = fn_type_id->return_type->di_type;
716 TypeTableEntry *gen_return_type;737 int gen_param_index = 0;
717 if (!type_has_bits(fn_type_id->return_type)) {738 TypeTableEntry *gen_return_type;
718 gen_return_type = g->builtin_types.entry_void;739 if (!type_has_bits(fn_type_id->return_type)) {
719 } else if (first_arg_return) {740 gen_return_type = g->builtin_types.entry_void;
720 TypeTableEntry *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false);741 } else if (first_arg_return) {
721 gen_param_types[gen_param_index] = gen_type->type_ref;742 TypeTableEntry *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false);
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 }
749 gen_param_types[gen_param_index] = gen_type->type_ref;743 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
753 gen_param_index += 1;744 gen_param_index += 1;
754
755 // after the gen_param_index += 1 because 0 is the return type745 // after the gen_param_index += 1 because 0 is the return type
756 param_di_types[gen_param_index] = gen_type->di_type;746 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;
757 }750 }
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,782 fn_type->data.fn.gen_param_count = gen_param_index;
763 gen_param_types, gen_param_index, fn_type_id->is_var_args);783
764 fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0);784 fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref,
765 fn_type->di_type = LLVMZigCreateSubroutineType(g->dbuilder, param_di_types, gen_param_index + 1, 0);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
767 g->fn_type_table.put(&fn_type->data.fn.fn_type_id, fn_type);790 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...@@ -862,8 +885,15 @@ static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, B
862 return analyze_type_expr_pointer_only(g, import, context, node, false);885 return analyze_type_expr_pointer_only(g, import, context, node, false);
863}886}
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
865static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *import, BlockContext *context,895static 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)
867{897{
868 assert(node->type == NodeTypeFnProto);898 assert(node->type == NodeTypeFnProto);
869 AstNodeFnProto *fn_proto = &node->data.fn_proto;899 AstNodeFnProto *fn_proto = &node->data.fn_proto;
...@@ -876,7 +906,6 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor...@@ -876,7 +906,6 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
876 fn_type_id.is_extern = fn_proto->is_extern || (fn_proto->top_level_decl.visib_mod == VisibModExport);906 fn_type_id.is_extern = fn_proto->is_extern || (fn_proto->top_level_decl.visib_mod == VisibModExport);
877 fn_type_id.is_naked = is_naked;907 fn_type_id.is_naked = is_naked;
878 fn_type_id.is_cold = is_cold;908 fn_type_id.is_cold = is_cold;
879 fn_type_id.is_inline = fn_proto->is_inline;
880 fn_type_id.param_count = fn_proto->params.length;909 fn_type_id.param_count = fn_proto->params.length;
881910
882 if (fn_type_id.param_count > fn_type_id_prealloc_param_info_count) {911 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...@@ -902,14 +931,6 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
902 buf_sprintf("return type '%s' not allowed", buf_ptr(&fn_type_id.return_type->name)));931 buf_sprintf("return type '%s' not allowed", buf_ptr(&fn_type_id.return_type->name)));
903 break;932 break;
904 case TypeTableEntryIdMetaType:933 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;
913 case TypeTableEntryIdUnreachable:934 case TypeTableEntryIdUnreachable:
914 case TypeTableEntryIdVoid:935 case TypeTableEntryIdVoid:
915 case TypeTableEntryIdBool:936 case TypeTableEntryIdBool:
...@@ -984,7 +1005,33 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor...@@ -984,7 +1005,33 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
984 return g->builtin_types.entry_invalid;1005 return g->builtin_types.entry_invalid;
985 }1006 }
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);
988}1035}
9891036
990static Buf *resolve_const_expr_str(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode **node) {1037static 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...@@ -1110,10 +1157,12 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
1110 bool enable;1157 bool enable;
1111 bool ok = resolve_const_expr_bool(g, import, import->block_context,1158 bool ok = resolve_const_expr_bool(g, import, import->block_context,
1112 &directive_node->data.directive.expr, &enable);1159 &directive_node->data.directive.expr, &enable);
1113 if (!enable || !ok) {1160 if (!ok || !enable) {
1114 fn_table_entry->want_pure = WantPureFalse;1161 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;
1115 }1165 }
1116 // TODO cause compile error if enable is true and impure fn
1117 }1166 }
1118 } else {1167 } else {
1119 add_node_error(g, directive_node,1168 add_node_error(g, directive_node,
...@@ -1129,21 +1178,24 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t...@@ -1129,21 +1178,24 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
11291178
11301179
1131 TypeTableEntry *fn_type = analyze_fn_proto_type(g, import, containing_context, nullptr, node,1180 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
1134 fn_table_entry->type_entry = fn_type;1183 fn_table_entry->type_entry = fn_type;
1135 fn_table_entry->is_test = is_test;1184 fn_table_entry->is_test = is_test;
1136 fn_table_entry->is_noinline = is_noinline;
11371185
1138 if (fn_type->id == TypeTableEntryIdInvalid) {1186 if (fn_type->id == TypeTableEntryIdInvalid) {
1139 fn_proto->skip = true;1187 fn_proto->skip = true;
1140 return;1188 return;
1141 }1189 }
11421190
1143 if (fn_proto->is_inline && fn_table_entry->is_noinline) {1191 if (fn_proto->is_inline && is_noinline) {
1144 add_node_error(g, node, buf_sprintf("function is both inline and noinline"));1192 add_node_error(g, node, buf_sprintf("function is both inline and noinline"));
1145 fn_proto->skip = true;1193 fn_proto->skip = true;
1146 return;1194 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;
1147 }1199 }
11481200
11491201
...@@ -1159,48 +1211,54 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t...@@ -1159,48 +1211,54 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
1159 fn_table_entry->fn_def_node->data.fn_def.block_context = context;1211 fn_table_entry->fn_def_node->data.fn_def.block_context = context;
1160 }1212 }
11611213
1162 fn_table_entry->fn_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_type->data.fn.raw_type_ref);1214 if (!fn_wants_full_static_eval(fn_table_entry)) {
11631215 fn_table_entry->fn_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_type->data.fn.raw_type_ref);
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 }
11731216
1174 LLVMSetLinkage(fn_table_entry->fn_value, fn_table_entry->internal_linkage ?1217 switch (fn_table_entry->fn_inline) {
1175 LLVMInternalLinkage : LLVMExternalLinkage);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) {1231 LLVMSetLinkage(fn_table_entry->fn_value, fn_table_entry->internal_linkage ?
1178 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoReturnAttribute);1232 LLVMInternalLinkage : LLVMExternalLinkage);
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 }
11881233
1189 if (fn_table_entry->fn_def_node) {1234 if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdUnreachable) {
1190 // Add debug info.1235 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoReturnAttribute);
1191 unsigned line_number = node->line + 1;1236 }
1192 unsigned scope_line = line_number;1237 LLVMSetFunctionCallConv(fn_table_entry->fn_value, fn_type->data.fn.calling_convention);
1193 bool is_definition = fn_table_entry->fn_def_node != nullptr;1238 if (!fn_table_entry->is_extern) {
1194 unsigned flags = 0;1239 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoUnwindAttribute);
1195 bool is_optimized = g->is_release_build;1240 }
1196 LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder,1241 if (!g->is_release_build && !fn_proto->is_inline) {
1197 containing_context->di_scope, buf_ptr(&fn_table_entry->symbol_name), "",1242 ZigLLVMAddFunctionAttr(fn_table_entry->fn_value, "no-frame-pointer-elim", "true");
1198 import->di_file, line_number,1243 ZigLLVMAddFunctionAttr(fn_table_entry->fn_value, "no-frame-pointer-elim-non-leaf", nullptr);
1199 fn_type->di_type, fn_table_entry->internal_linkage,1244 }
1200 is_definition, scope_line, flags, is_optimized, nullptr);
12011245
1202 fn_table_entry->fn_def_node->data.fn_def.block_context->di_scope = LLVMZigSubprogramToScope(subprogram);1246 if (fn_table_entry->fn_def_node) {
1203 ZigLLVMFnSetSubprogram(fn_table_entry->fn_value, subprogram);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 }
1204 }1262 }
1205}1263}
12061264
...@@ -1609,29 +1667,31 @@ static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstN...@@ -1609,29 +1667,31 @@ static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstN
16091667
16101668
1611 } else {1669 } 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) {1672 if (!fn_wants_full_static_eval(fn_table_entry)) {
1615 g->fn_defs.append(fn_table_entry);1673 g->fn_protos.append(fn_table_entry);
1616 }
16171674
1618 bool is_main_fn = !is_generic_instance &&1675 if (fn_def_node) {
1619 !parent_decl && (import == g->root_import) &&1676 g->fn_defs.append(fn_table_entry);
1620 buf_eql_str(proto_name, "main");1677 }
1621 if (is_main_fn) {
1622 g->main_fn = fn_table_entry;
1623 }
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) {1686 if (is_main_fn && !g->link_libc) {
1628 TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void);1687 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;1688 TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
1630 if (actual_return_type != err_void) {1689 if (actual_return_type != err_void) {
1631 AstNode *return_type_node = fn_table_entry->proto_node->data.fn_proto.return_type;1690 AstNode *return_type_node = fn_table_entry->proto_node->data.fn_proto.return_type;
1632 add_node_error(g, return_type_node,1691 add_node_error(g, return_type_node,
1633 buf_sprintf("expected return type of main to be '%%void', instead is '%s'",1692 buf_sprintf("expected return type of main to be '%%void', instead is '%s'",
1634 buf_ptr(&actual_return_type->name)));1693 buf_ptr(&actual_return_type->name)));
1694 }
1635 }1695 }
1636 }1696 }
1637 }1697 }
...@@ -3022,7 +3082,7 @@ static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, Variabl...@@ -3022,7 +3082,7 @@ static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, Variabl
3022{3082{
3023 get_resolved_expr(source_node)->variable = var;3083 get_resolved_expr(source_node)->variable = var;
3024 if (!var_is_pure(var, context)) {3084 if (!var_is_pure(var, context)) {
3025 mark_impure_fn(context);3085 mark_impure_fn(g, context, source_node);
3026 }3086 }
3027 if (var->is_const && var->val_node) {3087 if (var->is_const && var->val_node) {
3028 ConstExprValue *other_const_val = &get_resolved_expr(var->val_node)->const_val;3088 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,...@@ -3102,7 +3162,7 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import,
3102 return g->builtin_types.entry_invalid;3162 return g->builtin_types.entry_invalid;
3103 }3163 }
31043164
3105 mark_impure_fn(context);3165 mark_impure_fn(g, context, node);
3106 add_node_error(g, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));3166 add_node_error(g, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
3107 return g->builtin_types.entry_invalid;3167 return g->builtin_types.entry_invalid;
3108}3168}
...@@ -3943,7 +4003,8 @@ static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import,...@@ -3943,7 +4003,8 @@ static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import,
3943static TypeTableEntry *analyze_fn_proto_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,4003static TypeTableEntry *analyze_fn_proto_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
3944 TypeTableEntry *expected_type, AstNode *node)4004 TypeTableEntry *expected_type, AstNode *node)
3945{4005{
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
3948 if (type_entry->id == TypeTableEntryIdInvalid) {4009 if (type_entry->id == TypeTableEntryIdInvalid) {
3949 return type_entry;4010 return type_entry;
...@@ -4386,7 +4447,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B...@@ -4386,7 +4447,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
4386 (wanted_type->data.structure.fields[0].type_entry->data.pointer.is_const ||4447 (wanted_type->data.structure.fields[0].type_entry->data.pointer.is_const ||
4387 !actual_type->data.structure.fields[0].type_entry->data.pointer.is_const))4448 !actual_type->data.structure.fields[0].type_entry->data.pointer.is_const))
4388 {4449 {
4389 mark_impure_fn(context);4450 mark_impure_fn(g, context, node);
4390 return resolve_cast(g, context, node, expr_node, wanted_type, CastOpResizeSlice, true);4451 return resolve_cast(g, context, node, expr_node, wanted_type, CastOpResizeSlice, true);
4391 }4452 }
43924453
...@@ -4395,7 +4456,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B...@@ -4395,7 +4456,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
4395 actual_type->id == TypeTableEntryIdArray &&4456 actual_type->id == TypeTableEntryIdArray &&
4396 is_u8(actual_type->data.array.child_type))4457 is_u8(actual_type->data.array.child_type))
4397 {4458 {
4398 mark_impure_fn(context);4459 mark_impure_fn(g, context, node);
4399 uint64_t child_type_size = type_size(g,4460 uint64_t child_type_size = type_size(g,
4400 wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type);4461 wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type);
4401 if (actual_type->data.array.len % child_type_size == 0) {4462 if (actual_type->data.array.len % child_type_size == 0) {
...@@ -5276,11 +5337,11 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry...@@ -5276,11 +5337,11 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
5276 case BuiltinFnIdErrName:5337 case BuiltinFnIdErrName:
5277 return analyze_err_name(g, import, context, node);5338 return analyze_err_name(g, import, context, node);
5278 case BuiltinFnIdBreakpoint:5339 case BuiltinFnIdBreakpoint:
5279 mark_impure_fn(context);5340 mark_impure_fn(g, context, node);
5280 return g->builtin_types.entry_void;5341 return g->builtin_types.entry_void;
5281 case BuiltinFnIdReturnAddress:5342 case BuiltinFnIdReturnAddress:
5282 case BuiltinFnIdFrameAddress:5343 case BuiltinFnIdFrameAddress:
5283 mark_impure_fn(context);5344 mark_impure_fn(g, context, node);
5284 return builtin_fn->return_type;5345 return builtin_fn->return_type;
5285 case BuiltinFnIdEmbedFile:5346 case BuiltinFnIdEmbedFile:
5286 return analyze_embed_file(g, import, context, node);5347 return analyze_embed_file(g, import, context, node);
...@@ -5388,6 +5449,9 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,...@@ -5388,6 +5449,9 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,
5388 if (ok_invocation && fn_table_entry && fn_table_entry->is_pure && fn_table_entry->want_pure != WantPureFalse) {5449 if (ok_invocation && fn_table_entry && fn_table_entry->is_pure && fn_table_entry->want_pure != WantPureFalse) {
5389 if (fn_table_entry->anal_state == FnAnalStateReady) {5450 if (fn_table_entry->anal_state == FnAnalStateReady) {
5390 analyze_fn_body(g, fn_table_entry);5451 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 }
5391 }5455 }
5392 if (all_args_const_expr) {5456 if (all_args_const_expr) {
5393 if (fn_table_entry->is_pure && fn_table_entry->anal_state == FnAnalStateComplete) {5457 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,...@@ -5401,7 +5465,10 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,
5401 }5465 }
5402 if (!ok_invocation || !fn_table_entry || !fn_table_entry->is_pure || fn_table_entry->want_pure == WantPureFalse) {5466 if (!ok_invocation || !fn_table_entry || !fn_table_entry->is_pure || fn_table_entry->want_pure == WantPureFalse) {
5403 // calling an impure fn is impure5467 // 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 }
5405 }5472 }
54065473
5407 if (handle_is_ptr(return_type)) {5474 if (handle_is_ptr(return_type)) {
...@@ -6298,7 +6365,7 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import,...@@ -6298,7 +6365,7 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import,
6298static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,6365static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
6299 TypeTableEntry *expected_type, AstNode *node)6366 TypeTableEntry *expected_type, AstNode *node)
6300{6367{
6301 mark_impure_fn(context);6368 mark_impure_fn(g, context, node);
63026369
6303 node->data.asm_expr.return_count = 0;6370 node->data.asm_expr.return_count = 0;
6304 TypeTableEntry *return_type = g->builtin_types.entry_void;6371 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);...@@ -24,7 +24,7 @@ TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits);
24TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type);24TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type);
25TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type);25TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type);
26TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *child_type);26TypeTableEntry *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);
28TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type);28TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type);
29TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size);29TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size);
30TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_const);30TypeTableEntry *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...@@ -600,7 +600,7 @@ static TypeTableEntry *resolve_type_with_table(Context *c, const Type *ty, const
600 param_info->is_noalias = qt.isRestrictQualified();600 param_info->is_noalias = qt.isRestrictQualified();
601 }601 }
602602
603 return get_fn_type(c->codegen, &fn_type_id);603 return get_fn_type(c->codegen, &fn_type_id, true);
604 }604 }
605 case Type::Record:605 case Type::Record:
606 {606 {
std/hash_map.zig+3-3
...@@ -6,8 +6,8 @@ const Allocator = mem.Allocator;...@@ -6,8 +6,8 @@ const Allocator = mem.Allocator;
6const want_modification_safety = !@compile_var("is_release");6const want_modification_safety = !@compile_var("is_release");
7const debug_u32 = if (want_modification_safety) u32 else void;7const debug_u32 = if (want_modification_safety) u32 else void;
88
9pub inline fn HashMap(inline K: type, inline V: type,9pub fn HashMap(inline K: type, inline V: type, inline hash: fn(key: K)->u32,
10 inline hash: fn(key: K)->u32, inline eql: fn(a: K, b: K)->bool)10 inline eql: fn(a: K, b: K)->bool) -> type
11{11{
12 SmallHashMap(K, V, hash, eql, 8)12 SmallHashMap(K, V, hash, eql, 8)
13}13}
...@@ -258,7 +258,7 @@ fn global_free(self: &Allocator, old_mem: []u8) {...@@ -258,7 +258,7 @@ fn global_free(self: &Allocator, old_mem: []u8) {
258258
259#attribute("test")259#attribute("test")
260fn basic_hash_map_test() {260fn 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;
262 map.init(&global_allocator);262 map.init(&global_allocator);
263 defer map.deinit();263 defer map.deinit();
264264
std/list.zig+2-2
...@@ -2,7 +2,7 @@ const assert = @import("debug.zig").assert;...@@ -2,7 +2,7 @@ const assert = @import("debug.zig").assert;
2const mem = @import("mem.zig");2const mem = @import("mem.zig");
3const Allocator = mem.Allocator;3const Allocator = mem.Allocator;
44
5pub inline fn List(inline T: type) -> type {5pub fn List(inline T: type) -> type {
6 SmallList(T, 8)6 SmallList(T, 8)
7}7}
88
...@@ -77,7 +77,7 @@ fn global_free(self: &Allocator, old_mem: []u8) {...@@ -77,7 +77,7 @@ fn global_free(self: &Allocator, old_mem: []u8) {
7777
78#attribute("test")78#attribute("test")
79fn basic_list_test() {79fn basic_list_test() {
80 var list: SmallList(i32, 4) = undefined;80 var list: List(i32) = undefined;
81 list.init(&global_allocator);81 list.init(&global_allocator);
82 defer list.deinit();82 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() {...@@ -1427,6 +1427,30 @@ fn f() {
1427 var foo = ([]u32)(array)[0];1427 var foo = ([]u32)(array)[0];
1428}1428}
1429 )SOURCE", 1, ".tmp_source.zig:4:22: error: unable to convert [5]u8 to []u32: size mismatch");1429 )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'");
1430}1454}
14311455
1432//////////////////////////////////////////////////////////////////////////////1456//////////////////////////////////////////////////////////////////////////////
test/self_hosted.zig+1
...@@ -3,6 +3,7 @@ const assert = std.debug.assert;...@@ -3,6 +3,7 @@ const assert = std.debug.assert;
3const str = std.str;3const str = std.str;
4const cstr = std.cstr;4const cstr = std.cstr;
5const other = @import("other.zig");5const other = @import("other.zig");
6const cases_return_type_type = @import("cases/return_type_type.zig");
67
7// normal comment8// normal comment
8/// this is a documentation comment9/// this is a documentation comment