| author | |
| committer | |
| log | cf39819478e237255109d0343e642db70e88071b |
| tree | 6c8232d28a484989f43075efeaa5501d5e60154b |
| parent | cacba6f4357fdec8db0ea792889c60022c39fbd3 |
* 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, unions21 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 | } |
| 122 | 123 | ||
| 123 | fn dependOnLib(lib_exe_obj: &std.build.LibExeObjStep, dep: &const LibraryDep) { | 124 | fn 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 | }; |
| 292 | 292 | ||
| ... | @@ -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(); |
| 869 | 872 | ||
| 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}); |
| 872 | 876 | ||
| 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 | } |
| 892 | 896 | ||
| 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'#} | ||
| 1917 | const Foo = enum { A, B, C }; | ||
| 1918 | export 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#} | ||
| 1924 | const Foo = extern enum { A, B, C }; | ||
| 1925 | export 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; } |
| 2662 | extern "kernel32" stdcallcc fn ExitProcess(exit_code: u32) -> noreturn; | 2677 | extern "kernel32" stdcallcc fn ExitProcess(exit_code: u32) -> noreturn; |
| 2663 | extern "c" fn atan2(a: f64, b: f64) -> f64; | 2678 | extern "c" fn atan2(a: f64, b: f64) -> f64; |
| 2664 | 2679 | ||
| 2665 | // coldcc makes a function use the cold calling convention. | 2680 | // The @setCold builtin tells the optimizer that a function is rarely called. |
| 2666 | coldcc fn abort() -> noreturn { | 2681 | fn abort() -> noreturn { |
| 2682 | @setCold(true); | ||
| 2667 | while (true) {} | 2683 | while (true) {} |
| 2668 | } | 2684 | } |
| 2669 | 2685 | ||
| ... | @@ -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 ";" |
| 5533 | 5555 | ||
| 5534 | ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";" | 5556 | ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";" |
| 5535 | 5557 | ||
| 5536 | FnProto = option("coldcc" | "nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("-&gt;" TypeExpr) | 5558 | FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("-&gt;" TypeExpr) |
| 5537 | 5559 | ||
| 5538 | FnDef = option("inline" | "export") FnProto Block | 5560 | FnDef = option("inline" | "export") FnProto Block |
| 5539 | 5561 | ||
| ... | @@ -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 cast | 217 | // 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 { |
| 1108 | 1108 | ||
| 1109 | bool zero_bits; | 1109 | bool zero_bits; |
| 1110 | bool is_copyable; | 1110 | bool is_copyable; |
| 1111 | bool gen_h_loop_flag; | ||
| 1111 | 1112 | ||
| 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; |
| 1206 | 1207 | ||
| 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 | }; |
| 2204 | 2210 | ||
| 2211 | struct IrInstructionSetCold { | ||
| 2212 | IrInstruction base; | ||
| 2213 | |||
| 2214 | IrInstruction *is_cold; | ||
| 2215 | }; | ||
| 2216 | |||
| 2205 | struct IrInstructionSetDebugSafety { | 2217 | struct IrInstructionSetDebugSafety { |
| 2206 | IrInstruction base; | 2218 | IrInstruction base; |
| 2207 | 2219 |
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 | } |
| 1160 | 1160 | ||
| 1161 | static 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 | |||
| 1203 | static 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 | |||
| 1161 | static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope) { | 1259 | static 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 | } |
| 1210 | 1308 | ||
| 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); |
| 1274 | 1380 | ||
| 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 | } |
| 1426 | 1540 | ||
| 1427 | static 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 | } | ||
| 1467 | 1541 | ||
| 1468 | TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[], | 1542 | TypeTableEntry *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 | } |
| 492 | 489 | ||
| 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)); |
| 494 | 497 | ||
| 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 Int | 5239 | 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 | }; |
| 5790 | 5795 | ||
| 5791 | static void get_c_type(CodeGen *g, TypeTableEntry *type_entry, Buf *out_buf) { | 5796 | struct GenH { |
| 5797 | ZigList<TypeTableEntry *> types_to_declare; | ||
| 5798 | }; | ||
| 5799 | |||
| 5800 | static 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 | |||
| 5865 | static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf *out_buf) { | ||
| 5792 | assert(type_entry); | 5866 | assert(type_entry); |
| 5793 | 5867 | ||
| 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 | } |
| 5818 | 5892 | ||
| 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); |
| 5860 | 5936 | ||
| 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; |
| 5944 | 6034 | ||
| 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"); |
| 5946 | 6039 | ||
| 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; |
| 5972 | 6065 | ||
| 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); |
| 5975 | 6068 | ||
| 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) { |
| 5987 | 6080 | ||
| 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"); |
| 6029 | 6122 | ||
| 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)); |
| 6031 | 6195 | ||
| 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 | } |
| 274 | 274 | ||
| 275 | static constexpr IrInstructionId ir_instruction_id(IrInstructionSetCold *) { | ||
| 276 | return IrInstructionIdSetCold; | ||
| 277 | } | ||
| 278 | |||
| 275 | static constexpr IrInstructionId ir_instruction_id(IrInstructionSetDebugSafety *) { | 279 | static 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 | } |
| 1264 | 1268 | ||
| 1269 | static 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 | |||
| 1265 | static IrInstruction *ir_build_set_debug_safety(IrBuilder *irb, Scope *scope, AstNode *source_node, | 1278 | static 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 | } |
| 11557 | 11579 | ||
| 11580 | static 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 | } | ||
| 11558 | static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira, | 11610 | static 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 | } |
| 370 | 370 | ||
| 371 | static 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 | |||
| 371 | static void ir_print_set_debug_safety(IrPrint *irp, IrInstructionSetDebugSafety *instruction) { | 377 | static 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 | } |
| 2251 | 2251 | ||
| 2252 | /* | 2252 | /* |
| 2253 | FnProto = option("coldcc" | "nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("-&gt;" TypeExpr) | 2253 | FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("-&gt;" TypeExpr) |
| 2254 | */ | 2254 | */ |
| 2255 | static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) { | 2255 | static 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 |
| 2258 | 2258 | ||
| 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. Otherwise | 128 | /// 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. |
| 130 | pub coldcc fn abort() -> noreturn { | 130 | pub 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 | } |
| 149 | 150 | ||
| 150 | /// Exits the program cleanly with the specified status code. | 151 | /// Exits the program cleanly with the specified status code. |
| 151 | pub coldcc fn exit(status: u8) -> noreturn { | 152 | pub 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"); |
| 5 | 5 | ||
| 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. |
| 8 | pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn { | 8 | pub 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; |
| 74 | 74 | ||
| 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. |
| 77 | pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn { | 77 | pub 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 @@ |
| 6 | const builtin = @import("builtin"); | 6 | const builtin = @import("builtin"); |
| 7 | const std = @import("std"); | 7 | const std = @import("std"); |
| 8 | 8 | ||
| 9 | pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn { | 9 | pub 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 | |||
| 612 | test "cold function" { | ||
| 613 | thisIsAColdFn(); | ||
| 614 | comptime thisIsAColdFn(); | ||
| 615 | } | ||
| 616 | |||
| 617 | fn thisIsAColdFn() { | ||
| 618 | @setCold(true); | ||
| 619 | } |
test/compile_errors.zig+25-2| ... | @@ -1,6 +1,29 @@ | ... | @@ -1,6 +1,29 @@ |
| 1 | const tests = @import("tests.zig"); | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | ||
| 3 | pub fn addCases(cases: &tests.CompileErrorContext) { | 3 | pub 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'"); |
| 1591 | 1614 | ||
| 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'"); |
| 1600 | 1623 | ||
| 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 @@ | ||
| 1 | const tests = @import("tests.zig"); | ||
| 2 | |||
| 3 | pub 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"); |
| 19 | const assemble_and_link = @import("assemble_and_link.zig"); | 19 | const assemble_and_link = @import("assemble_and_link.zig"); |
| 20 | const debug_safety = @import("debug_safety.zig"); | 20 | const debug_safety = @import("debug_safety.zig"); |
| 21 | const translate_c = @import("translate_c.zig"); | 21 | const translate_c = @import("translate_c.zig"); |
| 22 | const gen_h = @import("gen_h.zig"); | ||
| 22 | 23 | ||
| 23 | const TestTarget = struct { | 24 | const 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 | } |
| 135 | 136 | ||
| 137 | pub 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 | |||
| 136 | pub fn addPkgTests(b: &build.Builder, test_filter: ?[]const u8, root_src: []const u8, | 152 | pub fn addPkgTests(b: &build.Builder, test_filter: ?[]const u8, root_src: []const u8, |
| 137 | name:[] const u8, desc: []const u8, with_lldb: bool) -> &build.Step | 153 | 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 | |||
| 997 | pub 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 | }; |