Skip to content

Commit cf39819

Browse files
committed
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
1 parent cacba6f commit cf39819

21 files changed

+682
-83
lines changed

build.zig

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ pub fn build(b: &Builder) -> %void {
118118
test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));
119119
test_step.dependOn(tests.addDebugSafetyTests(b, test_filter));
120120
test_step.dependOn(tests.addTranslateCTests(b, test_filter));
121+
test_step.dependOn(tests.addGenHTests(b, test_filter));
121122
}
122123

123124
fn dependOnLib(lib_exe_obj: &std.build.LibExeObjStep, dep: &const LibraryDep) {

doc/docgen.zig

+38-6
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ const Code = struct {
286286
TestError: []const u8,
287287
TestSafety: []const u8,
288288
Exe: ExpectedOutcome,
289-
Obj,
289+
Obj: ?[]const u8,
290290
};
291291
};
292292

@@ -442,9 +442,12 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) -> %Toc {
442442
code_kind_id = Code.Id { .TestSafety = name};
443443
name = "test";
444444
} else if (mem.eql(u8, code_kind_str, "obj")) {
445-
code_kind_id = Code.Id.Obj;
445+
code_kind_id = Code.Id { .Obj = null };
446+
} else if (mem.eql(u8, code_kind_str, "obj_err")) {
447+
code_kind_id = Code.Id { .Obj = name };
448+
name = "test";
446449
} else if (mem.eql(u8, code_kind_str, "syntax")) {
447-
code_kind_id = Code.Id.Obj;
450+
code_kind_id = Code.Id { .Obj = null };
448451
is_inline = true;
449452
} else {
450453
return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {}", code_kind_str);
@@ -861,13 +864,14 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io
861864
const colored_stderr = try termColor(allocator, escaped_stderr);
862865
try out.print("<pre><code class=\"shell\">$ zig test {}.zig\n{}</code></pre>\n", code.name, colored_stderr);
863866
},
864-
Code.Id.Obj => {
867+
Code.Id.Obj => |maybe_error_match| {
865868
const name_plus_obj_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, obj_ext);
866869
const tmp_obj_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_obj_ext);
867870
var build_args = std.ArrayList([]const u8).init(allocator);
868871
defer build_args.deinit();
869872

870873
try build_args.appendSlice([][]const u8 {zig_exe, "build-obj", tmp_source_file_name,
874+
"--color", "on",
871875
"--output", tmp_obj_file_name});
872876

873877
if (!code.is_inline) {
@@ -890,8 +894,36 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io
890894
},
891895
}
892896

