From a5379aa3ee376197820f1526c88921607a787a11 Mon Sep 17 00:00:00 2001
From: Vexu
Date: Thu, 18 Jun 2020 20:45:48 +0300
Subject: implement `@src`
---
src/codegen.cpp | 1 +
1 file changed, 1 insertion(+)
(limited to 'src/codegen.cpp')
diff --git a/src/codegen.cpp b/src/codegen.cpp
index f945dd6545..e20d6d60f5 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -8714,6 +8714,7 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdBitSizeof, "bitSizeOf", 1);
create_builtin_fn(g, BuiltinFnIdWasmMemorySize, "wasmMemorySize", 1);
create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2);
+ create_builtin_fn(g, BuiltinFnIdSrc, "src", 0);
}
static const char *bool_to_str(bool b) {
--
cgit v1.2.3
From 7e58c56ca72099f6e71752289be7165947bfaa04 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 17 Jun 2020 04:29:54 -0400
Subject: self-hosted: implement Decl lookup
* Take advantage of coercing anonymous struct literals to struct types.
* Reworks Module to favor Zig source as the primary use case.
Breaks ZIR compilation, which will have to be restored in a future commit.
* Decl uses src_index rather then src, pointing to an AST Decl node
index, or ZIR Module Decl index, rather than a byte offset.
* ZIR instructions have an `analyzed_inst` field instead of Module
having a hash table.
* Module.Fn loses the `fn_type` field since it is redundant with
its `owner_decl` `TypedValue` type.
* Implement Type and Value copying. A ZIR Const instruction's TypedValue
is copied to the Decl arena during analysis, which allows freeing the
ZIR text instructions post-analysis.
* Don't flush the ELF file if there are compilation errors.
* Function return types allow arbitrarily complex expressions.
* AST->ZIR for function calls and return statements.
---
lib/std/zig/ast.zig | 2 +
src-self-hosted/Module.zig | 950 +++++++++++++++++++++++------------------
src-self-hosted/TypedValue.zig | 8 +
src-self-hosted/codegen.zig | 19 +-
src-self-hosted/ir.zig | 9 +
src-self-hosted/link.zig | 12 +-
src-self-hosted/type.zig | 97 ++++-
src-self-hosted/value.zig | 120 +++++-
src-self-hosted/zir.zig | 74 +++-
src/codegen.cpp | 6 +
10 files changed, 867 insertions(+), 430 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig
index 4d63011266..370f42b463 100644
--- a/lib/std/zig/ast.zig
+++ b/lib/std/zig/ast.zig
@@ -2260,6 +2260,8 @@ pub const Node = struct {
}
};
+ /// TODO break this into separate Break, Continue, Return AST Nodes to save memory.
+ /// Could be further broken into LabeledBreak, LabeledContinue, and ReturnVoid to save even more.
pub const ControlFlowExpression = struct {
base: Node = Node{ .id = .ControlFlowExpression },
ltoken: TokenIndex,
diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig
index a157c53491..1dfca43c8d 100644
--- a/src-self-hosted/Module.zig
+++ b/src-self-hosted/Module.zig
@@ -37,10 +37,10 @@ decl_exports: std.AutoHashMap(*Decl, []*Export),
/// This table owns the Export memory.
export_owners: std.AutoHashMap(*Decl, []*Export),
/// Maps fully qualified namespaced names to the Decl struct for them.
-decl_table: std.AutoHashMap(Decl.Hash, *Decl),
+decl_table: std.AutoHashMap(Scope.NameHash, *Decl),
optimize_mode: std.builtin.Mode,
-link_error_flags: link.ElfFile.ErrorFlags = link.ElfFile.ErrorFlags{},
+link_error_flags: link.ElfFile.ErrorFlags = .{},
work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
@@ -64,20 +64,15 @@ generation: u32 = 0,
/// Candidates for deletion. After a semantic analysis update completes, this list
/// contains Decls that need to be deleted if they end up having no references to them.
-deletion_set: std.ArrayListUnmanaged(*Decl) = std.ArrayListUnmanaged(*Decl){},
+deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
const WorkItem = union(enum) {
/// Write the machine code for a Decl to the output file.
codegen_decl: *Decl,
/// Decl has been determined to be outdated; perform semantic analysis again.
re_analyze_decl: *Decl,
- /// This AST node needs to be converted to a Decl and then semantically analyzed.
- ast_gen_decl: AstGenDecl,
-
- const AstGenDecl = struct {
- ast_node: *ast.Node,
- scope: *Scope,
- };
+ /// The Decl needs to be analyzed and possibly export itself.
+ analyze_decl: *Decl,
};
pub const Export = struct {
@@ -111,9 +106,9 @@ pub const Decl = struct {
/// The direct parent container of the Decl. This is either a `Scope.File` or `Scope.ZIRModule`.
/// Reference to externally owned memory.
scope: *Scope,
- /// Byte offset into the source file that contains this declaration.
- /// This is the base offset that src offsets within this Decl are relative to.
- src: usize,
+ /// The AST Node decl index or ZIR Inst index that contains this declaration.
+ /// Must be recomputed when the corresponding source file is modified.
+ src_index: usize,
/// The most recent value of the Decl after a successful semantic analysis.
typed_value: union(enum) {
never_succeeded: void,
@@ -124,6 +119,9 @@ pub const Decl = struct {
/// analysis of the function body is performed with this value set to `success`. Functions
/// have their own analysis status field.
analysis: enum {
+ /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore
+ /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced.
+ unreferenced,
/// Semantic analysis for this Decl is running right now. This state detects dependency loops.
in_progress,
/// This Decl might be OK but it depends on another one which did not successfully complete
@@ -133,6 +131,10 @@ pub const Decl = struct {
/// There will be a corresponding ErrorMsg in Module.failed_decls.
sema_failure,
/// There will be a corresponding ErrorMsg in Module.failed_decls.
+ /// This indicates the failure was something like running out of disk space,
+ /// and attempting semantic analysis again may succeed.
+ sema_failure_retryable,
+ /// There will be a corresponding ErrorMsg in Module.failed_decls.
codegen_failure,
/// There will be a corresponding ErrorMsg in Module.failed_decls.
/// This indicates the failure was something like running out of disk space,
@@ -158,7 +160,7 @@ pub const Decl = struct {
/// This is populated regardless of semantic analysis and code generation.
link: link.ElfFile.TextBlock = link.ElfFile.TextBlock.empty,
- contents_hash: Hash,
+ contents_hash: std.zig.SrcHash,
/// The shallow set of other decls whose typed_value could possibly change if this Decl's
/// typed_value is modified.
@@ -177,19 +179,28 @@ pub const Decl = struct {
allocator.destroy(self);
}
- pub const Hash = [16]u8;
-
- pub fn hashSimpleName(name: []const u8) Hash {
- return std.zig.hashSrc(name);
+ pub fn src(self: Decl) usize {
+ switch (self.scope.tag) {
+ .file => {
+ const file = @fieldParentPtr(Scope.File, "base", self.scope);
+ const tree = file.contents.tree;
+ const decl_node = tree.root_node.decls()[self.src_index];
+ return tree.token_locs[decl_node.firstToken()].start;
+ },
+ .zir_module => {
+ const zir_module = @fieldParentPtr(Scope.ZIRModule, "base", self.scope);
+ const module = zir_module.contents.module;
+ const decl_inst = module.decls[self.src_index];
+ return decl_inst.src;
+ },
+ .block => unreachable,
+ .gen_zir => unreachable,
+ .decl => unreachable,
+ }
}
- /// Must generate unique bytes with no collisions with other decls.
- /// The point of hashing here is only to limit the number of bytes of
- /// the unique identifier to a fixed size (16 bytes).
- pub fn fullyQualifiedNameHash(self: Decl) Hash {
- // Right now we only have ZIRModule as the source. So this is simply the
- // relative name of the decl.
- return hashSimpleName(mem.spanZ(self.name));
+ pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash {
+ return self.scope.fullyQualifiedNameHash(mem.spanZ(self.name));
}
pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {
@@ -247,11 +258,9 @@ pub const Decl = struct {
/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
pub const Fn = struct {
/// This memory owned by the Decl's TypedValue.Managed arena allocator.
- fn_type: Type,
analysis: union(enum) {
- /// The value is the source instruction.
- queued: *zir.Inst.Fn,
- in_progress: *Analysis,
+ queued: *ZIR,
+ in_progress,
/// There will be a corresponding ErrorMsg in Module.failed_decls
sema_failure,
/// This Fn might be OK but it depends on another Decl which did not successfully complete
@@ -265,16 +274,20 @@ pub const Fn = struct {
/// of Fn analysis.
pub const Analysis = struct {
inner_block: Scope.Block,
- /// TODO Performance optimization idea: instead of this inst_table,
- /// use a field in the zir.Inst instead to track corresponding instructions
- inst_table: std.AutoHashMap(*zir.Inst, *Inst),
- needed_inst_capacity: usize,
+ };
+
+ /// Contains un-analyzed ZIR instructions generated from Zig source AST.
+ pub const ZIR = struct {
+ body: zir.Module.Body,
+ arena: std.heap.ArenaAllocator.State,
};
};
pub const Scope = struct {
tag: Tag,
+ pub const NameHash = [16]u8;
+
pub fn cast(base: *Scope, comptime T: type) ?*T {
if (base.tag != T.base_tag)
return null;
@@ -288,6 +301,7 @@ pub const Scope = struct {
switch (self.tag) {
.block => return self.cast(Block).?.arena,
.decl => return &self.cast(DeclAnalysis).?.arena.allocator,
+ .gen_zir => return &self.cast(GenZIR).?.arena.allocator,
.zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
.file => unreachable,
}
@@ -298,6 +312,7 @@ pub const Scope = struct {
pub fn decl(self: *Scope) ?*Decl {
return switch (self.tag) {
.block => self.cast(Block).?.decl,
+ .gen_zir => self.cast(GenZIR).?.decl,
.decl => self.cast(DeclAnalysis).?.decl,
.zir_module => null,
.file => null,
@@ -309,11 +324,25 @@ pub const Scope = struct {
pub fn namespace(self: *Scope) *Scope {
switch (self.tag) {
.block => return self.cast(Block).?.decl.scope,
+ .gen_zir => return self.cast(GenZIR).?.decl.scope,
.decl => return self.cast(DeclAnalysis).?.decl.scope,
.zir_module, .file => return self,
}
}
+ /// Must generate unique bytes with no collisions with other decls.
+ /// The point of hashing here is only to limit the number of bytes of
+ /// the unique identifier to a fixed size (16 bytes).
+ pub fn fullyQualifiedNameHash(self: *Scope, name: []const u8) NameHash {
+ switch (self.tag) {
+ .block => unreachable,
+ .gen_zir => unreachable,
+ .decl => unreachable,
+ .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
+ .file => return self.cast(File).?.fullyQualifiedNameHash(name),
+ }
+ }
+
/// Asserts the scope is a child of a File and has an AST tree and returns the tree.
pub fn tree(self: *Scope) *ast.Tree {
switch (self.tag) {
@@ -321,6 +350,7 @@ pub const Scope = struct {
.zir_module => unreachable,
.decl => return self.cast(DeclAnalysis).?.decl.scope.cast(File).?.contents.tree,
.block => return self.cast(Block).?.decl.scope.cast(File).?.contents.tree,
+ .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(File).?.contents.tree,
}
}
@@ -343,6 +373,7 @@ pub const Scope = struct {
.file => return @fieldParentPtr(File, "base", base).sub_file_path,
.zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
.block => unreachable,
+ .gen_zir => unreachable,
.decl => unreachable,
}
}
@@ -352,6 +383,7 @@ pub const Scope = struct {
.file => return @fieldParentPtr(File, "base", base).unload(allocator),
.zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(allocator),
.block => unreachable,
+ .gen_zir => unreachable,
.decl => unreachable,
}
}
@@ -360,6 +392,7 @@ pub const Scope = struct {
switch (base.tag) {
.file => return @fieldParentPtr(File, "base", base).getSource(module),
.zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),
+ .gen_zir => unreachable,
.block => unreachable,
.decl => unreachable,
}
@@ -379,6 +412,7 @@ pub const Scope = struct {
allocator.destroy(scope_zir_module);
},
.block => unreachable,
+ .gen_zir => unreachable,
.decl => unreachable,
}
}
@@ -390,6 +424,7 @@ pub const Scope = struct {
file,
block,
decl,
+ gen_zir,
};
pub const File = struct {
@@ -461,6 +496,11 @@ pub const Scope = struct {
.bytes => |bytes| return bytes,
}
}
+
+ pub fn fullyQualifiedNameHash(self: *File, name: []const u8) NameHash {
+ // We don't have struct scopes yet so this is currently just a simple name hash.
+ return std.zig.hashSrc(name);
+ }
};
pub const ZIRModule = struct {
@@ -541,6 +581,11 @@ pub const Scope = struct {
.bytes => |bytes| return bytes,
}
}
+
+ pub fn fullyQualifiedNameHash(self: *ZIRModule, name: []const u8) NameHash {
+ // ZIR modules only have 1 file with all decls global in the same namespace.
+ return std.zig.hashSrc(name);
+ }
};
/// This is a temporary structure, references to it are valid only
@@ -548,7 +593,7 @@ pub const Scope = struct {
pub const Block = struct {
pub const base_tag: Tag = .block;
base: Scope = Scope{ .tag = base_tag },
- func: *Fn,
+ func: ?*Fn,
decl: *Decl,
instructions: ArrayListUnmanaged(*Inst),
/// Points to the arena allocator of DeclAnalysis
@@ -563,6 +608,16 @@ pub const Scope = struct {
decl: *Decl,
arena: std.heap.ArenaAllocator,
};
+
+ /// This is a temporary structure, references to it are valid only
+ /// during semantic analysis of the decl.
+ pub const GenZIR = struct {
+ pub const base_tag: Tag = .gen_zir;
+ base: Scope = Scope{ .tag = base_tag },
+ decl: *Decl,
+ arena: std.heap.ArenaAllocator,
+ instructions: std.ArrayList(*zir.Inst),
+ };
};
pub const Body = struct {
@@ -656,7 +711,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
.bin_file_path = options.bin_file_path,
.bin_file = bin_file,
.optimize_mode = options.optimize_mode,
- .decl_table = std.AutoHashMap(Decl.Hash, *Decl).init(gpa),
+ .decl_table = std.AutoHashMap(Scope.NameHash, *Decl).init(gpa),
.decl_exports = std.AutoHashMap(*Decl, []*Export).init(gpa),
.export_owners = std.AutoHashMap(*Decl, []*Export).init(gpa),
.failed_decls = std.AutoHashMap(*Decl, *ErrorMsg).init(gpa),
@@ -765,14 +820,14 @@ pub fn update(self: *Module) !void {
try self.deleteDecl(decl);
}
+ self.link_error_flags = self.bin_file.error_flags;
+
// If there are any errors, we anticipate the source files being loaded
// to report error messages. Otherwise we unload all source files to save memory.
if (self.totalErrorCount() == 0) {
self.root_scope.unload(self.allocator);
+ try self.bin_file.flush();
}
-
- try self.bin_file.flush();
- self.link_error_flags = self.bin_file.error_flags;
}
/// Having the file open for writing is problematic as far as executing the
@@ -852,12 +907,14 @@ const InnerError = error{ OutOfMemory, AnalysisFail };
pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
while (self.work_queue.readItem()) |work_item| switch (work_item) {
.codegen_decl => |decl| switch (decl.analysis) {
+ .unreferenced => unreachable,
.in_progress => unreachable,
.outdated => unreachable,
.sema_failure,
.codegen_failure,
.dependency_failure,
+ .sema_failure_retryable,
=> continue,
.complete, .codegen_failure_retryable => {
@@ -865,12 +922,10 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
switch (payload.func.analysis) {
.queued => self.analyzeFnBody(decl, payload.func) catch |err| switch (err) {
error.AnalysisFail => {
- if (payload.func.analysis == .queued) {
- payload.func.analysis = .dependency_failure;
- }
+ assert(payload.func.analysis != .in_progress);
continue;
},
- else => |e| return e,
+ error.OutOfMemory => return error.OutOfMemory,
},
.in_progress => unreachable,
.sema_failure, .dependency_failure => continue,
@@ -889,7 +944,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
self.allocator,
- decl.src,
+ decl.src(),
"unable to codegen: {}",
.{@errorName(err)},
));
@@ -899,6 +954,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
},
},
.re_analyze_decl => |decl| switch (decl.analysis) {
+ .unreferenced => unreachable,
.in_progress => unreachable,
.sema_failure,
@@ -906,6 +962,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
.dependency_failure,
.complete,
.codegen_failure_retryable,
+ .sema_failure_retryable,
=> continue,
.outdated => {
@@ -918,7 +975,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
self.allocator,
- decl.src,
+ decl.src(),
"unable to load source file '{}': {}",
.{ zir_scope.sub_file_path, @errorName(err) },
));
@@ -929,7 +986,8 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
const decl_name = mem.spanZ(decl.name);
// We already detected deletions, so we know this will be found.
const src_decl = zir_module.findDecl(decl_name).?;
- self.reAnalyzeDecl(decl, src_decl) catch |err| switch (err) {
+ decl.src_index = src_decl.index;
+ self.reAnalyzeDecl(decl, src_decl.decl) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.AnalysisFail => continue,
};
@@ -938,8 +996,8 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
}
},
},
- .ast_gen_decl => |item| {
- self.astGenDecl(item.scope, item.ast_node) catch |err| switch (err) {
+ .analyze_decl => |decl| {
+ self.ensureDeclAnalyzed(decl) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.AnalysisFail => continue,
};
@@ -947,51 +1005,83 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
};
}
-fn astGenDecl(self: *Module, parent_scope: *Scope, ast_node: *ast.Node) !void {
+fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
+ switch (decl.analysis) {
+ .in_progress => unreachable,
+ .outdated => unreachable,
+
+ .sema_failure,
+ .sema_failure_retryable,
+ .codegen_failure,
+ .dependency_failure,
+ .codegen_failure_retryable,
+ => return error.AnalysisFail,
+
+ .complete => return,
+
+ .unreferenced => {
+ self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.AnalysisFail => return error.AnalysisFail,
+ else => {
+ try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
+ self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
+ self.allocator,
+ decl.src(),
+ "unable to analyze: {}",
+ .{@errorName(err)},
+ ));
+ decl.analysis = .sema_failure_retryable;
+ return error.AnalysisFail;
+ },
+ };
+ },
+ }
+}
+
+fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void {
+ const file_scope = decl.scope.cast(Scope.File).?;
+ const tree = try self.getAstTree(file_scope);
+ const ast_node = tree.root_node.decls()[decl.src_index];
switch (ast_node.id) {
.FnProto => {
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node);
- const name_tok = fn_proto.name_token orelse
- return self.failTok(parent_scope, fn_proto.fn_token, "missing function name", .{});
- const tree = parent_scope.tree();
- const name_loc = tree.token_locs[name_tok];
- const name = tree.tokenSliceLoc(name_loc);
- const name_hash = Decl.hashSimpleName(name);
- const contents_hash = std.zig.hashSrc(tree.getNodeSource(ast_node));
- const new_decl = try self.createNewDecl(parent_scope, name, name_loc.start, name_hash, contents_hash);
-
- // This DeclAnalysis scope's arena memory is discarded after the ZIR generation
- // pass completes, and semantic analysis of it completes.
- var gen_scope: Scope.DeclAnalysis = .{
- .decl = new_decl,
+ decl.analysis = .in_progress;
+
+ // This arena allocator's memory is discarded at the end of this function. It is used
+ // to determine the type of the function, and hence the type of the decl, which is needed
+ // to complete the Decl analysis.
+ var fn_type_scope: Scope.GenZIR = .{
+ .decl = decl,
.arena = std.heap.ArenaAllocator.init(self.allocator),
+ .instructions = std.ArrayList(*zir.Inst).init(self.allocator),
};
- // TODO free this memory
- //defer gen_scope.arena.deinit();
+ defer fn_type_scope.arena.deinit();
+ defer fn_type_scope.instructions.deinit();
const body_node = fn_proto.body_node orelse
- return self.failTok(&gen_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{});
+ return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{});
if (fn_proto.params_len != 0) {
return self.failTok(
- &gen_scope.base,
+ &fn_type_scope.base,
fn_proto.params()[0].name_token.?,
"TODO implement function parameters",
.{},
);
}
if (fn_proto.lib_name) |lib_name| {
- return self.failNode(&gen_scope.base, lib_name, "TODO implement function library name", .{});
+ return self.failNode(&fn_type_scope.base, lib_name, "TODO implement function library name", .{});
}
if (fn_proto.align_expr) |align_expr| {
- return self.failNode(&gen_scope.base, align_expr, "TODO implement function align expression", .{});
+ return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{});
}
if (fn_proto.section_expr) |sect_expr| {
- return self.failNode(&gen_scope.base, sect_expr, "TODO implement function section expression", .{});
+ return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{});
}
if (fn_proto.callconv_expr) |callconv_expr| {
return self.failNode(
- &gen_scope.base,
+ &fn_type_scope.base,
callconv_expr,
"TODO implement function calling convention expression",
.{},
@@ -999,82 +1089,94 @@ fn astGenDecl(self: *Module, parent_scope: *Scope, ast_node: *ast.Node) !void {
}
const return_type_expr = switch (fn_proto.return_type) {
.Explicit => |node| node,
- .InferErrorSet => |node| return self.failNode(&gen_scope.base, node, "TODO implement inferred error sets", .{}),
- .Invalid => |tok| return self.failTok(&gen_scope.base, tok, "unable to parse return type", .{}),
+ .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}),
+ .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}),
};
- const return_type_inst = try self.astGenExpr(&gen_scope.base, return_type_expr);
- const body_block = body_node.cast(ast.Node.Block).?;
- const body = try self.astGenBlock(&gen_scope.base, body_block);
- const fn_type_inst = try gen_scope.arena.allocator.create(zir.Inst.FnType);
- fn_type_inst.* = .{
- .base = .{
- .tag = zir.Inst.FnType.base_tag,
- .name = "",
- .src = name_loc.start,
- },
- .positionals = .{
- .return_type = return_type_inst,
- .param_types = &[0]*zir.Inst{},
- },
- .kw_args = .{},
+ const return_type_inst = try self.astGenExpr(&fn_type_scope.base, return_type_expr);
+ const fn_src = tree.token_locs[fn_proto.fn_token].start;
+ const fn_type_inst = try self.addZIRInst(&fn_type_scope.base, fn_src, zir.Inst.FnType, .{
+ .return_type = return_type_inst,
+ .param_types = &[0]*zir.Inst{},
+ }, .{});
+ _ = try self.addZIRInst(&fn_type_scope.base, fn_src, zir.Inst.Return, .{ .operand = fn_type_inst }, .{});
+
+ // We need the memory for the Type to go into the arena for the Decl
+ var decl_arena = std.heap.ArenaAllocator.init(self.allocator);
+ errdefer decl_arena.deinit();
+ const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
+
+ var block_scope: Scope.Block = .{
+ .func = null,
+ .decl = decl,
+ .instructions = .{},
+ .arena = &decl_arena.allocator,
};
- const fn_inst = try gen_scope.arena.allocator.create(zir.Inst.Fn);
- fn_inst.* = .{
- .base = .{
- .tag = zir.Inst.Fn.base_tag,
- .name = name,
- .src = name_loc.start,
- .contents_hash = contents_hash,
- },
- .positionals = .{
- .fn_type = &fn_type_inst.base,
- .body = body,
+ defer block_scope.instructions.deinit(self.allocator);
+
+ const fn_type = try self.analyzeBodyValueAsType(&block_scope, .{
+ .instructions = fn_type_scope.instructions.items,
+ });
+ const new_func = try decl_arena.allocator.create(Fn);
+ const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
+
+ const fn_zir = blk: {
+ // This scope's arena memory is discarded after the ZIR generation
+ // pass completes, and semantic analysis of it completes.
+ var gen_scope: Scope.GenZIR = .{
+ .decl = decl,
+ .arena = std.heap.ArenaAllocator.init(self.allocator),
+ .instructions = std.ArrayList(*zir.Inst).init(self.allocator),
+ };
+ errdefer gen_scope.arena.deinit();
+ defer gen_scope.instructions.deinit();
+
+ const body_block = body_node.cast(ast.Node.Block).?;
+
+ try self.astGenBlock(&gen_scope.base, body_block);
+
+ const fn_zir = try gen_scope.arena.allocator.create(Fn.ZIR);
+ fn_zir.* = .{
+ .body = .{
+ .instructions = try gen_scope.arena.allocator.dupe(*zir.Inst, gen_scope.instructions.items),
+ },
+ .arena = gen_scope.arena.state,
+ };
+ break :blk fn_zir;
+ };
+
+ new_func.* = .{
+ .analysis = .{ .queued = fn_zir },
+ .owner_decl = decl,
+ };
+ fn_payload.* = .{ .func = new_func };
+
+ decl_arena_state.* = decl_arena.state;
+ decl.typed_value = .{
+ .most_recent = .{
+ .typed_value = .{
+ .ty = fn_type,
+ .val = Value.initPayload(&fn_payload.base),
+ },
+ .arena = decl_arena_state,
},
- .kw_args = .{},
};
- try self.analyzeNewDecl(new_decl, &fn_inst.base);
+ decl.analysis = .complete;
+ decl.generation = self.generation;
+
+ // We don't fully codegen the decl until later, but we do need to reserve a global
+ // offset table index for it. This allows us to codegen decls out of dependency order,
+ // increasing how many computations can be done in parallel.
+ try self.bin_file.allocateDeclIndexes(decl);
+ try self.work_queue.writeItem(.{ .codegen_decl = decl });
if (fn_proto.extern_export_inline_token) |maybe_export_token| {
if (tree.token_ids[maybe_export_token] == .Keyword_export) {
- var str_inst = zir.Inst.Str{
- .base = .{
- .tag = zir.Inst.Str.base_tag,
- .name = "",
- .src = name_loc.start,
- },
- .positionals = .{
- .bytes = name,
- },
- .kw_args = .{},
- };
- var ref_inst = zir.Inst.Ref{
- .base = .{
- .tag = zir.Inst.Ref.base_tag,
- .name = "",
- .src = name_loc.start,
- },
- .positionals = .{
- .operand = &str_inst.base,
- },
- .kw_args = .{},
- };
- var export_inst = zir.Inst.Export{
- .base = .{
- .tag = zir.Inst.Export.base_tag,
- .name = "",
- .src = name_loc.start,
- .contents_hash = contents_hash,
- },
- .positionals = .{
- .symbol_name = &ref_inst.base,
- .value = &fn_inst.base,
- },
- .kw_args = .{},
- };
- // Here we analyze the export using the arena that expires at the end of this
- // function call.
- try self.analyzeExport(&gen_scope.base, &export_inst);
+ const export_src = tree.token_locs[maybe_export_token].start;
+ const name_loc = tree.token_locs[fn_proto.name_token.?];
+ const name = tree.tokenSliceLoc(name_loc);
+ // The scope needs to have the decl in it.
+ try self.analyzeExport(&block_scope.base, export_src, name, decl);
}
}
},
@@ -1085,6 +1187,19 @@ fn astGenDecl(self: *Module, parent_scope: *Scope, ast_node: *ast.Node) !void {
}
}
+fn analyzeBodyValueAsType(self: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type {
+ try self.analyzeBody(&block_scope.base, body);
+ for (block_scope.instructions.items) |inst| {
+ if (inst.cast(Inst.Ret)) |ret| {
+ const val = try self.resolveConstValue(&block_scope.base, ret.args.operand);
+ return val.toType();
+ } else {
+ return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
+ }
+ }
+ unreachable;
+}
+
fn astGenExpr(self: *Module, scope: *Scope, ast_node: *ast.Node) InnerError!*zir.Inst {
switch (ast_node.id) {
.Identifier => return self.astGenIdent(scope, @fieldParentPtr(ast.Node.Identifier, "base", ast_node)),
@@ -1092,11 +1207,33 @@ fn astGenExpr(self: *Module, scope: *Scope, ast_node: *ast.Node) InnerError!*zir
.StringLiteral => return self.astGenStringLiteral(scope, @fieldParentPtr(ast.Node.StringLiteral, "base", ast_node)),
.IntegerLiteral => return self.astGenIntegerLiteral(scope, @fieldParentPtr(ast.Node.IntegerLiteral, "base", ast_node)),
.BuiltinCall => return self.astGenBuiltinCall(scope, @fieldParentPtr(ast.Node.BuiltinCall, "base", ast_node)),
+ .Call => return self.astGenCall(scope, @fieldParentPtr(ast.Node.Call, "base", ast_node)),
.Unreachable => return self.astGenUnreachable(scope, @fieldParentPtr(ast.Node.Unreachable, "base", ast_node)),
+ .ControlFlowExpression => return self.astGenControlFlowExpression(scope, @fieldParentPtr(ast.Node.ControlFlowExpression, "base", ast_node)),
else => return self.failNode(scope, ast_node, "TODO implement astGenExpr for {}", .{@tagName(ast_node.id)}),
}
}
+fn astGenControlFlowExpression(
+ self: *Module,
+ scope: *Scope,
+ cfe: *ast.Node.ControlFlowExpression,
+) InnerError!*zir.Inst {
+ switch (cfe.kind) {
+ .Break => return self.failNode(scope, &cfe.base, "TODO implement astGenExpr for Break", .{}),
+ .Continue => return self.failNode(scope, &cfe.base, "TODO implement astGenExpr for Continue", .{}),
+ .Return => {},
+ }
+ const tree = scope.tree();
+ const src = tree.token_locs[cfe.ltoken].start;
+ if (cfe.rhs) |rhs_node| {
+ const operand = try self.astGenExpr(scope, rhs_node);
+ return self.addZIRInst(scope, src, zir.Inst.Return, .{ .operand = operand }, .{});
+ } else {
+ return self.addZIRInst(scope, src, zir.Inst.ReturnVoid, .{}, .{});
+ }
+}
+
fn astGenIdent(self: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerError!*zir.Inst {
const tree = scope.tree();
const ident_name = tree.tokenSlice(ident.token);
@@ -1105,19 +1242,8 @@ fn astGenIdent(self: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerE
}
if (getSimplePrimitiveValue(ident_name)) |typed_value| {
- const const_inst = try scope.arena().create(zir.Inst.Const);
- const_inst.* = .{
- .base = .{
- .tag = zir.Inst.Const.base_tag,
- .name = "",
- .src = tree.token_locs[ident.token].start,
- },
- .positionals = .{
- .typed_value = typed_value,
- },
- .kw_args = .{},
- };
- return &const_inst.base;
+ const src = tree.token_locs[ident.token].start;
+ return self.addZIRInstConst(scope, src, typed_value);
}
if (ident_name.len >= 2) integer: {
@@ -1137,7 +1263,15 @@ fn astGenIdent(self: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerE
}
}
- return self.failNode(scope, &ident.base, "TODO implement identifier lookup", .{});
+ // Decl lookup
+ const namespace = scope.namespace();
+ const name_hash = namespace.fullyQualifiedNameHash(ident_name);
+ if (self.decl_table.getValue(name_hash)) |decl| {
+ const src = tree.token_locs[ident.token].start;
+ return try self.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});
+ }
+
+ return self.failNode(scope, &ident.base, "TODO implement local variable identifier lookup", .{});
}
fn astGenStringLiteral(self: *Module, scope: *Scope, str_lit: *ast.Node.StringLiteral) InnerError!*zir.Inst {
@@ -1155,31 +1289,9 @@ fn astGenStringLiteral(self: *Module, scope: *Scope, str_lit: *ast.Node.StringLi
else => |e| return e,
};
- var str_inst = try arena.create(zir.Inst.Str);
- str_inst.* = .{
- .base = .{
- .tag = zir.Inst.Str.base_tag,
- .name = "",
- .src = tree.token_locs[str_lit.token].start,
- },
- .positionals = .{
- .bytes = bytes,
- },
- .kw_args = .{},
- };
- var ref_inst = try arena.create(zir.Inst.Ref);
- ref_inst.* = .{
- .base = .{
- .tag = zir.Inst.Ref.base_tag,
- .name = "",
- .src = tree.token_locs[str_lit.token].start,
- },
- .positionals = .{
- .operand = &str_inst.base,
- },
- .kw_args = .{},
- };
- return &ref_inst.base;
+ const src = tree.token_locs[str_lit.token].start;
+ const str_inst = try self.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
+ return self.addZIRInst(scope, src, zir.Inst.Ref, .{ .operand = str_inst }, .{});
}
fn astGenIntegerLiteral(self: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral) InnerError!*zir.Inst {
@@ -1195,48 +1307,25 @@ fn astGenIntegerLiteral(self: *Module, scope: *Scope, int_lit: *ast.Node.Integer
return self.failTok(scope, int_lit.token, "TODO implement 0b int prefix", .{});
}
if (std.fmt.parseInt(u64, bytes, 10)) |small_int| {
- var int_payload = try arena.create(Value.Payload.Int_u64);
- int_payload.* = .{
- .int = small_int,
- };
- var const_inst = try arena.create(zir.Inst.Const);
- const_inst.* = .{
- .base = .{
- .tag = zir.Inst.Const.base_tag,
- .name = "",
- .src = tree.token_locs[int_lit.token].start,
- },
- .positionals = .{
- .typed_value = .{
- .ty = Type.initTag(.comptime_int),
- .val = Value.initPayload(&int_payload.base),
- },
- },
- .kw_args = .{},
- };
- return &const_inst.base;
+ const int_payload = try arena.create(Value.Payload.Int_u64);
+ int_payload.* = .{ .int = small_int };
+ const src = tree.token_locs[int_lit.token].start;
+ return self.addZIRInstConst(scope, src, .{
+ .ty = Type.initTag(.comptime_int),
+ .val = Value.initPayload(&int_payload.base),
+ });
} else |err| {
return self.failTok(scope, int_lit.token, "TODO implement int literals that don't fit in a u64", .{});
}
}
-fn astGenBlock(self: *Module, scope: *Scope, block_node: *ast.Node.Block) !zir.Module.Body {
+fn astGenBlock(self: *Module, scope: *Scope, block_node: *ast.Node.Block) !void {
if (block_node.label) |label| {
return self.failTok(scope, label, "TODO implement labeled blocks", .{});
}
- const arena = scope.arena();
- var instructions = std.ArrayList(*zir.Inst).init(arena);
-
- try instructions.ensureCapacity(block_node.statements_len);
-
for (block_node.statements()) |statement| {
- const inst = try self.astGenExpr(scope, statement);
- instructions.appendAssumeCapacity(inst);
+ _ = try self.astGenExpr(scope, statement);
}
-
- return zir.Module.Body{
- .instructions = instructions.items,
- };
}
fn astGenAsm(self: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {
@@ -1255,84 +1344,59 @@ fn astGenAsm(self: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*
args[i] = try self.astGenExpr(scope, input.expr);
}
- const return_type = try arena.create(zir.Inst.Const);
- return_type.* = .{
- .base = .{
- .tag = zir.Inst.Const.base_tag,
- .name = "",
- .src = tree.token_locs[asm_node.asm_token].start,
- },
- .positionals = .{
- .typed_value = .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.void_type),
- },
- },
- .kw_args = .{},
- };
-
- const asm_inst = try arena.create(zir.Inst.Asm);
- asm_inst.* = .{
- .base = .{
- .tag = zir.Inst.Asm.base_tag,
- .name = "",
- .src = tree.token_locs[asm_node.asm_token].start,
- },
- .positionals = .{
- .asm_source = try self.astGenExpr(scope, asm_node.template),
- .return_type = &return_type.base,
- },
- .kw_args = .{
- .@"volatile" = asm_node.volatile_token != null,
- //.clobbers = TODO handle clobbers
- .inputs = inputs,
- .args = args,
- },
- };
- return &asm_inst.base;
+ const src = tree.token_locs[asm_node.asm_token].start;
+ const return_type = try self.addZIRInstConst(scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.void_type),
+ });
+ const asm_inst = try self.addZIRInst(scope, src, zir.Inst.Asm, .{
+ .asm_source = try self.astGenExpr(scope, asm_node.template),
+ .return_type = return_type,
+ }, .{
+ .@"volatile" = asm_node.volatile_token != null,
+ //.clobbers = TODO handle clobbers
+ .inputs = inputs,
+ .args = args,
+ });
+ return asm_inst;
}
fn astGenBuiltinCall(self: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
const tree = scope.tree();
const builtin_name = tree.tokenSlice(call.builtin_token);
- const arena = scope.arena();
if (mem.eql(u8, builtin_name, "@ptrToInt")) {
if (call.params_len != 1) {
return self.failTok(scope, call.builtin_token, "expected 1 parameter, found {}", .{call.params_len});
}
- const ptrtoint = try arena.create(zir.Inst.PtrToInt);
- ptrtoint.* = .{
- .base = .{
- .tag = zir.Inst.PtrToInt.base_tag,
- .name = "",
- .src = tree.token_locs[call.builtin_token].start,
- },
- .positionals = .{
- .ptr = try self.astGenExpr(scope, call.params()[0]),
- },
- .kw_args = .{},
- };
- return &ptrtoint.base;
+ const src = tree.token_locs[call.builtin_token].start;
+ return self.addZIRInst(scope, src, zir.Inst.PtrToInt, .{
+ .ptr = try self.astGenExpr(scope, call.params()[0]),
+ }, .{});
} else {
return self.failTok(scope, call.builtin_token, "TODO implement builtin call for '{}'", .{builtin_name});
}
}
+fn astGenCall(self: *Module, scope: *Scope, call: *ast.Node.Call) InnerError!*zir.Inst {
+ const tree = scope.tree();
+
+ if (call.params_len != 0) {
+ return self.failNode(scope, &call.base, "TODO implement fn calls with parameters", .{});
+ }
+ const lhs = try self.astGenExpr(scope, call.lhs);
+
+ const src = tree.token_locs[call.lhs.firstToken()].start;
+ return self.addZIRInst(scope, src, zir.Inst.Call, .{
+ .func = lhs,
+ .args = &[0]*zir.Inst{},
+ }, .{});
+}
+
fn astGenUnreachable(self: *Module, scope: *Scope, unreach_node: *ast.Node.Unreachable) InnerError!*zir.Inst {
const tree = scope.tree();
- const arena = scope.arena();
- const unreach = try arena.create(zir.Inst.Unreachable);
- unreach.* = .{
- .base = .{
- .tag = zir.Inst.Unreachable.base_tag,
- .name = "",
- .src = tree.token_locs[unreach_node.token].start,
- },
- .positionals = .{},
- .kw_args = .{},
- };
- return &unreach.base;
+ const src = tree.token_locs[unreach_node.token].start;
+ return self.addZIRInst(scope, src, zir.Inst.Unreachable, .{}, .{});
}
fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
@@ -1501,19 +1565,23 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
try self.work_queue.ensureUnusedCapacity(decls.len);
- for (decls) |decl| {
- if (decl.cast(ast.Node.FnProto)) |proto_decl| {
- if (proto_decl.extern_export_inline_token) |maybe_export_token| {
+ for (decls) |src_decl, decl_i| {
+ if (src_decl.cast(ast.Node.FnProto)) |fn_proto| {
+ // We will create a Decl for it regardless of analysis status.
+ const name_tok = fn_proto.name_token orelse
+ @panic("TODO handle missing function name in the parser");
+ const name_loc = tree.token_locs[name_tok];
+ const name = tree.tokenSliceLoc(name_loc);
+ const name_hash = root_scope.fullyQualifiedNameHash(name);
+ const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
+ const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);
+ if (fn_proto.extern_export_inline_token) |maybe_export_token| {
if (tree.token_ids[maybe_export_token] == .Keyword_export) {
- self.work_queue.writeItemAssumeCapacity(.{
- .ast_gen_decl = .{
- .ast_node = decl,
- .scope = &root_scope.base,
- },
- });
+ self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
}
}
}
+ // TODO also look for global variable declarations
// TODO also look for comptime blocks and exported globals
}
},
@@ -1567,7 +1635,7 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
}
for (src_module.decls) |src_decl| {
- const name_hash = Decl.hashSimpleName(src_decl.name);
+ const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name);
if (self.decl_table.get(name_hash)) |kv| {
const decl = kv.value;
deleted_decls.removeAssertDiscard(decl);
@@ -1664,36 +1732,33 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
// Use the Decl's arena for function memory.
var arena = decl.typed_value.most_recent.arena.?.promote(self.allocator);
defer decl.typed_value.most_recent.arena.?.* = arena.state;
- var analysis: Fn.Analysis = .{
- .inner_block = .{
- .func = func,
- .decl = decl,
- .instructions = .{},
- .arena = &arena.allocator,
- },
- .needed_inst_capacity = 0,
- .inst_table = std.AutoHashMap(*zir.Inst, *Inst).init(self.allocator),
+ var inner_block: Scope.Block = .{
+ .func = func,
+ .decl = decl,
+ .instructions = .{},
+ .arena = &arena.allocator,
};
- defer analysis.inner_block.instructions.deinit(self.allocator);
- defer analysis.inst_table.deinit();
+ defer inner_block.instructions.deinit(self.allocator);
- const fn_inst = func.analysis.queued;
- func.analysis = .{ .in_progress = &analysis };
+ const fn_zir = func.analysis.queued;
+ defer fn_zir.arena.promote(self.allocator).deinit();
+ func.analysis = .{ .in_progress = {} };
+ std.debug.warn("set {} to in_progress\n", .{decl.name});
- try self.analyzeBody(&analysis.inner_block.base, fn_inst.positionals.body);
+ try self.analyzeBody(&inner_block.base, fn_zir.body);
- func.analysis = .{
- .success = .{
- .instructions = try arena.allocator.dupe(*Inst, analysis.inner_block.instructions.items),
- },
- };
+ const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
+ func.analysis = .{ .success = .{ .instructions = instructions } };
+ std.debug.warn("set {} to success\n", .{decl.name});
}
fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!void {
switch (decl.analysis) {
+ .unreferenced => unreachable,
.in_progress => unreachable,
.dependency_failure,
.sema_failure,
+ .sema_failure_retryable,
.codegen_failure,
.codegen_failure_retryable,
.complete,
@@ -1702,7 +1767,6 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi
.outdated => {}, // Decl re-analysis
}
//std.debug.warn("re-analyzing {}\n", .{decl.name});
- decl.src = old_inst.src;
// The exports this Decl performs will be re-discovered, so we remove them here
// prior to re-analysis.
@@ -1771,11 +1835,13 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi
if (type_changed or typed_value.val.tag() != .function) {
for (decl.dependants.items) |dep| {
switch (dep.analysis) {
+ .unreferenced => unreachable,
.in_progress => unreachable,
.outdated => continue, // already queued for update
.dependency_failure,
.sema_failure,
+ .sema_failure_retryable,
.codegen_failure,
.codegen_failure_retryable,
.complete,
@@ -1799,16 +1865,16 @@ fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
fn allocateNewDecl(
self: *Module,
scope: *Scope,
- src: usize,
+ src_index: usize,
contents_hash: std.zig.SrcHash,
) !*Decl {
const new_decl = try self.allocator.create(Decl);
new_decl.* = .{
.name = "",
.scope = scope.namespace(),
- .src = src,
+ .src_index = src_index,
.typed_value = .{ .never_succeeded = {} },
- .analysis = .in_progress,
+ .analysis = .unreferenced,
.deletion_flag = false,
.contents_hash = contents_hash,
.link = link.ElfFile.TextBlock.empty,
@@ -1821,12 +1887,12 @@ fn createNewDecl(
self: *Module,
scope: *Scope,
decl_name: []const u8,
- src: usize,
- name_hash: Decl.Hash,
+ src_index: usize,
+ name_hash: Scope.NameHash,
contents_hash: std.zig.SrcHash,
) !*Decl {
try self.decl_table.ensureCapacity(self.decl_table.size + 1);
- const new_decl = try self.allocateNewDecl(scope, src, contents_hash);
+ const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash);
errdefer self.allocator.destroy(new_decl);
new_decl.name = try mem.dupeZ(self.allocator, u8, decl_name);
self.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl);
@@ -1840,6 +1906,8 @@ fn analyzeNewDecl(self: *Module, new_decl: *Decl, old_inst: *zir.Inst) InnerErro
};
errdefer decl_scope.arena.deinit();
+ new_decl.analysis = .in_progress;
+
const typed_value = self.analyzeConstInst(&decl_scope.base, old_inst) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.AnalysisFail => {
@@ -1873,37 +1941,40 @@ fn analyzeNewDecl(self: *Module, new_decl: *Decl, old_inst: *zir.Inst) InnerErro
}
fn resolveDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {
- if (old_inst.name.len == 0) {
- // If the name is empty, then we make this an anonymous Decl.
- const new_decl = try self.allocateNewDecl(scope, old_inst.src, old_inst.contents_hash);
- try self.analyzeNewDecl(new_decl, old_inst);
- return new_decl;
- }
- const name_hash = Decl.hashSimpleName(old_inst.name);
- if (self.decl_table.get(name_hash)) |kv| {
- const decl = kv.value;
- try self.reAnalyzeDecl(decl, old_inst);
- return decl;
- } else if (old_inst.cast(zir.Inst.DeclVal)) |decl_val| {
- // This is just a named reference to another decl.
- return self.analyzeDeclVal(scope, decl_val);
- } else {
- const new_decl = try self.createNewDecl(scope, old_inst.name, old_inst.src, name_hash, old_inst.contents_hash);
- try self.analyzeNewDecl(new_decl, old_inst);
-
- return new_decl;
- }
+ assert(old_inst.name.len == 0);
+ // If the name is empty, then we make this an anonymous Decl.
+ const scope_decl = scope.decl().?;
+ const new_decl = try self.allocateNewDecl(scope, scope_decl.src_index, old_inst.contents_hash);
+ try self.analyzeNewDecl(new_decl, old_inst);
+ return new_decl;
+ //const name_hash = Decl.hashSimpleName(old_inst.name);
+ //if (self.decl_table.get(name_hash)) |kv| {
+ // const decl = kv.value;
+ // decl.src = old_inst.src;
+ // try self.reAnalyzeDecl(decl, old_inst);
+ // return decl;
+ //} else if (old_inst.cast(zir.Inst.DeclVal)) |decl_val| {
+ // // This is just a named reference to another decl.
+ // return self.analyzeDeclVal(scope, decl_val);
+ //} else {
+ // const new_decl = try self.createNewDecl(scope, old_inst.name, old_inst.src, name_hash, old_inst.contents_hash);
+ // try self.analyzeNewDecl(new_decl, old_inst);
+
+ // return new_decl;
+ //}
}
/// Declares a dependency on the decl.
fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {
const decl = try self.resolveDecl(scope, old_inst);
switch (decl.analysis) {
+ .unreferenced => unreachable,
.in_progress => unreachable,
.outdated => unreachable,
.dependency_failure,
.sema_failure,
+ .sema_failure_retryable,
.codegen_failure,
.codegen_failure_retryable,
=> return error.AnalysisFail,
@@ -1916,20 +1987,9 @@ fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerE
return decl;
}
+/// TODO look into removing this function
fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
- if (scope.cast(Scope.Block)) |block| {
- if (block.func.analysis.in_progress.inst_table.get(old_inst)) |kv| {
- return kv.value;
- }
- }
-
- if (scope.namespace().tag == .zir_module) {
- const decl = try self.resolveCompleteDecl(scope, old_inst);
- const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl);
- return self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);
- }
-
- return self.analyzeInst(scope, old_inst);
+ return old_inst.analyzed_inst;
}
fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
@@ -1977,21 +2037,15 @@ fn resolveType(self: *Module, scope: *Scope, old_inst: *zir.Inst) !Type {
return val.toType();
}
-fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!void {
- try self.decl_exports.ensureCapacity(self.decl_exports.size + 1);
- try self.export_owners.ensureCapacity(self.export_owners.size + 1);
- const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name);
- const exported_decl = try self.resolveCompleteDecl(scope, export_inst.positionals.value);
+fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const u8, exported_decl: *Decl) !void {
const typed_value = exported_decl.typed_value.most_recent.typed_value;
switch (typed_value.ty.zigTypeTag()) {
.Fn => {},
- else => return self.fail(
- scope,
- export_inst.positionals.value.src,
- "unable to export type '{}'",
- .{typed_value.ty},
- ),
+ else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}),
}
+ try self.decl_exports.ensureCapacity(self.decl_exports.size + 1);
+ try self.export_owners.ensureCapacity(self.export_owners.size + 1);
+
const new_export = try self.allocator.create(Export);
errdefer self.allocator.destroy(new_export);
@@ -1999,7 +2053,7 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In
new_export.* = .{
.options = .{ .name = symbol_name },
- .src = export_inst.base.src,
+ .src = src,
.link = .{},
.owner_decl = owner_decl,
.exported_decl = exported_decl,
@@ -2030,7 +2084,7 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In
try self.failed_exports.ensureCapacity(self.failed_exports.size + 1);
self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
self.allocator,
- export_inst.base.src,
+ src,
"unable to export: {}",
.{@errorName(err)},
));
@@ -2039,7 +2093,6 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In
};
}
-/// TODO should not need the cast on the last parameter at the callsites
fn addNewInstArgs(
self: *Module,
block: *Scope.Block,
@@ -2053,6 +2106,47 @@ fn addNewInstArgs(
return &inst.base;
}
+fn newZIRInst(
+ allocator: *Allocator,
+ src: usize,
+ comptime T: type,
+ positionals: std.meta.fieldInfo(T, "positionals").field_type,
+ kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
+) !*zir.Inst {
+ const inst = try allocator.create(T);
+ inst.* = .{
+ .base = .{
+ .tag = T.base_tag,
+ .name = "",
+ .src = src,
+ },
+ .positionals = positionals,
+ .kw_args = kw_args,
+ };
+ return &inst.base;
+}
+
+fn addZIRInst(
+ self: *Module,
+ scope: *Scope,
+ src: usize,
+ comptime T: type,
+ positionals: std.meta.fieldInfo(T, "positionals").field_type,
+ kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
+) !*zir.Inst {
+ const gen_zir = scope.cast(Scope.GenZIR).?;
+ try gen_zir.instructions.ensureCapacity(gen_zir.instructions.items.len + 1);
+ const inst = try newZIRInst(&gen_zir.arena.allocator, src, T, positionals, kw_args);
+ gen_zir.instructions.appendAssumeCapacity(inst);
+ return inst;
+}
+
+/// TODO The existence of this function is a workaround for a bug in stage1.
+fn addZIRInstConst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst {
+ const P = std.meta.fieldInfo(zir.Inst.Const, "positionals").field_type;
+ return self.addZIRInst(scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});
+}
+
fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime T: type) !*T {
const inst = try block.arena.create(T);
inst.* = .{
@@ -2107,6 +2201,13 @@ fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
});
}
+fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
+ return self.constInst(scope, src, .{
+ .ty = Type.initTag(.noreturn),
+ .val = Value.initTag(.the_one_possible_value),
+ });
+}
+
fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
return self.constInst(scope, src, .{
.ty = ty,
@@ -2179,7 +2280,10 @@ fn analyzeConstInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerErro
}
fn analyzeInstConst(self: *Module, scope: *Scope, const_inst: *zir.Inst.Const) InnerError!*Inst {
- return self.constInst(scope, const_inst.base.src, const_inst.positionals.typed_value);
+ // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions
+ // after analysis.
+ const typed_value_copy = try const_inst.positionals.typed_value.copy(scope.arena());
+ return self.constInst(scope, const_inst.base.src, typed_value_copy);
}
fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
@@ -2190,6 +2294,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
.@"const" => return self.analyzeInstConst(scope, old_inst.cast(zir.Inst.Const).?),
.declref => return self.analyzeInstDeclRef(scope, old_inst.cast(zir.Inst.DeclRef).?),
.declval => return self.analyzeInstDeclVal(scope, old_inst.cast(zir.Inst.DeclVal).?),
+ .declval_in_module => return self.analyzeInstDeclValInModule(scope, old_inst.cast(zir.Inst.DeclValInModule).?),
.str => {
const bytes = old_inst.cast(zir.Inst.Str).?.positionals.bytes;
// The bytes references memory inside the ZIR module, which can get deallocated
@@ -2208,11 +2313,9 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
.@"asm" => return self.analyzeInstAsm(scope, old_inst.cast(zir.Inst.Asm).?),
.@"unreachable" => return self.analyzeInstUnreachable(scope, old_inst.cast(zir.Inst.Unreachable).?),
.@"return" => return self.analyzeInstRet(scope, old_inst.cast(zir.Inst.Return).?),
+ .returnvoid => return self.analyzeInstRetVoid(scope, old_inst.cast(zir.Inst.ReturnVoid).?),
.@"fn" => return self.analyzeInstFn(scope, old_inst.cast(zir.Inst.Fn).?),
- .@"export" => {
- try self.analyzeExport(scope, old_inst.cast(zir.Inst.Export).?);
- return self.constVoid(scope, old_inst.src);
- },
+ .@"export" => return self.analyzeInstExport(scope, old_inst.cast(zir.Inst.Export).?),
.primitive => return self.analyzeInstPrimitive(scope, old_inst.cast(zir.Inst.Primitive).?),
.ref => return self.analyzeInstRef(scope, old_inst.cast(zir.Inst.Ref).?),
.fntype => return self.analyzeInstFnType(scope, old_inst.cast(zir.Inst.FnType).?),
@@ -2227,13 +2330,20 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
}
}
+fn analyzeInstExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst {
+ const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name);
+ const exported_decl = try self.resolveCompleteDecl(scope, export_inst.positionals.value);
+ try self.analyzeExport(scope, export_inst.base.src, symbol_name, exported_decl);
+ return self.constVoid(scope, export_inst.base.src);
+}
+
fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst {
return self.fail(scope, inst.base.src, "{}", .{inst.positionals.msg});
}
fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst {
const b = try self.requireRuntimeBlock(scope, inst.base.src);
- return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){});
+ return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, {});
}
fn analyzeInstRef(self: *Module, scope: *Scope, inst: *zir.Inst.Ref) InnerError!*Inst {
@@ -2251,7 +2361,7 @@ fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) Inn
const src_decl = zir_module.contents.module.findDecl(decl_name) orelse
return self.fail(scope, inst.positionals.name.src, "use of undeclared identifier '{}'", .{decl_name});
- const decl = try self.resolveCompleteDecl(scope, src_decl);
+ const decl = try self.resolveCompleteDecl(scope, src_decl.decl);
return self.analyzeDeclRef(scope, inst.base.src, decl);
} else {
unreachable;
@@ -2264,7 +2374,7 @@ fn analyzeDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerEr
const src_decl = zir_module.contents.module.findDecl(decl_name) orelse
return self.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name});
- const decl = try self.resolveCompleteDecl(scope, src_decl);
+ const decl = try self.resolveCompleteDecl(scope, src_decl.decl);
return decl;
}
@@ -2275,12 +2385,34 @@ fn analyzeInstDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) Inn
return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src);
}
+fn analyzeInstDeclValInModule(self: *Module, scope: *Scope, inst: *zir.Inst.DeclValInModule) InnerError!*Inst {
+ const decl = inst.positionals.decl;
+ const ptr = try self.analyzeDeclRef(scope, inst.base.src, decl);
+ return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src);
+}
+
fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
+ const scope_decl = scope.decl().?;
+ try self.declareDeclDependency(scope_decl, decl);
+ self.ensureDeclAnalyzed(decl) catch |err| {
+ if (scope.cast(Scope.Block)) |block| {
+ if (block.func) |func| {
+ func.analysis = .dependency_failure;
+ } else {
+ block.decl.analysis = .dependency_failure;
+ }
+ } else {
+ scope_decl.analysis = .dependency_failure;
+ }
+ return err;
+ };
+
const decl_tv = try decl.typedValue();
const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
ty_payload.* = .{ .pointee_type = decl_tv.ty };
const val_payload = try scope.arena().create(Value.Payload.DeclRef);
val_payload.* = .{ .decl = decl };
+
return self.constInst(scope, src, .{
.ty = Type.initPayload(&ty_payload.base),
.val = Value.initPayload(&val_payload.base),
@@ -2345,26 +2477,26 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro
}
const b = try self.requireRuntimeBlock(scope, inst.base.src);
- return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, Inst.Args(Inst.Call){
+ return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, .{
.func = func,
.args = casted_args,
});
}
fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {
- const fn_type = try self.resolveType(scope, fn_inst.positionals.fn_type);
- const new_func = try scope.arena().create(Fn);
- new_func.* = .{
- .fn_type = fn_type,
- .analysis = .{ .queued = fn_inst },
- .owner_decl = scope.decl().?,
- };
- const fn_payload = try scope.arena().create(Value.Payload.Function);
- fn_payload.* = .{ .func = new_func };
- return self.constInst(scope, fn_inst.base.src, .{
- .ty = fn_type,
- .val = Value.initPayload(&fn_payload.base),
- });
+ return self.fail(scope, fn_inst.base.src, "TODO implement ZIR fn inst", .{});
+ //const fn_type = try self.resolveType(scope, fn_inst.positionals.fn_type);
+ //const new_func = try scope.arena().create(Fn);
+ //new_func.* = .{
+ // .analysis = .{ .queued = fn_inst },
+ // .owner_decl = scope.decl().?,
+ //};
+ //const fn_payload = try scope.arena().create(Value.Payload.Function);
+ //fn_payload.* = .{ .func = new_func };
+ //return self.constInst(scope, fn_inst.base.src, .{
+ // .ty = fn_type,
+ // .val = Value.initPayload(&fn_payload.base),
+ //});
}
fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
@@ -2377,6 +2509,13 @@ fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) Inn
return self.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args));
}
+ if (return_type.zigTypeTag() == .Void and
+ fntype.positionals.param_types.len == 0 and
+ fntype.kw_args.cc == .Unspecified)
+ {
+ return self.constType(scope, fntype.base.src, Type.initTag(.fn_void_no_args));
+ }
+
if (return_type.zigTypeTag() == .NoReturn and
fntype.positionals.param_types.len == 0 and
fntype.kw_args.cc == .Naked)
@@ -2412,7 +2551,7 @@ fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToIn
// TODO handle known-pointer-address
const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src);
const ty = Type.initTag(.usize);
- return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr });
+ return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, .{ .ptr = ptr });
}
fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst {
@@ -2604,7 +2743,7 @@ fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerEr
}
const b = try self.requireRuntimeBlock(scope, assembly.base.src);
- return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){
+ return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, .{
.asm_source = asm_source,
.is_volatile = assembly.kw_args.@"volatile",
.output = output,
@@ -2640,20 +2779,12 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!
}
const b = try self.requireRuntimeBlock(scope, inst.base.src);
switch (op) {
- .eq => return self.addNewInstArgs(
- b,
- inst.base.src,
- Type.initTag(.bool),
- Inst.IsNull,
- Inst.Args(Inst.IsNull){ .operand = opt_operand },
- ),
- .neq => return self.addNewInstArgs(
- b,
- inst.base.src,
- Type.initTag(.bool),
- Inst.IsNonNull,
- Inst.Args(Inst.IsNonNull){ .operand = opt_operand },
- ),
+ .eq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNull, .{
+ .operand = opt_operand,
+ }),
+ .neq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNonNull, .{
+ .operand = opt_operand,
+ }),
else => unreachable,
}
} else if (is_equality_cmp and
@@ -2748,23 +2879,19 @@ fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.Unrea
}
fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst {
+ const operand = try self.resolveInst(scope, inst.positionals.operand);
const b = try self.requireRuntimeBlock(scope, inst.base.src);
- return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, {});
+ return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, .{ .operand = operand });
+}
+
+fn analyzeInstRetVoid(self: *Module, scope: *Scope, inst: *zir.Inst.ReturnVoid) InnerError!*Inst {
+ const b = try self.requireRuntimeBlock(scope, inst.base.src);
+ return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.RetVoid, {});
}
fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void {
- if (scope.cast(Scope.Block)) |b| {
- const analysis = b.func.analysis.in_progress;
- analysis.needed_inst_capacity += body.instructions.len;
- try analysis.inst_table.ensureCapacity(analysis.needed_inst_capacity);
- for (body.instructions) |src_inst| {
- const new_inst = try self.analyzeInst(scope, src_inst);
- analysis.inst_table.putAssumeCapacityNoClobber(src_inst, new_inst);
- }
- } else {
- for (body.instructions) |src_inst| {
- _ = try self.analyzeInst(scope, src_inst);
- }
+ for (body.instructions) |src_inst| {
+ src_inst.analyzed_inst = try self.analyzeInst(scope, src_inst);
}
}
@@ -2847,7 +2974,7 @@ fn cmpNumeric(
};
const casted_lhs = try self.coerce(scope, dest_type, lhs);
const casted_rhs = try self.coerce(scope, dest_type, rhs);
- return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
+ return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, .{
.lhs = casted_lhs,
.rhs = casted_rhs,
.op = op,
@@ -2951,7 +3078,7 @@ fn cmpNumeric(
const casted_lhs = try self.coerce(scope, dest_type, lhs);
const casted_rhs = try self.coerce(scope, dest_type, lhs);
- return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
+ return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, .{
.lhs = casted_lhs,
.rhs = casted_rhs,
.op = op,
@@ -3028,7 +3155,7 @@ fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
}
// TODO validate the type size and other compile errors
const b = try self.requireRuntimeBlock(scope, inst.src);
- return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst });
+ return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, .{ .operand = inst });
}
fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
@@ -3083,9 +3210,18 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err
},
.block => {
const block = scope.cast(Scope.Block).?;
- block.func.analysis = .sema_failure;
+ if (block.func) |func| {
+ func.analysis = .sema_failure;
+ } else {
+ block.decl.analysis = .sema_failure;
+ }
self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
},
+ .gen_zir => {
+ const gen_zir = scope.cast(Scope.GenZIR).?;
+ gen_zir.decl.analysis = .sema_failure;
+ self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
+ },
.zir_module => {
const zir_module = scope.cast(Scope.ZIRModule).?;
zir_module.status = .loaded_sema_failure;
diff --git a/src-self-hosted/TypedValue.zig b/src-self-hosted/TypedValue.zig
index 83a8f3c09f..48b2c04970 100644
--- a/src-self-hosted/TypedValue.zig
+++ b/src-self-hosted/TypedValue.zig
@@ -21,3 +21,11 @@ pub const Managed = struct {
self.* = undefined;
}
};
+
+/// Assumes arena allocation. Does a recursive copy.
+pub fn copy(self: TypedValue, allocator: *Allocator) error{OutOfMemory}!TypedValue {
+ return TypedValue{
+ .ty = try self.ty.copy(allocator),
+ .val = try self.val.copy(allocator),
+ };
+}
diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig
index a3a15b463e..f412b2dad2 100644
--- a/src-self-hosted/codegen.zig
+++ b/src-self-hosted/codegen.zig
@@ -178,6 +178,7 @@ const Function = struct {
.ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
.bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
.ret => return self.genRet(inst.cast(ir.Inst.Ret).?),
+ .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?),
.cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?),
.condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?),
.isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?),
@@ -213,7 +214,7 @@ const Function = struct {
try self.code.resize(self.code.items.len + 7);
self.code.items[self.code.items.len - 7 ..][0..3].* = [3]u8{ 0xff, 0x14, 0x25 };
mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], got_addr);
- const return_type = func.fn_type.fnReturnType();
+ const return_type = func.owner_decl.typed_value.most_recent.typed_value.ty.fnReturnType();
switch (return_type.zigTypeTag()) {
.Void => return MCValue{ .none = {} },
.NoReturn => return MCValue{ .unreach = {} },
@@ -230,16 +231,28 @@ const Function = struct {
}
}
- fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue {
+ fn ret(self: *Function, src: usize, mcv: MCValue) !MCValue {
+ if (mcv != .none) {
+ return self.fail(src, "TODO implement return with non-void operand", .{});
+ }
switch (self.target.cpu.arch) {
.i386, .x86_64 => {
try self.code.append(0xc3); // ret
},
- else => return self.fail(inst.base.src, "TODO implement return for {}", .{self.target.cpu.arch}),
+ else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
}
return .unreach;
}
+ fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue {
+ const operand = try self.resolveInst(inst.args.operand);
+ return self.ret(inst.base.src, operand);
+ }
+
+ fn genRetVoid(self: *Function, inst: *ir.Inst.RetVoid) !MCValue {
+ return self.ret(inst.base.src, .none);
+ }
+
fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue {
switch (self.target.cpu.arch) {
else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig
index 330b1c4135..387c88df3b 100644
--- a/src-self-hosted/ir.zig
+++ b/src-self-hosted/ir.zig
@@ -26,6 +26,7 @@ pub const Inst = struct {
isnull,
ptrtoint,
ret,
+ retvoid,
unreach,
};
@@ -146,6 +147,14 @@ pub const Inst = struct {
pub const Ret = struct {
pub const base_tag = Tag.ret;
base: Inst,
+ args: struct {
+ operand: *Inst,
+ },
+ };
+
+ pub const RetVoid = struct {
+ pub const base_tag = Tag.retvoid;
+ base: Inst,
args: void,
};
diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig
index f394d45864..fd38bee863 100644
--- a/src-self-hosted/link.zig
+++ b/src-self-hosted/link.zig
@@ -956,10 +956,10 @@ pub const ElfFile = struct {
try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
if (self.local_symbol_free_list.popOrNull()) |i| {
- //std.debug.warn("reusing symbol index {} for {}\n", .{i, decl.name});
+ std.debug.warn("reusing symbol index {} for {}\n", .{i, decl.name});
decl.link.local_sym_index = i;
} else {
- //std.debug.warn("allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
+ std.debug.warn("allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
_ = self.local_symbols.addOneAssumeCapacity();
}
@@ -1002,7 +1002,7 @@ pub const ElfFile = struct {
defer code_buffer.deinit();
const typed_value = decl.typed_value.most_recent.typed_value;
- const code = switch (try codegen.generateSymbol(self, decl.src, typed_value, &code_buffer)) {
+ const code = switch (try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer)) {
.externally_managed => |x| x,
.appended => code_buffer.items,
.fail => |em| {
@@ -1027,11 +1027,11 @@ pub const ElfFile = struct {
!mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
if (need_realloc) {
const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
- //std.debug.warn("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
+ std.debug.warn("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
if (vaddr != local_sym.st_value) {
local_sym.st_value = vaddr;
- //std.debug.warn(" (writing new offset table entry)\n", .{});
+ std.debug.warn(" (writing new offset table entry)\n", .{});
self.offset_table.items[decl.link.offset_table_index] = vaddr;
try self.writeOffsetTableEntry(decl.link.offset_table_index);
}
@@ -1049,7 +1049,7 @@ pub const ElfFile = struct {
const decl_name = mem.spanZ(decl.name);
const name_str_index = try self.makeString(decl_name);
const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
- //std.debug.warn("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
+ std.debug.warn("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
errdefer self.freeTextBlock(&decl.link);
local_sym.* = .{
diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig
index aa8c000095..d8bc40a4f8 100644
--- a/src-self-hosted/type.zig
+++ b/src-self-hosted/type.zig
@@ -54,6 +54,7 @@ pub const Type = extern union {
.@"undefined" => return .Undefined,
.fn_noreturn_no_args => return .Fn,
+ .fn_void_no_args => return .Fn,
.fn_naked_noreturn_no_args => return .Fn,
.fn_ccc_void_no_args => return .Fn,
@@ -163,6 +164,77 @@ pub const Type = extern union {
}
}
+ pub fn copy(self: Type, allocator: *Allocator) error{OutOfMemory}!Type {
+ if (self.tag_if_small_enough < Tag.no_payload_count) {
+ return Type{ .tag_if_small_enough = self.tag_if_small_enough };
+ } else switch (self.ptr_otherwise.tag) {
+ .u8,
+ .i8,
+ .isize,
+ .usize,
+ .c_short,
+ .c_ushort,
+ .c_int,
+ .c_uint,
+ .c_long,
+ .c_ulong,
+ .c_longlong,
+ .c_ulonglong,
+ .c_longdouble,
+ .c_void,
+ .f16,
+ .f32,
+ .f64,
+ .f128,
+ .bool,
+ .void,
+ .type,
+ .anyerror,
+ .comptime_int,
+ .comptime_float,
+ .noreturn,
+ .@"null",
+ .@"undefined",
+ .fn_noreturn_no_args,
+ .fn_void_no_args,
+ .fn_naked_noreturn_no_args,
+ .fn_ccc_void_no_args,
+ .single_const_pointer_to_comptime_int,
+ .const_slice_u8,
+ => unreachable,
+
+ .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0),
+ .array => {
+ const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
+ const new_payload = try allocator.create(Payload.Array);
+ new_payload.* = .{
+ .base = payload.base,
+ .len = payload.len,
+ .elem_type = try payload.elem_type.copy(allocator),
+ };
+ return Type{ .ptr_otherwise = &new_payload.base };
+ },
+ .single_const_pointer => {
+ const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", self.ptr_otherwise);
+ const new_payload = try allocator.create(Payload.SingleConstPointer);
+ new_payload.* = .{
+ .base = payload.base,
+ .pointee_type = try payload.pointee_type.copy(allocator),
+ };
+ return Type{ .ptr_otherwise = &new_payload.base };
+ },
+ .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),
+ .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),
+ }
+ }
+
+ fn copyPayloadShallow(self: Type, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Type {
+ const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
+ const new_payload = try allocator.create(T);
+ new_payload.* = payload.*;
+ return Type{ .ptr_otherwise = &new_payload.base };
+ }
+
pub fn format(
self: Type,
comptime fmt: []const u8,
@@ -206,6 +278,7 @@ pub const Type = extern union {
.const_slice_u8 => return out_stream.writeAll("[]const u8"),
.fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
+ .fn_void_no_args => return out_stream.writeAll("fn() void"),
.fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
.fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"),
.single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
@@ -269,6 +342,7 @@ pub const Type = extern union {
.@"null" => return Value.initTag(.null_type),
.@"undefined" => return Value.initTag(.undefined_type),
.fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type),
+ .fn_void_no_args => return Value.initTag(.fn_void_no_args_type),
.fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
.fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),
.single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
@@ -303,6 +377,7 @@ pub const Type = extern union {
.bool,
.anyerror,
.fn_noreturn_no_args,
+ .fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer_to_comptime_int,
@@ -333,6 +408,7 @@ pub const Type = extern union {
.i8,
.bool,
.fn_noreturn_no_args, // represents machine code; not a pointer
+ .fn_void_no_args, // represents machine code; not a pointer
.fn_naked_noreturn_no_args, // represents machine code; not a pointer
.fn_ccc_void_no_args, // represents machine code; not a pointer
.array_u8_sentinel_0,
@@ -420,6 +496,7 @@ pub const Type = extern union {
.array_u8_sentinel_0,
.const_slice_u8,
.fn_noreturn_no_args,
+ .fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.int_unsigned,
@@ -466,6 +543,7 @@ pub const Type = extern union {
.single_const_pointer,
.single_const_pointer_to_comptime_int,
.fn_noreturn_no_args,
+ .fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.int_unsigned,
@@ -509,6 +587,7 @@ pub const Type = extern union {
.array,
.array_u8_sentinel_0,
.fn_noreturn_no_args,
+ .fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.int_unsigned,
@@ -553,6 +632,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
+ .fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.int_unsigned,
@@ -597,6 +677,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
+ .fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer,
@@ -642,6 +723,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
+ .fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer,
@@ -675,6 +757,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
+ .fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.array,
@@ -721,6 +804,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
+ .fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.array,
@@ -777,6 +861,7 @@ pub const Type = extern union {
pub fn fnParamLen(self: Type) usize {
return switch (self.tag()) {
.fn_noreturn_no_args => 0,
+ .fn_void_no_args => 0,
.fn_naked_noreturn_no_args => 0,
.fn_ccc_void_no_args => 0,
@@ -823,6 +908,7 @@ pub const Type = extern union {
pub fn fnParamTypes(self: Type, types: []Type) void {
switch (self.tag()) {
.fn_noreturn_no_args => return,
+ .fn_void_no_args => return,
.fn_naked_noreturn_no_args => return,
.fn_ccc_void_no_args => return,
@@ -869,7 +955,10 @@ pub const Type = extern union {
return switch (self.tag()) {
.fn_noreturn_no_args => Type.initTag(.noreturn),
.fn_naked_noreturn_no_args => Type.initTag(.noreturn),
- .fn_ccc_void_no_args => Type.initTag(.void),
+
+ .fn_void_no_args,
+ .fn_ccc_void_no_args,
+ => Type.initTag(.void),
.f16,
.f32,
@@ -913,6 +1002,7 @@ pub const Type = extern union {
pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {
return switch (self.tag()) {
.fn_noreturn_no_args => .Unspecified,
+ .fn_void_no_args => .Unspecified,
.fn_naked_noreturn_no_args => .Naked,
.fn_ccc_void_no_args => .C,
@@ -958,6 +1048,7 @@ pub const Type = extern union {
pub fn fnIsVarArgs(self: Type) bool {
return switch (self.tag()) {
.fn_noreturn_no_args => false,
+ .fn_void_no_args => false,
.fn_naked_noreturn_no_args => false,
.fn_ccc_void_no_args => false,
@@ -1033,6 +1124,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
+ .fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.array,
@@ -1070,6 +1162,7 @@ pub const Type = extern union {
.type,
.anyerror,
.fn_noreturn_no_args,
+ .fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer_to_comptime_int,
@@ -1126,6 +1219,7 @@ pub const Type = extern union {
.type,
.anyerror,
.fn_noreturn_no_args,
+ .fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer_to_comptime_int,
@@ -1180,6 +1274,7 @@ pub const Type = extern union {
@"null",
@"undefined",
fn_noreturn_no_args,
+ fn_void_no_args,
fn_naked_noreturn_no_args,
fn_ccc_void_no_args,
single_const_pointer_to_comptime_int,
diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig
index 7caacc7960..fc5854c40f 100644
--- a/src-self-hosted/value.zig
+++ b/src-self-hosted/value.zig
@@ -49,6 +49,7 @@ pub const Value = extern union {
null_type,
undefined_type,
fn_noreturn_no_args_type,
+ fn_void_no_args_type,
fn_naked_noreturn_no_args_type,
fn_ccc_void_no_args_type,
single_const_pointer_to_comptime_int_type,
@@ -107,6 +108,109 @@ pub const Value = extern union {
return @fieldParentPtr(T, "base", self.ptr_otherwise);
}
+ pub fn copy(self: Value, allocator: *Allocator) error{OutOfMemory}!Value {
+ if (self.tag_if_small_enough < Tag.no_payload_count) {
+ return Value{ .tag_if_small_enough = self.tag_if_small_enough };
+ } else switch (self.ptr_otherwise.tag) {
+ .u8_type,
+ .i8_type,
+ .isize_type,
+ .usize_type,
+ .c_short_type,
+ .c_ushort_type,
+ .c_int_type,
+ .c_uint_type,
+ .c_long_type,
+ .c_ulong_type,
+ .c_longlong_type,
+ .c_ulonglong_type,
+ .c_longdouble_type,
+ .f16_type,
+ .f32_type,
+ .f64_type,
+ .f128_type,
+ .c_void_type,
+ .bool_type,
+ .void_type,
+ .type_type,
+ .anyerror_type,
+ .comptime_int_type,
+ .comptime_float_type,
+ .noreturn_type,
+ .null_type,
+ .undefined_type,
+ .fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
+ .fn_naked_noreturn_no_args_type,
+ .fn_ccc_void_no_args_type,
+ .single_const_pointer_to_comptime_int_type,
+ .const_slice_u8_type,
+ .undef,
+ .zero,
+ .the_one_possible_value,
+ .null_value,
+ .bool_true,
+ .bool_false,
+ => unreachable,
+
+ .ty => {
+ const payload = @fieldParentPtr(Payload.Ty, "base", self.ptr_otherwise);
+ const new_payload = try allocator.create(Payload.Ty);
+ new_payload.* = .{
+ .base = payload.base,
+ .ty = try payload.ty.copy(allocator),
+ };
+ return Value{ .ptr_otherwise = &new_payload.base };
+ },
+ .int_u64 => return self.copyPayloadShallow(allocator, Payload.Int_u64),
+ .int_i64 => return self.copyPayloadShallow(allocator, Payload.Int_i64),
+ .int_big_positive => {
+ @panic("TODO implement copying of big ints");
+ },
+ .int_big_negative => {
+ @panic("TODO implement copying of big ints");
+ },
+ .function => return self.copyPayloadShallow(allocator, Payload.Function),
+ .ref_val => {
+ const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise);
+ const new_payload = try allocator.create(Payload.RefVal);
+ new_payload.* = .{
+ .base = payload.base,
+ .val = try payload.val.copy(allocator),
+ };
+ return Value{ .ptr_otherwise = &new_payload.base };
+ },
+ .decl_ref => return self.copyPayloadShallow(allocator, Payload.DeclRef),
+ .elem_ptr => {
+ const payload = @fieldParentPtr(Payload.ElemPtr, "base", self.ptr_otherwise);
+ const new_payload = try allocator.create(Payload.ElemPtr);
+ new_payload.* = .{
+ .base = payload.base,
+ .array_ptr = try payload.array_ptr.copy(allocator),
+ .index = payload.index,
+ };
+ return Value{ .ptr_otherwise = &new_payload.base };
+ },
+ .bytes => return self.copyPayloadShallow(allocator, Payload.Bytes),
+ .repeated => {
+ const payload = @fieldParentPtr(Payload.Repeated, "base", self.ptr_otherwise);
+ const new_payload = try allocator.create(Payload.Repeated);
+ new_payload.* = .{
+ .base = payload.base,
+ .val = try payload.val.copy(allocator),
+ };
+ return Value{ .ptr_otherwise = &new_payload.base };
+ },
+ }
+ }
+
+ fn copyPayloadShallow(self: Value, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Value {
+ const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
+ const new_payload = try allocator.create(T);
+ new_payload.* = payload.*;
+ return Value{ .ptr_otherwise = &new_payload.base };
+ }
+
pub fn format(
self: Value,
comptime fmt: []const u8,
@@ -144,6 +248,7 @@ pub const Value = extern union {
.null_type => return out_stream.writeAll("@TypeOf(null)"),
.undefined_type => return out_stream.writeAll("@TypeOf(undefined)"),
.fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),
+ .fn_void_no_args_type => return out_stream.writeAll("fn() void"),
.fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
.fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
.single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
@@ -229,6 +334,7 @@ pub const Value = extern union {
.null_type => Type.initTag(.@"null"),
.undefined_type => Type.initTag(.@"undefined"),
.fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args),
+ .fn_void_no_args_type => Type.initTag(.fn_void_no_args),
.fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
.fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),
.single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
@@ -286,6 +392,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@@ -345,6 +452,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@@ -405,6 +513,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@@ -470,6 +579,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@@ -564,6 +674,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@@ -620,6 +731,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@@ -721,6 +833,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@@ -783,6 +896,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@@ -862,6 +976,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@@ -929,11 +1044,6 @@ pub const Value = extern union {
len: u64,
};
- pub const SingleConstPtrType = struct {
- base: Payload = Payload{ .tag = .single_const_ptr_type },
- elem_type: *Type,
- };
-
/// Represents a pointer to another immutable value.
pub const RefVal = struct {
base: Payload = Payload{ .tag = .ref_val },
diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig
index c1b547ce99..90793c51f9 100644
--- a/src-self-hosted/zir.zig
+++ b/src-self-hosted/zir.zig
@@ -25,6 +25,9 @@ pub const Inst = struct {
/// Hash of slice into the source of the part after the = and before the next instruction.
contents_hash: std.zig.SrcHash = undefined,
+ /// Pre-allocated field for mapping ZIR text instructions to post-analysis instructions.
+ analyzed_inst: *ir.Inst = undefined,
+
/// These names are used directly as the instruction names in the text format.
pub const Tag = enum {
breakpoint,
@@ -37,6 +40,8 @@ pub const Inst = struct {
/// The syntax `@foo` is equivalent to `declval("foo")`.
/// declval is equivalent to declref followed by deref.
declval,
+ /// Same as declval but the parameter is a `*Module.Decl` rather than a name.
+ declval_in_module,
str,
int,
ptrtoint,
@@ -46,6 +51,7 @@ pub const Inst = struct {
@"asm",
@"unreachable",
@"return",
+ returnvoid,
@"fn",
fntype,
@"export",
@@ -67,6 +73,7 @@ pub const Inst = struct {
.call => Call,
.declref => DeclRef,
.declval => DeclVal,
+ .declval_in_module => DeclValInModule,
.compileerror => CompileError,
.@"const" => Const,
.str => Str,
@@ -78,6 +85,7 @@ pub const Inst = struct {
.@"asm" => Asm,
.@"unreachable" => Unreachable,
.@"return" => Return,
+ .returnvoid => ReturnVoid,
.@"fn" => Fn,
.@"export" => Export,
.primitive => Primitive,
@@ -142,6 +150,16 @@ pub const Inst = struct {
kw_args: struct {},
};
+ pub const DeclValInModule = struct {
+ pub const base_tag = Tag.declval_in_module;
+ base: Inst,
+
+ positionals: struct {
+ decl: *IrModule.Decl,
+ },
+ kw_args: struct {},
+ };
+
pub const CompileError = struct {
pub const base_tag = Tag.compileerror;
base: Inst,
@@ -253,6 +271,16 @@ pub const Inst = struct {
pub const base_tag = Tag.@"return";
base: Inst,
+ positionals: struct {
+ operand: *Inst,
+ },
+ kw_args: struct {},
+ };
+
+ pub const ReturnVoid = struct {
+ pub const base_tag = Tag.returnvoid;
+ base: Inst,
+
positionals: struct {},
kw_args: struct {},
};
@@ -492,11 +520,19 @@ pub const Module = struct {
const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize });
+ const DeclAndIndex = struct {
+ decl: *Inst,
+ index: usize,
+ };
+
/// TODO Look into making a table to speed this up.
- pub fn findDecl(self: Module, name: []const u8) ?*Inst {
- for (self.decls) |decl| {
+ pub fn findDecl(self: Module, name: []const u8) ?DeclAndIndex {
+ for (self.decls) |decl, i| {
if (mem.eql(u8, decl.name, name)) {
- return decl;
+ return DeclAndIndex{
+ .decl = decl,
+ .index = i,
+ };
}
}
return null;
@@ -540,6 +576,7 @@ pub const Module = struct {
.call => return self.writeInstToStreamGeneric(stream, .call, decl, inst_table),
.declref => return self.writeInstToStreamGeneric(stream, .declref, decl, inst_table),
.declval => return self.writeInstToStreamGeneric(stream, .declval, decl, inst_table),
+ .declval_in_module => return self.writeInstToStreamGeneric(stream, .declval_in_module, decl, inst_table),
.compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, decl, inst_table),
.@"const" => return self.writeInstToStreamGeneric(stream, .@"const", decl, inst_table),
.str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
@@ -551,6 +588,7 @@ pub const Module = struct {
.@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table),
.@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table),
.@"return" => return self.writeInstToStreamGeneric(stream, .@"return", decl, inst_table),
+ .returnvoid => return self.writeInstToStreamGeneric(stream, .returnvoid, decl, inst_table),
.@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table),
.@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
.ref => return self.writeInstToStreamGeneric(stream, .ref, decl, inst_table),
@@ -636,6 +674,7 @@ pub const Module = struct {
[]u8, []const u8 => return std.zig.renderStringLiteral(param, stream),
BigIntConst => return stream.print("{}", .{param}),
TypedValue => unreachable, // this is a special case
+ *IrModule.Decl => unreachable, // this is a special case
else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
}
}
@@ -649,6 +688,8 @@ pub const Module = struct {
}
} else if (inst.cast(Inst.DeclVal)) |decl_val| {
try stream.print("@{}", .{decl_val.positionals.name});
+ } else if (inst.cast(Inst.DeclValInModule)) |decl_val| {
+ try stream.print("@{}", .{decl_val.positionals.decl.name});
} else {
//try stream.print("?", .{});
unreachable;
@@ -996,6 +1037,7 @@ const Parser = struct {
[]u8, []const u8 => return self.parseStringLiteral(),
BigIntConst => return self.parseIntegerLiteral(),
TypedValue => return self.fail("'const' is a special instruction; not legal in ZIR text", .{}),
+ *IrModule.Decl => return self.fail("'declval_in_module' is a special instruction; not legal in ZIR text", .{}),
else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
}
return self.fail("TODO parse parameter {}", .{@typeName(T)});
@@ -1105,7 +1147,7 @@ const EmitZIR = struct {
}
std.sort.sort(*IrModule.Decl, src_decls.items, {}, (struct {
fn lessThan(context: void, a: *IrModule.Decl, b: *IrModule.Decl) bool {
- return a.src < b.src;
+ return a.src_index < b.src_index;
}
}).lessThan);
@@ -1113,7 +1155,7 @@ const EmitZIR = struct {
for (src_decls.items) |ir_decl| {
if (self.old_module.export_owners.getValue(ir_decl)) |exports| {
for (exports) |module_export| {
- const declval = try self.emitDeclVal(ir_decl.src, mem.spanZ(module_export.exported_decl.name));
+ const declval = try self.emitDeclVal(ir_decl.src(), mem.spanZ(module_export.exported_decl.name));
const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name);
const export_inst = try self.arena.allocator.create(Inst.Export);
export_inst.* = .{
@@ -1131,7 +1173,7 @@ const EmitZIR = struct {
try self.decls.append(self.allocator, &export_inst.base);
}
} else {
- const new_decl = try self.emitTypedValue(ir_decl.src, ir_decl.typed_value.most_recent.typed_value);
+ const new_decl = try self.emitTypedValue(ir_decl.src(), ir_decl.typed_value.most_recent.typed_value);
new_decl.name = try self.arena.allocator.dupe(u8, mem.spanZ(ir_decl.name));
}
}
@@ -1301,7 +1343,7 @@ const EmitZIR = struct {
},
}
- const fn_type = try self.emitType(src, module_fn.fn_type);
+ const fn_type = try self.emitType(src, typed_value.ty);
const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);
mem.copy(*Inst, arena_instrs, instructions.items);
@@ -1399,7 +1441,23 @@ const EmitZIR = struct {
break :blk &new_inst.base;
},
.unreach => try self.emitTrivial(inst.src, Inst.Unreachable),
- .ret => try self.emitTrivial(inst.src, Inst.Return),
+ .ret => blk: {
+ const old_inst = inst.cast(ir.Inst.Ret).?;
+ const new_inst = try self.arena.allocator.create(Inst.Return);
+ new_inst.* = .{
+ .base = .{
+ .name = try self.autoName(),
+ .src = inst.src,
+ .tag = Inst.Return.base_tag,
+ },
+ .positionals = .{
+ .operand = try self.resolveInst(inst_table, old_inst.args.operand),
+ },
+ .kw_args = .{},
+ };
+ break :blk &new_inst.base;
+ },
+ .retvoid => try self.emitTrivial(inst.src, Inst.ReturnVoid),
.constant => unreachable, // excluded from function bodies
.assembly => blk: {
const old_inst = inst.cast(ir.Inst.Assembly).?;
diff --git a/src/codegen.cpp b/src/codegen.cpp
index e20d6d60f5..75d126eaf6 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -7473,6 +7473,12 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n
continue;
}
ZigValue *field_val = const_val->data.x_struct.fields[i];
+ if (field_val == nullptr) {
+ add_node_error(g, type_struct_field->decl_node,
+ buf_sprintf("compiler bug: generating const value for struct field '%s'",
+ buf_ptr(type_struct_field->name)));
+ codegen_report_errors_and_exit(g);
+ }
ZigType *field_type = field_val->type;
assert(field_type != nullptr);
if ((err = ensure_const_val_repr(nullptr, g, nullptr, field_val, field_type))) {
--
cgit v1.2.3
From 46b57748a52c32b973903166d40c7ebee53d88c5 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 17 Jun 2020 06:07:53 -0400
Subject: stage1: stop emitting memset to undefined when safety is off
---
src/codegen.cpp | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 75d126eaf6..6c3fdddfb1 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -5583,8 +5583,12 @@ static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutableGen *executable, Ir
bool val_is_undef = value_is_all_undef(g, instruction->byte->value);
LLVMValueRef fill_char;
- if (val_is_undef && ir_want_runtime_safety_scope(g, instruction->base.base.scope)) {
- fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false);
+ if (val_is_undef) {
+ if (ir_want_runtime_safety_scope(g, instruction->base.base.scope)) {
+ fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false);
+ } else {
+ return nullptr;
+ }
} else {
fill_char = ir_llvm_value(g, instruction->byte);
}
--
cgit v1.2.3
From 355319fb674073234ea2f356a310577fb982f72b Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 18 Jun 2020 18:17:26 -0400
Subject: zig cc: add missing cxxabi include path
---
src/codegen.cpp | 6 ++++++
1 file changed, 6 insertions(+)
(limited to 'src/codegen.cpp')
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 6c3fdddfb1..a0e49ccf9e 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -9475,9 +9475,15 @@ void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_pa
const char *libcxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include",
buf_ptr(g->zig_lib_dir)));
+ const char *libcxxabi_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "include",
+ buf_ptr(g->zig_lib_dir)));
+
args.append("-isystem");
args.append(libcxx_include_path);
+ args.append("-isystem");
+ args.append(libcxxabi_include_path);
+
if (target_abi_is_musl(g->zig_target->abi)) {
args.append("-D_LIBCPP_HAS_MUSL_LIBC");
}
--
cgit v1.2.3
From 8696e52a3d617ce30ec6202adc89cb10c67bcc43 Mon Sep 17 00:00:00 2001
From: Robin Voetter
Date: Sun, 21 Jun 2020 20:55:44 +0200
Subject: Make unary minus for unsigned types a compile error (#5654)
* Make unary minus for unsigned types a compile error
* Add unreachable when generating unsigned negate
---
src/codegen.cpp | 12 ++++++------
src/ir.cpp | 37 ++++++++++++++++++++++---------------
test/compile_errors.zig | 9 +++++++++
3 files changed, 37 insertions(+), 21 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/src/codegen.cpp b/src/codegen.cpp
index e20d6d60f5..f7c3575f86 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -3540,7 +3540,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutableGen *executabl
for (size_t field_i = 0; field_i < field_count; field_i += 1) {
TypeEnumField *type_enum_field = &wanted_type->data.enumeration.fields[field_i];
-
+
Buf *name = type_enum_field->name;
auto entry = occupied_tag_values.put_unique(type_enum_field->value, name);
if (entry != nullptr) {
@@ -3654,7 +3654,7 @@ static LLVMValueRef ir_gen_negation(CodeGen *g, IrInstGen *inst, IrInstGen *oper
} else if (scalar_type->data.integral.is_signed) {
return LLVMBuildNSWNeg(g->builder, llvm_operand, "");
} else {
- return LLVMBuildNUWNeg(g->builder, llvm_operand, "");
+ zig_unreachable();
}
} else {
zig_unreachable();
@@ -3984,7 +3984,7 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutableGen *executable,
assert(array_type->data.pointer.child_type->id == ZigTypeIdArray);
array_type = array_type->data.pointer.child_type;
}
-
+
assert(array_type->data.array.len != 0 || array_type->data.array.sentinel != nullptr);
if (safety_check_on) {
@@ -5258,7 +5258,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) {
for (size_t field_i = 0; field_i < field_count; field_i += 1) {
TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
-
+
Buf *name = type_enum_field->name;
auto entry = occupied_tag_values.put_unique(type_enum_field->value, name);
if (entry != nullptr) {
@@ -5471,7 +5471,7 @@ static LLVMTypeRef get_atomic_abi_type(CodeGen *g, IrInstGen *instruction) {
}
auto bit_count = operand_type->data.integral.bit_count;
bool is_signed = operand_type->data.integral.is_signed;
-
+
ir_assert(bit_count != 0, instruction);
if (bit_count == 1 || !is_power_of_2(bit_count)) {
return get_llvm_type(g, get_int_type(g, is_signed, operand_type->abi_size * 8));
@@ -9265,7 +9265,7 @@ static void init(CodeGen *g) {
abi_name = (g->zig_target->arch == ZigLLVM_riscv32) ? "ilp32" : "lp64";
}
}
-
+
g->target_machine = ZigLLVMCreateTargetMachine(target_ref, buf_ptr(&g->llvm_triple_str),
target_specific_cpu_args, target_specific_features, opt_level, reloc_mode,
to_llvm_code_model(g), g->function_sections, float_abi, abi_name);
diff --git a/src/ir.cpp b/src/ir.cpp
index 635af397c4..bb6ca554df 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -12602,28 +12602,28 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
if (prev_type->id == ZigTypeIdPointer &&
prev_type->data.pointer.ptr_len == PtrLenSingle &&
prev_type->data.pointer.child_type->id == ZigTypeIdArray &&
- ((cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenUnknown)))
+ ((cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenUnknown)))
{
- prev_inst = cur_inst;
+ prev_inst = cur_inst;
if (prev_type->data.pointer.is_const && !cur_type->data.pointer.is_const) {
// const array pointer and non-const unknown pointer
make_the_pointer_const = true;
}
- continue;
+ continue;
}
// *[N]T to [*]T
if (cur_type->id == ZigTypeIdPointer &&
cur_type->data.pointer.ptr_len == PtrLenSingle &&
cur_type->data.pointer.child_type->id == ZigTypeIdArray &&
- ((prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenUnknown)))
+ ((prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenUnknown)))
{
if (cur_type->data.pointer.is_const && !prev_type->data.pointer.is_const) {
// const array pointer and non-const unknown pointer
make_the_pointer_const = true;
}
- continue;
+ continue;
}
// *[N]T to []T
@@ -20987,17 +20987,24 @@ static IrInstGen *ir_analyze_negation(IrAnalyze *ira, IrInstSrcUnOp *instruction
if (type_is_invalid(expr_type))
return ira->codegen->invalid_inst_gen;
- if (!(expr_type->id == ZigTypeIdInt || expr_type->id == ZigTypeIdComptimeInt ||
- expr_type->id == ZigTypeIdFloat || expr_type->id == ZigTypeIdComptimeFloat ||
- expr_type->id == ZigTypeIdVector))
- {
- ir_add_error(ira, &instruction->base.base,
- buf_sprintf("negation of type '%s'", buf_ptr(&expr_type->name)));
- return ira->codegen->invalid_inst_gen;
- }
-
bool is_wrap_op = (instruction->op_id == IrUnOpNegationWrap);
+ switch (expr_type->id) {
+ case ZigTypeIdComptimeInt:
+ case ZigTypeIdFloat:
+ case ZigTypeIdComptimeFloat:
+ case ZigTypeIdVector:
+ break;
+ case ZigTypeIdInt:
+ if (is_wrap_op || expr_type->data.integral.is_signed)
+ break;
+ ZIG_FALLTHROUGH;
+ default:
+ ir_add_error(ira, &instruction->base.base,
+ buf_sprintf("negation of type '%s'", buf_ptr(&expr_type->name)));
+ return ira->codegen->invalid_inst_gen;
+ }
+
ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type;
if (instr_is_comptime(value)) {
@@ -30380,7 +30387,7 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, IrInst* source_instr, BuiltinF
case BuiltinFnIdTrunc:
f128M_trunc(in, out);
break;
- case BuiltinFnIdRound:
+ case BuiltinFnIdRound:
f128M_roundToInt(in, softfloat_round_near_maxMag, false, out);
break;
case BuiltinFnIdNearbyInt:
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 3f898cc337..e3dd1f0d8f 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -7530,4 +7530,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
, &[_][]const u8{
"tmp.zig:2:9: error: @wasmMemoryGrow is a wasm32 feature only",
});
+
+ cases.add("Issue #5586: Make unary minus for unsigned types a compile error",
+ \\export fn f(x: u32) u32 {
+ \\ const y = -%x;
+ \\ return -y;
+ \\}
+ , &[_][]const u8{
+ "tmp.zig:3:12: error: negation of type 'u32'"
+ });
}
--
cgit v1.2.3
From 51f8c306d9fa32c849b26c2d95a45901a02f448e Mon Sep 17 00:00:00 2001
From: xackus <14938807+xackus@users.noreply.github.com>
Date: Sun, 5 Jul 2020 17:58:21 +0200
Subject: stage1: add missing runtime safety for @intCast unsigned -> signed of
same bit count
---
src/codegen.cpp | 10 ++++++----
test/runtime_safety.zig | 10 ++++++++++
2 files changed, 16 insertions(+), 4 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 18ef7d9182..2f72861bc2 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -1535,9 +1535,11 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
zig_unreachable();
}
- if (actual_type->id == ZigTypeIdInt &&
- !wanted_type->data.integral.is_signed && actual_type->data.integral.is_signed &&
- want_runtime_safety)
+ if (actual_type->id == ZigTypeIdInt && want_runtime_safety && (
+ // negative to unsigned
+ (!wanted_type->data.integral.is_signed && actual_type->data.integral.is_signed) ||
+ // unsigned would become negative
+ (wanted_type->data.integral.is_signed && !actual_type->data.integral.is_signed && actual_bits == wanted_bits)))
{
LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, actual_type));
LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntSGE, expr_val, zero, "");
@@ -1547,7 +1549,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
LLVMPositionBuilderAtEnd(g->builder, fail_block);
- gen_safety_crash(g, PanicMsgIdCastNegativeToUnsigned);
+ gen_safety_crash(g, actual_type->data.integral.is_signed ? PanicMsgIdCastNegativeToUnsigned : PanicMsgIdCastTruncatedData);
LLVMPositionBuilderAtEnd(g->builder, ok_block);
}
diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig
index e60d115a00..e27ae7e16e 100644
--- a/test/runtime_safety.zig
+++ b/test/runtime_safety.zig
@@ -757,6 +757,16 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\}
);
+ cases.addRuntimeSafety("unsigned integer not fitting in cast to signed integer - same bit count",
+ \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
+ \\ @import("std").os.exit(126);
+ \\}
+ \\pub fn main() void {
+ \\ var value: u8 = 245;
+ \\ var casted = @intCast(i8, value);
+ \\}
+ );
+
cases.addRuntimeSafety("unwrap error",
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
\\ if (@import("std").mem.eql(u8, message, "attempt to unwrap error: Whatever")) {
--
cgit v1.2.3
From 8110639c7964fcb23c2b715f97ab6caa27506b93 Mon Sep 17 00:00:00 2001
From: Vexu
Date: Sat, 11 Jul 2020 14:08:20 +0300
Subject: add 'anytype' to stage1 and langref
---
doc/langref.html.in | 87 +++++++++++++++++++++++++++--------------------------
src/all_types.hpp | 8 ++---
src/analyze.cpp | 16 +++++-----
src/ast_render.cpp | 16 +++++-----
src/codegen.cpp | 4 +--
src/ir.cpp | 59 +++++++++++++++++++-----------------
src/parser.cpp | 26 ++++++++--------
src/tokenizer.cpp | 2 ++
src/tokenizer.hpp | 1 +
9 files changed, 114 insertions(+), 105 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/doc/langref.html.in b/doc/langref.html.in
index f64170817f..015865ee3b 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -1785,7 +1785,7 @@ test "fully anonymous list literal" {
dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi"});
}
-fn dump(args: var) void {
+fn dump(args: anytype) void {
assert(args.@"0" == 1234);
assert(args.@"1" == 12.34);
assert(args.@"2");
@@ -2717,7 +2717,7 @@ test "fully anonymous struct" {
});
}
-fn dump(args: var) void {
+fn dump(args: anytype) void {
assert(args.int == 1234);
assert(args.float == 12.34);
assert(args.b);
@@ -4181,14 +4181,14 @@ test "pass struct to function" {
{#header_close#}
{#header_open|Function Parameter Type Inference#}
- Function parameters can be declared with {#syntax#}var{#endsyntax#} in place of the type.
+ Function parameters can be declared with {#syntax#}anytype{#endsyntax#} in place of the type.
In this case the parameter types will be inferred when the function is called.
Use {#link|@TypeOf#} and {#link|@typeInfo#} to get information about the inferred type.
{#code_begin|test#}
const assert = @import("std").debug.assert;
-fn addFortyTwo(x: var) @TypeOf(x) {
+fn addFortyTwo(x: anytype) @TypeOf(x) {
return x + 42;
}
@@ -5974,7 +5974,7 @@ pub fn main() void {
{#code_begin|syntax#}
/// Calls print and then flushes the buffer.
-pub fn printf(self: *OutStream, comptime format: []const u8, args: var) anyerror!void {
+pub fn printf(self: *OutStream, comptime format: []const u8, args: anytype) anyerror!void {
const State = enum {
Start,
OpenBrace,
@@ -6060,7 +6060,7 @@ pub fn printf(self: *OutStream, arg0: i32, arg1: []const u8) !void {
on the type:
{#code_begin|syntax#}
-pub fn printValue(self: *OutStream, value: var) !void {
+pub fn printValue(self: *OutStream, value: anytype) !void {
switch (@typeInfo(@TypeOf(value))) {
.Int => {
return self.printInt(T, value);
@@ -6686,7 +6686,7 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {
{#header_close#}
{#header_open|@alignCast#}
- {#syntax#}@alignCast(comptime alignment: u29, ptr: var) var{#endsyntax#}
+ {#syntax#}@alignCast(comptime alignment: u29, ptr: anytype) anytype{#endsyntax#}
{#syntax#}ptr{#endsyntax#} can be {#syntax#}*T{#endsyntax#}, {#syntax#}fn(){#endsyntax#}, {#syntax#}?*T{#endsyntax#},
{#syntax#}?fn(){#endsyntax#}, or {#syntax#}[]T{#endsyntax#}. It returns the same type as {#syntax#}ptr{#endsyntax#}
@@ -6723,7 +6723,7 @@ comptime {
{#header_close#}
{#header_open|@asyncCall#}
-
{#syntax#}@asyncCall(frame_buffer: []align(@alignOf(@Frame(anyAsyncFunction))) u8, result_ptr, function_ptr, args: var) anyframe->T{#endsyntax#}
+ {#syntax#}@asyncCall(frame_buffer: []align(@alignOf(@Frame(anyAsyncFunction))) u8, result_ptr, function_ptr, args: anytype) anyframe->T{#endsyntax#}
{#syntax#}@asyncCall{#endsyntax#} performs an {#syntax#}async{#endsyntax#} call on a function pointer,
which may or may not be an {#link|async function|Async Functions#}.
@@ -6811,7 +6811,7 @@ fn func(y: *i32) void {
{#header_close#}
{#header_open|@bitCast#}
- {#syntax#}@bitCast(comptime DestType: type, value: var) DestType{#endsyntax#}
+ {#syntax#}@bitCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}
Converts a value of one type to another type.
@@ -6932,7 +6932,7 @@ fn func(y: *i32) void {
{#header_close#}
{#header_open|@call#}
- {#syntax#}@call(options: std.builtin.CallOptions, function: var, args: var) var{#endsyntax#}
+ {#syntax#}@call(options: std.builtin.CallOptions, function: anytype, args: anytype) anytype{#endsyntax#}
Calls a function, in the same way that invoking an expression with parentheses does:
@@ -7279,7 +7279,7 @@ test "main" {
{#header_close#}
{#header_open|@enumToInt#}
- {#syntax#}@enumToInt(enum_or_tagged_union: var) var{#endsyntax#}
+ {#syntax#}@enumToInt(enum_or_tagged_union: anytype) anytype{#endsyntax#}
Converts an enumeration value into its integer tag type. When a tagged union is passed,
the tag value is used as the enumeration value.
@@ -7314,7 +7314,7 @@ test "main" {
{#header_close#}
{#header_open|@errorToInt#}
-
{#syntax#}@errorToInt(err: var) std.meta.IntType(false, @sizeOf(anyerror) * 8){#endsyntax#}
+ {#syntax#}@errorToInt(err: anytype) std.meta.IntType(false, @sizeOf(anyerror) * 8){#endsyntax#}
Supports the following types:
@@ -7334,7 +7334,7 @@ test "main" {
{#header_close#}
{#header_open|@errSetCast#}
- {#syntax#}@errSetCast(comptime T: DestType, value: var) DestType{#endsyntax#}
+ {#syntax#}@errSetCast(comptime T: DestType, value: anytype) DestType{#endsyntax#}
Converts an error value from one error set to another error set. Attempting to convert an error
which is not in the destination error set results in safety-protected {#link|Undefined Behavior#}.
@@ -7342,7 +7342,7 @@ test "main" {
{#header_close#}
{#header_open|@export#}
-
{#syntax#}@export(target: var, comptime options: std.builtin.ExportOptions) void{#endsyntax#}
+ {#syntax#}@export(target: anytype, comptime options: std.builtin.ExportOptions) void{#endsyntax#}
Creates a symbol in the output object file.
@@ -7387,7 +7387,7 @@ export fn @"A function name that is a complete sentence."() void {}
{#header_close#}
{#header_open|@field#}
- {#syntax#}@field(lhs: var, comptime field_name: []const u8) (field){#endsyntax#}
+ {#syntax#}@field(lhs: anytype, comptime field_name: []const u8) (field){#endsyntax#}
Performs field access by a compile-time string.
{#code_begin|test#}
@@ -7421,7 +7421,7 @@ test "field access by string" {
{#header_close#}
{#header_open|@floatCast#}
- {#syntax#}@floatCast(comptime DestType: type, value: var) DestType{#endsyntax#}
+ {#syntax#}@floatCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}
Convert from one float type to another. This cast is safe, but may cause the
numeric value to lose precision.
@@ -7429,7 +7429,7 @@ test "field access by string" {
{#header_close#}
{#header_open|@floatToInt#}
-
{#syntax#}@floatToInt(comptime DestType: type, float: var) DestType{#endsyntax#}
+ {#syntax#}@floatToInt(comptime DestType: type, float: anytype) DestType{#endsyntax#}
Converts the integer part of a floating point number to the destination type.
@@ -7455,7 +7455,7 @@ test "field access by string" {
{#header_close#}
{#header_open|@Frame#}
- {#syntax#}@Frame(func: var) type{#endsyntax#}
+ {#syntax#}@Frame(func: anytype) type{#endsyntax#}
This function returns the frame type of a function. This works for {#link|Async Functions#}
as well as any function without a specific calling convention.
@@ -7581,7 +7581,7 @@ test "@hasDecl" {
{#header_close#}
{#header_open|@intCast#}
-
{#syntax#}@intCast(comptime DestType: type, int: var) DestType{#endsyntax#}
+ {#syntax#}@intCast(comptime DestType: type, int: anytype) DestType{#endsyntax#}
Converts an integer to another integer while keeping the same numerical value.
Attempting to convert a number which is out of range of the destination type results in
@@ -7622,7 +7622,7 @@ test "@hasDecl" {
{#header_close#}
{#header_open|@intToFloat#}
-
{#syntax#}@intToFloat(comptime DestType: type, int: var) DestType{#endsyntax#}
+ {#syntax#}@intToFloat(comptime DestType: type, int: anytype) DestType{#endsyntax#}
Converts an integer to the closest floating point representation. To convert the other way, use {#link|@floatToInt#}. This cast is always safe.
@@ -7773,7 +7773,7 @@ test "@wasmMemoryGrow" {
{#header_close#}
{#header_open|@ptrCast#}
- {#syntax#}@ptrCast(comptime DestType: type, value: var) DestType{#endsyntax#}
+ {#syntax#}@ptrCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}
Converts a pointer of one type to a pointer of another type.
@@ -7784,7 +7784,7 @@ test "@wasmMemoryGrow" {
{#header_close#}
{#header_open|@ptrToInt#}
- {#syntax#}@ptrToInt(value: var) usize{#endsyntax#}
+ {#syntax#}@ptrToInt(value: anytype) usize{#endsyntax#}
Converts {#syntax#}value{#endsyntax#} to a {#syntax#}usize{#endsyntax#} which is the address of the pointer. {#syntax#}value{#endsyntax#} can be one of these types:
@@ -8042,7 +8042,7 @@ test "@setRuntimeSafety" {
{#header_close#}
{#header_open|@splat#}
- {#syntax#}@splat(comptime len: u32, scalar: var) std.meta.Vector(len, @TypeOf(scalar)){#endsyntax#}
+ {#syntax#}@splat(comptime len: u32, scalar: anytype) std.meta.Vector(len, @TypeOf(scalar)){#endsyntax#}
Produces a vector of length {#syntax#}len{#endsyntax#} where each element is the value
{#syntax#}scalar{#endsyntax#}:
@@ -8088,7 +8088,7 @@ fn doTheTest() void {
{#code_end#}
{#header_close#}
{#header_open|@sqrt#}
-
{#syntax#}@sqrt(value: var) @TypeOf(value){#endsyntax#}
+ {#syntax#}@sqrt(value: anytype) @TypeOf(value){#endsyntax#}
Performs the square root of a floating point number. Uses a dedicated hardware instruction
when available.
@@ -8099,7 +8099,7 @@ fn doTheTest() void {
{#header_close#}
{#header_open|@sin#}
- {#syntax#}@sin(value: var) @TypeOf(value){#endsyntax#}
+ {#syntax#}@sin(value: anytype) @TypeOf(value){#endsyntax#}
Sine trigometric function on a floating point number. Uses a dedicated hardware instruction
when available.
@@ -8110,7 +8110,7 @@ fn doTheTest() void {
{#header_close#}
{#header_open|@cos#}
- {#syntax#}@cos(value: var) @TypeOf(value){#endsyntax#}
+ {#syntax#}@cos(value: anytype) @TypeOf(value){#endsyntax#}
Cosine trigometric function on a floating point number. Uses a dedicated hardware instruction
when available.
@@ -8121,7 +8121,7 @@ fn doTheTest() void {
{#header_close#}
{#header_open|@exp#}
- {#syntax#}@exp(value: var) @TypeOf(value){#endsyntax#}
+ {#syntax#}@exp(value: anytype) @TypeOf(value){#endsyntax#}
Base-e exponential function on a floating point number. Uses a dedicated hardware instruction
when available.
@@ -8132,7 +8132,7 @@ fn doTheTest() void {
{#header_close#}
{#header_open|@exp2#}
- {#syntax#}@exp2(value: var) @TypeOf(value){#endsyntax#}
+ {#syntax#}@exp2(value: anytype) @TypeOf(value){#endsyntax#}
Base-2 exponential function on a floating point number. Uses a dedicated hardware instruction
when available.
@@ -8143,7 +8143,7 @@ fn doTheTest() void {
{#header_close#}
{#header_open|@log#}
- {#syntax#}@log(value: var) @TypeOf(value){#endsyntax#}
+ {#syntax#}@log(value: anytype) @TypeOf(value){#endsyntax#}
Returns the natural logarithm of a floating point number. Uses a dedicated hardware instruction
when available.
@@ -8154,7 +8154,7 @@ fn doTheTest() void {
{#header_close#}
{#header_open|@log2#}
- {#syntax#}@log2(value: var) @TypeOf(value){#endsyntax#}
+ {#syntax#}@log2(value: anytype) @TypeOf(value){#endsyntax#}
Returns the logarithm to the base 2 of a floating point number. Uses a dedicated hardware instruction
when available.
@@ -8165,7 +8165,7 @@ fn doTheTest() void {
{#header_close#}
{#header_open|@log10#}
- {#syntax#}@log10(value: var) @TypeOf(value){#endsyntax#}
+ {#syntax#}@log10(value: anytype) @TypeOf(value){#endsyntax#}
Returns the logarithm to the base 10 of a floating point number. Uses a dedicated hardware instruction
when available.
@@ -8176,7 +8176,7 @@ fn doTheTest() void {
{#header_close#}
{#header_open|@fabs#}
- {#syntax#}@fabs(value: var) @TypeOf(value){#endsyntax#}
+ {#syntax#}@fabs(value: anytype) @TypeOf(value){#endsyntax#}
Returns the absolute value of a floating point number. Uses a dedicated hardware instruction
when available.
@@ -8187,7 +8187,7 @@ fn doTheTest() void {
{#header_close#}
{#header_open|@floor#}
- {#syntax#}@floor(value: var) @TypeOf(value){#endsyntax#}
+ {#syntax#}@floor(value: anytype) @TypeOf(value){#endsyntax#}
Returns the largest integral value not greater than the given floating point number.
Uses a dedicated hardware instruction when available.
@@ -8198,7 +8198,7 @@ fn doTheTest() void {
{#header_close#}
{#header_open|@ceil#}
- {#syntax#}@ceil(value: var) @TypeOf(value){#endsyntax#}
+ {#syntax#}@ceil(value: anytype) @TypeOf(value){#endsyntax#}
Returns the largest integral value not less than the given floating point number.
Uses a dedicated hardware instruction when available.
@@ -8209,7 +8209,7 @@ fn doTheTest() void {
{#header_close#}
{#header_open|@trunc#}
- {#syntax#}@trunc(value: var) @TypeOf(value){#endsyntax#}
+ {#syntax#}@trunc(value: anytype) @TypeOf(value){#endsyntax#}
Rounds the given floating point number to an integer, towards zero.
Uses a dedicated hardware instruction when available.
@@ -8220,7 +8220,7 @@ fn doTheTest() void {
{#header_close#}
{#header_open|@round#}
- {#syntax#}@round(value: var) @TypeOf(value){#endsyntax#}
+ {#syntax#}@round(value: anytype) @TypeOf(value){#endsyntax#}
Rounds the given floating point number to an integer, away from zero. Uses a dedicated hardware instruction
when available.
@@ -8241,7 +8241,7 @@ fn doTheTest() void {
{#header_close#}
{#header_open|@tagName#}
-
{#syntax#}@tagName(value: var) []const u8{#endsyntax#}
+ {#syntax#}@tagName(value: anytype) []const u8{#endsyntax#}
Converts an enum value or union value to a slice of bytes representing the name.
If the enum is non-exhaustive and the tag value does not map to a name, it invokes safety-checked {#link|Undefined Behavior#}.
@@ -8292,7 +8292,7 @@ fn List(comptime T: type) type {
{#header_close#}
{#header_open|@truncate#}
- {#syntax#}@truncate(comptime T: type, integer: var) T{#endsyntax#}
+ {#syntax#}@truncate(comptime T: type, integer: anytype) T{#endsyntax#}
This function truncates bits from an integer type, resulting in a smaller
or same-sized integer type.
@@ -10214,7 +10214,7 @@ TopLevelDecl
/ (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
/ KEYWORD_usingnamespace Expr SEMICOLON
-FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr)
+FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_anytype / TypeExpr)
VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
@@ -10386,7 +10386,7 @@ LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN
ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
ParamType
- <- KEYWORD_var
+ <- KEYWORD_anytype
/ DOT3
/ TypeExpr
@@ -10624,6 +10624,7 @@ KEYWORD_align <- 'align' end_of_word
KEYWORD_allowzero <- 'allowzero' end_of_word
KEYWORD_and <- 'and' end_of_word
KEYWORD_anyframe <- 'anyframe' end_of_word
+KEYWORD_anytype <- 'anytype' end_of_word
KEYWORD_asm <- 'asm' end_of_word
KEYWORD_async <- 'async' end_of_word
KEYWORD_await <- 'await' end_of_word
@@ -10669,14 +10670,14 @@ KEYWORD_var <- 'var' end_of_word
KEYWORD_volatile <- 'volatile' end_of_word
KEYWORD_while <- 'while' end_of_word
-keyword <- KEYWORD_align / KEYWORD_and / KEYWORD_allowzero / KEYWORD_asm
- / KEYWORD_async / KEYWORD_await / KEYWORD_break
+keyword <- KEYWORD_align / KEYWORD_and / KEYWORD_anyframe / KEYWORD_anytype
+ / KEYWORD_allowzero / KEYWORD_asm / KEYWORD_async / KEYWORD_await / KEYWORD_break
/ KEYWORD_catch / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue
/ KEYWORD_defer / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer
/ KEYWORD_error / KEYWORD_export / KEYWORD_extern / KEYWORD_false
/ KEYWORD_fn / KEYWORD_for / KEYWORD_if / KEYWORD_inline
/ KEYWORD_noalias / KEYWORD_null / KEYWORD_or
- / KEYWORD_orelse / KEYWORD_packed / KEYWORD_anyframe / KEYWORD_pub
+ / KEYWORD_orelse / KEYWORD_packed / KEYWORD_pub
/ KEYWORD_resume / KEYWORD_return / KEYWORD_linksection
/ KEYWORD_struct / KEYWORD_suspend
/ KEYWORD_switch / KEYWORD_test / KEYWORD_threadlocal / KEYWORD_true / KEYWORD_try
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 4465bf674c..a73efe2c82 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -692,7 +692,7 @@ enum NodeType {
NodeTypeSuspend,
NodeTypeAnyFrameType,
NodeTypeEnumLiteral,
- NodeTypeVarFieldType,
+ NodeTypeAnyTypeField,
};
enum FnInline {
@@ -705,7 +705,7 @@ struct AstNodeFnProto {
Buf *name;
ZigList params;
AstNode *return_type;
- Token *return_var_token;
+ Token *return_anytype_token;
AstNode *fn_def_node;
// populated if this is an extern declaration
Buf *lib_name;
@@ -734,7 +734,7 @@ struct AstNodeFnDef {
struct AstNodeParamDecl {
Buf *name;
AstNode *type;
- Token *var_token;
+ Token *anytype_token;
Buf doc_comments;
bool is_noalias;
bool is_comptime;
@@ -2145,7 +2145,7 @@ struct CodeGen {
ZigType *entry_num_lit_float;
ZigType *entry_undef;
ZigType *entry_null;
- ZigType *entry_var;
+ ZigType *entry_anytype;
ZigType *entry_global_error_set;
ZigType *entry_enum_literal;
ZigType *entry_any_frame;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index afe0fe6849..5eba515e68 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -1129,7 +1129,7 @@ ZigValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *
ZigValue *result = g->pass1_arena->create();
ZigValue *result_ptr = g->pass1_arena->create();
result->special = ConstValSpecialUndef;
- result->type = (type_entry == nullptr) ? g->builtin_types.entry_var : type_entry;
+ result->type = (type_entry == nullptr) ? g->builtin_types.entry_anytype : type_entry;
result_ptr->special = ConstValSpecialStatic;
result_ptr->type = get_pointer_to_type(g, result->type, false);
result_ptr->data.x_ptr.mut = ConstPtrMutComptimeVar;
@@ -1230,7 +1230,7 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent
Error type_val_resolve_is_opaque_type(CodeGen *g, ZigValue *type_val, bool *is_opaque_type) {
if (type_val->special != ConstValSpecialLazy) {
assert(type_val->special == ConstValSpecialStatic);
- if (type_val->data.x_type == g->builtin_types.entry_var) {
+ if (type_val->data.x_type == g->builtin_types.entry_anytype) {
*is_opaque_type = false;
return ErrorNone;
}
@@ -1853,10 +1853,10 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
buf_sprintf("var args only allowed in functions with C calling convention"));
return g->builtin_types.entry_invalid;
}
- } else if (param_node->data.param_decl.var_token != nullptr) {
+ } else if (param_node->data.param_decl.anytype_token != nullptr) {
if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
add_node_error(g, param_node,
- buf_sprintf("parameter of type 'var' not allowed in function with calling convention '%s'",
+ buf_sprintf("parameter of type 'anytype' not allowed in function with calling convention '%s'",
calling_convention_name(fn_type_id.cc)));
return g->builtin_types.entry_invalid;
}
@@ -1942,10 +1942,10 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
fn_entry->align_bytes = fn_type_id.alignment;
}
- if (fn_proto->return_var_token != nullptr) {
+ if (fn_proto->return_anytype_token != nullptr) {
if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
add_node_error(g, fn_proto->return_type,
- buf_sprintf("return type 'var' not allowed in function with calling convention '%s'",
+ buf_sprintf("return type 'anytype' not allowed in function with calling convention '%s'",
calling_convention_name(fn_type_id.cc)));
return g->builtin_types.entry_invalid;
}
@@ -3802,7 +3802,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
case NodeTypeEnumLiteral:
case NodeTypeAnyFrameType:
case NodeTypeErrorSetField:
- case NodeTypeVarFieldType:
+ case NodeTypeAnyTypeField:
zig_unreachable();
}
}
@@ -5868,7 +5868,7 @@ ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry) {
ReqCompTime type_requires_comptime(CodeGen *g, ZigType *ty) {
Error err;
- if (ty == g->builtin_types.entry_var) {
+ if (ty == g->builtin_types.entry_anytype) {
return ReqCompTimeYes;
}
switch (ty->id) {
diff --git a/src/ast_render.cpp b/src/ast_render.cpp
index 49fad80a40..ad308bf416 100644
--- a/src/ast_render.cpp
+++ b/src/ast_render.cpp
@@ -270,8 +270,8 @@ static const char *node_type_str(NodeType node_type) {
return "EnumLiteral";
case NodeTypeErrorSetField:
return "ErrorSetField";
- case NodeTypeVarFieldType:
- return "VarFieldType";
+ case NodeTypeAnyTypeField:
+ return "AnyTypeField";
}
zig_unreachable();
}
@@ -466,8 +466,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
}
if (param_decl->data.param_decl.is_var_args) {
fprintf(ar->f, "...");
- } else if (param_decl->data.param_decl.var_token != nullptr) {
- fprintf(ar->f, "var");
+ } else if (param_decl->data.param_decl.anytype_token != nullptr) {
+ fprintf(ar->f, "anytype");
} else {
render_node_grouped(ar, param_decl->data.param_decl.type);
}
@@ -496,8 +496,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
fprintf(ar->f, ")");
}
- if (node->data.fn_proto.return_var_token != nullptr) {
- fprintf(ar->f, "var");
+ if (node->data.fn_proto.return_anytype_token != nullptr) {
+ fprintf(ar->f, "anytype");
} else {
AstNode *return_type_node = node->data.fn_proto.return_type;
assert(return_type_node != nullptr);
@@ -1216,8 +1216,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
fprintf(ar->f, ".%s", buf_ptr(&node->data.enum_literal.identifier->data.str_lit.str));
break;
}
- case NodeTypeVarFieldType: {
- fprintf(ar->f, "var");
+ case NodeTypeAnyTypeField: {
+ fprintf(ar->f, "anytype");
break;
}
case NodeTypeParamDecl:
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 2f72861bc2..3473a2b0ac 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -8448,8 +8448,8 @@ static void define_builtin_types(CodeGen *g) {
}
{
ZigType *entry = new_type_table_entry(ZigTypeIdOpaque);
- buf_init_from_str(&entry->name, "(var)");
- g->builtin_types.entry_var = entry;
+ buf_init_from_str(&entry->name, "(anytype)");
+ g->builtin_types.entry_anytype = entry;
}
for (size_t i = 0; i < array_length(c_int_type_infos); i += 1) {
diff --git a/src/ir.cpp b/src/ir.cpp
index ca0fd47c49..6447db8c71 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -9942,7 +9942,7 @@ static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNod
is_var_args = true;
break;
}
- if (param_node->data.param_decl.var_token == nullptr) {
+ if (param_node->data.param_decl.anytype_token == nullptr) {
AstNode *type_node = param_node->data.param_decl.type;
IrInstSrc *type_value = ir_gen_node(irb, type_node, parent_scope);
if (type_value == irb->codegen->invalid_inst_src)
@@ -9968,7 +9968,7 @@ static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNod
}
IrInstSrc *return_type;
- if (node->data.fn_proto.return_var_token == nullptr) {
+ if (node->data.fn_proto.return_anytype_token == nullptr) {
if (node->data.fn_proto.return_type == nullptr) {
return_type = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_void);
} else {
@@ -10226,9 +10226,9 @@ static IrInstSrc *ir_gen_node_raw(IrBuilderSrc *irb, AstNode *node, Scope *scope
add_node_error(irb->codegen, node,
buf_sprintf("inferred array size invalid here"));
return irb->codegen->invalid_inst_src;
- case NodeTypeVarFieldType:
+ case NodeTypeAnyTypeField:
return ir_lval_wrap(irb, scope,
- ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_var), lval, result_loc);
+ ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_anytype), lval, result_loc);
}
zig_unreachable();
}
@@ -10296,7 +10296,7 @@ static IrInstSrc *ir_gen_node_extra(IrBuilderSrc *irb, AstNode *node, Scope *sco
case NodeTypeSuspend:
case NodeTypeEnumLiteral:
case NodeTypeInferredArrayType:
- case NodeTypeVarFieldType:
+ case NodeTypeAnyTypeField:
case NodeTypePrefixOpExpr:
add_node_error(irb->codegen, node,
buf_sprintf("invalid left-hand side to assignment"));
@@ -10518,7 +10518,7 @@ ZigValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ZigValue *const_va
if (val == nullptr) return nullptr;
assert(const_val->type->id == ZigTypeIdPointer);
ZigType *expected_type = const_val->type->data.pointer.child_type;
- if (expected_type == codegen->builtin_types.entry_var) {
+ if (expected_type == codegen->builtin_types.entry_anytype) {
return val;
}
switch (type_has_one_possible_value(codegen, expected_type)) {
@@ -15040,7 +15040,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,
}
// This means the wanted type is anything.
- if (wanted_type == ira->codegen->builtin_types.entry_var) {
+ if (wanted_type == ira->codegen->builtin_types.entry_anytype) {
return value;
}
@@ -15635,7 +15635,7 @@ static IrInstGen *ir_implicit_cast(IrAnalyze *ira, IrInstGen *value, ZigType *ex
static ZigType *get_ptr_elem_type(CodeGen *g, IrInstGen *ptr) {
ir_assert_gen(ptr->value->type->id == ZigTypeIdPointer, ptr);
ZigType *elem_type = ptr->value->type->data.pointer.child_type;
- if (elem_type != g->builtin_types.entry_var)
+ if (elem_type != g->builtin_types.entry_anytype)
return elem_type;
if (ir_resolve_lazy(g, ptr->base.source_node, ptr->value))
@@ -15687,7 +15687,7 @@ static IrInstGen *ir_get_deref(IrAnalyze *ira, IrInst* source_instruction, IrIns
}
if (ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
ZigValue *pointee = const_ptr_pointee_unchecked(ira->codegen, ptr->value);
- if (child_type == ira->codegen->builtin_types.entry_var) {
+ if (child_type == ira->codegen->builtin_types.entry_anytype) {
child_type = pointee->type;
}
if (pointee->special != ConstValSpecialRuntime) {
@@ -19087,7 +19087,7 @@ static Error ir_result_has_type(IrAnalyze *ira, ResultLoc *result_loc, bool *out
ZigType *dest_type = ir_resolve_type(ira, result_cast->base.source_instruction->child);
if (type_is_invalid(dest_type))
return ErrorSemanticAnalyzeFail;
- *out = (dest_type != ira->codegen->builtin_types.entry_var);
+ *out = (dest_type != ira->codegen->builtin_types.entry_anytype);
return ErrorNone;
}
case ResultLocIdVar:
@@ -19293,7 +19293,7 @@ static IrInstGen *ir_resolve_result_raw(IrAnalyze *ira, IrInst *suspend_source_i
if (type_is_invalid(dest_type))
return ira->codegen->invalid_inst_gen;
- if (dest_type == ira->codegen->builtin_types.entry_var) {
+ if (dest_type == ira->codegen->builtin_types.entry_anytype) {
return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type);
}
@@ -19439,7 +19439,7 @@ static IrInstGen *ir_resolve_result_raw(IrAnalyze *ira, IrInst *suspend_source_i
return ira->codegen->invalid_inst_gen;
}
- if (child_type != ira->codegen->builtin_types.entry_var) {
+ if (child_type != ira->codegen->builtin_types.entry_anytype) {
if (type_size(ira->codegen, child_type) != type_size(ira->codegen, value_type)) {
// pointer cast won't work; we need a temporary location.
result_bit_cast->parent->written = parent_was_written;
@@ -19600,9 +19600,9 @@ static IrInstGen *ir_analyze_instruction_resolve_result(IrAnalyze *ira, IrInstSr
if (type_is_invalid(implicit_elem_type))
return ira->codegen->invalid_inst_gen;
} else {
- implicit_elem_type = ira->codegen->builtin_types.entry_var;
+ implicit_elem_type = ira->codegen->builtin_types.entry_anytype;
}
- if (implicit_elem_type == ira->codegen->builtin_types.entry_var) {
+ if (implicit_elem_type == ira->codegen->builtin_types.entry_anytype) {
Buf *bare_name = buf_alloc();
Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct),
instruction->base.base.scope, instruction->base.base.source_node, bare_name);
@@ -19759,7 +19759,7 @@ static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node
assert(param_decl_node->type == NodeTypeParamDecl);
IrInstGen *casted_arg;
- if (param_decl_node->data.param_decl.var_token == nullptr) {
+ if (param_decl_node->data.param_decl.anytype_token == nullptr) {
AstNode *param_type_node = param_decl_node->data.param_decl.type;
ZigType *param_type = ir_analyze_type_expr(ira, *exec_scope, param_type_node);
if (type_is_invalid(param_type))
@@ -19799,7 +19799,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
arg_part_of_generic_id = true;
casted_arg = arg;
} else {
- if (param_decl_node->data.param_decl.var_token == nullptr) {
+ if (param_decl_node->data.param_decl.anytype_token == nullptr) {
AstNode *param_type_node = param_decl_node->data.param_decl.type;
ZigType *param_type = ir_analyze_type_expr(ira, *child_scope, param_type_node);
if (type_is_invalid(param_type))
@@ -20011,7 +20011,7 @@ static IrInstGen *ir_analyze_store_ptr(IrAnalyze *ira, IrInst* source_instr,
}
if (ptr->value->type->data.pointer.inferred_struct_field != nullptr &&
- child_type == ira->codegen->builtin_types.entry_var)
+ child_type == ira->codegen->builtin_types.entry_anytype)
{
child_type = ptr->value->type->data.pointer.inferred_struct_field->inferred_struct_type;
}
@@ -20202,6 +20202,11 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
}
AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
+ if (return_type_node == nullptr) {
+ ir_add_error(ira, &fn_ref->base,
+ buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"));
+ return ira->codegen->invalid_inst_gen;
+ }
ZigType *specified_return_type = ir_analyze_type_expr(ira, exec_scope, return_type_node);
if (type_is_invalid(specified_return_type))
return ira->codegen->invalid_inst_gen;
@@ -20364,7 +20369,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
inst_fn_type_id.alignment = align_bytes;
}
- if (fn_proto_node->data.fn_proto.return_var_token == nullptr) {
+ if (fn_proto_node->data.fn_proto.return_anytype_token == nullptr) {
AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
ZigType *specified_return_type = ir_analyze_type_expr(ira, impl_fn->child_scope, return_type_node);
if (type_is_invalid(specified_return_type))
@@ -20463,7 +20468,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
if (type_is_invalid(dummy_result->value->type))
return ira->codegen->invalid_inst_gen;
ZigType *res_child_type = result_loc->value->type->data.pointer.child_type;
- if (res_child_type == ira->codegen->builtin_types.entry_var) {
+ if (res_child_type == ira->codegen->builtin_types.entry_anytype) {
res_child_type = impl_fn_type_id->return_type;
}
if (!handle_is_ptr(ira->codegen, res_child_type)) {
@@ -20606,7 +20611,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
if (type_is_invalid(dummy_result->value->type))
return ira->codegen->invalid_inst_gen;
ZigType *res_child_type = result_loc->value->type->data.pointer.child_type;
- if (res_child_type == ira->codegen->builtin_types.entry_var) {
+ if (res_child_type == ira->codegen->builtin_types.entry_anytype) {
res_child_type = return_type;
}
if (!handle_is_ptr(ira->codegen, res_child_type)) {
@@ -22337,7 +22342,7 @@ static IrInstGen *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name,
inferred_struct_field->inferred_struct_type = container_type;
inferred_struct_field->field_name = field_name;
- ZigType *elem_type = ira->codegen->builtin_types.entry_var;
+ ZigType *elem_type = ira->codegen->builtin_types.entry_anytype;
ZigType *field_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
container_ptr_type->data.pointer.is_const, container_ptr_type->data.pointer.is_volatile,
PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, inferred_struct_field, nullptr);
@@ -25115,7 +25120,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_ent
fields[5]->special = ConstValSpecialStatic;
fields[5]->type = ira->codegen->builtin_types.entry_bool;
fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero;
- // sentinel: var
+ // sentinel: anytype
ensure_field_index(result->type, "sentinel", 6);
fields[6]->special = ConstValSpecialStatic;
if (attrs_type->data.pointer.child_type->id != ZigTypeIdOpaque) {
@@ -25243,7 +25248,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
fields[1]->special = ConstValSpecialStatic;
fields[1]->type = ira->codegen->builtin_types.entry_type;
fields[1]->data.x_type = type_entry->data.array.child_type;
- // sentinel: var
+ // sentinel: anytype
fields[2]->special = ConstValSpecialStatic;
fields[2]->type = get_optional_type(ira->codegen, type_entry->data.array.child_type);
fields[2]->data.x_optional = type_entry->data.array.sentinel;
@@ -25598,7 +25603,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
inner_fields[2]->type = ira->codegen->builtin_types.entry_type;
inner_fields[2]->data.x_type = struct_field->type_entry;
- // default_value: var
+ // default_value: anytype
inner_fields[3]->special = ConstValSpecialStatic;
inner_fields[3]->type = get_optional_type2(ira->codegen, struct_field->type_entry);
if (inner_fields[3]->type == nullptr) return ErrorSemanticAnalyzeFail;
@@ -25736,7 +25741,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1);
result->data.x_struct.fields = fields;
ZigFn *fn = type_entry->data.frame.fn;
- // function: var
+ // function: anytype
ensure_field_index(result->type, "function", 0);
fields[0] = create_const_fn(ira->codegen, fn);
break;
@@ -29996,7 +30001,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy
if (arg_index >= fn_type_id->param_count) {
if (instruction->allow_var) {
// TODO remove this with var args
- return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_var);
+ return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);
}
ir_add_error(ira, &arg_index_inst->base,
buf_sprintf("arg index %" ZIG_PRI_u64 " out of bounds; '%s' has %" ZIG_PRI_usize " arguments",
@@ -30010,7 +30015,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy
ir_assert(fn_type->data.fn.is_generic, &instruction->base.base);
if (instruction->allow_var) {
- return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_var);
+ return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);
} else {
ir_add_error(ira, &arg_index_inst->base,
buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic",
diff --git a/src/parser.cpp b/src/parser.cpp
index c6c5823672..fcd1133b0d 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -786,7 +786,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B
return nullptr;
}
-// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr)
+// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_anytype / TypeExpr)
static AstNode *ast_parse_fn_proto(ParseContext *pc) {
Token *first = eat_token_if(pc, TokenIdKeywordFn);
if (first == nullptr) {
@@ -801,10 +801,10 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
AstNode *align_expr = ast_parse_byte_align(pc);
AstNode *section_expr = ast_parse_link_section(pc);
AstNode *callconv_expr = ast_parse_callconv(pc);
- Token *var = eat_token_if(pc, TokenIdKeywordVar);
+ Token *anytype = eat_token_if(pc, TokenIdKeywordAnyType);
Token *exmark = nullptr;
AstNode *return_type = nullptr;
- if (var == nullptr) {
+ if (anytype == nullptr) {
exmark = eat_token_if(pc, TokenIdBang);
return_type = ast_expect(pc, ast_parse_type_expr);
}
@@ -816,7 +816,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
res->data.fn_proto.align_expr = align_expr;
res->data.fn_proto.section_expr = section_expr;
res->data.fn_proto.callconv_expr = callconv_expr;
- res->data.fn_proto.return_var_token = var;
+ res->data.fn_proto.return_anytype_token = anytype;
res->data.fn_proto.auto_err_set = exmark != nullptr;
res->data.fn_proto.return_type = return_type;
@@ -870,9 +870,9 @@ static AstNode *ast_parse_container_field(ParseContext *pc) {
AstNode *type_expr = nullptr;
if (eat_token_if(pc, TokenIdColon) != nullptr) {
- Token *var_tok = eat_token_if(pc, TokenIdKeywordVar);
- if (var_tok != nullptr) {
- type_expr = ast_create_node(pc, NodeTypeVarFieldType, var_tok);
+ Token *anytype_tok = eat_token_if(pc, TokenIdKeywordAnyType);
+ if (anytype_tok != nullptr) {
+ type_expr = ast_create_node(pc, NodeTypeAnyTypeField, anytype_tok);
} else {
type_expr = ast_expect(pc, ast_parse_type_expr);
}
@@ -2191,14 +2191,14 @@ static AstNode *ast_parse_param_decl(ParseContext *pc) {
}
// ParamType
-// <- KEYWORD_var
+// <- KEYWORD_anytype
// / DOT3
// / TypeExpr
static AstNode *ast_parse_param_type(ParseContext *pc) {
- Token *var_token = eat_token_if(pc, TokenIdKeywordVar);
- if (var_token != nullptr) {
- AstNode *res = ast_create_node(pc, NodeTypeParamDecl, var_token);
- res->data.param_decl.var_token = var_token;
+ Token *anytype_token = eat_token_if(pc, TokenIdKeywordAnyType);
+ if (anytype_token != nullptr) {
+ AstNode *res = ast_create_node(pc, NodeTypeParamDecl, anytype_token);
+ res->data.param_decl.anytype_token = anytype_token;
return res;
}
@@ -3207,7 +3207,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
visit_field(&node->data.suspend.block, visit, context);
break;
case NodeTypeEnumLiteral:
- case NodeTypeVarFieldType:
+ case NodeTypeAnyTypeField:
break;
}
}
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index f09a146f2b..487a125d62 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -106,6 +106,7 @@ static const struct ZigKeyword zig_keywords[] = {
{"allowzero", TokenIdKeywordAllowZero},
{"and", TokenIdKeywordAnd},
{"anyframe", TokenIdKeywordAnyFrame},
+ {"anytype", TokenIdKeywordAnyType},
{"asm", TokenIdKeywordAsm},
{"async", TokenIdKeywordAsync},
{"await", TokenIdKeywordAwait},
@@ -1569,6 +1570,7 @@ const char * token_name(TokenId id) {
case TokenIdKeywordAlign: return "align";
case TokenIdKeywordAnd: return "and";
case TokenIdKeywordAnyFrame: return "anyframe";
+ case TokenIdKeywordAnyType: return "anytype";
case TokenIdKeywordAsm: return "asm";
case TokenIdKeywordBreak: return "break";
case TokenIdKeywordCatch: return "catch";
diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp
index 552ded4ef8..d8af21ee00 100644
--- a/src/tokenizer.hpp
+++ b/src/tokenizer.hpp
@@ -54,6 +54,7 @@ enum TokenId {
TokenIdKeywordAllowZero,
TokenIdKeywordAnd,
TokenIdKeywordAnyFrame,
+ TokenIdKeywordAnyType,
TokenIdKeywordAsm,
TokenIdKeywordAsync,
TokenIdKeywordAwait,
--
cgit v1.2.3