stage2: improvements towards zig test

* There is now a main_pkg in addition to root_pkg. They are usually the
   same. When using `zig test`, main_pkg is the user's source file and
   root_pkg has the test runner.
 * scanDecl no longer looks for test decls outside the package being
   tested. honoring `--test-filter` is still TODO.
 * test runner main function has a void return value rather than
   `anyerror!void`
 * Sema is improved to generate better AIR for for loops on slices.
 * Sema: fix incorrect capacity calculation in zirBoolBr
 * Sema: add compile errors for trying to use slice fields as an lvalue.
 * Sema: fix type coercion for error unions
 * Sema: fix analyzeVarRef generating garbage AIR
 * C codegen: fix renderValue for error unions with 0 bit payload
 * C codegen: implement function pointer calls
 * CLI: fix usage text

 Adds 4 new AIR instructions:

  * slice_len, slice_ptr: to get the ptr and len fields of a slice.
  * slice_elem_val, ptr_slice_elem_val: to get the element value of
    a slice, and a pointer to a slice.

AstGen gains a new functionality:

 * One of the unused flags of struct decls is now used to indicate
   structs that are known to have non-zero size based on the AST alone.
This commit is contained in:
Andrew Kelley
2021-07-23 22:23:03 -07:00
parent f9798108f8
commit 7b8cb881df
25 changed files with 612 additions and 175 deletions

View File

@@ -35,8 +35,11 @@ comp: *Compilation,
/// Where our incremental compilation metadata serialization will go.
zig_cache_artifact_directory: Compilation.Directory,
/// Pointer to externally managed resource. `null` if there is no zig file being compiled.
/// Pointer to externally managed resource.
root_pkg: *Package,
/// Normally, `main_pkg` and `root_pkg` are the same. The exception is `zig test`, in which
/// `root_pkg` is the test runner, and `main_pkg` is the user's source file which has the tests.
main_pkg: *Package,
/// Used by AstGen worker to load and store ZIR cache.
global_zir_cache: Compilation.Directory,
@@ -598,6 +601,9 @@ pub const Struct = struct {
layout_wip,
have_layout,
},
/// If true, definitely nonzero size at runtime. If false, resolving the fields
/// is necessary to determine whether it has bits at runtime.
known_has_bits: bool,
pub const Field = struct {
/// Uses `noreturn` to indicate `anytype`.
@@ -2048,19 +2054,22 @@ pub fn deinit(mod: *Module) void {
mod.deletion_set.deinit(gpa);
// The callsite of `Compilation.create` owns the `root_pkg`, however
// The callsite of `Compilation.create` owns the `main_pkg`, however
// Module owns the builtin and std packages that it adds.
if (mod.root_pkg.table.fetchRemove("builtin")) |kv| {
if (mod.main_pkg.table.fetchRemove("builtin")) |kv| {
gpa.free(kv.key);
kv.value.destroy(gpa);
}
if (mod.root_pkg.table.fetchRemove("std")) |kv| {
if (mod.main_pkg.table.fetchRemove("std")) |kv| {
gpa.free(kv.key);
kv.value.destroy(gpa);
}
if (mod.root_pkg.table.fetchRemove("root")) |kv| {
if (mod.main_pkg.table.fetchRemove("root")) |kv| {
gpa.free(kv.key);
}
if (mod.root_pkg != mod.main_pkg) {
mod.root_pkg.destroy(gpa);
}
mod.compile_log_text.deinit(gpa);
@@ -2148,7 +2157,7 @@ pub fn astGenFile(mod: *Module, file: *Scope.File) !void {
const stat = try source_file.stat();
const want_local_cache = file.pkg == mod.root_pkg;
const want_local_cache = file.pkg == mod.main_pkg;
const digest = hash: {
var path_hash: Cache.HashHelper = .{};
path_hash.addBytes(build_options.version);
@@ -2792,6 +2801,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
.zir_index = undefined, // set below
.layout = .Auto,
.status = .none,
.known_has_bits = undefined,
.namespace = .{
.parent = null,
.ty = struct_ty,
@@ -3301,10 +3311,23 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
gop.value_ptr.* = new_decl;
// Exported decls, comptime decls, usingnamespace decls, and
// test decls if in test mode, get analyzed.
const decl_pkg = namespace.file_scope.pkg;
const want_analysis = is_exported or switch (decl_name_index) {
0 => true, // comptime decl
1 => mod.comp.bin_file.options.is_test, // test decl
else => is_named_test and mod.comp.bin_file.options.is_test,
1 => blk: {
// test decl with no name. Skip the part where we check against
// the test name filter.
if (!mod.comp.bin_file.options.is_test) break :blk false;
if (decl_pkg != mod.main_pkg) break :blk false;
break :blk true;
},
else => blk: {
if (!is_named_test) break :blk false;
if (!mod.comp.bin_file.options.is_test) break :blk false;
if (decl_pkg != mod.main_pkg) break :blk false;
// TODO check the name against --test-filter
break :blk true;
},
};
if (want_analysis) {
mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });