Skip to content

Commit

Permalink
add new kind of test: generating .h files. and more
Browse files Browse the repository at this point in the history
 * 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
  • Loading branch information
andrewrk committed Jan 23, 2018
1 parent cacba6f commit cf39819
Show file tree
Hide file tree
Showing 21 changed files with 682 additions and 83 deletions.
1 change: 1 addition & 0 deletions build.zig
Expand Up @@ -118,6 +118,7 @@ pub fn build(b: &Builder) -> %void {
test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));
test_step.dependOn(tests.addDebugSafetyTests(b, test_filter));
test_step.dependOn(tests.addTranslateCTests(b, test_filter));
test_step.dependOn(tests.addGenHTests(b, test_filter));
}

fn dependOnLib(lib_exe_obj: &std.build.LibExeObjStep, dep: &const LibraryDep) {
Expand Down
44 changes: 38 additions & 6 deletions doc/docgen.zig
Expand Up @@ -286,7 +286,7 @@ const Code = struct {
TestError: []const u8,
TestSafety: []const u8,
Exe: ExpectedOutcome,
Obj,
Obj: ?[]const u8,
};
};

Expand Down Expand Up @@ -442,9 +442,12 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) -> %Toc {
code_kind_id = Code.Id { .TestSafety = name};
name = "test";
} else if (mem.eql(u8, code_kind_str, "obj")) {
code_kind_id = Code.Id.Obj;
code_kind_id = Code.Id { .Obj = null };
} else if (mem.eql(u8, code_kind_str, "obj_err")) {
code_kind_id = Code.Id { .Obj = name };
name = "test";
} else if (mem.eql(u8, code_kind_str, "syntax")) {
code_kind_id = Code.Id.Obj;
code_kind_id = Code.Id { .Obj = null };
is_inline = true;
} else {
return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {}", code_kind_str);
Expand Down Expand Up @@ -861,13 +864,14 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io
const colored_stderr = try termColor(allocator, escaped_stderr);
try out.print("<pre><code class=\"shell\">$ zig test {}.zig\n{}</code></pre>\n", code.name, colored_stderr);
},
Code.Id.Obj => {
Code.Id.Obj => |maybe_error_match| {
const name_plus_obj_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, obj_ext);
const tmp_obj_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_obj_ext);
var build_args = std.ArrayList([]const u8).init(allocator);
defer build_args.deinit();

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

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

