authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-01-22 22:24:07-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-01-22 22:24:07-05:00
logcf39819478e237255109d0343e642db70e88071b
tree6c8232d28a484989f43075efeaa5501d5e60154b
parentcacba6f4357fdec8db0ea792889c60022c39fbd3

add new kind of test: generating .h files. and more

* docgen supports obj_err code kind for demonstrating errors without explicit test cases * add documentation for `extern enum`. See #367 * remove coldcc keyword and add @setIsCold. See #661 * add compile errors for non-extern struct, enum, unions in function signatures * add .h file generation for extern struct, enum, unions

21 files changed, 682 insertions(+), 83 deletions(-)

build.zig+1
......@@ -118,6 +118,7 @@ pub fn build(b: &Builder) -> %void {
118118 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));
119119 test_step.dependOn(tests.addDebugSafetyTests(b, test_filter));
120120 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
121 test_step.dependOn(tests.addGenHTests(b, test_filter));
121122}
122123
123124fn dependOnLib(lib_exe_obj: &std.build.LibExeObjStep, dep: &const LibraryDep) {
doc/docgen.zig+38-6
......@@ -286,7 +286,7 @@ const Code = struct {
286286 TestError: []const u8,
287287 TestSafety: []const u8,
288288 Exe: ExpectedOutcome,
289 Obj,
289 Obj: ?[]const u8,
290290 };
291291};
292292
......@@ -442,9 +442,12 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) -> %Toc {
442442 code_kind_id = Code.Id { .TestSafety = name};
443443 name = "test";
444444 } else if (mem.eql(u8, code_kind_str, "obj")) {
445 code_kind_id = Code.Id.Obj;
445 code_kind_id = Code.Id { .Obj = null };
446 } else if (mem.eql(u8, code_kind_str, "obj_err")) {
447 code_kind_id = Code.Id { .Obj = name };
448 name = "test";
446449 } else if (mem.eql(u8, code_kind_str, "syntax")) {
447 code_kind_id = Code.Id.Obj;
450 code_kind_id = Code.Id { .Obj = null };
448451 is_inline = true;
449452 } else {
450453 return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {}", code_kind_str);
......@@ -861,13 +864,14 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io
861864 const colored_stderr = try termColor(allocator, escaped_stderr);
862865 try out.print("<pre><code class=\"shell\">$ zig test {}.zig\n{}</code></pre>\n", code.name, colored_stderr);
863866 },
864 Code.Id.Obj => {
867 Code.Id.Obj => |maybe_error_match| {
865868 const name_plus_obj_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, obj_ext);
866869 const tmp_obj_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_obj_ext);
867870 var build_args = std.ArrayList([]const u8).init(allocator);
868871 defer build_args.deinit();
869872
870873 try build_args.appendSlice([][]const u8 {zig_exe, "build-obj", tmp_source_file_name,
874 "--color", "on",
871875 "--output", tmp_obj_file_name});
872876
873877 if (!code.is_inline) {
......@@ -890,8 +894,36 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io
890894 },
891895 }
892896
893 _ = exec(allocator, build_args.toSliceConst()) catch return parseError(
894 tokenizer, code.source_token, "example failed to compile");
897 if (maybe_error_match) |error_match| {
898 const result = try os.ChildProcess.exec(allocator, build_args.toSliceConst(), null, null, max_doc_file_size);
899 switch (result.term) {
900 os.ChildProcess.Term.Exited => |exit_code| {
901 if (exit_code == 0) {
902 warn("{}\nThe following command incorrectly succeeded:\n", result.stderr);
903 for (build_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
904 return parseError(tokenizer, code.source_token, "example build incorrectly succeeded");
905 }
906 },
907 else => {
908 warn("{}\nThe following command crashed:\n", result.stderr);
909 for (build_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
910 return parseError(tokenizer, code.source_token, "example compile crashed");
911 },
912 }
913 if (mem.indexOf(u8, result.stderr, error_match) == null) {
914 warn("{}\nExpected to find '{}' in stderr", result.stderr, error_match);
915 return parseError(tokenizer, code.source_token, "example did not have expected compile error message");
916 }
917 const escaped_stderr = try escapeHtml(allocator, result.stderr);
918 const colored_stderr = try termColor(allocator, escaped_stderr);
919 try out.print("\n{}\n", colored_stderr);
920 if (!code.is_inline) {
921 try out.print("</code></pre>\n");
922 }
923 } else {
924 _ = exec(allocator, build_args.toSliceConst()) catch return parseError(
925 tokenizer, code.source_token, "example failed to compile");
926 }
895927 if (!code.is_inline) {
896928 try out.print("</code></pre>\n");
897929 }
doc/langref.html.in+28-6
......@@ -1909,7 +1909,22 @@ test "@tagName" {
19091909 assert(mem.eql(u8, @tagName(Small.Three), "Three"));
19101910}
19111911 {#code_end#}
1912 <p>TODO extern enum</p>
1912 {#header_open|extern enum#}
1913 <p>
1914 By default, enums are not guaranteed to be compatible with the C ABI:
1915 </p>
1916 {#code_begin|obj_err|parameter of type 'Foo' not allowed in function with calling convention 'ccc'#}
1917const Foo = enum { A, B, C };
1918export fn entry(foo: Foo) { }
1919 {#code_end#}
1920 <p>
1921 For a C-ABI-compatible enum, use <code class="zig">extern enum</code>:
1922 </p>
1923 {#code_begin|obj#}
1924const Foo = extern enum { A, B, C };
1925export fn entry(foo: Foo) { }
1926 {#code_end#}
1927 {#header_close#}
19131928 <p>TODO packed enum</p>
19141929 {#see_also|@memberName|@memberCount|@tagName#}
19151930 {#header_close#}
......@@ -2662,8 +2677,9 @@ export fn sub(a: i8, b: i8) -> i8 { return a - b; }
26622677extern "kernel32" stdcallcc fn ExitProcess(exit_code: u32) -> noreturn;
26632678extern "c" fn atan2(a: f64, b: f64) -> f64;
26642679
2665// coldcc makes a function use the cold calling convention.
2666coldcc fn abort() -> noreturn {
2680// The @setCold builtin tells the optimizer that a function is rarely called.
2681fn abort() -> noreturn {
2682 @setCold(true);
26672683 while (true) {}
26682684}
26692685
......@@ -4300,6 +4316,12 @@ test "call foo" {
43004316 This function is only valid within function scope.
43014317 </p>
43024318 {#header_close#}
4319 {#header_open|@setCold#}
4320 <pre><code class="zig">@setCold(is_cold: bool)</code></pre>
4321 <p>
4322 Tells the optimizer that a function is rarely called.
4323 </p>
4324 {#header_close#}
43034325 {#header_open|@setDebugSafety#}
43044326 <pre><code class="zig">@setDebugSafety(scope, safety_on: bool)</code></pre>
43054327 <p>
......@@ -5533,7 +5555,7 @@ UseDecl = "use" Expression ";"
55335555
55345556ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";"
55355557
5536FnProto = option("coldcc" | "nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("-&gt;" TypeExpr)
5558FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("-&gt;" TypeExpr)
55375559
55385560FnDef = option("inline" | "export") FnProto Block
55395561
......@@ -5739,8 +5761,8 @@ hljs.registerLanguage("zig", function(t) {
57395761 },
57405762 a = t.IR + "\\s*\\(",
57415763 c = {
5742 keyword: "const align var extern stdcallcc coldcc nakedcc volatile export pub noalias inline struct packed enum union goto break return try catch test continue unreachable comptime and or asm defer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong",
5743 built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setDebugSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate",
5764 keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union goto break return try catch test continue unreachable comptime and or asm defer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong",
5765 built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setDebugSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate",
57445766 literal: "true false null undefined"
57455767 },
57465768 n = [e, t.CLCM, t.CBCM, s, r];
src-self-hosted/parser.zig+1-1
......@@ -211,7 +211,7 @@ pub const Parser = struct {
211211 Token.Id.StringLiteral => {
212212 @panic("TODO extern with string literal");
213213 },
214 Token.Id.Keyword_coldcc, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
214 Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
215215 stack.append(State.TopLevel) catch unreachable;
216216 const fn_token = try self.eatToken(Token.Id.Keyword_fn);
217217 // TODO shouldn't need this cast
src-self-hosted/tokenizer.zig-2
......@@ -16,7 +16,6 @@ pub const Token = struct {
1616 KeywordId{.bytes="and", .id = Id.Keyword_and},
1717 KeywordId{.bytes="asm", .id = Id.Keyword_asm},
1818 KeywordId{.bytes="break", .id = Id.Keyword_break},
19 KeywordId{.bytes="coldcc", .id = Id.Keyword_coldcc},
2019 KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
2120 KeywordId{.bytes="const", .id = Id.Keyword_const},
2221 KeywordId{.bytes="continue", .id = Id.Keyword_continue},
......@@ -97,7 +96,6 @@ pub const Token = struct {
9796 Keyword_and,
9897 Keyword_asm,
9998 Keyword_break,
100 Keyword_coldcc,
10199 Keyword_comptime,
102100 Keyword_const,
103101 Keyword_continue,
src/all_types.hpp+12
......@@ -1108,6 +1108,7 @@ struct TypeTableEntry {
11081108
11091109 bool zero_bits;
11101110 bool is_copyable;
1111 bool gen_h_loop_flag;
11111112
11121113 union {
11131114 TypeTableEntryPointer pointer;
......@@ -1204,6 +1205,9 @@ struct FnTableEntry {
12041205 AstNode *set_alignstack_node;
12051206 uint32_t alignstack_value;
12061207
1208 AstNode *set_cold_node;
1209 bool is_cold;
1210
12071211 ZigList<FnExport> export_list;
12081212 bool calls_errorable_function;
12091213};
......@@ -1250,6 +1254,7 @@ enum BuiltinFnId {
12501254 BuiltinFnIdMod,
12511255 BuiltinFnIdTruncate,
12521256 BuiltinFnIdIntType,
1257 BuiltinFnIdSetCold,
12531258 BuiltinFnIdSetDebugSafety,
12541259 BuiltinFnIdSetFloatMode,
12551260 BuiltinFnIdTypeName,
......@@ -1830,6 +1835,7 @@ enum IrInstructionId {
18301835 IrInstructionIdTypeOf,
18311836 IrInstructionIdToPtrType,
18321837 IrInstructionIdPtrTypeChild,
1838 IrInstructionIdSetCold,
18331839 IrInstructionIdSetDebugSafety,
18341840 IrInstructionIdSetFloatMode,
18351841 IrInstructionIdArrayType,
......@@ -2202,6 +2208,12 @@ struct IrInstructionPtrTypeChild {
22022208 IrInstruction *value;
22032209};
22042210
2211struct IrInstructionSetCold {
2212 IrInstruction base;
2213
2214 IrInstruction *is_cold;
2215};
2216
22052217struct IrInstructionSetDebugSafety {
22062218 IrInstruction base;
22072219
src/analyze.cpp+114-40
......@@ -1158,6 +1158,104 @@ static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **
11581158 return true;
11591159}
11601160
1161static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) {
1162 switch (type_entry->id) {
1163 case TypeTableEntryIdInvalid:
1164 case TypeTableEntryIdVar:
1165 zig_unreachable();
1166 case TypeTableEntryIdMetaType:
1167 case TypeTableEntryIdUnreachable:
1168 case TypeTableEntryIdNumLitFloat:
1169 case TypeTableEntryIdNumLitInt:
1170 case TypeTableEntryIdUndefLit:
1171 case TypeTableEntryIdNullLit:
1172 case TypeTableEntryIdErrorUnion:
1173 case TypeTableEntryIdPureError:
1174 case TypeTableEntryIdNamespace:
1175 case TypeTableEntryIdBlock:
1176 case TypeTableEntryIdBoundFn:
1177 case TypeTableEntryIdArgTuple:
1178 case TypeTableEntryIdOpaque:
1179 return false;
1180 case TypeTableEntryIdVoid:
1181 case TypeTableEntryIdBool:
1182 case TypeTableEntryIdInt:
1183 case TypeTableEntryIdFloat:
1184 case TypeTableEntryIdPointer:
1185 case TypeTableEntryIdArray:
1186 case TypeTableEntryIdFn:
1187 return true;
1188 case TypeTableEntryIdStruct:
1189 return type_entry->data.structure.layout == ContainerLayoutPacked;
1190 case TypeTableEntryIdUnion:
1191 return type_entry->data.unionation.layout == ContainerLayoutPacked;
1192 case TypeTableEntryIdMaybe:
1193 {
1194 TypeTableEntry *child_type = type_entry->data.maybe.child_type;
1195 return child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn;
1196 }
1197 case TypeTableEntryIdEnum:
1198 return type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr;
1199 }
1200 zig_unreachable();
1201}
1202
1203static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {
1204 switch (type_entry->id) {
1205 case TypeTableEntryIdInvalid:
1206 case TypeTableEntryIdVar:
1207 zig_unreachable();
1208 case TypeTableEntryIdMetaType:
1209 case TypeTableEntryIdNumLitFloat:
1210 case TypeTableEntryIdNumLitInt:
1211 case TypeTableEntryIdUndefLit:
1212 case TypeTableEntryIdNullLit:
1213 case TypeTableEntryIdErrorUnion:
1214 case TypeTableEntryIdPureError:
1215 case TypeTableEntryIdNamespace:
1216 case TypeTableEntryIdBlock:
1217 case TypeTableEntryIdBoundFn:
1218 case TypeTableEntryIdArgTuple:
1219 return false;
1220 case TypeTableEntryIdOpaque:
1221 case TypeTableEntryIdUnreachable:
1222 case TypeTableEntryIdVoid:
1223 case TypeTableEntryIdBool:
1224 return true;
1225 case TypeTableEntryIdInt:
1226 switch (type_entry->data.integral.bit_count) {
1227 case 8:
1228 case 16:
1229 case 32:
1230 case 64:
1231 case 128:
1232 return true;
1233 default:
1234 return false;
1235 }
1236 case TypeTableEntryIdFloat:
1237 return true;
1238 case TypeTableEntryIdArray:
1239 return type_allowed_in_extern(g, type_entry->data.array.child_type);
1240 case TypeTableEntryIdFn:
1241 return type_entry->data.fn.fn_type_id.cc == CallingConventionC;
1242 case TypeTableEntryIdPointer:
1243 return type_allowed_in_extern(g, type_entry->data.pointer.child_type);
1244 case TypeTableEntryIdStruct:
1245 return type_entry->data.structure.layout == ContainerLayoutExtern;
1246 case TypeTableEntryIdMaybe:
1247 {
1248 TypeTableEntry *child_type = type_entry->data.maybe.child_type;
1249 return child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn;
1250 }
1251 case TypeTableEntryIdEnum:
1252 return type_entry->data.enumeration.layout == ContainerLayoutExtern;
1253 case TypeTableEntryIdUnion:
1254 return type_entry->data.unionation.layout == ContainerLayoutExtern;
1255 }
1256 zig_unreachable();
1257}
1258
11611259static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope) {
11621260 assert(proto_node->type == NodeTypeFnProto);
11631261 AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
......@@ -1208,6 +1306,14 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
12081306 }
12091307 }
12101308
1309 if (fn_type_id.cc != CallingConventionUnspecified && !type_allowed_in_extern(g, type_entry)) {
1310 add_node_error(g, param_node->data.param_decl.type,
1311 buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'",
1312 buf_ptr(&type_entry->name),
1313 calling_convention_name(fn_type_id.cc)));
1314 return g->builtin_types.entry_invalid;
1315 }
1316
12111317 switch (type_entry->id) {
12121318 case TypeTableEntryIdInvalid:
12131319 return g->builtin_types.entry_invalid;
......@@ -1272,6 +1378,14 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
12721378 fn_type_id.return_type = (fn_proto->return_type == nullptr) ?
12731379 g->builtin_types.entry_void : analyze_type_expr(g, child_scope, fn_proto->return_type);
12741380
1381 if (fn_type_id.cc != CallingConventionUnspecified && !type_allowed_in_extern(g, fn_type_id.return_type)) {
1382 add_node_error(g, fn_proto->return_type,
1383 buf_sprintf("return type '%s' not allowed in function with calling convention '%s'",
1384 buf_ptr(&fn_type_id.return_type->name),
1385 calling_convention_name(fn_type_id.cc)));
1386 return g->builtin_types.entry_invalid;
1387 }
1388
12751389 switch (fn_type_id.return_type->id) {
12761390 case TypeTableEntryIdInvalid:
12771391 return g->builtin_types.entry_invalid;
......@@ -1424,46 +1538,6 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
14241538 enum_type->di_type = tag_di_type;
14251539}
14261540
1427static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) {
1428 switch (type_entry->id) {
1429 case TypeTableEntryIdInvalid:
1430 case TypeTableEntryIdVar:
1431 zig_unreachable();
1432 case TypeTableEntryIdMetaType:
1433 case TypeTableEntryIdUnreachable:
1434 case TypeTableEntryIdNumLitFloat:
1435 case TypeTableEntryIdNumLitInt:
1436 case TypeTableEntryIdUndefLit:
1437 case TypeTableEntryIdNullLit:
1438 case TypeTableEntryIdErrorUnion:
1439 case TypeTableEntryIdPureError:
1440 case TypeTableEntryIdNamespace:
1441 case TypeTableEntryIdBlock:
1442 case TypeTableEntryIdBoundFn:
1443 case TypeTableEntryIdArgTuple:
1444 case TypeTableEntryIdOpaque:
1445 return false;
1446 case TypeTableEntryIdVoid:
1447 case TypeTableEntryIdBool:
1448 case TypeTableEntryIdInt:
1449 case TypeTableEntryIdFloat:
1450 case TypeTableEntryIdPointer:
1451 case TypeTableEntryIdArray:
1452 case TypeTableEntryIdUnion:
1453 case TypeTableEntryIdFn:
1454 return true;
1455 case TypeTableEntryIdStruct:
1456 return type_entry->data.structure.layout == ContainerLayoutPacked;
1457 case TypeTableEntryIdMaybe:
1458 {
1459 TypeTableEntry *child_type = type_entry->data.maybe.child_type;
1460 return child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn;
1461 }
1462 case TypeTableEntryIdEnum:
1463 return type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr;
1464 }
1465 zig_unreachable();
1466}
14671541
14681542TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[],
14691543 TypeTableEntry *field_types[], size_t field_count)
src/codegen.cpp+175-11
......@@ -485,11 +485,14 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
485485 addLLVMFnAttr(fn_table_entry->llvm_value, "naked");
486486 } else {
487487 LLVMSetFunctionCallConv(fn_table_entry->llvm_value, get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc));
488 if (fn_type->data.fn.fn_type_id.cc == CallingConventionCold) {
489 ZigLLVMAddFunctionAttrCold(fn_table_entry->llvm_value);
490 }
491488 }
492489
490 bool want_cold = fn_table_entry->is_cold || fn_type->data.fn.fn_type_id.cc == CallingConventionCold;
491 if (want_cold) {
492 ZigLLVMAddFunctionAttrCold(fn_table_entry->llvm_value);
493 }
494
495
493496 LLVMSetLinkage(fn_table_entry->llvm_value, to_llvm_linkage(linkage));
494497
495498 if (linkage == GlobalLinkageIdInternal) {
......@@ -3656,6 +3659,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
36563659 case IrInstructionIdToPtrType:
36573660 case IrInstructionIdPtrTypeChild:
36583661 case IrInstructionIdFieldPtr:
3662 case IrInstructionIdSetCold:
36593663 case IrInstructionIdSetDebugSafety:
36603664 case IrInstructionIdSetFloatMode:
36613665 case IrInstructionIdArrayType:
......@@ -5233,6 +5237,7 @@ static void define_builtin_fns(CodeGen *g) {
52335237 create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);
52345238 create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX);
52355239 create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int
5240 create_builtin_fn(g, BuiltinFnIdSetCold, "setCold", 1);
52365241 create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2);
52375242 create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 2);
52385243 create_builtin_fn(g, BuiltinFnIdPanic, "panic", 1);
......@@ -5788,7 +5793,76 @@ static const char *c_int_type_names[] = {
57885793 "unsigned long long",
57895794};
57905795
5791static void get_c_type(CodeGen *g, TypeTableEntry *type_entry, Buf *out_buf) {
5796struct GenH {
5797 ZigList<TypeTableEntry *> types_to_declare;
5798};
5799
5800static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry) {
5801 if (type_entry->gen_h_loop_flag)
5802 return;
5803 type_entry->gen_h_loop_flag = true;
5804
5805 switch (type_entry->id) {
5806 case TypeTableEntryIdInvalid:
5807 case TypeTableEntryIdVar:
5808 case TypeTableEntryIdMetaType:
5809 case TypeTableEntryIdNumLitFloat:
5810 case TypeTableEntryIdNumLitInt:
5811 case TypeTableEntryIdUndefLit:
5812 case TypeTableEntryIdNullLit:
5813 case TypeTableEntryIdNamespace:
5814 case TypeTableEntryIdBlock:
5815 case TypeTableEntryIdBoundFn:
5816 case TypeTableEntryIdArgTuple:
5817 case TypeTableEntryIdErrorUnion:
5818 case TypeTableEntryIdPureError:
5819 zig_unreachable();
5820 case TypeTableEntryIdVoid:
5821 case TypeTableEntryIdUnreachable:
5822 case TypeTableEntryIdBool:
5823 case TypeTableEntryIdInt:
5824 case TypeTableEntryIdFloat:
5825 return;
5826 case TypeTableEntryIdOpaque:
5827 gen_h->types_to_declare.append(type_entry);
5828 return;
5829 case TypeTableEntryIdStruct:
5830 for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
5831 TypeStructField *field = &type_entry->data.structure.fields[i];
5832 prepend_c_type_to_decl_list(g, gen_h, field->type_entry);
5833 }
5834 gen_h->types_to_declare.append(type_entry);
5835 return;
5836 case TypeTableEntryIdUnion:
5837 for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
5838 TypeUnionField *field = &type_entry->data.unionation.fields[i];
5839 prepend_c_type_to_decl_list(g, gen_h, field->type_entry);
5840 }
5841 gen_h->types_to_declare.append(type_entry);
5842 return;
5843 case TypeTableEntryIdEnum:
5844 prepend_c_type_to_decl_list(g, gen_h, type_entry->data.enumeration.tag_int_type);
5845 gen_h->types_to_declare.append(type_entry);
5846 return;
5847 case TypeTableEntryIdPointer:
5848 prepend_c_type_to_decl_list(g, gen_h, type_entry->data.pointer.child_type);
5849 return;
5850 case TypeTableEntryIdArray:
5851 prepend_c_type_to_decl_list(g, gen_h, type_entry->data.array.child_type);
5852 return;
5853 case TypeTableEntryIdMaybe:
5854 prepend_c_type_to_decl_list(g, gen_h, type_entry->data.maybe.child_type);
5855 return;
5856 case TypeTableEntryIdFn:
5857 for (size_t i = 0; i < type_entry->data.fn.fn_type_id.param_count; i += 1) {
5858 prepend_c_type_to_decl_list(g, gen_h, type_entry->data.fn.fn_type_id.param_info[i].type);
5859 }
5860 prepend_c_type_to_decl_list(g, gen_h, type_entry->data.fn.fn_type_id.return_type);
5861 return;
5862 }
5863}
5864
5865static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf *out_buf) {
57925866 assert(type_entry);
57935867
57945868 for (size_t i = 0; i < array_length(c_int_type_names); i += 1) {
......@@ -5816,6 +5890,8 @@ static void get_c_type(CodeGen *g, TypeTableEntry *type_entry, Buf *out_buf) {
58165890 return;
58175891 }
58185892
5893 prepend_c_type_to_decl_list(g, gen_h, type_entry);
5894
58195895 switch (type_entry->id) {
58205896 case TypeTableEntryIdVoid:
58215897 buf_init_from_str(out_buf, "void");
......@@ -5856,7 +5932,7 @@ static void get_c_type(CodeGen *g, TypeTableEntry *type_entry, Buf *out_buf) {
58565932 {
58575933 Buf child_buf = BUF_INIT;
58585934 TypeTableEntry *child_type = type_entry->data.pointer.child_type;
5859 get_c_type(g, child_type, &child_buf);
5935 get_c_type(g, gen_h, child_type, &child_buf);
58605936
58615937 const char *const_str = type_entry->data.pointer.is_const ? "const " : "";
58625938 buf_resize(out_buf, 0);
......@@ -5872,23 +5948,37 @@ static void get_c_type(CodeGen *g, TypeTableEntry *type_entry, Buf *out_buf) {
58725948 } else if (child_type->id == TypeTableEntryIdPointer ||
58735949 child_type->id == TypeTableEntryIdFn)
58745950 {
5875 return get_c_type(g, child_type, out_buf);
5951 return get_c_type(g, gen_h, child_type, out_buf);
58765952 } else {
58775953 zig_unreachable();
58785954 }
58795955 }
58805956 case TypeTableEntryIdStruct:
5957 {
5958 buf_init_from_str(out_buf, "struct ");
5959 buf_append_buf(out_buf, &type_entry->name);
5960 return;
5961 }
5962 case TypeTableEntryIdUnion:
5963 {
5964 buf_init_from_str(out_buf, "union ");
5965 buf_append_buf(out_buf, &type_entry->name);
5966 return;
5967 }
5968 case TypeTableEntryIdEnum:
5969 {
5970 buf_init_from_str(out_buf, "enum ");
5971 buf_append_buf(out_buf, &type_entry->name);
5972 return;
5973 }
58815974 case TypeTableEntryIdOpaque:
58825975 {
5883 // TODO add to table of structs we need to declare
58845976 buf_init_from_buf(out_buf, &type_entry->name);
58855977 return;
58865978 }
58875979 case TypeTableEntryIdArray:
58885980 case TypeTableEntryIdErrorUnion:
58895981 case TypeTableEntryIdPureError:
5890 case TypeTableEntryIdEnum:
5891 case TypeTableEntryIdUnion:
58925982 case TypeTableEntryIdFn:
58935983 zig_panic("TODO implement get_c_type for more types");
58945984 case TypeTableEntryIdInvalid:
......@@ -5942,6 +6032,9 @@ static void gen_h_file(CodeGen *g) {
59426032 if (!g->want_h_file)
59436033 return;
59446034
6035 GenH gen_h_data = {0};
6036 GenH *gen_h = &gen_h_data;
6037
59456038 codegen_add_time_event(g, "Generate .h");
59466039
59476040 assert(!g->is_test_build);
......@@ -5971,7 +6064,7 @@ static void gen_h_file(CodeGen *g) {
59716064 FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id;
59726065
59736066 Buf return_type_c = BUF_INIT;
5974 get_c_type(g, fn_type_id->return_type, &return_type_c);
6067 get_c_type(g, gen_h, fn_type_id->return_type, &return_type_c);
59756068
59766069 buf_appendf(&h_buf, "%s %s %s(",
59776070 buf_ptr(export_macro),
......@@ -5987,7 +6080,7 @@ static void gen_h_file(CodeGen *g) {
59876080
59886081 const char *comma_str = (param_i == 0) ? "" : ", ";
59896082 const char *restrict_str = param_info->is_noalias ? "restrict" : "";
5990 get_c_type(g, param_info->type, &param_type_c);
6083 get_c_type(g, gen_h, param_info->type, &param_type_c);
59916084 buf_appendf(&h_buf, "%s%s%s %s", comma_str, buf_ptr(&param_type_c),
59926085 restrict_str, buf_ptr(param_name));
59936086 }
......@@ -6027,6 +6120,77 @@ static void gen_h_file(CodeGen *g) {
60276120 fprintf(out_h, "#endif\n");
60286121 fprintf(out_h, "\n");
60296122
6123 for (size_t type_i = 0; type_i < gen_h->types_to_declare.length; type_i += 1) {
6124 TypeTableEntry *type_entry = gen_h->types_to_declare.at(type_i);
6125 switch (type_entry->id) {
6126 case TypeTableEntryIdInvalid:
6127 case TypeTableEntryIdVar:
6128 case TypeTableEntryIdMetaType:
6129 case TypeTableEntryIdVoid:
6130 case TypeTableEntryIdBool:
6131 case TypeTableEntryIdUnreachable:
6132 case TypeTableEntryIdInt:
6133 case TypeTableEntryIdFloat:
6134 case TypeTableEntryIdPointer:
6135 case TypeTableEntryIdNumLitFloat:
6136 case TypeTableEntryIdNumLitInt:
6137 case TypeTableEntryIdArray:
6138 case TypeTableEntryIdUndefLit:
6139 case TypeTableEntryIdNullLit:
6140 case TypeTableEntryIdErrorUnion:
6141 case TypeTableEntryIdPureError:
6142 case TypeTableEntryIdNamespace:
6143 case TypeTableEntryIdBlock:
6144 case TypeTableEntryIdBoundFn:
6145 case TypeTableEntryIdArgTuple:
6146 case TypeTableEntryIdMaybe:
6147 case TypeTableEntryIdFn:
6148 zig_unreachable();
6149 case TypeTableEntryIdEnum:
6150 assert(type_entry->data.enumeration.layout == ContainerLayoutExtern);
6151 fprintf(out_h, "enum %s {\n", buf_ptr(&type_entry->name));
6152 for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) {
6153 TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i];
6154 Buf *value_buf = buf_alloc();
6155 bigint_append_buf(value_buf, &enum_field->value, 10);
6156 fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf));
6157 if (field_i != type_entry->data.enumeration.src_field_count - 1) {
6158 fprintf(out_h, ",");
6159 }
6160 fprintf(out_h, "\n");
6161 }
6162 fprintf(out_h, "};\n\n");
6163 break;
6164 case TypeTableEntryIdStruct:
6165 assert(type_entry->data.structure.layout == ContainerLayoutExtern);
6166 fprintf(out_h, "struct %s {\n", buf_ptr(&type_entry->name));
6167 for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) {
6168 TypeStructField *struct_field = &type_entry->data.structure.fields[field_i];
6169
6170 Buf *type_name_buf = buf_alloc();
6171 get_c_type(g, gen_h, struct_field->type_entry, type_name_buf);
6172 fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name));
6173 }
6174 fprintf(out_h, "};\n\n");
6175 break;
6176 case TypeTableEntryIdUnion:
6177 assert(type_entry->data.unionation.layout == ContainerLayoutExtern);
6178 fprintf(out_h, "union %s {\n", buf_ptr(&type_entry->name));
6179 for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) {
6180 TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i];
6181
6182 Buf *type_name_buf = buf_alloc();
6183 get_c_type(g, gen_h, union_field->type_entry, type_name_buf);
6184 fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name));
6185 }
6186 fprintf(out_h, "};\n\n");
6187 break;
6188 case TypeTableEntryIdOpaque:
6189 fprintf(out_h, "struct %s;\n\n", buf_ptr(&type_entry->name));
6190 break;
6191 }
6192 }
6193
60306194 fprintf(out_h, "%s", buf_ptr(&h_buf));
60316195
60326196 fprintf(out_h, "\n#endif\n");
src/ir.cpp+55
......@@ -272,6 +272,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeChild *)
272272 return IrInstructionIdPtrTypeChild;
273273}
274274
275static constexpr IrInstructionId ir_instruction_id(IrInstructionSetCold *) {
276 return IrInstructionIdSetCold;
277}
278
275279static constexpr IrInstructionId ir_instruction_id(IrInstructionSetDebugSafety *) {
276280 return IrInstructionIdSetDebugSafety;
277281}
......@@ -1262,6 +1266,15 @@ static IrInstruction *ir_build_ptr_type_child(IrBuilder *irb, Scope *scope, AstN
12621266 return &instruction->base;
12631267}
12641268
1269static IrInstruction *ir_build_set_cold(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *is_cold) {
1270 IrInstructionSetCold *instruction = ir_build_instruction<IrInstructionSetCold>(irb, scope, source_node);
1271 instruction->is_cold = is_cold;
1272
1273 ir_ref_instruction(is_cold, irb->current_basic_block);
1274
1275 return &instruction->base;
1276}
1277
12651278static IrInstruction *ir_build_set_debug_safety(IrBuilder *irb, Scope *scope, AstNode *source_node,
12661279 IrInstruction *scope_value, IrInstruction *debug_safety_on)
12671280{
......@@ -3065,6 +3078,15 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
30653078 return arg;
30663079 return ir_build_typeof(irb, scope, node, arg);
30673080 }
3081 case BuiltinFnIdSetCold:
3082 {
3083 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
3084 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
3085 if (arg0_value == irb->codegen->invalid_instruction)
3086 return arg0_value;
3087
3088 return ir_build_set_cold(irb, scope, node, arg0_value);
3089 }
30683090 case BuiltinFnIdSetDebugSafety:
30693091 {
30703092 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
......@@ -11555,6 +11577,36 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira,
1155511577 return ira->codegen->builtin_types.entry_type;
1155611578}
1155711579
11580static TypeTableEntry *ir_analyze_instruction_set_cold(IrAnalyze *ira, IrInstructionSetCold *instruction) {
11581 if (ira->new_irb.exec->is_inline) {
11582 // ignore setCold when running functions at compile time
11583 ir_build_const_from(ira, &instruction->base);
11584 return ira->codegen->builtin_types.entry_void;
11585 }
11586
11587 IrInstruction *is_cold_value = instruction->is_cold->other;
11588 bool want_cold;
11589 if (!ir_resolve_bool(ira, is_cold_value, &want_cold))
11590 return ira->codegen->builtin_types.entry_invalid;
11591
11592 FnTableEntry *fn_entry = scope_fn_entry(instruction->base.scope);
11593 if (fn_entry == nullptr) {
11594 ir_add_error(ira, &instruction->base, buf_sprintf("@setCold outside function"));
11595 return ira->codegen->builtin_types.entry_invalid;
11596 }
11597
11598 if (fn_entry->set_cold_node != nullptr) {
11599 ErrorMsg *msg = ir_add_error(ira, &instruction->base, buf_sprintf("cold set twice in same function"));
11600 add_error_note(ira->codegen, msg, fn_entry->set_cold_node, buf_sprintf("first set here"));
11601 return ira->codegen->builtin_types.entry_invalid;
11602 }
11603
11604 fn_entry->set_cold_node = instruction->base.source_node;
11605 fn_entry->is_cold = want_cold;
11606
11607 ir_build_const_from(ira, &instruction->base);
11608 return ira->codegen->builtin_types.entry_void;
11609}
1155811610static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira,
1155911611 IrInstructionSetDebugSafety *set_debug_safety_instruction)
1156011612{
......@@ -15239,6 +15291,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
1523915291 return ir_analyze_instruction_to_ptr_type(ira, (IrInstructionToPtrType *)instruction);
1524015292 case IrInstructionIdPtrTypeChild:
1524115293 return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction);
15294 case IrInstructionIdSetCold:
15295 return ir_analyze_instruction_set_cold(ira, (IrInstructionSetCold *)instruction);
1524215296 case IrInstructionIdSetDebugSafety:
1524315297 return ir_analyze_instruction_set_debug_safety(ira, (IrInstructionSetDebugSafety *)instruction);
1524415298 case IrInstructionIdSetFloatMode:
......@@ -15475,6 +15529,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
1547515529 case IrInstructionIdCall:
1547615530 case IrInstructionIdReturn:
1547715531 case IrInstructionIdUnreachable:
15532 case IrInstructionIdSetCold:
1547815533 case IrInstructionIdSetDebugSafety:
1547915534 case IrInstructionIdSetFloatMode:
1548015535 case IrInstructionIdImport:
src/ir_print.cpp+9
......@@ -368,6 +368,12 @@ static void ir_print_union_field_ptr(IrPrint *irp, IrInstructionUnionFieldPtr *i
368368 fprintf(irp->f, ")");
369369}
370370
371static void ir_print_set_cold(IrPrint *irp, IrInstructionSetCold *instruction) {
372 fprintf(irp->f, "@setCold(");
373 ir_print_other_instruction(irp, instruction->is_cold);
374 fprintf(irp->f, ")");
375}
376
371377static void ir_print_set_debug_safety(IrPrint *irp, IrInstructionSetDebugSafety *instruction) {
372378 fprintf(irp->f, "@setDebugSafety(");
373379 ir_print_other_instruction(irp, instruction->scope_value);
......@@ -1081,6 +1087,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
10811087 case IrInstructionIdUnionFieldPtr:
10821088 ir_print_union_field_ptr(irp, (IrInstructionUnionFieldPtr *)instruction);
10831089 break;
1090 case IrInstructionIdSetCold:
1091 ir_print_set_cold(irp, (IrInstructionSetCold *)instruction);
1092 break;
10841093 case IrInstructionIdSetDebugSafety:
10851094 ir_print_set_debug_safety(irp, (IrInstructionSetDebugSafety *)instruction);
10861095 break;
src/parser.cpp+2-6
......@@ -2250,7 +2250,7 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand
22502250}
22512251
22522252/*
2253FnProto = option("coldcc" | "nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("-&gt;" TypeExpr)
2253FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("-&gt;" TypeExpr)
22542254*/
22552255static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) {
22562256 Token *first_token = &pc->tokens->at(*token_index);
......@@ -2258,11 +2258,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
22582258
22592259 CallingConvention cc;
22602260 bool is_extern = false;
2261 if (first_token->id == TokenIdKeywordColdCC) {
2262 *token_index += 1;
2263 fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
2264 cc = CallingConventionCold;
2265 } else if (first_token->id == TokenIdKeywordNakedCC) {
2261 if (first_token->id == TokenIdKeywordNakedCC) {
22662262 *token_index += 1;
22672263 fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
22682264 cc = CallingConventionNaked;
src/tokenizer.cpp-2
......@@ -112,7 +112,6 @@ static const struct ZigKeyword zig_keywords[] = {
112112 {"asm", TokenIdKeywordAsm},
113113 {"break", TokenIdKeywordBreak},
114114 {"catch", TokenIdKeywordCatch},
115 {"coldcc", TokenIdKeywordColdCC},
116115 {"comptime", TokenIdKeywordCompTime},
117116 {"const", TokenIdKeywordConst},
118117 {"continue", TokenIdKeywordContinue},
......@@ -1509,7 +1508,6 @@ const char * token_name(TokenId id) {
15091508 case TokenIdKeywordAsm: return "asm";
15101509 case TokenIdKeywordBreak: return "break";
15111510 case TokenIdKeywordCatch: return "catch";
1512 case TokenIdKeywordColdCC: return "coldcc";
15131511 case TokenIdKeywordCompTime: return "comptime";
15141512 case TokenIdKeywordConst: return "const";
15151513 case TokenIdKeywordContinue: return "continue";
src/tokenizer.hpp-1
......@@ -51,7 +51,6 @@ enum TokenId {
5151 TokenIdKeywordAsm,
5252 TokenIdKeywordBreak,
5353 TokenIdKeywordCatch,
54 TokenIdKeywordColdCC,
5554 TokenIdKeywordCompTime,
5655 TokenIdKeywordConst,
5756 TokenIdKeywordContinue,
std/os/index.zig+4-2
......@@ -127,7 +127,8 @@ test "os.getRandomBytes" {
127127/// Raises a signal in the current kernel thread, ending its execution.
128128/// If linking against libc, this calls the abort() libc function. Otherwise
129129/// it uses the zig standard library implementation.
130pub coldcc fn abort() -> noreturn {
130pub fn abort() -> noreturn {
131 @setCold(true);
131132 if (builtin.link_libc) {
132133 c.abort();
133134 }
......@@ -148,7 +149,8 @@ pub coldcc fn abort() -> noreturn {
148149}
149150
150151/// Exits the program cleanly with the specified status code.
151pub coldcc fn exit(status: u8) -> noreturn {
152pub fn exit(status: u8) -> noreturn {
153 @setCold(true);
152154 if (builtin.link_libc) {
153155 c.exit(status);
154156 }
std/special/builtin.zig+2-1
......@@ -5,8 +5,9 @@ const builtin = @import("builtin");
55
66// Avoid dragging in the debug safety mechanisms into this .o file,
77// unless we're trying to test this file.
8pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
8pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
99 if (builtin.is_test) {
10 @setCold(true);
1011 @import("std").debug.panic("{}", msg);
1112 } else {
1213 unreachable;
std/special/compiler_rt/index.zig+2-1
......@@ -74,7 +74,8 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4;
7474
7575// Avoid dragging in the debug safety mechanisms into this .o file,
7676// unless we're trying to test this file.
77pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
77pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
78 @setCold(true);
7879 if (is_test) {
7980 @import("std").debug.panic("{}", msg);
8081 } else {
std/special/panic.zig+2-1
......@@ -6,7 +6,8 @@
66const builtin = @import("builtin");
77const std = @import("std");
88
9pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
9pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
10 @setCold(true);
1011 switch (builtin.os) {
1112 // TODO: fix panic in zen.
1213 builtin.Os.freestanding, builtin.Os.zen => {
test/cases/misc.zig+9
......@@ -608,3 +608,12 @@ test "function closes over local const" {
608608 const x = fnThatClosesOverLocalConst().g();
609609 assert(x == 1);
610610}
611
612test "cold function" {
613 thisIsAColdFn();
614 comptime thisIsAColdFn();
615}
616
617fn thisIsAColdFn() {
618 @setCold(true);
619}
test/compile_errors.zig+25-2
......@@ -1,6 +1,29 @@
11const tests = @import("tests.zig");
22
33pub fn addCases(cases: &tests.CompileErrorContext) {
4 cases.add("function with non-extern enum parameter",
5 \\const Foo = enum { A, B, C };
6 \\export fn entry(foo: Foo) { }
7 , ".tmp_source.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'");
8
9 cases.add("function with non-extern struct parameter",
10 \\const Foo = struct {
11 \\ A: i32,
12 \\ B: f32,
13 \\ C: bool,
14 \\};
15 \\export fn entry(foo: Foo) { }
16 , ".tmp_source.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'");
17
18 cases.add("function with non-extern union parameter",
19 \\const Foo = union {
20 \\ A: i32,
21 \\ B: f32,
22 \\ C: bool,
23 \\};
24 \\export fn entry(foo: Foo) { }
25 , ".tmp_source.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'");
26
427 cases.add("switch on enum with 1 field with no prongs",
528 \\const Foo = enum { M };
629 \\
......@@ -1590,7 +1613,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
15901613 , ".tmp_source.zig:3:28: error: expected struct type, found 'i32'");
15911614
15921615 cases.add("@fieldParentPtr - bad field name",
1593 \\const Foo = struct {
1616 \\const Foo = extern struct {
15941617 \\ derp: i32,
15951618 \\};
15961619 \\export fn foo(a: &i32) -> &Foo {
......@@ -1599,7 +1622,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
15991622 , ".tmp_source.zig:5:33: error: struct 'Foo' has no field 'a'");
16001623
16011624 cases.add("@fieldParentPtr - field pointer is not pointer",
1602 \\const Foo = struct {
1625 \\const Foo = extern struct {
16031626 \\ a: i32,
16041627 \\};
16051628 \\export fn foo(a: i32) -> &Foo {
test/gen_h.zig created+53
......@@ -0,0 +1,53 @@
1const tests = @import("tests.zig");
2
3pub fn addCases(cases: &tests.GenHContext) {
4 cases.add("declare enum",
5 \\const Foo = extern enum { A, B, C };
6 \\export fn entry(foo: Foo) { }
7 ,
8 \\enum Foo {
9 \\ A = 0,
10 \\ B = 1,
11 \\ C = 2
12 \\};
13 \\
14 \\TEST_EXPORT void entry(enum Foo foo);
15 \\
16 );
17
18 cases.add("declare struct",
19 \\const Foo = extern struct {
20 \\ A: i32,
21 \\ B: f32,
22 \\ C: bool,
23 \\};
24 \\export fn entry(foo: Foo) { }
25 ,
26 \\struct Foo {
27 \\ int32_t A;
28 \\ float B;
29 \\ bool C;
30 \\};
31 \\
32 \\TEST_EXPORT void entry(struct Foo foo);
33 \\
34 );
35
36 cases.add("declare union",
37 \\const Foo = extern union {
38 \\ A: i32,
39 \\ B: f32,
40 \\ C: bool,
41 \\};
42 \\export fn entry(foo: Foo) { }
43 ,
44 \\union Foo {
45 \\ int32_t A;
46 \\ float B;
47 \\ bool C;
48 \\};
49 \\
50 \\TEST_EXPORT void entry(union Foo foo);
51 \\
52 );
53}
test/tests.zig+150-1
......@@ -19,6 +19,7 @@ const compile_errors = @import("compile_errors.zig");
1919const assemble_and_link = @import("assemble_and_link.zig");
2020const debug_safety = @import("debug_safety.zig");
2121const translate_c = @import("translate_c.zig");
22const gen_h = @import("gen_h.zig");
2223
2324const TestTarget = struct {
2425 os: builtin.Os,
......@@ -123,7 +124,7 @@ pub fn addTranslateCTests(b: &build.Builder, test_filter: ?[]const u8) -> &build
123124 const cases = b.allocator.create(TranslateCContext) catch unreachable;
124125 *cases = TranslateCContext {
125126 .b = b,
126 .step = b.step("test-translate-c", "Run the C header file parsing tests"),
127 .step = b.step("test-translate-c", "Run the C transation tests"),
127128 .test_index = 0,
128129 .test_filter = test_filter,
129130 };
......@@ -133,6 +134,21 @@ pub fn addTranslateCTests(b: &build.Builder, test_filter: ?[]const u8) -> &build
133134 return cases.step;
134135}
135136
137pub fn addGenHTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
138 const cases = b.allocator.create(GenHContext) catch unreachable;
139 *cases = GenHContext {
140 .b = b,
141 .step = b.step("test-gen-h", "Run the C header file generation tests"),
142 .test_index = 0,
143 .test_filter = test_filter,
144 };
145
146 gen_h.addCases(cases);
147
148 return cases.step;
149}
150
151
136152pub fn addPkgTests(b: &build.Builder, test_filter: ?[]const u8, root_src: []const u8,
137153 name:[] const u8, desc: []const u8, with_lldb: bool) -> &build.Step
138154{
......@@ -977,3 +993,136 @@ pub const TranslateCContext = struct {
977993 }
978994 }
979995};
996
997pub const GenHContext = struct {
998 b: &build.Builder,
999 step: &build.Step,
1000 test_index: usize,
1001 test_filter: ?[]const u8,
1002
1003 const TestCase = struct {
1004 name: []const u8,
1005 sources: ArrayList(SourceFile),
1006 expected_lines: ArrayList([]const u8),
1007
1008 const SourceFile = struct {
1009 filename: []const u8,
1010 source: []const u8,
1011 };
1012
1013 pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) {
1014 self.sources.append(SourceFile {
1015 .filename = filename,
1016 .source = source,
1017 }) catch unreachable;
1018 }
1019
1020 pub fn addExpectedLine(self: &TestCase, text: []const u8) {
1021 self.expected_lines.append(text) catch unreachable;
1022 }
1023 };
1024
1025 const GenHCmpOutputStep = struct {
1026 step: build.Step,
1027 context: &GenHContext,
1028 h_path: []const u8,
1029 name: []const u8,
1030 test_index: usize,
1031 case: &const TestCase,
1032
1033 pub fn create(context: &GenHContext, h_path: []const u8, name: []const u8, case: &const TestCase) -> &GenHCmpOutputStep {
1034 const allocator = context.b.allocator;
1035 const ptr = allocator.create(GenHCmpOutputStep) catch unreachable;
1036 *ptr = GenHCmpOutputStep {
1037 .step = build.Step.init("ParseCCmpOutput", allocator, make),
1038 .context = context,
1039 .h_path = h_path,
1040 .name = name,
1041 .test_index = context.test_index,
1042 .case = case,
1043 };
1044 context.test_index += 1;
1045 return ptr;
1046 }
1047
1048 fn make(step: &build.Step) -> %void {
1049 const self = @fieldParentPtr(GenHCmpOutputStep, "step", step);
1050 const b = self.context.b;
1051
1052 warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
1053
1054 const full_h_path = b.pathFromRoot(self.h_path);
1055 const actual_h = try io.readFileAlloc(full_h_path, b.allocator);
1056
1057 for (self.case.expected_lines.toSliceConst()) |expected_line| {
1058 if (mem.indexOf(u8, actual_h, expected_line) == null) {
1059 warn(
1060 \\
1061 \\========= Expected this output: ================
1062 \\{}
1063 \\================================================
1064 \\{}
1065 \\
1066 , expected_line, actual_h);
1067 return error.TestFailed;
1068 }
1069 }
1070 warn("OK\n");
1071 }
1072 };
1073
1074 fn printInvocation(args: []const []const u8) {
1075 for (args) |arg| {
1076 warn("{} ", arg);
1077 }
1078 warn("\n");
1079 }
1080
1081 pub fn create(self: &GenHContext, filename: []const u8, name: []const u8,
1082 source: []const u8, expected_lines: ...) -> &TestCase
1083 {
1084 const tc = self.b.allocator.create(TestCase) catch unreachable;
1085 *tc = TestCase {
1086 .name = name,
1087 .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator),
1088 .expected_lines = ArrayList([]const u8).init(self.b.allocator),
1089 };
1090 tc.addSourceFile(filename, source);
1091 comptime var arg_i = 0;
1092 inline while (arg_i < expected_lines.len) : (arg_i += 1) {
1093 tc.addExpectedLine(expected_lines[arg_i]);
1094 }
1095 return tc;
1096 }
1097
1098 pub fn add(self: &GenHContext, name: []const u8, source: []const u8, expected_lines: ...) {
1099 const tc = self.create("test.zig", name, source, expected_lines);
1100 self.addCase(tc);
1101 }
1102
1103 pub fn addCase(self: &GenHContext, case: &const TestCase) {
1104 const b = self.b;
1105 const root_src = os.path.join(b.allocator, b.cache_root, case.sources.items[0].filename) catch unreachable;
1106
1107 const mode = builtin.Mode.Debug;
1108 const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {} ({})", case.name, @tagName(mode)) catch unreachable;
1109 if (self.test_filter) |filter| {
1110 if (mem.indexOf(u8, annotated_case_name, filter) == null)
1111 return;
1112 }
1113
1114 const obj = b.addObject("test", root_src);
1115 obj.setBuildMode(mode);
1116
1117 for (case.sources.toSliceConst()) |src_file| {
1118 const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable;
1119 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
1120 obj.step.dependOn(&write_src.step);
1121 }
1122
1123 const cmp_h = GenHCmpOutputStep.create(self, obj.getOutputHPath(), annotated_case_name, case);
1124 cmp_h.step.dependOn(&obj.step);
1125
1126 self.step.dependOn(&cmp_h.step);
1127 }
1128};