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 {...@@ -118,6 +118,7 @@ pub fn build(b: &Builder) -> %void {
118 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));118 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));
119 test_step.dependOn(tests.addDebugSafetyTests(b, test_filter));119 test_step.dependOn(tests.addDebugSafetyTests(b, test_filter));
120 test_step.dependOn(tests.addTranslateCTests(b, test_filter));120 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
121 test_step.dependOn(tests.addGenHTests(b, test_filter));
121}122}
122123
123fn dependOnLib(lib_exe_obj: &std.build.LibExeObjStep, dep: &const LibraryDep) {124fn dependOnLib(lib_exe_obj: &std.build.LibExeObjStep, dep: &const LibraryDep) {
doc/docgen.zig+38-6
...@@ -286,7 +286,7 @@ const Code = struct {...@@ -286,7 +286,7 @@ const Code = struct {
286 TestError: []const u8,286 TestError: []const u8,
287 TestSafety: []const u8,287 TestSafety: []const u8,
288 Exe: ExpectedOutcome,288 Exe: ExpectedOutcome,
289 Obj,289 Obj: ?[]const u8,
290 };290 };
291};291};
292292
...@@ -442,9 +442,12 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) -> %Toc {...@@ -442,9 +442,12 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) -> %Toc {
442 code_kind_id = Code.Id { .TestSafety = name};442 code_kind_id = Code.Id { .TestSafety = name};
443 name = "test";443 name = "test";
444 } else if (mem.eql(u8, code_kind_str, "obj")) {444 } 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";
446 } else if (mem.eql(u8, code_kind_str, "syntax")) {449 } else if (mem.eql(u8, code_kind_str, "syntax")) {
447 code_kind_id = Code.Id.Obj;450 code_kind_id = Code.Id { .Obj = null };
448 is_inline = true;451 is_inline = true;
449 } else {452 } else {
450 return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {}", code_kind_str);453 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...@@ -861,13 +864,14 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io
861 const colored_stderr = try termColor(allocator, escaped_stderr);864 const colored_stderr = try termColor(allocator, escaped_stderr);
862 try out.print("<pre><code class=\"shell\">$ zig test {}.zig\n{}</code></pre>\n", code.name, colored_stderr);865 try out.print("<pre><code class=\"shell\">$ zig test {}.zig\n{}</code></pre>\n", code.name, colored_stderr);
863 },866 },
864 Code.Id.Obj => {867 Code.Id.Obj => |maybe_error_match| {
865 const name_plus_obj_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, obj_ext);868 const name_plus_obj_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, obj_ext);
866 const tmp_obj_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_obj_ext);869 const tmp_obj_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_obj_ext);
867 var build_args = std.ArrayList([]const u8).init(allocator);870 var build_args = std.ArrayList([]const u8).init(allocator);
868 defer build_args.deinit();871 defer build_args.deinit();
869872
870 try build_args.appendSlice([][]const u8 {zig_exe, "build-obj", tmp_source_file_name,873 try build_args.appendSlice([][]const u8 {zig_exe, "build-obj", tmp_source_file_name,
874 "--color", "on",
871 "--output", tmp_obj_file_name});875 "--output", tmp_obj_file_name});
872876
873 if (!code.is_inline) {877 if (!code.is_inline) {
...@@ -890,8 +894,36 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io...@@ -890,8 +894,36 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io
890 },894 },
891 }895 }
892896
893 _ = exec(allocator, build_args.toSliceConst()) catch return parseError(897 if (maybe_error_match) |error_match| {
894 tokenizer, code.source_token, "example failed to compile");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 }
895 if (!code.is_inline) {927 if (!code.is_inline) {
896 try out.print("</code></pre>\n");928 try out.print("</code></pre>\n");
897 }929 }
doc/langref.html.in+28-6
...@@ -1909,7 +1909,22 @@ test "@tagName" {...@@ -1909,7 +1909,22 @@ test "@tagName" {
1909 assert(mem.eql(u8, @tagName(Small.Three), "Three"));1909 assert(mem.eql(u8, @tagName(Small.Three), "Three"));
1910}1910}
1911 {#code_end#}1911 {#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#}
1913 <p>TODO packed enum</p>1928 <p>TODO packed enum</p>
1914 {#see_also|@memberName|@memberCount|@tagName#}1929 {#see_also|@memberName|@memberCount|@tagName#}
1915 {#header_close#}1930 {#header_close#}
...@@ -2662,8 +2677,9 @@ export fn sub(a: i8, b: i8) -> i8 { return a - b; }...@@ -2662,8 +2677,9 @@ export fn sub(a: i8, b: i8) -> i8 { return a - b; }
2662extern "kernel32" stdcallcc fn ExitProcess(exit_code: u32) -> noreturn;2677extern "kernel32" stdcallcc fn ExitProcess(exit_code: u32) -> noreturn;
2663extern "c" fn atan2(a: f64, b: f64) -> f64;2678extern "c" fn atan2(a: f64, b: f64) -> f64;
26642679
2665// coldcc makes a function use the cold calling convention.2680// The @setCold builtin tells the optimizer that a function is rarely called.
2666coldcc fn abort() -> noreturn {2681fn abort() -> noreturn {
2682 @setCold(true);
2667 while (true) {}2683 while (true) {}
2668}2684}
26692685
...@@ -4300,6 +4316,12 @@ test "call foo" {...@@ -4300,6 +4316,12 @@ test "call foo" {
4300 This function is only valid within function scope.4316 This function is only valid within function scope.
4301 </p>4317 </p>
4302 {#header_close#}4318 {#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#}
4303 {#header_open|@setDebugSafety#}4325 {#header_open|@setDebugSafety#}
4304 <pre><code class="zig">@setDebugSafety(scope, safety_on: bool)</code></pre>4326 <pre><code class="zig">@setDebugSafety(scope, safety_on: bool)</code></pre>
4305 <p>4327 <p>
...@@ -5533,7 +5555,7 @@ UseDecl = "use" Expression ";"...@@ -5533,7 +5555,7 @@ UseDecl = "use" Expression ";"
55335555
5534ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";"5556ExternDecl = "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
5538FnDef = option("inline" | "export") FnProto Block5560FnDef = option("inline" | "export") FnProto Block
55395561
...@@ -5739,8 +5761,8 @@ hljs.registerLanguage("zig", function(t) {...@@ -5739,8 +5761,8 @@ hljs.registerLanguage("zig", function(t) {
5739 },5761 },
5740 a = t.IR + "\\s*\\(",5762 a = t.IR + "\\s*\\(",
5741 c = {5763 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",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",
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",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",
5744 literal: "true false null undefined"5766 literal: "true false null undefined"
5745 },5767 },
5746 n = [e, t.CLCM, t.CBCM, s, r];5768 n = [e, t.CLCM, t.CBCM, s, r];
src-self-hosted/parser.zig+1-1
...@@ -211,7 +211,7 @@ pub const Parser = struct {...@@ -211,7 +211,7 @@ pub const Parser = struct {
211 Token.Id.StringLiteral => {211 Token.Id.StringLiteral => {
212 @panic("TODO extern with string literal");212 @panic("TODO extern with string literal");
213 },213 },
214 Token.Id.Keyword_coldcc, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {214 Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
215 stack.append(State.TopLevel) catch unreachable;215 stack.append(State.TopLevel) catch unreachable;
216 const fn_token = try self.eatToken(Token.Id.Keyword_fn);216 const fn_token = try self.eatToken(Token.Id.Keyword_fn);
217 // TODO shouldn't need this cast217 // TODO shouldn't need this cast
src-self-hosted/tokenizer.zig-2
...@@ -16,7 +16,6 @@ pub const Token = struct {...@@ -16,7 +16,6 @@ pub const Token = struct {
16 KeywordId{.bytes="and", .id = Id.Keyword_and},16 KeywordId{.bytes="and", .id = Id.Keyword_and},
17 KeywordId{.bytes="asm", .id = Id.Keyword_asm},17 KeywordId{.bytes="asm", .id = Id.Keyword_asm},
18 KeywordId{.bytes="break", .id = Id.Keyword_break},18 KeywordId{.bytes="break", .id = Id.Keyword_break},
19 KeywordId{.bytes="coldcc", .id = Id.Keyword_coldcc},
20 KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},19 KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
21 KeywordId{.bytes="const", .id = Id.Keyword_const},20 KeywordId{.bytes="const", .id = Id.Keyword_const},
22 KeywordId{.bytes="continue", .id = Id.Keyword_continue},21 KeywordId{.bytes="continue", .id = Id.Keyword_continue},
...@@ -97,7 +96,6 @@ pub const Token = struct {...@@ -97,7 +96,6 @@ pub const Token = struct {
97 Keyword_and,96 Keyword_and,
98 Keyword_asm,97 Keyword_asm,
99 Keyword_break,98 Keyword_break,
100 Keyword_coldcc,
101 Keyword_comptime,99 Keyword_comptime,
102 Keyword_const,100 Keyword_const,
103 Keyword_continue,101 Keyword_continue,
src/all_types.hpp+12
...@@ -1108,6 +1108,7 @@ struct TypeTableEntry {...@@ -1108,6 +1108,7 @@ struct TypeTableEntry {
11081108
1109 bool zero_bits;1109 bool zero_bits;
1110 bool is_copyable;1110 bool is_copyable;
1111 bool gen_h_loop_flag;
11111112
1112 union {1113 union {
1113 TypeTableEntryPointer pointer;1114 TypeTableEntryPointer pointer;
...@@ -1204,6 +1205,9 @@ struct FnTableEntry {...@@ -1204,6 +1205,9 @@ struct FnTableEntry {
1204 AstNode *set_alignstack_node;1205 AstNode *set_alignstack_node;
1205 uint32_t alignstack_value;1206 uint32_t alignstack_value;
12061207
1208 AstNode *set_cold_node;
1209 bool is_cold;
1210
1207 ZigList<FnExport> export_list;1211 ZigList<FnExport> export_list;
1208 bool calls_errorable_function;1212 bool calls_errorable_function;
1209};1213};
...@@ -1250,6 +1254,7 @@ enum BuiltinFnId {...@@ -1250,6 +1254,7 @@ enum BuiltinFnId {
1250 BuiltinFnIdMod,1254 BuiltinFnIdMod,
1251 BuiltinFnIdTruncate,1255 BuiltinFnIdTruncate,
1252 BuiltinFnIdIntType,1256 BuiltinFnIdIntType,
1257 BuiltinFnIdSetCold,
1253 BuiltinFnIdSetDebugSafety,1258 BuiltinFnIdSetDebugSafety,
1254 BuiltinFnIdSetFloatMode,1259 BuiltinFnIdSetFloatMode,
1255 BuiltinFnIdTypeName,1260 BuiltinFnIdTypeName,
...@@ -1830,6 +1835,7 @@ enum IrInstructionId {...@@ -1830,6 +1835,7 @@ enum IrInstructionId {
1830 IrInstructionIdTypeOf,1835 IrInstructionIdTypeOf,
1831 IrInstructionIdToPtrType,1836 IrInstructionIdToPtrType,
1832 IrInstructionIdPtrTypeChild,1837 IrInstructionIdPtrTypeChild,
1838 IrInstructionIdSetCold,
1833 IrInstructionIdSetDebugSafety,1839 IrInstructionIdSetDebugSafety,
1834 IrInstructionIdSetFloatMode,1840 IrInstructionIdSetFloatMode,
1835 IrInstructionIdArrayType,1841 IrInstructionIdArrayType,
...@@ -2202,6 +2208,12 @@ struct IrInstructionPtrTypeChild {...@@ -2202,6 +2208,12 @@ struct IrInstructionPtrTypeChild {
2202 IrInstruction *value;2208 IrInstruction *value;
2203};2209};
22042210
2211struct IrInstructionSetCold {
2212 IrInstruction base;
2213
2214 IrInstruction *is_cold;
2215};
2216
2205struct IrInstructionSetDebugSafety {2217struct IrInstructionSetDebugSafety {
2206 IrInstruction base;2218 IrInstruction base;
22072219
src/analyze.cpp+114-40
...@@ -1158,6 +1158,104 @@ static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **...@@ -1158,6 +1158,104 @@ static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **
1158 return true;1158 return true;
1159}1159}
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
1161static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope) {1259static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope) {
1162 assert(proto_node->type == NodeTypeFnProto);1260 assert(proto_node->type == NodeTypeFnProto);
1163 AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;1261 AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
...@@ -1208,6 +1306,14 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c...@@ -1208,6 +1306,14 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
1208 }1306 }
1209 }1307 }
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
1211 switch (type_entry->id) {1317 switch (type_entry->id) {
1212 case TypeTableEntryIdInvalid:1318 case TypeTableEntryIdInvalid:
1213 return g->builtin_types.entry_invalid;1319 return g->builtin_types.entry_invalid;
...@@ -1272,6 +1378,14 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c...@@ -1272,6 +1378,14 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
1272 fn_type_id.return_type = (fn_proto->return_type == nullptr) ?1378 fn_type_id.return_type = (fn_proto->return_type == nullptr) ?
1273 g->builtin_types.entry_void : analyze_type_expr(g, child_scope, fn_proto->return_type);1379 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
1275 switch (fn_type_id.return_type->id) {1389 switch (fn_type_id.return_type->id) {
1276 case TypeTableEntryIdInvalid:1390 case TypeTableEntryIdInvalid:
1277 return g->builtin_types.entry_invalid;1391 return g->builtin_types.entry_invalid;
...@@ -1424,46 +1538,6 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {...@@ -1424,46 +1538,6 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
1424 enum_type->di_type = tag_di_type;1538 enum_type->di_type = tag_di_type;
1425}1539}
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
1468TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[],1542TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[],
1469 TypeTableEntry *field_types[], size_t field_count)1543 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) {...@@ -485,11 +485,14 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
485 addLLVMFnAttr(fn_table_entry->llvm_value, "naked");485 addLLVMFnAttr(fn_table_entry->llvm_value, "naked");
486 } else {486 } else {
487 LLVMSetFunctionCallConv(fn_table_entry->llvm_value, get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc));487 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 }
491 }488 }
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
493 LLVMSetLinkage(fn_table_entry->llvm_value, to_llvm_linkage(linkage));496 LLVMSetLinkage(fn_table_entry->llvm_value, to_llvm_linkage(linkage));
494497
495 if (linkage == GlobalLinkageIdInternal) {498 if (linkage == GlobalLinkageIdInternal) {
...@@ -3656,6 +3659,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -3656,6 +3659,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
3656 case IrInstructionIdToPtrType:3659 case IrInstructionIdToPtrType:
3657 case IrInstructionIdPtrTypeChild:3660 case IrInstructionIdPtrTypeChild:
3658 case IrInstructionIdFieldPtr:3661 case IrInstructionIdFieldPtr:
3662 case IrInstructionIdSetCold:
3659 case IrInstructionIdSetDebugSafety:3663 case IrInstructionIdSetDebugSafety:
3660 case IrInstructionIdSetFloatMode:3664 case IrInstructionIdSetFloatMode:
3661 case IrInstructionIdArrayType:3665 case IrInstructionIdArrayType:
...@@ -5233,6 +5237,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -5233,6 +5237,7 @@ static void define_builtin_fns(CodeGen *g) {
5233 create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);5237 create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);
5234 create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX);5238 create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX);
5235 create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int5239 create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int
5240 create_builtin_fn(g, BuiltinFnIdSetCold, "setCold", 1);
5236 create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2);5241 create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2);
5237 create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 2);5242 create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 2);
5238 create_builtin_fn(g, BuiltinFnIdPanic, "panic", 1);5243 create_builtin_fn(g, BuiltinFnIdPanic, "panic", 1);
...@@ -5788,7 +5793,76 @@ static const char *c_int_type_names[] = {...@@ -5788,7 +5793,76 @@ static const char *c_int_type_names[] = {
5788 "unsigned long long",5793 "unsigned long long",
5789};5794};
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) {
5792 assert(type_entry);5866 assert(type_entry);
57935867
5794 for (size_t i = 0; i < array_length(c_int_type_names); i += 1) {5868 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) {...@@ -5816,6 +5890,8 @@ static void get_c_type(CodeGen *g, TypeTableEntry *type_entry, Buf *out_buf) {
5816 return;5890 return;
5817 }5891 }
58185892
5893 prepend_c_type_to_decl_list(g, gen_h, type_entry);
5894
5819 switch (type_entry->id) {5895 switch (type_entry->id) {
5820 case TypeTableEntryIdVoid:5896 case TypeTableEntryIdVoid:
5821 buf_init_from_str(out_buf, "void");5897 buf_init_from_str(out_buf, "void");
...@@ -5856,7 +5932,7 @@ static void get_c_type(CodeGen *g, TypeTableEntry *type_entry, Buf *out_buf) {...@@ -5856,7 +5932,7 @@ static void get_c_type(CodeGen *g, TypeTableEntry *type_entry, Buf *out_buf) {
5856 {5932 {
5857 Buf child_buf = BUF_INIT;5933 Buf child_buf = BUF_INIT;
5858 TypeTableEntry *child_type = type_entry->data.pointer.child_type;5934 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
5861 const char *const_str = type_entry->data.pointer.is_const ? "const " : "";5937 const char *const_str = type_entry->data.pointer.is_const ? "const " : "";
5862 buf_resize(out_buf, 0);5938 buf_resize(out_buf, 0);
...@@ -5872,23 +5948,37 @@ static void get_c_type(CodeGen *g, TypeTableEntry *type_entry, Buf *out_buf) {...@@ -5872,23 +5948,37 @@ static void get_c_type(CodeGen *g, TypeTableEntry *type_entry, Buf *out_buf) {
5872 } else if (child_type->id == TypeTableEntryIdPointer ||5948 } else if (child_type->id == TypeTableEntryIdPointer ||
5873 child_type->id == TypeTableEntryIdFn)5949 child_type->id == TypeTableEntryIdFn)
5874 {5950 {
5875 return get_c_type(g, child_type, out_buf);5951 return get_c_type(g, gen_h, child_type, out_buf);
5876 } else {5952 } else {
5877 zig_unreachable();5953 zig_unreachable();
5878 }5954 }
5879 }5955 }
5880 case TypeTableEntryIdStruct:5956 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 }
5881 case TypeTableEntryIdOpaque:5974 case TypeTableEntryIdOpaque:
5882 {5975 {
5883 // TODO add to table of structs we need to declare
5884 buf_init_from_buf(out_buf, &type_entry->name);5976 buf_init_from_buf(out_buf, &type_entry->name);
5885 return;5977 return;
5886 }5978 }
5887 case TypeTableEntryIdArray:5979 case TypeTableEntryIdArray:
5888 case TypeTableEntryIdErrorUnion:5980 case TypeTableEntryIdErrorUnion:
5889 case TypeTableEntryIdPureError:5981 case TypeTableEntryIdPureError:
5890 case TypeTableEntryIdEnum:
5891 case TypeTableEntryIdUnion:
5892 case TypeTableEntryIdFn:5982 case TypeTableEntryIdFn:
5893 zig_panic("TODO implement get_c_type for more types");5983 zig_panic("TODO implement get_c_type for more types");
5894 case TypeTableEntryIdInvalid:5984 case TypeTableEntryIdInvalid:
...@@ -5942,6 +6032,9 @@ static void gen_h_file(CodeGen *g) {...@@ -5942,6 +6032,9 @@ static void gen_h_file(CodeGen *g) {
5942 if (!g->want_h_file)6032 if (!g->want_h_file)
5943 return;6033 return;
59446034
6035 GenH gen_h_data = {0};
6036 GenH *gen_h = &gen_h_data;
6037
5945 codegen_add_time_event(g, "Generate .h");6038 codegen_add_time_event(g, "Generate .h");
59466039
5947 assert(!g->is_test_build);6040 assert(!g->is_test_build);
...@@ -5971,7 +6064,7 @@ static void gen_h_file(CodeGen *g) {...@@ -5971,7 +6064,7 @@ static void gen_h_file(CodeGen *g) {
5971 FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id;6064 FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id;
59726065
5973 Buf return_type_c = BUF_INIT;6066 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
5976 buf_appendf(&h_buf, "%s %s %s(",6069 buf_appendf(&h_buf, "%s %s %s(",
5977 buf_ptr(export_macro),6070 buf_ptr(export_macro),
...@@ -5987,7 +6080,7 @@ static void gen_h_file(CodeGen *g) {...@@ -5987,7 +6080,7 @@ static void gen_h_file(CodeGen *g) {
59876080
5988 const char *comma_str = (param_i == 0) ? "" : ", ";6081 const char *comma_str = (param_i == 0) ? "" : ", ";
5989 const char *restrict_str = param_info->is_noalias ? "restrict" : "";6082 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);
5991 buf_appendf(&h_buf, "%s%s%s %s", comma_str, buf_ptr(&param_type_c),6084 buf_appendf(&h_buf, "%s%s%s %s", comma_str, buf_ptr(&param_type_c),
5992 restrict_str, buf_ptr(param_name));6085 restrict_str, buf_ptr(param_name));
5993 }6086 }
...@@ -6027,6 +6120,77 @@ static void gen_h_file(CodeGen *g) {...@@ -6027,6 +6120,77 @@ static void gen_h_file(CodeGen *g) {
6027 fprintf(out_h, "#endif\n");6120 fprintf(out_h, "#endif\n");
6028 fprintf(out_h, "\n");6121 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
6030 fprintf(out_h, "%s", buf_ptr(&h_buf));6194 fprintf(out_h, "%s", buf_ptr(&h_buf));
60316195
6032 fprintf(out_h, "\n#endif\n");6196 fprintf(out_h, "\n#endif\n");
src/ir.cpp+55
...@@ -272,6 +272,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeChild *)...@@ -272,6 +272,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeChild *)
272 return IrInstructionIdPtrTypeChild;272 return IrInstructionIdPtrTypeChild;
273}273}
274274
275static constexpr IrInstructionId ir_instruction_id(IrInstructionSetCold *) {
276 return IrInstructionIdSetCold;
277}
278
275static constexpr IrInstructionId ir_instruction_id(IrInstructionSetDebugSafety *) {279static constexpr IrInstructionId ir_instruction_id(IrInstructionSetDebugSafety *) {
276 return IrInstructionIdSetDebugSafety;280 return IrInstructionIdSetDebugSafety;
277}281}
...@@ -1262,6 +1266,15 @@ static IrInstruction *ir_build_ptr_type_child(IrBuilder *irb, Scope *scope, AstN...@@ -1262,6 +1266,15 @@ static IrInstruction *ir_build_ptr_type_child(IrBuilder *irb, Scope *scope, AstN
1262 return &instruction->base;1266 return &instruction->base;
1263}1267}
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
1265static IrInstruction *ir_build_set_debug_safety(IrBuilder *irb, Scope *scope, AstNode *source_node,1278static IrInstruction *ir_build_set_debug_safety(IrBuilder *irb, Scope *scope, AstNode *source_node,
1266 IrInstruction *scope_value, IrInstruction *debug_safety_on)1279 IrInstruction *scope_value, IrInstruction *debug_safety_on)
1267{1280{
...@@ -3065,6 +3078,15 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3065,6 +3078,15 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3065 return arg;3078 return arg;
3066 return ir_build_typeof(irb, scope, node, arg);3079 return ir_build_typeof(irb, scope, node, arg);
3067 }3080 }
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 }
3068 case BuiltinFnIdSetDebugSafety:3090 case BuiltinFnIdSetDebugSafety:
3069 {3091 {
3070 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);3092 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,...@@ -11555,6 +11577,36 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira,
11555 return ira->codegen->builtin_types.entry_type;11577 return ira->codegen->builtin_types.entry_type;
11556}11578}
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}
11558static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira,11610static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira,
11559 IrInstructionSetDebugSafety *set_debug_safety_instruction)11611 IrInstructionSetDebugSafety *set_debug_safety_instruction)
11560{11612{
...@@ -15239,6 +15291,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -15239,6 +15291,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
15239 return ir_analyze_instruction_to_ptr_type(ira, (IrInstructionToPtrType *)instruction);15291 return ir_analyze_instruction_to_ptr_type(ira, (IrInstructionToPtrType *)instruction);
15240 case IrInstructionIdPtrTypeChild:15292 case IrInstructionIdPtrTypeChild:
15241 return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction);15293 return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction);
15294 case IrInstructionIdSetCold:
15295 return ir_analyze_instruction_set_cold(ira, (IrInstructionSetCold *)instruction);
15242 case IrInstructionIdSetDebugSafety:15296 case IrInstructionIdSetDebugSafety:
15243 return ir_analyze_instruction_set_debug_safety(ira, (IrInstructionSetDebugSafety *)instruction);15297 return ir_analyze_instruction_set_debug_safety(ira, (IrInstructionSetDebugSafety *)instruction);
15244 case IrInstructionIdSetFloatMode:15298 case IrInstructionIdSetFloatMode:
...@@ -15475,6 +15529,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -15475,6 +15529,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
15475 case IrInstructionIdCall:15529 case IrInstructionIdCall:
15476 case IrInstructionIdReturn:15530 case IrInstructionIdReturn:
15477 case IrInstructionIdUnreachable:15531 case IrInstructionIdUnreachable:
15532 case IrInstructionIdSetCold:
15478 case IrInstructionIdSetDebugSafety:15533 case IrInstructionIdSetDebugSafety:
15479 case IrInstructionIdSetFloatMode:15534 case IrInstructionIdSetFloatMode:
15480 case IrInstructionIdImport:15535 case IrInstructionIdImport:
src/ir_print.cpp+9
...@@ -368,6 +368,12 @@ static void ir_print_union_field_ptr(IrPrint *irp, IrInstructionUnionFieldPtr *i...@@ -368,6 +368,12 @@ static void ir_print_union_field_ptr(IrPrint *irp, IrInstructionUnionFieldPtr *i
368 fprintf(irp->f, ")");368 fprintf(irp->f, ")");
369}369}
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
371static void ir_print_set_debug_safety(IrPrint *irp, IrInstructionSetDebugSafety *instruction) {377static void ir_print_set_debug_safety(IrPrint *irp, IrInstructionSetDebugSafety *instruction) {
372 fprintf(irp->f, "@setDebugSafety(");378 fprintf(irp->f, "@setDebugSafety(");
373 ir_print_other_instruction(irp, instruction->scope_value);379 ir_print_other_instruction(irp, instruction->scope_value);
...@@ -1081,6 +1087,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1081,6 +1087,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1081 case IrInstructionIdUnionFieldPtr:1087 case IrInstructionIdUnionFieldPtr:
1082 ir_print_union_field_ptr(irp, (IrInstructionUnionFieldPtr *)instruction);1088 ir_print_union_field_ptr(irp, (IrInstructionUnionFieldPtr *)instruction);
1083 break;1089 break;
1090 case IrInstructionIdSetCold:
1091 ir_print_set_cold(irp, (IrInstructionSetCold *)instruction);
1092 break;
1084 case IrInstructionIdSetDebugSafety:1093 case IrInstructionIdSetDebugSafety:
1085 ir_print_set_debug_safety(irp, (IrInstructionSetDebugSafety *)instruction);1094 ir_print_set_debug_safety(irp, (IrInstructionSetDebugSafety *)instruction);
1086 break;1095 break;
src/parser.cpp+2-6
...@@ -2250,7 +2250,7 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand...@@ -2250,7 +2250,7 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand
2250}2250}
22512251
2252/*2252/*
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)
2254*/2254*/
2255static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) {2255static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) {
2256 Token *first_token = &pc->tokens->at(*token_index);2256 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...@@ -2258,11 +2258,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
22582258
2259 CallingConvention cc;2259 CallingConvention cc;
2260 bool is_extern = false;2260 bool is_extern = false;
2261 if (first_token->id == TokenIdKeywordColdCC) {2261 if (first_token->id == TokenIdKeywordNakedCC) {
2262 *token_index += 1;
2263 fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
2264 cc = CallingConventionCold;
2265 } else if (first_token->id == TokenIdKeywordNakedCC) {
2266 *token_index += 1;2262 *token_index += 1;
2267 fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);2263 fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
2268 cc = CallingConventionNaked;2264 cc = CallingConventionNaked;
src/tokenizer.cpp-2
...@@ -112,7 +112,6 @@ static const struct ZigKeyword zig_keywords[] = {...@@ -112,7 +112,6 @@ static const struct ZigKeyword zig_keywords[] = {
112 {"asm", TokenIdKeywordAsm},112 {"asm", TokenIdKeywordAsm},
113 {"break", TokenIdKeywordBreak},113 {"break", TokenIdKeywordBreak},
114 {"catch", TokenIdKeywordCatch},114 {"catch", TokenIdKeywordCatch},
115 {"coldcc", TokenIdKeywordColdCC},
116 {"comptime", TokenIdKeywordCompTime},115 {"comptime", TokenIdKeywordCompTime},
117 {"const", TokenIdKeywordConst},116 {"const", TokenIdKeywordConst},
118 {"continue", TokenIdKeywordContinue},117 {"continue", TokenIdKeywordContinue},
...@@ -1509,7 +1508,6 @@ const char * token_name(TokenId id) {...@@ -1509,7 +1508,6 @@ const char * token_name(TokenId id) {
1509 case TokenIdKeywordAsm: return "asm";1508 case TokenIdKeywordAsm: return "asm";
1510 case TokenIdKeywordBreak: return "break";1509 case TokenIdKeywordBreak: return "break";
1511 case TokenIdKeywordCatch: return "catch";1510 case TokenIdKeywordCatch: return "catch";
1512 case TokenIdKeywordColdCC: return "coldcc";
1513 case TokenIdKeywordCompTime: return "comptime";1511 case TokenIdKeywordCompTime: return "comptime";
1514 case TokenIdKeywordConst: return "const";1512 case TokenIdKeywordConst: return "const";
1515 case TokenIdKeywordContinue: return "continue";1513 case TokenIdKeywordContinue: return "continue";
src/tokenizer.hpp-1
...@@ -51,7 +51,6 @@ enum TokenId {...@@ -51,7 +51,6 @@ enum TokenId {
51 TokenIdKeywordAsm,51 TokenIdKeywordAsm,
52 TokenIdKeywordBreak,52 TokenIdKeywordBreak,
53 TokenIdKeywordCatch,53 TokenIdKeywordCatch,
54 TokenIdKeywordColdCC,
55 TokenIdKeywordCompTime,54 TokenIdKeywordCompTime,
56 TokenIdKeywordConst,55 TokenIdKeywordConst,
57 TokenIdKeywordContinue,56 TokenIdKeywordContinue,
std/os/index.zig+4-2
...@@ -127,7 +127,8 @@ test "os.getRandomBytes" {...@@ -127,7 +127,8 @@ test "os.getRandomBytes" {
127/// Raises a signal in the current kernel thread, ending its execution.127/// Raises a signal in the current kernel thread, ending its execution.
128/// If linking against libc, this calls the abort() libc function. Otherwise128/// If linking against libc, this calls the abort() libc function. Otherwise
129/// it uses the zig standard library implementation.129/// it uses the zig standard library implementation.
130pub coldcc fn abort() -> noreturn {130pub fn abort() -> noreturn {
131 @setCold(true);
131 if (builtin.link_libc) {132 if (builtin.link_libc) {
132 c.abort();133 c.abort();
133 }134 }
...@@ -148,7 +149,8 @@ pub coldcc fn abort() -> noreturn {...@@ -148,7 +149,8 @@ pub coldcc fn abort() -> noreturn {
148}149}
149150
150/// Exits the program cleanly with the specified status code.151/// 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);
152 if (builtin.link_libc) {154 if (builtin.link_libc) {
153 c.exit(status);155 c.exit(status);
154 }156 }
std/special/builtin.zig+2-1
...@@ -5,8 +5,9 @@ const builtin = @import("builtin");...@@ -5,8 +5,9 @@ const builtin = @import("builtin");
55
6// Avoid dragging in the debug safety mechanisms into this .o file,6// Avoid dragging in the debug safety mechanisms into this .o file,
7// unless we're trying to test this file.7// 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 {
9 if (builtin.is_test) {9 if (builtin.is_test) {
10 @setCold(true);
10 @import("std").debug.panic("{}", msg);11 @import("std").debug.panic("{}", msg);
11 } else {12 } else {
12 unreachable;13 unreachable;
std/special/compiler_rt/index.zig+2-1
...@@ -74,7 +74,8 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4;...@@ -74,7 +74,8 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4;
7474
75// Avoid dragging in the debug safety mechanisms into this .o file,75// Avoid dragging in the debug safety mechanisms into this .o file,
76// unless we're trying to test this file.76// 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);
78 if (is_test) {79 if (is_test) {
79 @import("std").debug.panic("{}", msg);80 @import("std").debug.panic("{}", msg);
80 } else {81 } else {
std/special/panic.zig+2-1
...@@ -6,7 +6,8 @@...@@ -6,7 +6,8 @@
6const builtin = @import("builtin");6const builtin = @import("builtin");
7const std = @import("std");7const 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);
10 switch (builtin.os) {11 switch (builtin.os) {
11 // TODO: fix panic in zen.12 // TODO: fix panic in zen.
12 builtin.Os.freestanding, builtin.Os.zen => {13 builtin.Os.freestanding, builtin.Os.zen => {
test/cases/misc.zig+9
...@@ -608,3 +608,12 @@ test "function closes over local const" {...@@ -608,3 +608,12 @@ test "function closes over local const" {
608 const x = fnThatClosesOverLocalConst().g();608 const x = fnThatClosesOverLocalConst().g();
609 assert(x == 1);609 assert(x == 1);
610}610}
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 @@...@@ -1,6 +1,29 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: &tests.CompileErrorContext) {3pub 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
4 cases.add("switch on enum with 1 field with no prongs",27 cases.add("switch on enum with 1 field with no prongs",
5 \\const Foo = enum { M };28 \\const Foo = enum { M };
6 \\29 \\
...@@ -1590,7 +1613,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {...@@ -1590,7 +1613,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
1590 , ".tmp_source.zig:3:28: error: expected struct type, found 'i32'");1613 , ".tmp_source.zig:3:28: error: expected struct type, found 'i32'");
15911614
1592 cases.add("@fieldParentPtr - bad field name",1615 cases.add("@fieldParentPtr - bad field name",
1593 \\const Foo = struct {1616 \\const Foo = extern struct {
1594 \\ derp: i32,1617 \\ derp: i32,
1595 \\};1618 \\};
1596 \\export fn foo(a: &i32) -> &Foo {1619 \\export fn foo(a: &i32) -> &Foo {
...@@ -1599,7 +1622,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {...@@ -1599,7 +1622,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
1599 , ".tmp_source.zig:5:33: error: struct 'Foo' has no field 'a'");1622 , ".tmp_source.zig:5:33: error: struct 'Foo' has no field 'a'");
16001623
1601 cases.add("@fieldParentPtr - field pointer is not pointer",1624 cases.add("@fieldParentPtr - field pointer is not pointer",
1602 \\const Foo = struct {1625 \\const Foo = extern struct {
1603 \\ a: i32,1626 \\ a: i32,
1604 \\};1627 \\};
1605 \\export fn foo(a: i32) -> &Foo {1628 \\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");...@@ -19,6 +19,7 @@ const compile_errors = @import("compile_errors.zig");
19const assemble_and_link = @import("assemble_and_link.zig");19const assemble_and_link = @import("assemble_and_link.zig");
20const debug_safety = @import("debug_safety.zig");20const debug_safety = @import("debug_safety.zig");
21const translate_c = @import("translate_c.zig");21const translate_c = @import("translate_c.zig");
22const gen_h = @import("gen_h.zig");
2223
23const TestTarget = struct {24const TestTarget = struct {
24 os: builtin.Os,25 os: builtin.Os,
...@@ -123,7 +124,7 @@ pub fn addTranslateCTests(b: &build.Builder, test_filter: ?[]const u8) -> &build...@@ -123,7 +124,7 @@ pub fn addTranslateCTests(b: &build.Builder, test_filter: ?[]const u8) -> &build
123 const cases = b.allocator.create(TranslateCContext) catch unreachable;124 const cases = b.allocator.create(TranslateCContext) catch unreachable;
124 *cases = TranslateCContext {125 *cases = TranslateCContext {
125 .b = b,126 .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"),
127 .test_index = 0,128 .test_index = 0,
128 .test_filter = test_filter,129 .test_filter = test_filter,
129 };130 };
...@@ -133,6 +134,21 @@ pub fn addTranslateCTests(b: &build.Builder, test_filter: ?[]const u8) -> &build...@@ -133,6 +134,21 @@ pub fn addTranslateCTests(b: &build.Builder, test_filter: ?[]const u8) -> &build
133 return cases.step;134 return cases.step;
134}135}
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
136pub fn addPkgTests(b: &build.Builder, test_filter: ?[]const u8, root_src: []const u8,152pub fn addPkgTests(b: &build.Builder, test_filter: ?[]const u8, root_src: []const u8,
137 name:[] const u8, desc: []const u8, with_lldb: bool) -> &build.Step153 name:[] const u8, desc: []const u8, with_lldb: bool) -> &build.Step
138{154{
...@@ -977,3 +993,136 @@ pub const TranslateCContext = struct {...@@ -977,3 +993,136 @@ pub const TranslateCContext = struct {
977 }993 }
978 }994 }
979};995};
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};