_ = exec(allocator, build_args.toSliceConst()) catch return parseError(
tokenizer, code.source_token, "example failed to compile");
if (maybe_error_match) |error_match| {
const result = try os.ChildProcess.exec(allocator, build_args.toSliceConst(), null, null, max_doc_file_size);
switch (result.term) {
os.ChildProcess.Term.Exited => |exit_code| {
if (exit_code == 0) {
warn("{}\nThe following command incorrectly succeeded:\n", result.stderr);
for (build_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
return parseError(tokenizer, code.source_token, "example build incorrectly succeeded");
}
},
else => {
warn("{}\nThe following command crashed:\n", result.stderr);
for (build_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
return parseError(tokenizer, code.source_token, "example compile crashed");
},
}
if (mem.indexOf(u8, result.stderr, error_match) == null) {
warn("{}\nExpected to find '{}' in stderr", result.stderr, error_match);
return parseError(tokenizer, code.source_token, "example did not have expected compile error message");
}
const escaped_stderr = try escapeHtml(allocator, result.stderr);
const colored_stderr = try termColor(allocator, escaped_stderr);
try out.print("\n{}\n", colored_stderr);
if (!code.is_inline) {
try out.print("</code></pre>\n");
}
} else {
_ = exec(allocator, build_args.toSliceConst()) catch return parseError(
tokenizer, code.source_token, "example failed to compile");
}
if (!code.is_inline) {
try out.print("</code></pre>\n");
}
Expand Down
34 changes: 28 additions & 6 deletions doc/langref.html.in
Expand Up @@ -1909,7 +1909,22 @@ test "@tagName" {
assert(mem.eql(u8, @tagName(Small.Three), "Three"));
}
{#code_end#}
<p>TODO extern enum</p>
{#header_open|extern enum#}
<p>
By default, enums are not guaranteed to be compatible with the C ABI:
</p>
{#code_begin|obj_err|parameter of type 'Foo' not allowed in function with calling convention 'ccc'#}
const Foo = enum { A, B, C };
export fn entry(foo: Foo) { }
{#code_end#}
<p>
For a C-ABI-compatible enum, use <code class="zig">extern enum</code>:
</p>
{#code_begin|obj#}
const Foo = extern enum { A, B, C };
export fn entry(foo: Foo) { }
{#code_end#}
{#header_close#}
<p>TODO packed enum</p>
{#see_also|@memberName|@memberCount|@tagName#}
{#header_close#}
Expand Down Expand Up @@ -2662,8 +2677,9 @@ export fn sub(a: i8, b: i8) -> i8 { return a - b; }
extern "kernel32" stdcallcc fn ExitProcess(exit_code: u32) -> noreturn;
extern "c" fn atan2(a: f64, b: f64) -> f64;

// coldcc makes a function use the cold calling convention.
coldcc fn abort() -> noreturn {
// The @setCold builtin tells the optimizer that a function is rarely called.
fn abort() -> noreturn {
@setCold(true);
while (true) {}
}

Expand Down Expand Up @@ -4300,6 +4316,12 @@ test "call foo" {
This function is only valid within function scope.
</p>
{#header_close#}
{#header_open|@setCold#}
<pre><code class="zig">@setCold(is_cold: bool)</code></pre>
<p>
Tells the optimizer that a function is rarely called.
</p>
{#header_close#}
{#header_open|@setDebugSafety#}
<pre><code class="zig">@setDebugSafety(scope, safety_on: bool)</code></pre>
<p>
Expand Down Expand Up @@ -5533,7 +5555,7 @@ UseDecl = "use" Expression ";"

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

FnProto = option("coldcc" | "nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("-&gt;" TypeExpr)
FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("-&gt;" TypeExpr)

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

Expand Down Expand Up @@ -5739,8 +5761,8 @@ hljs.registerLanguage("zig", function(t) {
},
a = t.IR + "\\s*\\(",
c = {
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",
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",
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",
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",
literal: "true false null undefined"
},
n = [e, t.CLCM, t.CBCM, s, r];
Expand Down
2 changes: 1 addition & 1 deletion src-self-hosted/parser.zig
Expand Up @@ -211,7 +211,7 @@ pub const Parser = struct {
Token.Id.StringLiteral => {
@panic("TODO extern with string literal");
},
Token.Id.Keyword_coldcc, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
stack.append(State.TopLevel) catch unreachable;
const fn_token = try self.eatToken(Token.Id.Keyword_fn);
// TODO shouldn't need this cast
Expand Down
2 changes: 0 additions & 2 deletions src-self-hosted/tokenizer.zig
Expand Up @@ -16,7 +16,6 @@ pub const Token = struct {
KeywordId{.bytes="and", .id = Id.Keyword_and},
KeywordId{.bytes="asm", .id = Id.Keyword_asm},
KeywordId{.bytes="break", .id = Id.Keyword_break},
KeywordId{.bytes="coldcc", .id = Id.Keyword_coldcc},
KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
KeywordId{.bytes="const", .id = Id.Keyword_const},
KeywordId{.bytes="continue", .id = Id.Keyword_continue},
Expand Down Expand Up @@ -97,7 +96,6 @@ pub const Token = struct {
Keyword_and,
Keyword_asm,
Keyword_break,
Keyword_coldcc,
Keyword_comptime,
Keyword_const,
Keyword_continue,
Expand Down
12 changes: 12 additions & 0 deletions src/all_types.hpp
Expand Up @@ -1108,6 +1108,7 @@ struct TypeTableEntry {

bool zero_bits;
bool is_copyable;
bool gen_h_loop_flag;

union {
TypeTableEntryPointer pointer;
Expand Down Expand Up @@ -1204,6 +1205,9 @@ struct FnTableEntry {
AstNode *set_alignstack_node;
uint32_t alignstack_value;

AstNode *set_cold_node;
bool is_cold;

ZigList<FnExport> export_list;
bool calls_errorable_function;
};
Expand Down Expand Up @@ -1250,6 +1254,7 @@ enum BuiltinFnId {
BuiltinFnIdMod,
BuiltinFnIdTruncate,
BuiltinFnIdIntType,
BuiltinFnIdSetCold,
BuiltinFnIdSetDebugSafety,
BuiltinFnIdSetFloatMode,
BuiltinFnIdTypeName,
Expand Down Expand Up @@ -1830,6 +1835,7 @@ enum IrInstructionId {
IrInstructionIdTypeOf,
IrInstructionIdToPtrType,
IrInstructionIdPtrTypeChild,
IrInstructionIdSetCold,
IrInstructionIdSetDebugSafety,
IrInstructionIdSetFloatMode,
IrInstructionIdArrayType,
Expand Down Expand Up @@ -2202,6 +2208,12 @@ struct IrInstructionPtrTypeChild {
IrInstruction *value;
};

struct IrInstructionSetCold {
IrInstruction base;

IrInstruction *is_cold;
};

struct IrInstructionSetDebugSafety {
IrInstruction base;

Expand Down

0 comments on commit cf39819

Please sign in to comment.