893-
_ = exec(allocator, build_args.toSliceConst()) catch return parseError(
894-
tokenizer, code.source_token, "example failed to compile");
897+
if (maybe_error_match) |error_match| {
898+
const result = try os.ChildProcess.exec(allocator, build_args.toSliceConst(), null, null, max_doc_file_size);
899+
switch (result.term) {
900+
os.ChildProcess.Term.Exited => |exit_code| {
901+
if (exit_code == 0) {
902+
warn("{}\nThe following command incorrectly succeeded:\n", result.stderr);
903+
for (build_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
904+
return parseError(tokenizer, code.source_token, "example build incorrectly succeeded");
905+
}
906+
},
907+
else => {
908+
warn("{}\nThe following command crashed:\n", result.stderr);
909+
for (build_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
910+
return parseError(tokenizer, code.source_token, "example compile crashed");
911+
},
912+
}
913+
if (mem.indexOf(u8, result.stderr, error_match) == null) {
914+
warn("{}\nExpected to find '{}' in stderr", result.stderr, error_match);
915+
return parseError(tokenizer, code.source_token, "example did not have expected compile error message");
916+
}
917+
const escaped_stderr = try escapeHtml(allocator, result.stderr);
918+
const colored_stderr = try termColor(allocator, escaped_stderr);
919+
try out.print("\n{}\n", colored_stderr);
920+
if (!code.is_inline) {
921+
try out.print("</code></pre>\n");
922+
}
923+
} else {
924+
_ = exec(allocator, build_args.toSliceConst()) catch return parseError(
925+
tokenizer, code.source_token, "example failed to compile");
926+
}
895927
if (!code.is_inline) {
896928
try out.print("</code></pre>\n");
897929
}

doc/langref.html.in

+28-6
Original file line numberDiff line numberDiff line change
@@ -1909,7 +1909,22 @@ test "@tagName" {
19091909
assert(mem.eql(u8, @tagName(Small.Three), "Three"));
19101910
}
19111911
{#code_end#}
1912-
<p>TODO extern enum</p>
1912+
{#header_open|extern enum#}
1913+
<p>
1914+
By default, enums are not guaranteed to be compatible with the C ABI:
1915+
</p>
1916+
{#code_begin|obj_err|parameter of type 'Foo' not allowed in function with calling convention 'ccc'#}
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#}
19131928
<p>TODO packed enum</p>
19141929
{#see_also|@memberName|@memberCount|@tagName#}
19151930
{#header_close#}
@@ -2662,8 +2677,9 @@ export fn sub(a: i8, b: i8) -> i8 { return a - b; }
26622677
extern "kernel32" stdcallcc fn ExitProcess(exit_code: u32) -> noreturn;
26632678
extern "c" fn atan2(a: f64, b: f64) -> f64;
26642679

2665-
// coldcc makes a function use the cold calling convention.
2666-
coldcc fn abort() -> noreturn {
2680+
// The @setCold builtin tells the optimizer that a function is rarely called.
2681+
fn abort() -> noreturn {
2682+
@setCold(true);
26672683
while (true) {}
26682684
}
26692685

@@ -4300,6 +4316,12 @@ test "call foo" {
43004316
This function is only valid within function scope.
43014317
</p>
43024318
{#header_close#}
4319+
{#header_open|@setCold#}
4320+
<pre><code class="zig">@setCold(is_cold: bool)</code></pre>
4321+
<p>
4322+
Tells the optimizer that a function is rarely called.
4323+
</p>
4324+
{#header_close#}
43034325
{#header_open|@setDebugSafety#}
43044326
<pre><code class="zig">@setDebugSafety(scope, safety_on: bool)</code></pre>
43054327
<p>
@@ -5533,7 +5555,7 @@ UseDecl = "use" Expression ";"
55335555

55345556
ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";"
55355557

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)
55375559

55385560
FnDef = option("inline" | "export") FnProto Block
55395561

@@ -5739,8 +5761,8 @@ hljs.registerLanguage("zig", function(t) {
57395761
},
57405762
a = t.IR + "\\s*\\(",
57415763
c = {
5742-
keyword: "const align var extern stdcallcc coldcc nakedcc volatile export pub noalias inline struct packed enum union goto break return try catch test continue unreachable comptime and or asm defer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong",
5743-
built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setDebugSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate",
5764+
keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union goto break return try catch test continue unreachable comptime and or asm defer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong",
5765+
built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setDebugSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate",
57445766
literal: "true false null undefined"
57455767
},
57465768
n = [e, t.CLCM, t.CBCM, s, r];

src-self-hosted/parser.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ pub const Parser = struct {
211211
Token.Id.StringLiteral => {
212212
@panic("TODO extern with string literal");
213213
},
214-
Token.Id.Keyword_coldcc, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
214+
Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
215215
stack.append(State.TopLevel) catch unreachable;
216216
const fn_token = try self.eatToken(Token.Id.Keyword_fn);
217217
// TODO shouldn't need this cast

src-self-hosted/tokenizer.zig

-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ pub const Token = struct {
1616
KeywordId{.bytes="and", .id = Id.Keyword_and},
1717
KeywordId{.bytes="asm", .id = Id.Keyword_asm},
1818
KeywordId{.bytes="break", .id = Id.Keyword_break},
19-
KeywordId{.bytes="coldcc", .id = Id.Keyword_coldcc},
2019
KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
2120
KeywordId{.bytes="const", .id = Id.Keyword_const},
2221
KeywordId{.bytes="continue", .id = Id.Keyword_continue},
@@ -97,7 +96,6 @@ pub const Token = struct {
9796
Keyword_and,
9897
Keyword_asm,
9998
Keyword_break,
100-
Keyword_coldcc,
10199
Keyword_comptime,
102100
Keyword_const,
103101
Keyword_continue,

src/all_types.hpp

+12
Original file line numberDiff line numberDiff line change
@@ -1108,6 +1108,7 @@ struct TypeTableEntry {
11081108

11091109
bool zero_bits;
11101110
bool is_copyable;
1111+
bool gen_h_loop_flag;
11111112

11121113
union {
11131114
TypeTableEntryPointer pointer;
@@ -1204,6 +1205,9 @@ struct FnTableEntry {
12041205
AstNode *set_alignstack_node;
12051206
uint32_t alignstack_value;
12061207

1208+
AstNode *set_cold_node;
1209+
bool is_cold;
1210+
12071211
ZigList<FnExport> export_list;
12081212
bool calls_errorable_function;
12091213
};
@@ -1250,6 +1254,7 @@ enum BuiltinFnId {
12501254
BuiltinFnIdMod,
12511255
BuiltinFnIdTruncate,
12521256
BuiltinFnIdIntType,
1257+
BuiltinFnIdSetCold,
12531258
BuiltinFnIdSetDebugSafety,
12541259
BuiltinFnIdSetFloatMode,
12551260
BuiltinFnIdTypeName,
@@ -1830,6 +1835,7 @@ enum IrInstructionId {
18301835
IrInstructionIdTypeOf,
18311836
IrInstructionIdToPtrType,
18321837
IrInstructionIdPtrTypeChild,
1838+
IrInstructionIdSetCold,
18331839
IrInstructionIdSetDebugSafety,
18341840
IrInstructionIdSetFloatMode,
18351841
IrInstructionIdArrayType,
@@ -2202,6 +2208,12 @@ struct IrInstructionPtrTypeChild {
22022208
IrInstruction *value;
22032209
};
22042210

2211+
struct IrInstructionSetCold {
2212+
IrInstruction base;
2213+
2214+
IrInstruction *is_cold;
2215+
};
2216+
22052217
struct IrInstructionSetDebugSafety {
22062218
IrInstruction base;
22072219

0 commit comments

Comments
 (0)