mirror of
https://github.com/ziglang/zig.git
synced 2026-01-27 09:45:27 +00:00
Merge pull request #6242 from Vexu/stage2
Stage2: slicing and split container scope from file scope
This commit is contained in:
commit
f2bbd8a548
@ -1175,6 +1175,7 @@ pub const Tokenizer = struct {
|
||||
},
|
||||
.num_dot_dec => switch (c) {
|
||||
'.' => {
|
||||
result.id = .IntegerLiteral;
|
||||
self.index -= 1;
|
||||
state = .start;
|
||||
break;
|
||||
@ -1183,7 +1184,6 @@ pub const Tokenizer = struct {
|
||||
state = .float_exponent_unsigned;
|
||||
},
|
||||
'0'...'9' => {
|
||||
result.id = .FloatLiteral;
|
||||
state = .float_fraction_dec;
|
||||
},
|
||||
else => {
|
||||
@ -1769,6 +1769,7 @@ test "tokenizer - number literals decimal" {
|
||||
testTokenize("7", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("8", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("9", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("1..", &[_]Token.Id{ .IntegerLiteral, .Ellipsis2 });
|
||||
testTokenize("0a", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("9b", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("1z", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
|
||||
@ -125,7 +125,7 @@ pub const Decl = struct {
|
||||
/// mapping them to an address in the output file.
|
||||
/// Memory owned by this decl, using Module's allocator.
|
||||
name: [*:0]const u8,
|
||||
/// The direct parent container of the Decl. This is either a `Scope.File` or `Scope.ZIRModule`.
|
||||
/// The direct parent container of the Decl. This is either a `Scope.Container` or `Scope.ZIRModule`.
|
||||
/// Reference to externally owned memory.
|
||||
scope: *Scope,
|
||||
/// The AST Node decl index or ZIR Inst index that contains this declaration.
|
||||
@ -217,9 +217,10 @@ pub const Decl = struct {
|
||||
|
||||
pub fn src(self: Decl) usize {
|
||||
switch (self.scope.tag) {
|
||||
.file => {
|
||||
const file = @fieldParentPtr(Scope.File, "base", self.scope);
|
||||
const tree = file.contents.tree;
|
||||
.container => {
|
||||
const container = @fieldParentPtr(Scope.Container, "base", self.scope);
|
||||
const tree = container.file_scope.contents.tree;
|
||||
// TODO Container should have it's own decls()
|
||||
const decl_node = tree.root_node.decls()[self.src_index];
|
||||
return tree.token_locs[decl_node.firstToken()].start;
|
||||
},
|
||||
@ -229,6 +230,7 @@ pub const Decl = struct {
|
||||
const src_decl = module.decls[self.src_index];
|
||||
return src_decl.inst.src;
|
||||
},
|
||||
.file,
|
||||
.block => unreachable,
|
||||
.gen_zir => unreachable,
|
||||
.local_val => unreachable,
|
||||
@ -359,6 +361,7 @@ pub const Scope = struct {
|
||||
.local_ptr => return self.cast(LocalPtr).?.gen_zir.arena,
|
||||
.zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
|
||||
.file => unreachable,
|
||||
.container => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,15 +371,16 @@ pub const Scope = struct {
|
||||
return switch (self.tag) {
|
||||
.block => self.cast(Block).?.decl,
|
||||
.gen_zir => self.cast(GenZIR).?.decl,
|
||||
.local_val => return self.cast(LocalVal).?.gen_zir.decl,
|
||||
.local_ptr => return self.cast(LocalPtr).?.gen_zir.decl,
|
||||
.local_val => self.cast(LocalVal).?.gen_zir.decl,
|
||||
.local_ptr => self.cast(LocalPtr).?.gen_zir.decl,
|
||||
.decl => self.cast(DeclAnalysis).?.decl,
|
||||
.zir_module => null,
|
||||
.file => null,
|
||||
.container => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the scope has a parent which is a ZIRModule or File and
|
||||
/// Asserts the scope has a parent which is a ZIRModule or Container and
|
||||
/// returns it.
|
||||
pub fn namespace(self: *Scope) *Scope {
|
||||
switch (self.tag) {
|
||||
@ -385,7 +389,8 @@ pub const Scope = struct {
|
||||
.local_val => return self.cast(LocalVal).?.gen_zir.decl.scope,
|
||||
.local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope,
|
||||
.decl => return self.cast(DeclAnalysis).?.decl.scope,
|
||||
.zir_module, .file => return self,
|
||||
.file => return &self.cast(File).?.root_container.base,
|
||||
.zir_module, .container => return self,
|
||||
}
|
||||
}
|
||||
|
||||
@ -399,8 +404,9 @@ pub const Scope = struct {
|
||||
.local_val => unreachable,
|
||||
.local_ptr => unreachable,
|
||||
.decl => unreachable,
|
||||
.file => unreachable,
|
||||
.zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
|
||||
.file => return self.cast(File).?.fullyQualifiedNameHash(name),
|
||||
.container => return self.cast(Container).?.fullyQualifiedNameHash(name),
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,11 +415,12 @@ pub const Scope = struct {
|
||||
switch (self.tag) {
|
||||
.file => return self.cast(File).?.contents.tree,
|
||||
.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,
|
||||
.local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(File).?.contents.tree,
|
||||
.local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(File).?.contents.tree,
|
||||
.decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree,
|
||||
.block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree,
|
||||
.gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree,
|
||||
.local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
|
||||
.local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
|
||||
.container => return self.cast(Container).?.file_scope.contents.tree,
|
||||
}
|
||||
}
|
||||
|
||||
@ -427,13 +434,15 @@ pub const Scope = struct {
|
||||
.decl => unreachable,
|
||||
.zir_module => unreachable,
|
||||
.file => unreachable,
|
||||
.container => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the scope has a parent which is a ZIRModule or File and
|
||||
/// Asserts the scope has a parent which is a ZIRModule, Contaienr or File and
|
||||
/// returns the sub_file_path field.
|
||||
pub fn subFilePath(base: *Scope) []const u8 {
|
||||
switch (base.tag) {
|
||||
.container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path,
|
||||
.file => return @fieldParentPtr(File, "base", base).sub_file_path,
|
||||
.zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
|
||||
.block => unreachable,
|
||||
@ -453,11 +462,13 @@ pub const Scope = struct {
|
||||
.local_val => unreachable,
|
||||
.local_ptr => unreachable,
|
||||
.decl => unreachable,
|
||||
.container => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 {
|
||||
switch (base.tag) {
|
||||
.container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module),
|
||||
.file => return @fieldParentPtr(File, "base", base).getSource(module),
|
||||
.zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),
|
||||
.gen_zir => unreachable,
|
||||
@ -471,8 +482,9 @@ pub const Scope = struct {
|
||||
/// Asserts the scope is a namespace Scope and removes the Decl from the namespace.
|
||||
pub fn removeDecl(base: *Scope, child: *Decl) void {
|
||||
switch (base.tag) {
|
||||
.file => return @fieldParentPtr(File, "base", base).removeDecl(child),
|
||||
.container => return @fieldParentPtr(Container, "base", base).removeDecl(child),
|
||||
.zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
|
||||
.file => unreachable,
|
||||
.block => unreachable,
|
||||
.gen_zir => unreachable,
|
||||
.local_val => unreachable,
|
||||
@ -499,6 +511,7 @@ pub const Scope = struct {
|
||||
.local_val => unreachable,
|
||||
.local_ptr => unreachable,
|
||||
.decl => unreachable,
|
||||
.container => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
@ -515,6 +528,8 @@ pub const Scope = struct {
|
||||
zir_module,
|
||||
/// .zig source code.
|
||||
file,
|
||||
/// struct, enum or union, every .file contains one of these.
|
||||
container,
|
||||
block,
|
||||
decl,
|
||||
gen_zir,
|
||||
@ -522,6 +537,38 @@ pub const Scope = struct {
|
||||
local_ptr,
|
||||
};
|
||||
|
||||
pub const Container = struct {
|
||||
pub const base_tag: Tag = .container;
|
||||
base: Scope = Scope{ .tag = base_tag },
|
||||
|
||||
file_scope: *Scope.File,
|
||||
|
||||
/// Direct children of the file.
|
||||
decls: ArrayListUnmanaged(*Decl),
|
||||
|
||||
// TODO implement container types and put this in a status union
|
||||
// ty: Type
|
||||
|
||||
pub fn deinit(self: *Container, gpa: *Allocator) void {
|
||||
self.decls.deinit(gpa);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn removeDecl(self: *Container, child: *Decl) void {
|
||||
for (self.decls.items) |item, i| {
|
||||
if (item == child) {
|
||||
_ = self.decls.swapRemove(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fullyQualifiedNameHash(self: *Container, name: []const u8) NameHash {
|
||||
// TODO container scope qualified names.
|
||||
return std.zig.hashSrc(name);
|
||||
}
|
||||
};
|
||||
|
||||
pub const File = struct {
|
||||
pub const base_tag: Tag = .file;
|
||||
base: Scope = Scope{ .tag = base_tag },
|
||||
@ -544,8 +591,7 @@ pub const Scope = struct {
|
||||
loaded_success,
|
||||
},
|
||||
|
||||
/// Direct children of the file.
|
||||
decls: ArrayListUnmanaged(*Decl),
|
||||
root_container: Container,
|
||||
|
||||
pub fn unload(self: *File, gpa: *Allocator) void {
|
||||
switch (self.status) {
|
||||
@ -569,20 +615,11 @@ pub const Scope = struct {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *File, gpa: *Allocator) void {
|
||||
self.decls.deinit(gpa);
|
||||
self.root_container.deinit(gpa);
|
||||
self.unload(gpa);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn removeDecl(self: *File, child: *Decl) void {
|
||||
for (self.decls.items) |item, i| {
|
||||
if (item == child) {
|
||||
_ = self.decls.swapRemove(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dumpSrc(self: *File, src: usize) void {
|
||||
const loc = std.zig.findLineColumn(self.source.bytes, src);
|
||||
std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
|
||||
@ -604,11 +641,6 @@ 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 {
|
||||
@ -861,7 +893,10 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
||||
.source = .{ .unloaded = {} },
|
||||
.contents = .{ .not_available = {} },
|
||||
.status = .never_loaded,
|
||||
.decls = .{},
|
||||
.root_container = .{
|
||||
.file_scope = root_scope,
|
||||
.decls = .{},
|
||||
},
|
||||
};
|
||||
break :blk &root_scope.base;
|
||||
} else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) {
|
||||
@ -969,7 +1004,7 @@ pub fn update(self: *Module) !void {
|
||||
// to force a refresh we unload now.
|
||||
if (self.root_scope.cast(Scope.File)) |zig_file| {
|
||||
zig_file.unload(self.gpa);
|
||||
self.analyzeRootSrcFile(zig_file) catch |err| switch (err) {
|
||||
self.analyzeContainer(&zig_file.root_container) catch |err| switch (err) {
|
||||
error.AnalysisFail => {
|
||||
assert(self.totalErrorCount() != 0);
|
||||
},
|
||||
@ -1237,8 +1272,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const file_scope = decl.scope.cast(Scope.File).?;
|
||||
const tree = try self.getAstTree(file_scope);
|
||||
const container_scope = decl.scope.cast(Scope.Container).?;
|
||||
const tree = try self.getAstTree(container_scope);
|
||||
const ast_node = tree.root_node.decls()[decl.src_index];
|
||||
switch (ast_node.tag) {
|
||||
.FnProto => {
|
||||
@ -1698,10 +1733,12 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
|
||||
}
|
||||
}
|
||||
|
||||
fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
|
||||
fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const root_scope = container_scope.file_scope;
|
||||
|
||||
switch (root_scope.status) {
|
||||
.never_loaded, .unloaded_success => {
|
||||
try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
|
||||
@ -1743,24 +1780,24 @@ fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
|
||||
}
|
||||
}
|
||||
|
||||
fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
|
||||
fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
// We may be analyzing it for the first time, or this may be
|
||||
// an incremental update. This code handles both cases.
|
||||
const tree = try self.getAstTree(root_scope);
|
||||
const tree = try self.getAstTree(container_scope);
|
||||
const decls = tree.root_node.decls();
|
||||
|
||||
try self.work_queue.ensureUnusedCapacity(decls.len);
|
||||
try root_scope.decls.ensureCapacity(self.gpa, decls.len);
|
||||
try container_scope.decls.ensureCapacity(self.gpa, decls.len);
|
||||
|
||||
// Keep track of the decls that we expect to see in this file so that
|
||||
// we know which ones have been deleted.
|
||||
var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
|
||||
defer deleted_decls.deinit();
|
||||
try deleted_decls.ensureCapacity(root_scope.decls.items.len);
|
||||
for (root_scope.decls.items) |file_decl| {
|
||||
try deleted_decls.ensureCapacity(container_scope.decls.items.len);
|
||||
for (container_scope.decls.items) |file_decl| {
|
||||
deleted_decls.putAssumeCapacityNoClobber(file_decl, {});
|
||||
}
|
||||
|
||||
@ -1773,7 +1810,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
|
||||
|
||||
const name_loc = tree.token_locs[name_tok];
|
||||
const name = tree.tokenSliceLoc(name_loc);
|
||||
const name_hash = root_scope.fullyQualifiedNameHash(name);
|
||||
const name_hash = container_scope.fullyQualifiedNameHash(name);
|
||||
const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
|
||||
if (self.decl_table.get(name_hash)) |decl| {
|
||||
// Update the AST Node index of the decl, even if its contents are unchanged, it may
|
||||
@ -1801,8 +1838,8 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);
|
||||
root_scope.decls.appendAssumeCapacity(new_decl);
|
||||
const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
|
||||
container_scope.decls.appendAssumeCapacity(new_decl);
|
||||
if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
|
||||
if (tree.token_ids[maybe_export_token] == .Keyword_export) {
|
||||
self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
|
||||
@ -1812,7 +1849,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
|
||||
} else if (src_decl.castTag(.VarDecl)) |var_decl| {
|
||||
const name_loc = tree.token_locs[var_decl.name_token];
|
||||
const name = tree.tokenSliceLoc(name_loc);
|
||||
const name_hash = root_scope.fullyQualifiedNameHash(name);
|
||||
const name_hash = container_scope.fullyQualifiedNameHash(name);
|
||||
const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
|
||||
if (self.decl_table.get(name_hash)) |decl| {
|
||||
// Update the AST Node index of the decl, even if its contents are unchanged, it may
|
||||
@ -1828,8 +1865,8 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
|
||||
decl.contents_hash = contents_hash;
|
||||
}
|
||||
} else {
|
||||
const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);
|
||||
root_scope.decls.appendAssumeCapacity(new_decl);
|
||||
const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
|
||||
container_scope.decls.appendAssumeCapacity(new_decl);
|
||||
if (var_decl.getExternExportToken()) |maybe_export_token| {
|
||||
if (tree.token_ids[maybe_export_token] == .Keyword_export) {
|
||||
self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
|
||||
@ -1841,11 +1878,11 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
|
||||
const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index});
|
||||
defer self.gpa.free(name);
|
||||
|
||||
const name_hash = root_scope.fullyQualifiedNameHash(name);
|
||||
const name_hash = container_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);
|
||||
root_scope.decls.appendAssumeCapacity(new_decl);
|
||||
const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
|
||||
container_scope.decls.appendAssumeCapacity(new_decl);
|
||||
self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
|
||||
} else if (src_decl.castTag(.ContainerField)) |container_field| {
|
||||
log.err("TODO: analyze container field", .{});
|
||||
@ -2591,6 +2628,72 @@ pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) In
|
||||
return self.fail(scope, src, "TODO implement analysis of iserr", .{});
|
||||
}
|
||||
|
||||
pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, start: *Inst, end_opt: ?*Inst, sentinel_opt: ?*Inst) InnerError!*Inst {
|
||||
const ptr_child = switch (array_ptr.ty.zigTypeTag()) {
|
||||
.Pointer => array_ptr.ty.elemType(),
|
||||
else => return self.fail(scope, src, "expected pointer, found '{}'", .{array_ptr.ty}),
|
||||
};
|
||||
|
||||
var array_type = ptr_child;
|
||||
const elem_type = switch (ptr_child.zigTypeTag()) {
|
||||
.Array => ptr_child.elemType(),
|
||||
.Pointer => blk: {
|
||||
if (ptr_child.isSinglePointer()) {
|
||||
if (ptr_child.elemType().zigTypeTag() == .Array) {
|
||||
array_type = ptr_child.elemType();
|
||||
break :blk ptr_child.elemType().elemType();
|
||||
}
|
||||
|
||||
return self.fail(scope, src, "slice of single-item pointer", .{});
|
||||
}
|
||||
break :blk ptr_child.elemType();
|
||||
},
|
||||
else => return self.fail(scope, src, "slice of non-array type '{}'", .{ptr_child}),
|
||||
};
|
||||
|
||||
const slice_sentinel = if (sentinel_opt) |sentinel| blk: {
|
||||
const casted = try self.coerce(scope, elem_type, sentinel);
|
||||
break :blk try self.resolveConstValue(scope, casted);
|
||||
} else null;
|
||||
|
||||
var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice;
|
||||
var return_elem_type = elem_type;
|
||||
if (end_opt) |end| {
|
||||
if (end.value()) |end_val| {
|
||||
if (start.value()) |start_val| {
|
||||
const start_u64 = start_val.toUnsignedInt();
|
||||
const end_u64 = end_val.toUnsignedInt();
|
||||
if (start_u64 > end_u64) {
|
||||
return self.fail(scope, src, "out of bounds slice", .{});
|
||||
}
|
||||
|
||||
const len = end_u64 - start_u64;
|
||||
const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen())
|
||||
array_type.sentinel()
|
||||
else
|
||||
slice_sentinel;
|
||||
return_elem_type = try self.arrayType(scope, len, array_sentinel, elem_type);
|
||||
return_ptr_size = .One;
|
||||
}
|
||||
}
|
||||
}
|
||||
const return_type = try self.ptrType(
|
||||
scope,
|
||||
src,
|
||||
return_elem_type,
|
||||
if (end_opt == null) slice_sentinel else null,
|
||||
0, // TODO alignment
|
||||
0,
|
||||
0,
|
||||
!ptr_child.isConstPtr(),
|
||||
ptr_child.isAllowzeroPtr(),
|
||||
ptr_child.isVolatilePtr(),
|
||||
return_ptr_size,
|
||||
);
|
||||
|
||||
return self.fail(scope, src, "TODO implement analysis of slice", .{});
|
||||
}
|
||||
|
||||
/// Asserts that lhs and rhs types are both numeric.
|
||||
pub fn cmpNumeric(
|
||||
self: *Module,
|
||||
@ -2801,6 +2904,12 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty
|
||||
prev_inst = next_inst;
|
||||
continue;
|
||||
}
|
||||
if (next_inst.ty.zigTypeTag() == .Undefined)
|
||||
continue;
|
||||
if (prev_inst.ty.zigTypeTag() == .Undefined) {
|
||||
prev_inst = next_inst;
|
||||
continue;
|
||||
}
|
||||
if (prev_inst.ty.isInt() and
|
||||
next_inst.ty.isInt() and
|
||||
prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt())
|
||||
@ -3052,6 +3161,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err
|
||||
self.failed_files.putAssumeCapacityNoClobber(scope, err_msg);
|
||||
},
|
||||
.file => unreachable,
|
||||
.container => unreachable,
|
||||
}
|
||||
return error.AnalysisFail;
|
||||
}
|
||||
|
||||
@ -275,16 +275,16 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
|
||||
.ErrorType => return rlWrap(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)),
|
||||
.For => return forExpr(mod, scope, rl, node.castTag(.For).?),
|
||||
.ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?),
|
||||
.Slice => return rlWrap(mod, scope, rl, try sliceExpr(mod, scope, node.castTag(.Slice).?)),
|
||||
.Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),
|
||||
.Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),
|
||||
.OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?),
|
||||
|
||||
.Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
|
||||
.Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}),
|
||||
.OrElse => return mod.failNode(scope, node, "TODO implement astgen.expr for .OrElse", .{}),
|
||||
.Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),
|
||||
.Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),
|
||||
.Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
|
||||
.Slice => return mod.failNode(scope, node, "TODO implement astgen.expr for .Slice", .{}),
|
||||
.ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}),
|
||||
.ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}),
|
||||
.StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),
|
||||
@ -790,13 +790,31 @@ fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*
|
||||
}
|
||||
|
||||
fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch) InnerError!*zir.Inst {
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[node.op_token].start;
|
||||
return orelseCatchExpr(mod, scope, rl, node.lhs, node.op_token, .iserr, .unwrap_err_unsafe, node.rhs, node.payload);
|
||||
}
|
||||
|
||||
const err_union_ptr = try expr(mod, scope, .ref, node.lhs);
|
||||
// TODO we could avoid an unnecessary copy if .iserr took a pointer
|
||||
const err_union = try addZIRUnOp(mod, scope, src, .deref, err_union_ptr);
|
||||
const cond = try addZIRUnOp(mod, scope, src, .iserr, err_union);
|
||||
fn orelseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
|
||||
return orelseCatchExpr(mod, scope, rl, node.lhs, node.op_token, .isnull, .unwrap_optional_unsafe, node.rhs, null);
|
||||
}
|
||||
|
||||
fn orelseCatchExpr(
|
||||
mod: *Module,
|
||||
scope: *Scope,
|
||||
rl: ResultLoc,
|
||||
lhs: *ast.Node,
|
||||
op_token: ast.TokenIndex,
|
||||
cond_op: zir.Inst.Tag,
|
||||
unwrap_op: zir.Inst.Tag,
|
||||
rhs: *ast.Node,
|
||||
payload_node: ?*ast.Node,
|
||||
) InnerError!*zir.Inst {
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[op_token].start;
|
||||
|
||||
const operand_ptr = try expr(mod, scope, .ref, lhs);
|
||||
// TODO we could avoid an unnecessary copy if .iserr, .isnull took a pointer
|
||||
const err_union = try addZIRUnOp(mod, scope, src, .deref, operand_ptr);
|
||||
const cond = try addZIRUnOp(mod, scope, src, cond_op, err_union);
|
||||
|
||||
var block_scope: Scope.GenZIR = .{
|
||||
.parent = scope,
|
||||
@ -825,55 +843,55 @@ fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch)
|
||||
.inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
|
||||
};
|
||||
|
||||
var err_scope: Scope.GenZIR = .{
|
||||
var then_scope: Scope.GenZIR = .{
|
||||
.parent = scope,
|
||||
.decl = block_scope.decl,
|
||||
.arena = block_scope.arena,
|
||||
.instructions = .{},
|
||||
};
|
||||
defer err_scope.instructions.deinit(mod.gpa);
|
||||
defer then_scope.instructions.deinit(mod.gpa);
|
||||
|
||||
var err_val_scope: Scope.LocalVal = undefined;
|
||||
const err_sub_scope = blk: {
|
||||
const payload = node.payload orelse
|
||||
break :blk &err_scope.base;
|
||||
const then_sub_scope = blk: {
|
||||
const payload = payload_node orelse
|
||||
break :blk &then_scope.base;
|
||||
|
||||
const err_name = tree.tokenSlice(payload.castTag(.Payload).?.error_symbol.firstToken());
|
||||
if (mem.eql(u8, err_name, "_"))
|
||||
break :blk &err_scope.base;
|
||||
break :blk &then_scope.base;
|
||||
|
||||
const unwrapped_err_ptr = try addZIRUnOp(mod, &err_scope.base, src, .unwrap_err_code, err_union_ptr);
|
||||
const unwrapped_err_ptr = try addZIRUnOp(mod, &then_scope.base, src, .unwrap_err_code, operand_ptr);
|
||||
err_val_scope = .{
|
||||
.parent = &err_scope.base,
|
||||
.gen_zir = &err_scope,
|
||||
.parent = &then_scope.base,
|
||||
.gen_zir = &then_scope,
|
||||
.name = err_name,
|
||||
.inst = try addZIRUnOp(mod, &err_scope.base, src, .deref, unwrapped_err_ptr),
|
||||
.inst = try addZIRUnOp(mod, &then_scope.base, src, .deref, unwrapped_err_ptr),
|
||||
};
|
||||
break :blk &err_val_scope.base;
|
||||
};
|
||||
|
||||
_ = try addZIRInst(mod, &err_scope.base, src, zir.Inst.Break, .{
|
||||
_ = try addZIRInst(mod, &then_scope.base, src, zir.Inst.Break, .{
|
||||
.block = block,
|
||||
.operand = try expr(mod, err_sub_scope, branch_rl, node.rhs),
|
||||
.operand = try expr(mod, then_sub_scope, branch_rl, rhs),
|
||||
}, .{});
|
||||
|
||||
var not_err_scope: Scope.GenZIR = .{
|
||||
var else_scope: Scope.GenZIR = .{
|
||||
.parent = scope,
|
||||
.decl = block_scope.decl,
|
||||
.arena = block_scope.arena,
|
||||
.instructions = .{},
|
||||
};
|
||||
defer not_err_scope.instructions.deinit(mod.gpa);
|
||||
defer else_scope.instructions.deinit(mod.gpa);
|
||||
|
||||
const unwrapped_payload = try addZIRUnOp(mod, ¬_err_scope.base, src, .unwrap_err_unsafe, err_union_ptr);
|
||||
_ = try addZIRInst(mod, ¬_err_scope.base, src, zir.Inst.Break, .{
|
||||
const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand_ptr);
|
||||
_ = try addZIRInst(mod, &else_scope.base, src, zir.Inst.Break, .{
|
||||
.block = block,
|
||||
.operand = unwrapped_payload,
|
||||
}, .{});
|
||||
|
||||
condbr.positionals.then_body = .{ .instructions = try err_scope.arena.dupe(*zir.Inst, err_scope.instructions.items) };
|
||||
condbr.positionals.else_body = .{ .instructions = try not_err_scope.arena.dupe(*zir.Inst, not_err_scope.instructions.items) };
|
||||
return rlWrap(mod, scope, rl, &block.base);
|
||||
condbr.positionals.then_body = .{ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items) };
|
||||
condbr.positionals.else_body = .{ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items) };
|
||||
return rlWrapPtr(mod, scope, rl, &block.base);
|
||||
}
|
||||
|
||||
/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.
|
||||
@ -933,6 +951,36 @@ fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Array
|
||||
return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ElemPtr, .{ .array_ptr = array_ptr, .index = index }, .{}));
|
||||
}
|
||||
|
||||
fn sliceExpr(mod: *Module, scope: *Scope, node: *ast.Node.Slice) InnerError!*zir.Inst {
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[node.rtoken].start;
|
||||
|
||||
const usize_type = try addZIRInstConst(mod, scope, src, .{
|
||||
.ty = Type.initTag(.type),
|
||||
.val = Value.initTag(.usize_type),
|
||||
});
|
||||
|
||||
const array_ptr = try expr(mod, scope, .ref, node.lhs);
|
||||
const start = try expr(mod, scope, .{ .ty = usize_type }, node.start);
|
||||
|
||||
if (node.end == null and node.sentinel == null) {
|
||||
return try addZIRBinOp(mod, scope, src, .slice_start, array_ptr, start);
|
||||
}
|
||||
|
||||
const end = if (node.end) |end| try expr(mod, scope, .{ .ty = usize_type }, end) else null;
|
||||
// we could get the child type here, but it is easier to just do it in semantic analysis.
|
||||
const sentinel = if (node.sentinel) |sentinel| try expr(mod, scope, .none, sentinel) else null;
|
||||
|
||||
return try addZIRInst(
|
||||
mod,
|
||||
scope,
|
||||
src,
|
||||
zir.Inst.Slice,
|
||||
.{ .array_ptr = array_ptr, .start = start },
|
||||
.{ .end = end, .sentinel = sentinel },
|
||||
);
|
||||
}
|
||||
|
||||
fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[node.rtoken].start;
|
||||
|
||||
@ -132,7 +132,7 @@ pub fn generateSymbol(
|
||||
.Array => {
|
||||
// TODO populate .debug_info for the array
|
||||
if (typed_value.val.cast(Value.Payload.Bytes)) |payload| {
|
||||
if (typed_value.ty.arraySentinel()) |sentinel| {
|
||||
if (typed_value.ty.sentinel()) |sentinel| {
|
||||
try code.ensureCapacity(code.items.len + payload.data.len + 1);
|
||||
code.appendSliceAssumeCapacity(payload.data);
|
||||
const prev_len = code.items.len;
|
||||
@ -436,8 +436,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
try branch_stack.append(.{});
|
||||
|
||||
const src_data: struct {lbrace_src: usize, rbrace_src: usize, source: []const u8} = blk: {
|
||||
if (module_fn.owner_decl.scope.cast(Module.Scope.File)) |scope_file| {
|
||||
const tree = scope_file.contents.tree;
|
||||
if (module_fn.owner_decl.scope.cast(Module.Scope.Container)) |container_scope| {
|
||||
const tree = container_scope.file_scope.contents.tree;
|
||||
const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?;
|
||||
const block = fn_proto.getBodyNode().?.castTag(.Block).?;
|
||||
const lbrace_src = tree.token_locs[block.lbrace].start;
|
||||
|
||||
@ -85,7 +85,7 @@ fn genArray(file: *C, decl: *Decl) !void {
|
||||
const name = try map(file.base.allocator, mem.span(decl.name));
|
||||
defer file.base.allocator.free(name);
|
||||
if (tv.val.cast(Value.Payload.Bytes)) |payload|
|
||||
if (tv.ty.arraySentinel()) |sentinel|
|
||||
if (tv.ty.sentinel()) |sentinel|
|
||||
if (sentinel.toUnsignedInt() == 0)
|
||||
try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data })
|
||||
else
|
||||
|
||||
@ -1656,8 +1656,8 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
|
||||
try dbg_line_buffer.ensureCapacity(26);
|
||||
|
||||
const line_off: u28 = blk: {
|
||||
if (decl.scope.cast(Module.Scope.File)) |scope_file| {
|
||||
const tree = scope_file.contents.tree;
|
||||
if (decl.scope.cast(Module.Scope.Container)) |container_scope| {
|
||||
const tree = container_scope.file_scope.contents.tree;
|
||||
const file_ast_decls = tree.root_node.decls();
|
||||
// TODO Look into improving the performance here by adding a token-index-to-line
|
||||
// lookup table. Currently this involves scanning over the source code for newlines.
|
||||
@ -2157,8 +2157,8 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const scope_file = decl.scope.cast(Module.Scope.File).?;
|
||||
const tree = scope_file.contents.tree;
|
||||
const container_scope = decl.scope.cast(Module.Scope.Container).?;
|
||||
const tree = container_scope.file_scope.contents.tree;
|
||||
const file_ast_decls = tree.root_node.decls();
|
||||
// TODO Look into improving the performance here by adding a token-index-to-line
|
||||
// lookup table. Currently this involves scanning over the source code for newlines.
|
||||
|
||||
@ -163,7 +163,7 @@ pub const Type = extern union {
|
||||
// Hot path for common case:
|
||||
if (a.castPointer()) |a_payload| {
|
||||
if (b.castPointer()) |b_payload| {
|
||||
return eql(a_payload.pointee_type, b_payload.pointee_type);
|
||||
return a.tag() == b.tag() and eql(a_payload.pointee_type, b_payload.pointee_type);
|
||||
}
|
||||
}
|
||||
const is_slice_a = isSlice(a);
|
||||
@ -189,10 +189,10 @@ pub const Type = extern union {
|
||||
.Array => {
|
||||
if (a.arrayLen() != b.arrayLen())
|
||||
return false;
|
||||
if (a.elemType().eql(b.elemType()))
|
||||
if (!a.elemType().eql(b.elemType()))
|
||||
return false;
|
||||
const sentinel_a = a.arraySentinel();
|
||||
const sentinel_b = b.arraySentinel();
|
||||
const sentinel_a = a.sentinel();
|
||||
const sentinel_b = b.sentinel();
|
||||
if (sentinel_a) |sa| {
|
||||
if (sentinel_b) |sb| {
|
||||
return sa.eql(sb);
|
||||
@ -501,9 +501,9 @@ pub const Type = extern union {
|
||||
.noreturn,
|
||||
=> return out_stream.writeAll(@tagName(t)),
|
||||
|
||||
.enum_literal => return out_stream.writeAll("@TypeOf(.EnumLiteral)"),
|
||||
.@"null" => return out_stream.writeAll("@TypeOf(null)"),
|
||||
.@"undefined" => return out_stream.writeAll("@TypeOf(undefined)"),
|
||||
.enum_literal => return out_stream.writeAll("@Type(.EnumLiteral)"),
|
||||
.@"null" => return out_stream.writeAll("@Type(.Null)"),
|
||||
.@"undefined" => return out_stream.writeAll("@Type(.Undefined)"),
|
||||
|
||||
.@"anyframe" => return out_stream.writeAll("anyframe"),
|
||||
.anyerror_void_error_union => return out_stream.writeAll("anyerror!void"),
|
||||
@ -630,8 +630,8 @@ pub const Type = extern union {
|
||||
const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
|
||||
if (payload.sentinel) |some| switch (payload.size) {
|
||||
.One, .C => unreachable,
|
||||
.Many => try out_stream.writeAll("[*:{}]"),
|
||||
.Slice => try out_stream.writeAll("[:{}]"),
|
||||
.Many => try out_stream.print("[*:{}]", .{some}),
|
||||
.Slice => try out_stream.print("[:{}]", .{some}),
|
||||
} else switch (payload.size) {
|
||||
.One => try out_stream.writeAll("*"),
|
||||
.Many => try out_stream.writeAll("[*]"),
|
||||
@ -1341,6 +1341,81 @@ pub const Type = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isAllowzeroPtr(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.u8,
|
||||
.i8,
|
||||
.u16,
|
||||
.i16,
|
||||
.u32,
|
||||
.i32,
|
||||
.u64,
|
||||
.i64,
|
||||
.usize,
|
||||
.isize,
|
||||
.c_short,
|
||||
.c_ushort,
|
||||
.c_int,
|
||||
.c_uint,
|
||||
.c_long,
|
||||
.c_ulong,
|
||||
.c_longlong,
|
||||
.c_ulonglong,
|
||||
.c_longdouble,
|
||||
.f16,
|
||||
.f32,
|
||||
.f64,
|
||||
.f128,
|
||||
.c_void,
|
||||
.bool,
|
||||
.void,
|
||||
.type,
|
||||
.anyerror,
|
||||
.comptime_int,
|
||||
.comptime_float,
|
||||
.noreturn,
|
||||
.@"null",
|
||||
.@"undefined",
|
||||
.array,
|
||||
.array_sentinel,
|
||||
.array_u8,
|
||||
.array_u8_sentinel_0,
|
||||
.fn_noreturn_no_args,
|
||||
.fn_void_no_args,
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.function,
|
||||
.int_unsigned,
|
||||
.int_signed,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.optional,
|
||||
.optional_single_mut_pointer,
|
||||
.optional_single_const_pointer,
|
||||
.enum_literal,
|
||||
.error_union,
|
||||
.@"anyframe",
|
||||
.anyframe_T,
|
||||
.anyerror_void_error_union,
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
=> false,
|
||||
|
||||
.pointer => {
|
||||
const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
|
||||
return payload.@"allowzero";
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts that the type is an optional
|
||||
pub fn isPtrLikeOptional(self: Type) bool {
|
||||
switch (self.tag()) {
|
||||
@ -1585,8 +1660,8 @@ pub const Type = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the type is an array or vector.
|
||||
pub fn arraySentinel(self: Type) ?Value {
|
||||
/// Asserts the type is an array, pointer or vector.
|
||||
pub fn sentinel(self: Type) ?Value {
|
||||
return switch (self.tag()) {
|
||||
.u8,
|
||||
.i8,
|
||||
@ -1626,16 +1701,8 @@ pub const Type = extern union {
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.function,
|
||||
.pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.int_unsigned,
|
||||
.int_signed,
|
||||
@ -1651,7 +1718,18 @@ pub const Type = extern union {
|
||||
.error_set_single,
|
||||
=> unreachable,
|
||||
|
||||
.array, .array_u8 => return null,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array,
|
||||
.array_u8,
|
||||
=> return null,
|
||||
|
||||
.pointer => return self.cast(Payload.Pointer).?.sentinel,
|
||||
.array_sentinel => return self.cast(Payload.ArraySentinel).?.sentinel,
|
||||
.array_u8_sentinel_0 => return Value.initTag(.zero),
|
||||
};
|
||||
|
||||
@ -301,15 +301,15 @@ pub const Value = extern union {
|
||||
.comptime_int_type => return out_stream.writeAll("comptime_int"),
|
||||
.comptime_float_type => return out_stream.writeAll("comptime_float"),
|
||||
.noreturn_type => return out_stream.writeAll("noreturn"),
|
||||
.null_type => return out_stream.writeAll("@TypeOf(null)"),
|
||||
.undefined_type => return out_stream.writeAll("@TypeOf(undefined)"),
|
||||
.null_type => return out_stream.writeAll("@Type(.Null)"),
|
||||
.undefined_type => return out_stream.writeAll("@Type(.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"),
|
||||
.const_slice_u8_type => return out_stream.writeAll("[]const u8"),
|
||||
.enum_literal_type => return out_stream.writeAll("@TypeOf(.EnumLiteral)"),
|
||||
.enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"),
|
||||
.anyframe_type => return out_stream.writeAll("anyframe"),
|
||||
|
||||
.null_value => return out_stream.writeAll("null"),
|
||||
|
||||
@ -231,6 +231,10 @@ pub const Inst = struct {
|
||||
const_slice_type,
|
||||
/// Create a pointer type with attributes
|
||||
ptr_type,
|
||||
/// Slice operation `array_ptr[start..end:sentinel]`
|
||||
slice,
|
||||
/// Slice operation with just start `lhs[rhs..]`
|
||||
slice_start,
|
||||
/// Write a value to a pointer. For loading, see `deref`.
|
||||
store,
|
||||
/// String Literal. Makes an anonymous Decl and then takes a pointer to it.
|
||||
@ -343,6 +347,7 @@ pub const Inst = struct {
|
||||
.xor,
|
||||
.error_union_type,
|
||||
.merge_error_sets,
|
||||
.slice_start,
|
||||
=> BinOp,
|
||||
|
||||
.block,
|
||||
@ -380,6 +385,7 @@ pub const Inst = struct {
|
||||
.ptr_type => PtrType,
|
||||
.enum_literal => EnumLiteral,
|
||||
.error_set => ErrorSet,
|
||||
.slice => Slice,
|
||||
};
|
||||
}
|
||||
|
||||
@ -481,6 +487,8 @@ pub const Inst = struct {
|
||||
.error_union_type,
|
||||
.bitnot,
|
||||
.error_set,
|
||||
.slice,
|
||||
.slice_start,
|
||||
=> false,
|
||||
|
||||
.@"break",
|
||||
@ -961,6 +969,20 @@ pub const Inst = struct {
|
||||
},
|
||||
kw_args: struct {},
|
||||
};
|
||||
|
||||
pub const Slice = struct {
|
||||
pub const base_tag = Tag.slice;
|
||||
base: Inst,
|
||||
|
||||
positionals: struct {
|
||||
array_ptr: *Inst,
|
||||
start: *Inst,
|
||||
},
|
||||
kw_args: struct {
|
||||
end: ?*Inst = null,
|
||||
sentinel: ?*Inst = null,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
pub const ErrorMsg = struct {
|
||||
@ -2574,7 +2596,7 @@ const EmitZIR = struct {
|
||||
var len_pl = Value.Payload.Int_u64{ .int = ty.arrayLen() };
|
||||
const len = Value.initPayload(&len_pl.base);
|
||||
|
||||
const inst = if (ty.arraySentinel()) |sentinel| blk: {
|
||||
const inst = if (ty.sentinel()) |sentinel| blk: {
|
||||
const inst = try self.arena.allocator.create(Inst.ArrayTypeSentinel);
|
||||
inst.* = .{
|
||||
.base = .{
|
||||
|
||||
@ -132,6 +132,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
|
||||
.error_union_type => return analyzeInstErrorUnionType(mod, scope, old_inst.castTag(.error_union_type).?),
|
||||
.anyframe_type => return analyzeInstAnyframeType(mod, scope, old_inst.castTag(.anyframe_type).?),
|
||||
.error_set => return analyzeInstErrorSet(mod, scope, old_inst.castTag(.error_set).?),
|
||||
.slice => return analyzeInstSlice(mod, scope, old_inst.castTag(.slice).?),
|
||||
.slice_start => return analyzeInstSliceStart(mod, scope, old_inst.castTag(.slice_start).?),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1172,6 +1174,22 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne
|
||||
return mod.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
|
||||
}
|
||||
|
||||
fn analyzeInstSlice(mod: *Module, scope: *Scope, inst: *zir.Inst.Slice) InnerError!*Inst {
|
||||
const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr);
|
||||
const start = try resolveInst(mod, scope, inst.positionals.start);
|
||||
const end = if (inst.kw_args.end) |end| try resolveInst(mod, scope, end) else null;
|
||||
const sentinel = if (inst.kw_args.sentinel) |sentinel| try resolveInst(mod, scope, sentinel) else null;
|
||||
|
||||
return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, end, sentinel);
|
||||
}
|
||||
|
||||
fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
|
||||
const array_ptr = try resolveInst(mod, scope, inst.positionals.lhs);
|
||||
const start = try resolveInst(mod, scope, inst.positionals.rhs);
|
||||
|
||||
return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null);
|
||||
}
|
||||
|
||||
fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
|
||||
return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{});
|
||||
}
|
||||
@ -1239,6 +1257,12 @@ fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn
|
||||
|
||||
if (casted_lhs.value()) |lhs_val| {
|
||||
if (casted_rhs.value()) |rhs_val| {
|
||||
if (lhs_val.isUndef() or rhs_val.isUndef()) {
|
||||
return mod.constInst(scope, inst.base.src, .{
|
||||
.ty = resolved_type,
|
||||
.val = Value.initTag(.undef),
|
||||
});
|
||||
}
|
||||
return analyzeInstComptimeOp(mod, scope, scalar_type, inst, lhs_val, rhs_val);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user