mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
290 lines
11 KiB
Plaintext
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,
|
|
};
|
|
|