zig/BRANCH_TODO
2021-04-29 16:57:13 -07:00

290 lines
11 KiB
Plaintext

* modify dbg_stmt ZIR instructions to have line/column rather than node indexes
* AstGen threadlocal
* extern "foo" for vars and for functions
* namespace decls table can't reference ZIR memory because it can get modified on updates
- change it for astgen worker to compare old and new ZIR, updating existing
namespaces & decls, and creating a changelist.
* reimplement semaDecl
* use a hash map for instructions because the array is too big
- no, actually modify the Zir.Inst.Ref strategy so that each decl gets
their indexes starting at 0 so that we can use an array to store Sema
results rather than a map.
* keep track of file dependencies/dependants
* unload files from memory when a dependency is dropped
* implement the new AstGen compile errors
* get rid of failed_root_src_file
* get rid of Scope.DeclRef
* get rid of NameHash
* handle decl collision with usingnamespace
* the decl doing the looking up needs to create a decl dependency
on each usingnamespace decl
* handle usingnamespace cycles
* compile error for return inside defer expression
* when block has noreturn statement
- avoid emitting defers
- compile error for unreachable code
* detect `return error.Foo` and emit ZIR that unconditionally generates errdefers
* `return`: check return operand and generate errdefers if necessary
* have failed_trees and just put the file in there
- this way we can emit all the parse errors not just the first one
- but maybe we want just the first one?
* need a var decl zir instruction which includes the name because we need to do the
compile error for a local shadowing a decl with Sema looking up the decl name.
- this means LocalVal and LocalPtr should use the string table
* sort compile errors before presenting them to eliminate nondeterministic error reporting
* when handling decls, catch the error and continue, so that
AstGen can report more than one compile error.
* AstGen: add result location pointers to function calls
* nested function decl: how to refer to params?
* fix the commented out behavior test regarding function alignment
- not sure why this happened, it's stage1 code??
- search the behavior test diff for "TODO"
* memory efficiency: add another representation for structs which use
natural alignment for fields and do not have any comptime fields. this
will save 16 bytes per struct field in the compilation.
fn getAnonTypeName(mod: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 {
// TODO add namespaces, generic function signatrues
const tree = scope.tree();
const token_tags = tree.tokens.items(.tag);
const base_name = switch (token_tags[base_token]) {
.keyword_struct => "struct",
.keyword_enum => "enum",
.keyword_union => "union",
.keyword_opaque => "opaque",
else => unreachable,
};
const loc = tree.tokenLocation(0, base_token);
return std.fmt.allocPrint(mod.gpa, "{s}:{d}:{d}", .{ base_name, loc.line, loc.column });
}
/// Returns `true` if the Decl type changed.
/// Returns `true` if this is the first time analyzing the Decl.
/// Returns `false` otherwise.
fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {
switch (node_tags[decl_node]) {
.@"usingnamespace" => {
decl.analysis = .in_progress;
var code: Zir = blk: {
var astgen = try AstGen.init(mod, decl, &analysis_arena.allocator);
defer astgen.deinit();
var gen_scope: Scope.GenZir = .{
.force_comptime = true,
.parent = &decl.namespace.base,
.astgen = &astgen,
};
defer gen_scope.instructions.deinit(mod.gpa);
const ns_type = try AstGen.typeExpr(&gen_scope, &gen_scope.base, type_expr);
};
try decl.namespace.usingnamespace_set.put(mod.gpa, ty.getNamespace().?, is_pub);
decl.analysis = .complete;
decl.generation = mod.generation;
return true;
},
else => unreachable,
}
}
if (mod.lookupIdentifier(scope, ident_name)) |decl| {
const msg = msg: {
const msg = try mod.errMsg(
scope,
name_src,
"redeclaration of '{s}'",
.{ident_name},
);
errdefer msg.destroy(gpa);
try mod.errNoteNonLazy(decl.srcLoc(), msg, "previously declared here", .{});
break :msg msg;
};
return mod.failWithOwnedErrorMsg(scope, msg);
}
const error_set = try arena.create(Module.ErrorSet);
error_set.* = .{
.owner_decl = astgen.decl,
.node_offset = astgen.decl.nodeIndexToRelative(node),
.names_ptr = fields.ptr,
.names_len = @intCast(u32, fields.len),
};
const error_set_ty = try Type.Tag.error_set.create(arena, error_set);
const error_set_val = try Value.Tag.ty.create(arena, error_set_ty);
const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
.ty = Type.initTag(.type),
.val = error_set_val,
});
const decl_index = try mod.declareDeclDependency(astgen.decl, new_decl);
const result = try gz.addDecl(.decl_val, decl_index, node);
return rvalue(gz, scope, rl, result, node);
// when implementing this be sure to add test coverage for the asm return type
// not resolving into a type (the node_offset_asm_ret_ty field of LazySrcLoc)
pub fn analyzeNamespace(
mod: *Module,
namespace: *Scope.Namespace,
decls: []const ast.Node.Index,
) InnerError!void {
for (decls) |decl_node| switch (node_tags[decl_node]) {
.@"comptime" => {
const name_index = mod.getNextAnonNameIndex();
const name = try std.fmt.allocPrint(mod.gpa, "__comptime_{d}", .{name_index});
defer mod.gpa.free(name);
const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash);
namespace.decls.putAssumeCapacity(new_decl, {});
mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
},
// Container fields are handled in AstGen.
.container_field_init,
.container_field_align,
.container_field,
=> continue,
.test_decl => {
if (mod.comp.bin_file.options.is_test) {
log.err("TODO: analyze test decl", .{});
}
},
.@"usingnamespace" => {
const name_index = mod.getNextAnonNameIndex();
const name = try std.fmt.allocPrint(mod.gpa, "__usingnamespace_{d}", .{name_index});
defer mod.gpa.free(name);
const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash);
namespace.decls.putAssumeCapacity(new_decl, {});
mod.ensureDeclAnalyzed(new_decl) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.AnalysisFail => continue,
};
},
else => unreachable,
};
}
/// Trailing:
/// 0. `EmitH` if `module.emit_h != null`.
/// 1. A per-Decl link object. Represents the position of the code in the output file.
/// This is populated regardless of semantic analysis and code generation.
/// Depending on the target, will be one of:
/// * Elf.TextBlock
/// * Coff.TextBlock
/// * MachO.TextBlock
/// * C.DeclBlock
/// * Wasm.DeclBlock
/// * void
/// 2. If it is a function, a per-Decl link function object. Represents the
/// function in the linked output file, if the `Decl` is a function.
/// This is stored here and not in `Fn` because `Decl` survives across updates but
/// `Fn` does not. Depending on the target, will be one of:
/// * Elf.SrcFn
/// * Coff.SrcFn
/// * MachO.SrcFn
/// * C.FnBlock
/// * Wasm.FnData
/// * SpirV.FnData
extra_index += @boolToInt(has_align);
extra_index += @boolToInt(has_section);
/// Contains un-analyzed ZIR instructions generated from Zig source AST.
/// Even after we finish analysis, the ZIR is kept in memory, so that
/// comptime and inline function calls can happen.
/// Parameter names are stored here so that they may be referenced for debug info,
/// without having source code bytes loaded into memory.
/// The number of parameters is determined by referring to the type.
/// The first N elements of `extra` are indexes into `string_bytes` to
/// a null-terminated string.
/// This memory is managed with gpa, must be freed when the function is freed.
zir: Zir,
if (fn_proto.lib_name) |lib_name_token| blk: {
log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str});
mod.comp.stage1AddLinkLib(lib_name_str) catch |err| {
return mod.failTok(
&fn_type_scope.base,
lib_name_token,
"unable to add link lib '{s}': {s}",
.{ lib_name_str, @errorName(err) },
);
};
const target = mod.comp.getTarget();
if (target_util.is_libc_lib_name(target, lib_name_str)) {
if (!mod.comp.bin_file.options.link_libc) {
return mod.failTok(
&fn_type_scope.base,
lib_name_token,
"dependency on libc must be explicitly specified in the build command",
.{},
);
}
break :blk;
}
if (target_util.is_libcpp_lib_name(target, lib_name_str)) {
if (!mod.comp.bin_file.options.link_libcpp) {
return mod.failTok(
&fn_type_scope.base,
lib_name_token,
"dependency on libc++ must be explicitly specified in the build command",
.{},
);
}
break :blk;
}
if (!target.isWasm() and !mod.comp.bin_file.options.pic) {
return mod.failTok(
&fn_type_scope.base,
lib_name_token,
"dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.",
.{ lib_name_str, lib_name_str },
);
}
}
const is_inline = decl_tv.ty.fnCallingConvention() == .Inline;
const anal_state: Fn.Analysis = if (is_inline) .inline_only else .queued;
new_func.* = .{
.state = anal_state,
.zir = fn_zir,
.body = undefined,
.owner_decl = decl,
};
fn_payload.* = .{
.base = .{ .tag = .function },
.data = new_func,
};