Sema: support enough to check main calling convention via @typeInfo
After this commit, `pub export fn main() c_int { ... }` will be
correctly detected as the intended entry point, and therefore start code
will not try to export its own conflicting `main` function.
* Implement basic union support
- lots of stuff is still TODO, including runtime field access
- also TODO: resolving the union tag type
- comptime field access is implemented
* DRY up some code by using the `Zir.DeclIterator` for skipping over
decls in structs and unions.
* Start to clean up Sema with regards to calling `.value()` to find out
a const value. Instead, Sema code should call one of these two:
- `resolvePossiblyUndefinedValue` (followed by logic dealing with
undefined values)
- `resolveDefinedValue` (a compile error will be emitted if the value
is undefined)
* An exported function with an unspecified calling convention gets the
C calling convention.
* Implement comptime field access for structs.
* Add another implementation of "type has one possible value" in Sema.
This is a bit unfortunate since the logic is duplicated, but the one
in Type asserts that the types are resolved already, and is
appropriate to call from codegen, while the one in Sema performs
type resolution if necessary, reporting any compile errors that occur
in the process.
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
* start.zig should support pub export fn main with -ofmt=c
|
||||
* get stage2 tests passing
|
||||
* modify stage2 tests so that only 1 uses _start and the rest use
|
||||
pub fn main
|
||||
@@ -61,6 +60,3 @@
|
||||
|
||||
* AstGen threadlocal
|
||||
* extern "foo" for vars
|
||||
|
||||
* TODO all decls should probably store source hash. Without this,
|
||||
we currently unnecessarily mark all anon decls outdated here.
|
||||
|
||||
238
src/Module.zig
238
src/Module.zig
@@ -456,6 +456,16 @@ pub const Decl = struct {
|
||||
return struct_obj;
|
||||
}
|
||||
|
||||
/// If the Decl has a value and it is a union, return it,
|
||||
/// otherwise null.
|
||||
pub fn getUnion(decl: *Decl) ?*Union {
|
||||
if (!decl.has_tv) return null;
|
||||
const ty = (decl.val.castTag(.ty) orelse return null).data;
|
||||
const union_obj = (ty.cast(Type.Payload.Union) orelse return null).data;
|
||||
if (union_obj.owner_decl != decl) return null;
|
||||
return union_obj;
|
||||
}
|
||||
|
||||
/// If the Decl has a value and it is a function, return it,
|
||||
/// otherwise null.
|
||||
pub fn getFunction(decl: *Decl) ?*Fn {
|
||||
@@ -571,6 +581,18 @@ pub const Struct = struct {
|
||||
.lazy = .{ .node_offset = s.node_offset },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn haveFieldTypes(s: Struct) bool {
|
||||
return switch (s.status) {
|
||||
.none,
|
||||
.field_types_wip,
|
||||
=> false,
|
||||
.have_field_types,
|
||||
.layout_wip,
|
||||
.have_layout,
|
||||
=> true,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Represents the data that an enum declaration provides, when the fields
|
||||
@@ -624,6 +646,52 @@ pub const EnumFull = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const Union = struct {
|
||||
/// The Decl that corresponds to the union itself.
|
||||
owner_decl: *Decl,
|
||||
/// An enum type which is used for the tag of the union.
|
||||
/// This type is created even for untagged unions, even when the memory
|
||||
/// layout does not store the tag.
|
||||
/// Whether zig chooses this type or the user specifies it, it is stored here.
|
||||
/// This will be set to the null type until status is `have_field_types`.
|
||||
tag_ty: Type,
|
||||
/// Set of field names in declaration order.
|
||||
fields: std.StringArrayHashMapUnmanaged(Field),
|
||||
/// Represents the declarations inside this union.
|
||||
namespace: Scope.Namespace,
|
||||
/// Offset from `owner_decl`, points to the union decl AST node.
|
||||
node_offset: i32,
|
||||
/// Index of the union_decl ZIR instruction.
|
||||
zir_index: Zir.Inst.Index,
|
||||
|
||||
layout: std.builtin.TypeInfo.ContainerLayout,
|
||||
status: enum {
|
||||
none,
|
||||
field_types_wip,
|
||||
have_field_types,
|
||||
layout_wip,
|
||||
have_layout,
|
||||
},
|
||||
|
||||
pub const Field = struct {
|
||||
/// undefined until `status` is `have_field_types` or `have_layout`.
|
||||
ty: Type,
|
||||
abi_align: Value,
|
||||
};
|
||||
|
||||
pub fn getFullyQualifiedName(s: *Union, gpa: *Allocator) ![]u8 {
|
||||
return s.owner_decl.getFullyQualifiedName(gpa);
|
||||
}
|
||||
|
||||
pub fn srcLoc(self: Union) SrcLoc {
|
||||
return .{
|
||||
.file_scope = self.owner_decl.getFileScope(),
|
||||
.parent_decl_node = self.owner_decl.src_node,
|
||||
.lazy = .{ .node_offset = self.node_offset },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
|
||||
/// Extern functions do not have this data structure; they are represented by
|
||||
/// the `Decl` only, with a `Value` tag of `extern_fn`.
|
||||
@@ -2401,6 +2469,7 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
|
||||
|
||||
/// Patch ups:
|
||||
/// * Struct.zir_index
|
||||
/// * Decl.zir_index
|
||||
/// * Fn.zir_body_inst
|
||||
/// * Decl.zir_decl_index
|
||||
/// * Decl.name
|
||||
@@ -2479,6 +2548,13 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void {
|
||||
};
|
||||
}
|
||||
|
||||
if (decl.getUnion()) |union_obj| {
|
||||
union_obj.zir_index = inst_map.get(union_obj.zir_index) orelse {
|
||||
try file.deleted_decls.append(gpa, decl);
|
||||
continue;
|
||||
};
|
||||
}
|
||||
|
||||
if (decl.getFunction()) |func| {
|
||||
func.zir_body_inst = inst_map.get(func.zir_body_inst) orelse {
|
||||
try file.deleted_decls.append(gpa, decl);
|
||||
@@ -2769,7 +2845,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
|
||||
};
|
||||
|
||||
if (decl.isRoot()) {
|
||||
log.debug("semaDecl root {*} ({s})", .{decl, decl.name});
|
||||
log.debug("semaDecl root {*} ({s})", .{ decl, decl.name });
|
||||
const main_struct_inst = zir.getMainStruct();
|
||||
const struct_obj = decl.getStruct().?;
|
||||
try sema.analyzeStructDecl(decl, main_struct_inst, struct_obj);
|
||||
@@ -4271,7 +4347,7 @@ pub const SwitchProngSrc = union(enum) {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn analyzeStructFields(mod: *Module, struct_obj: *Module.Struct) InnerError!void {
|
||||
pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) InnerError!void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@@ -4284,26 +4360,9 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Module.Struct) InnerError!
|
||||
const decls_len = extra.data.decls_len;
|
||||
|
||||
// Skip over decls.
|
||||
var extra_index = extra.end;
|
||||
{
|
||||
const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
|
||||
var bit_bag_index: usize = extra_index;
|
||||
extra_index += bit_bags_count;
|
||||
var cur_bit_bag: u32 = undefined;
|
||||
var decl_i: u32 = 0;
|
||||
while (decl_i < decls_len) : (decl_i += 1) {
|
||||
if (decl_i % 8 == 0) {
|
||||
cur_bit_bag = zir.extra[bit_bag_index];
|
||||
bit_bag_index += 1;
|
||||
}
|
||||
const flags = @truncate(u4, cur_bit_bag);
|
||||
cur_bit_bag >>= 4;
|
||||
|
||||
extra_index += 7; // src_hash(4) + line(1) + name(1) + value(1)
|
||||
extra_index += @truncate(u1, flags >> 2);
|
||||
extra_index += @truncate(u1, flags >> 3);
|
||||
}
|
||||
}
|
||||
var decls_it = zir.declIterator(struct_obj.zir_index);
|
||||
while (decls_it.next()) |_| {}
|
||||
var extra_index = decls_it.extra_index;
|
||||
|
||||
const body = zir.extra[extra_index..][0..extra.data.body_len];
|
||||
if (fields_len == 0) {
|
||||
@@ -4417,6 +4476,141 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Module.Struct) InnerError!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) InnerError!void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const gpa = mod.gpa;
|
||||
const zir = union_obj.owner_decl.namespace.file_scope.zir;
|
||||
const inst_data = zir.instructions.items(.data)[union_obj.zir_index].pl_node;
|
||||
const src = inst_data.src();
|
||||
const extra = zir.extraData(Zir.Inst.UnionDecl, inst_data.payload_index);
|
||||
const fields_len = extra.data.fields_len;
|
||||
const decls_len = extra.data.decls_len;
|
||||
|
||||
// Skip over decls.
|
||||
var decls_it = zir.declIterator(union_obj.zir_index);
|
||||
while (decls_it.next()) |_| {}
|
||||
var extra_index = decls_it.extra_index;
|
||||
|
||||
const body = zir.extra[extra_index..][0..extra.data.body_len];
|
||||
if (fields_len == 0) {
|
||||
assert(body.len == 0);
|
||||
return;
|
||||
}
|
||||
extra_index += body.len;
|
||||
|
||||
var decl_arena = union_obj.owner_decl.value_arena.?.promote(gpa);
|
||||
defer union_obj.owner_decl.value_arena.?.* = decl_arena.state;
|
||||
|
||||
try union_obj.fields.ensureCapacity(&decl_arena.allocator, fields_len);
|
||||
|
||||
// We create a block for the field type instructions because they
|
||||
// may need to reference Decls from inside the struct namespace.
|
||||
// Within the field type, default value, and alignment expressions, the "owner decl"
|
||||
// should be the struct itself. Thus we need a new Sema.
|
||||
var sema: Sema = .{
|
||||
.mod = mod,
|
||||
.gpa = gpa,
|
||||
.arena = &decl_arena.allocator,
|
||||
.code = zir,
|
||||
.inst_map = try gpa.alloc(*ir.Inst, zir.instructions.len),
|
||||
.owner_decl = union_obj.owner_decl,
|
||||
.namespace = &union_obj.namespace,
|
||||
.owner_func = null,
|
||||
.func = null,
|
||||
.param_inst_list = &.{},
|
||||
};
|
||||
defer gpa.free(sema.inst_map);
|
||||
|
||||
var block: Scope.Block = .{
|
||||
.parent = null,
|
||||
.sema = &sema,
|
||||
.src_decl = union_obj.owner_decl,
|
||||
.instructions = .{},
|
||||
.inlining = null,
|
||||
.is_comptime = true,
|
||||
};
|
||||
defer assert(block.instructions.items.len == 0); // should all be comptime instructions
|
||||
|
||||
_ = try sema.analyzeBody(&block, body);
|
||||
|
||||
var auto_enum_tag: ?bool = null;
|
||||
|
||||
const bits_per_field = 4;
|
||||
const fields_per_u32 = 32 / bits_per_field;
|
||||
const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
|
||||
var bit_bag_index: usize = extra_index;
|
||||
extra_index += bit_bags_count;
|
||||
var cur_bit_bag: u32 = undefined;
|
||||
var field_i: u32 = 0;
|
||||
while (field_i < fields_len) : (field_i += 1) {
|
||||
if (field_i % fields_per_u32 == 0) {
|
||||
cur_bit_bag = zir.extra[bit_bag_index];
|
||||
bit_bag_index += 1;
|
||||
}
|
||||
const has_type = @truncate(u1, cur_bit_bag) != 0;
|
||||
cur_bit_bag >>= 1;
|
||||
const has_align = @truncate(u1, cur_bit_bag) != 0;
|
||||
cur_bit_bag >>= 1;
|
||||
const has_tag = @truncate(u1, cur_bit_bag) != 0;
|
||||
cur_bit_bag >>= 1;
|
||||
const unused = @truncate(u1, cur_bit_bag) != 0;
|
||||
cur_bit_bag >>= 1;
|
||||
|
||||
if (auto_enum_tag == null) {
|
||||
auto_enum_tag = unused;
|
||||
}
|
||||
|
||||
const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
|
||||
const field_type_ref: Zir.Inst.Ref = if (has_type) blk: {
|
||||
const field_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
break :blk field_type_ref;
|
||||
} else .none;
|
||||
|
||||
const align_ref: Zir.Inst.Ref = if (has_align) blk: {
|
||||
const align_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
break :blk align_ref;
|
||||
} else .none;
|
||||
|
||||
const tag_ref: Zir.Inst.Ref = if (has_tag) blk: {
|
||||
const tag_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
break :blk tag_ref;
|
||||
} else .none;
|
||||
|
||||
// This string needs to outlive the ZIR code.
|
||||
const field_name = try decl_arena.allocator.dupe(u8, field_name_zir);
|
||||
const field_ty: Type = if (field_type_ref == .none)
|
||||
Type.initTag(.void)
|
||||
else
|
||||
// TODO: if we need to report an error here, use a source location
|
||||
// that points to this type expression rather than the union.
|
||||
// But only resolve the source location if we need to emit a compile error.
|
||||
try sema.resolveType(&block, src, field_type_ref);
|
||||
|
||||
const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
|
||||
assert(!gop.found_existing);
|
||||
gop.entry.value = .{
|
||||
.ty = field_ty,
|
||||
.abi_align = Value.initTag(.abi_align_default),
|
||||
};
|
||||
|
||||
if (align_ref != .none) {
|
||||
// TODO: if we need to report an error here, use a source location
|
||||
// that points to this alignment expression rather than the struct.
|
||||
// But only resolve the source location if we need to emit a compile error.
|
||||
gop.entry.value.abi_align = (try sema.resolveInstConst(&block, src, align_ref)).val;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO resolve the union tag type
|
||||
}
|
||||
|
||||
/// Called from `performAllTheWork`, after all AstGen workers have finished,
|
||||
/// and before the main semantic analysis loop begins.
|
||||
pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {
|
||||
|
||||
353
src/Sema.zig
353
src/Sema.zig
@@ -591,7 +591,7 @@ fn resolveConstValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *i
|
||||
}
|
||||
|
||||
fn resolveDefinedValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *ir.Inst) !?Value {
|
||||
if (base.value()) |val| {
|
||||
if (try sema.resolvePossiblyUndefinedValue(block, src, base)) |val| {
|
||||
if (val.isUndef()) {
|
||||
return sema.failWithUseOfUndef(block, src);
|
||||
}
|
||||
@@ -600,6 +600,19 @@ fn resolveDefinedValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base:
|
||||
return null;
|
||||
}
|
||||
|
||||
fn resolvePossiblyUndefinedValue(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
src: LazySrcLoc,
|
||||
base: *ir.Inst,
|
||||
) !?Value {
|
||||
if (try sema.typeHasOnePossibleValue(block, src, base.ty)) |opv| {
|
||||
return opv;
|
||||
}
|
||||
const inst = base.castTag(.constant) orelse return null;
|
||||
return inst.val;
|
||||
}
|
||||
|
||||
fn failWithNeededComptime(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) InnerError {
|
||||
return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{});
|
||||
}
|
||||
@@ -889,9 +902,40 @@ fn zirUnionDecl(
|
||||
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
|
||||
const extra = sema.code.extraData(Zir.Inst.UnionDecl, inst_data.payload_index);
|
||||
const decls_len = extra.data.decls_len;
|
||||
|
||||
return sema.mod.fail(&block.base, sema.src, "TODO implement zirUnionDecl", .{});
|
||||
var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
|
||||
|
||||
const union_obj = try new_decl_arena.allocator.create(Module.Union);
|
||||
const union_ty = try Type.Tag.@"union".create(&new_decl_arena.allocator, union_obj);
|
||||
const union_val = try Value.Tag.ty.create(&new_decl_arena.allocator, union_ty);
|
||||
const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{
|
||||
.ty = Type.initTag(.type),
|
||||
.val = union_val,
|
||||
});
|
||||
union_obj.* = .{
|
||||
.owner_decl = new_decl,
|
||||
.tag_ty = Type.initTag(.@"null"),
|
||||
.fields = .{},
|
||||
.node_offset = inst_data.src_node,
|
||||
.zir_index = inst,
|
||||
.layout = layout,
|
||||
.status = .none,
|
||||
.namespace = .{
|
||||
.parent = sema.owner_decl.namespace,
|
||||
.ty = union_ty,
|
||||
.file_scope = block.getFileScope(),
|
||||
},
|
||||
};
|
||||
std.log.scoped(.module).debug("create union {*} owned by {*} ({s})", .{
|
||||
&union_obj.namespace, new_decl, new_decl.name,
|
||||
});
|
||||
|
||||
_ = try sema.mod.scanNamespace(&union_obj.namespace, extra.end, decls_len, new_decl);
|
||||
|
||||
try new_decl.finalizeNewArena(&new_decl_arena);
|
||||
return sema.analyzeDeclVal(block, src, new_decl);
|
||||
}
|
||||
|
||||
fn zirOpaqueDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
|
||||
@@ -1277,6 +1321,33 @@ fn failWithBadFieldAccess(
|
||||
return mod.failWithOwnedErrorMsg(&block.base, msg);
|
||||
}
|
||||
|
||||
fn failWithBadUnionFieldAccess(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
union_obj: *Module.Union,
|
||||
field_src: LazySrcLoc,
|
||||
field_name: []const u8,
|
||||
) InnerError {
|
||||
const mod = sema.mod;
|
||||
const gpa = sema.gpa;
|
||||
|
||||
const fqn = try union_obj.getFullyQualifiedName(gpa);
|
||||
defer gpa.free(fqn);
|
||||
|
||||
const msg = msg: {
|
||||
const msg = try mod.errMsg(
|
||||
&block.base,
|
||||
field_src,
|
||||
"no field named '{s}' in union '{s}'",
|
||||
.{ field_name, fqn },
|
||||
);
|
||||
errdefer msg.destroy(gpa);
|
||||
try mod.errNoteNonLazy(union_obj.srcLoc(), msg, "union declared here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return mod.failWithOwnedErrorMsg(&block.base, msg);
|
||||
}
|
||||
|
||||
fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
@@ -1484,7 +1555,7 @@ fn zirCompileLog(
|
||||
if (i != 0) try writer.print(", ", .{});
|
||||
|
||||
const arg = try sema.resolveInst(arg_ref);
|
||||
if (arg.value()) |val| {
|
||||
if (try sema.resolvePossiblyUndefinedValue(block, src, arg)) |val| {
|
||||
try writer.print("@as({}, {})", .{ arg.ty, val });
|
||||
} else {
|
||||
try writer.print("@as({}, [runtime value])", .{arg.ty});
|
||||
@@ -2204,21 +2275,25 @@ fn zirErrorToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
|
||||
const op = try sema.resolveInst(inst_data.operand);
|
||||
const op_coerced = try sema.coerce(block, Type.initTag(.anyerror), op, operand_src);
|
||||
const result_ty = Type.initTag(.u16);
|
||||
|
||||
if (op_coerced.value()) |val| {
|
||||
if (try sema.resolvePossiblyUndefinedValue(block, src, op_coerced)) |val| {
|
||||
if (val.isUndef()) {
|
||||
return sema.mod.constUndef(sema.arena, src, result_ty);
|
||||
}
|
||||
const payload = try sema.arena.create(Value.Payload.U64);
|
||||
payload.* = .{
|
||||
.base = .{ .tag = .int_u64 },
|
||||
.data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value,
|
||||
};
|
||||
return sema.mod.constInst(sema.arena, src, .{
|
||||
.ty = Type.initTag(.u16),
|
||||
.ty = result_ty,
|
||||
.val = Value.initPayload(&payload.base),
|
||||
});
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addUnOp(src, Type.initTag(.u16), .error_to_int, op_coerced);
|
||||
return block.addUnOp(src, result_ty, .error_to_int, op_coerced);
|
||||
}
|
||||
|
||||
fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
|
||||
@@ -2377,7 +2452,7 @@ fn zirEnumToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
|
||||
var int_tag_type_buffer: Type.Payload.Bits = undefined;
|
||||
const int_tag_ty = try enum_tag.ty.intTagType(&int_tag_type_buffer).copy(arena);
|
||||
|
||||
if (enum_tag.ty.onePossibleValue()) |opv| {
|
||||
if (try sema.typeHasOnePossibleValue(block, src, enum_tag.ty)) |opv| {
|
||||
return mod.constInst(arena, src, .{
|
||||
.ty = int_tag_ty,
|
||||
.val = opv,
|
||||
@@ -2729,13 +2804,18 @@ fn zirFunc(
|
||||
src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
|
||||
}
|
||||
|
||||
const cc: std.builtin.CallingConvention = if (sema.owner_decl.is_exported)
|
||||
.C
|
||||
else
|
||||
.Unspecified;
|
||||
|
||||
return sema.funcCommon(
|
||||
block,
|
||||
inst_data.src_node,
|
||||
param_types,
|
||||
body_inst,
|
||||
extra.data.return_type,
|
||||
.Unspecified,
|
||||
cc,
|
||||
Value.initTag(.null_value),
|
||||
false,
|
||||
inferred_error_set,
|
||||
@@ -4268,10 +4348,7 @@ fn zirBitwise(
|
||||
if (casted_lhs.value()) |lhs_val| {
|
||||
if (casted_rhs.value()) |rhs_val| {
|
||||
if (lhs_val.isUndef() or rhs_val.isUndef()) {
|
||||
return sema.mod.constInst(sema.arena, src, .{
|
||||
.ty = resolved_type,
|
||||
.val = Value.initTag(.undef),
|
||||
});
|
||||
return sema.mod.constUndef(sema.arena, src, resolved_type);
|
||||
}
|
||||
return sema.mod.fail(&block.base, src, "TODO implement comptime bitwise operations", .{});
|
||||
}
|
||||
@@ -4395,10 +4472,7 @@ fn analyzeArithmetic(
|
||||
if (casted_lhs.value()) |lhs_val| {
|
||||
if (casted_rhs.value()) |rhs_val| {
|
||||
if (lhs_val.isUndef() or rhs_val.isUndef()) {
|
||||
return sema.mod.constInst(sema.arena, src, .{
|
||||
.ty = resolved_type,
|
||||
.val = Value.initTag(.undef),
|
||||
});
|
||||
return sema.mod.constUndef(sema.arena, src, resolved_type);
|
||||
}
|
||||
// incase rhs is 0, simply return lhs without doing any calculations
|
||||
// TODO Once division is implemented we should throw an error when dividing by 0.
|
||||
@@ -4635,10 +4709,7 @@ fn zirCmp(
|
||||
if (casted_lhs.value()) |lhs_val| {
|
||||
if (casted_rhs.value()) |rhs_val| {
|
||||
if (lhs_val.isUndef() or rhs_val.isUndef()) {
|
||||
return sema.mod.constInst(sema.arena, src, .{
|
||||
.ty = resolved_type,
|
||||
.val = Value.initTag(.undef),
|
||||
});
|
||||
return sema.mod.constUndef(sema.arena, src, resolved_type);
|
||||
}
|
||||
const result = lhs_val.compare(op, rhs_val);
|
||||
return sema.mod.constBool(sema.arena, src, result);
|
||||
@@ -4721,7 +4792,7 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
|
||||
@enumToInt(ty.fnCallingConvention()),
|
||||
);
|
||||
// alignment: comptime_int,
|
||||
field_values[1] = try Value.Tag.int_u64.create(sema.arena, ty.ptrAlignment(target));
|
||||
field_values[1] = try Value.Tag.int_u64.create(sema.arena, ty.abiAlignment(target));
|
||||
// is_generic: bool,
|
||||
field_values[2] = Value.initTag(.bool_false); // TODO
|
||||
// is_var_args: bool,
|
||||
@@ -6033,6 +6104,7 @@ fn namedFieldPtr(
|
||||
}
|
||||
},
|
||||
.Struct => return sema.analyzeStructFieldPtr(block, src, object_ptr, field_name, field_name_src, elem_ty),
|
||||
.Union => return sema.analyzeUnionFieldPtr(block, src, object_ptr, field_name, field_name_src, elem_ty),
|
||||
else => {},
|
||||
}
|
||||
return mod.fail(&block.base, src, "type '{}' does not support field access", .{elem_ty});
|
||||
@@ -6083,11 +6155,58 @@ fn analyzeStructFieldPtr(
|
||||
return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);
|
||||
const field = struct_obj.fields.entries.items[field_index].value;
|
||||
const ptr_field_ty = try mod.simplePtrType(arena, field.ty, true, .One);
|
||||
// TODO comptime field access
|
||||
|
||||
if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
|
||||
return mod.constInst(arena, src, .{
|
||||
.ty = ptr_field_ty,
|
||||
.val = try Value.Tag.field_ptr.create(arena, .{
|
||||
.container_ptr = struct_ptr_val,
|
||||
.field_index = field_index,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addStructFieldPtr(src, ptr_field_ty, struct_ptr, @intCast(u32, field_index));
|
||||
}
|
||||
|
||||
fn analyzeUnionFieldPtr(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
src: LazySrcLoc,
|
||||
union_ptr: *Inst,
|
||||
field_name: []const u8,
|
||||
field_name_src: LazySrcLoc,
|
||||
unresolved_union_ty: Type,
|
||||
) InnerError!*Inst {
|
||||
const mod = sema.mod;
|
||||
const arena = sema.arena;
|
||||
assert(unresolved_union_ty.zigTypeTag() == .Union);
|
||||
|
||||
const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);
|
||||
const union_obj = union_ty.cast(Type.Payload.Union).?.data;
|
||||
|
||||
const field_index = union_obj.fields.getIndex(field_name) orelse
|
||||
return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
|
||||
|
||||
const field = union_obj.fields.entries.items[field_index].value;
|
||||
const ptr_field_ty = try mod.simplePtrType(arena, field.ty, true, .One);
|
||||
|
||||
if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| {
|
||||
// TODO detect inactive union field and emit compile error
|
||||
return mod.constInst(arena, src, .{
|
||||
.ty = ptr_field_ty,
|
||||
.val = try Value.Tag.field_ptr.create(arena, .{
|
||||
.container_ptr = union_ptr_val,
|
||||
.field_index = field_index,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return mod.fail(&block.base, src, "TODO implement runtime union field access", .{});
|
||||
}
|
||||
|
||||
fn elemPtr(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
@@ -6382,7 +6501,7 @@ fn storePtr(
|
||||
|
||||
const elem_ty = ptr.ty.elemType();
|
||||
const value = try sema.coerce(block, elem_ty, uncasted_value, src);
|
||||
if (elem_ty.onePossibleValue() != null)
|
||||
if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
|
||||
return;
|
||||
|
||||
// TODO handle comptime pointer writes
|
||||
@@ -6477,7 +6596,7 @@ fn analyzeRef(
|
||||
) InnerError!*Inst {
|
||||
const ptr_type = try sema.mod.simplePtrType(sema.arena, operand.ty, false, .One);
|
||||
|
||||
if (operand.value()) |val| {
|
||||
if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |val| {
|
||||
return sema.mod.constInst(sema.arena, src, .{
|
||||
.ty = ptr_type,
|
||||
.val = try Value.Tag.ref_val.create(sema.arena, val),
|
||||
@@ -6499,10 +6618,10 @@ fn analyzeLoad(
|
||||
.Pointer => ptr.ty.elemType(),
|
||||
else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
|
||||
};
|
||||
if (ptr.value()) |val| {
|
||||
if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
|
||||
return sema.mod.constInst(sema.arena, src, .{
|
||||
.ty = elem_ty,
|
||||
.val = try val.pointerDeref(sema.arena),
|
||||
.val = try ptr_val.pointerDeref(sema.arena),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6517,14 +6636,18 @@ fn analyzeIsNull(
|
||||
operand: *Inst,
|
||||
invert_logic: bool,
|
||||
) InnerError!*Inst {
|
||||
if (operand.value()) |opt_val| {
|
||||
const result_ty = Type.initTag(.bool);
|
||||
if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |opt_val| {
|
||||
if (opt_val.isUndef()) {
|
||||
return sema.mod.constUndef(sema.arena, src, result_ty);
|
||||
}
|
||||
const is_null = opt_val.isNull();
|
||||
const bool_value = if (invert_logic) !is_null else is_null;
|
||||
return sema.mod.constBool(sema.arena, src, bool_value);
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
const inst_tag: Inst.Tag = if (invert_logic) .is_non_null else .is_null;
|
||||
return block.addUnOp(src, Type.initTag(.bool), inst_tag, operand);
|
||||
return block.addUnOp(src, result_ty, inst_tag, operand);
|
||||
}
|
||||
|
||||
fn analyzeIsErr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, operand: *Inst) InnerError!*Inst {
|
||||
@@ -6532,11 +6655,15 @@ fn analyzeIsErr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, operand: *Ins
|
||||
if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, false);
|
||||
if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, true);
|
||||
assert(ot == .ErrorUnion);
|
||||
if (operand.value()) |err_union| {
|
||||
const result_ty = Type.initTag(.bool);
|
||||
if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |err_union| {
|
||||
if (err_union.isUndef()) {
|
||||
return sema.mod.constUndef(sema.arena, src, result_ty);
|
||||
}
|
||||
return sema.mod.constBool(sema.arena, src, err_union.getError() != null);
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addUnOp(src, Type.initTag(.bool), .is_err, operand);
|
||||
return block.addUnOp(src, result_ty, .is_err, operand);
|
||||
}
|
||||
|
||||
fn analyzeSlice(
|
||||
@@ -6953,6 +7080,23 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type
|
||||
.float_mode => return sema.resolveBuiltinTypeFields(block, src, ty, "FloatMode"),
|
||||
.reduce_op => return sema.resolveBuiltinTypeFields(block, src, ty, "ReduceOp"),
|
||||
.call_options => return sema.resolveBuiltinTypeFields(block, src, ty, "CallOptions"),
|
||||
|
||||
.@"union", .union_tagged => {
|
||||
const union_obj = ty.cast(Type.Payload.Union).?.data;
|
||||
switch (union_obj.status) {
|
||||
.none => {},
|
||||
.field_types_wip => {
|
||||
return sema.mod.fail(&block.base, src, "union {} depends on itself", .{
|
||||
ty,
|
||||
});
|
||||
},
|
||||
.have_field_types, .have_layout, .layout_wip => return ty,
|
||||
}
|
||||
union_obj.status = .field_types_wip;
|
||||
try sema.mod.analyzeUnionFields(union_obj);
|
||||
union_obj.status = .have_field_types;
|
||||
return ty;
|
||||
},
|
||||
else => return ty,
|
||||
}
|
||||
}
|
||||
@@ -6994,3 +7138,150 @@ fn getBuiltinType(
|
||||
const ty_inst = try sema.analyzeLoad(block, src, opt_ty_inst.?, src);
|
||||
return sema.resolveAirAsType(block, src, ty_inst);
|
||||
}
|
||||
|
||||
/// There is another implementation of this in `Type.onePossibleValue`. This one
|
||||
/// in `Sema` is for calling during semantic analysis, and peforms field resolution
|
||||
/// to get the answer. The one in `Type` is for calling during codegen and asserts
|
||||
/// that the types are already resolved.
|
||||
fn typeHasOnePossibleValue(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
src: LazySrcLoc,
|
||||
starting_type: Type,
|
||||
) InnerError!?Value {
|
||||
var ty = starting_type;
|
||||
while (true) switch (ty.tag()) {
|
||||
.f16,
|
||||
.f32,
|
||||
.f64,
|
||||
.f128,
|
||||
.c_longdouble,
|
||||
.comptime_int,
|
||||
.comptime_float,
|
||||
.u8,
|
||||
.i8,
|
||||
.u16,
|
||||
.i16,
|
||||
.u32,
|
||||
.i32,
|
||||
.u64,
|
||||
.i64,
|
||||
.u128,
|
||||
.i128,
|
||||
.usize,
|
||||
.isize,
|
||||
.c_short,
|
||||
.c_ushort,
|
||||
.c_int,
|
||||
.c_uint,
|
||||
.c_long,
|
||||
.c_ulong,
|
||||
.c_longlong,
|
||||
.c_ulonglong,
|
||||
.bool,
|
||||
.type,
|
||||
.anyerror,
|
||||
.fn_noreturn_no_args,
|
||||
.fn_void_no_args,
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.function,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_sentinel,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.c_void,
|
||||
.optional,
|
||||
.optional_single_mut_pointer,
|
||||
.optional_single_const_pointer,
|
||||
.enum_literal,
|
||||
.anyerror_void_error_union,
|
||||
.error_union,
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.@"opaque",
|
||||
.var_args_param,
|
||||
.manyptr_u8,
|
||||
.manyptr_const_u8,
|
||||
.atomic_ordering,
|
||||
.atomic_rmw_op,
|
||||
.calling_convention,
|
||||
.float_mode,
|
||||
.reduce_op,
|
||||
.call_options,
|
||||
.export_options,
|
||||
.extern_options,
|
||||
.@"anyframe",
|
||||
.anyframe_T,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.pointer,
|
||||
=> return null,
|
||||
|
||||
.@"struct" => {
|
||||
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
|
||||
const s = resolved_ty.castTag(.@"struct").?.data;
|
||||
for (s.fields.entries.items) |entry| {
|
||||
const field_ty = entry.value.ty;
|
||||
if ((try sema.typeHasOnePossibleValue(block, src, field_ty)) == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return Value.initTag(.empty_struct_value);
|
||||
},
|
||||
.enum_full => {
|
||||
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
|
||||
const enum_full = resolved_ty.castTag(.enum_full).?.data;
|
||||
if (enum_full.fields.count() == 1) {
|
||||
return enum_full.values.entries.items[0].key;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
.enum_simple => {
|
||||
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
|
||||
const enum_simple = resolved_ty.castTag(.enum_simple).?.data;
|
||||
if (enum_simple.fields.count() == 1) {
|
||||
return Value.initTag(.zero);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
.enum_nonexhaustive => ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty,
|
||||
.@"union" => {
|
||||
return null; // TODO
|
||||
},
|
||||
.union_tagged => {
|
||||
return null; // TODO
|
||||
},
|
||||
|
||||
.empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value),
|
||||
.void => return Value.initTag(.void_value),
|
||||
.noreturn => return Value.initTag(.unreachable_value),
|
||||
.@"null" => return Value.initTag(.null_value),
|
||||
.@"undefined" => return Value.initTag(.undef),
|
||||
|
||||
.int_unsigned, .int_signed => {
|
||||
if (ty.cast(Type.Payload.Bits).?.data == 0) {
|
||||
return Value.initTag(.zero);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
.vector, .array, .array_u8 => {
|
||||
if (ty.arrayLen() == 0)
|
||||
return Value.initTag(.empty_array);
|
||||
ty = ty.elemType();
|
||||
continue;
|
||||
},
|
||||
|
||||
.inferred_alloc_const => unreachable,
|
||||
.inferred_alloc_mut => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -255,6 +255,9 @@ pub const Inst = struct {
|
||||
}
|
||||
|
||||
/// Returns `null` if runtime-known.
|
||||
/// Should be called by codegen, not by Sema. Sema functions should call
|
||||
/// `resolvePossiblyUndefinedValue` or `resolveDefinedValue` instead.
|
||||
/// TODO audit Sema code for violations to the above guidance.
|
||||
pub fn value(base: *Inst) ?Value {
|
||||
if (base.ty.onePossibleValue()) |opv| return opv;
|
||||
|
||||
|
||||
122
src/type.zig
122
src/type.zig
@@ -122,6 +122,10 @@ pub const Type = extern union {
|
||||
.reduce_op,
|
||||
=> return .Enum,
|
||||
|
||||
.@"union",
|
||||
.union_tagged,
|
||||
=> return .Union,
|
||||
|
||||
.var_args_param => unreachable, // can be any type
|
||||
}
|
||||
}
|
||||
@@ -506,11 +510,18 @@ pub const Type = extern union {
|
||||
}
|
||||
return a.tag() == b.tag();
|
||||
},
|
||||
.Union => {
|
||||
if (a.cast(Payload.Union)) |a_payload| {
|
||||
if (b.cast(Payload.Union)) |b_payload| {
|
||||
return a_payload.data == b_payload.data;
|
||||
}
|
||||
}
|
||||
return a.tag() == b.tag();
|
||||
},
|
||||
.Opaque,
|
||||
.Float,
|
||||
.ErrorUnion,
|
||||
.ErrorSet,
|
||||
.Union,
|
||||
.BoundFn,
|
||||
.Frame,
|
||||
=> std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
|
||||
@@ -735,6 +746,7 @@ pub const Type = extern union {
|
||||
.error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),
|
||||
.empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope),
|
||||
.@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),
|
||||
.@"union", .union_tagged => return self.copyPayloadShallow(allocator, Payload.Union),
|
||||
.enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple),
|
||||
.enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull),
|
||||
.@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque),
|
||||
@@ -806,6 +818,10 @@ pub const Type = extern union {
|
||||
const struct_obj = ty.castTag(.@"struct").?.data;
|
||||
return struct_obj.owner_decl.renderFullyQualifiedName(writer);
|
||||
},
|
||||
.@"union", .union_tagged => {
|
||||
const union_obj = ty.cast(Payload.Union).?.data;
|
||||
return union_obj.owner_decl.renderFullyQualifiedName(writer);
|
||||
},
|
||||
.enum_full, .enum_nonexhaustive => {
|
||||
const enum_full = ty.cast(Payload.EnumFull).?.data;
|
||||
return enum_full.owner_decl.renderFullyQualifiedName(writer);
|
||||
@@ -1151,6 +1167,27 @@ pub const Type = extern union {
|
||||
const int_tag_ty = self.intTagType(&buffer);
|
||||
return int_tag_ty.hasCodeGenBits();
|
||||
},
|
||||
.@"union" => {
|
||||
const union_obj = self.castTag(.@"union").?.data;
|
||||
for (union_obj.fields.entries.items) |entry| {
|
||||
if (entry.value.ty.hasCodeGenBits())
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
.union_tagged => {
|
||||
const union_obj = self.castTag(.@"union").?.data;
|
||||
if (union_obj.tag_ty.hasCodeGenBits()) {
|
||||
return true;
|
||||
}
|
||||
for (union_obj.fields.entries.items) |entry| {
|
||||
if (entry.value.ty.hasCodeGenBits())
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
// TODO lazy types
|
||||
.array, .vector => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
|
||||
@@ -1359,6 +1396,34 @@ pub const Type = extern union {
|
||||
const int_tag_ty = self.intTagType(&buffer);
|
||||
return int_tag_ty.abiAlignment(target);
|
||||
},
|
||||
.union_tagged => {
|
||||
const union_obj = self.castTag(.union_tagged).?.data;
|
||||
var biggest: u32 = union_obj.tag_ty.abiAlignment(target);
|
||||
for (union_obj.fields.entries.items) |entry| {
|
||||
const field_ty = entry.value.ty;
|
||||
if (!field_ty.hasCodeGenBits()) continue;
|
||||
const field_align = field_ty.abiAlignment(target);
|
||||
if (field_align > biggest) {
|
||||
return field_align;
|
||||
}
|
||||
}
|
||||
assert(biggest != 0);
|
||||
return biggest;
|
||||
},
|
||||
.@"union" => {
|
||||
const union_obj = self.castTag(.@"union").?.data;
|
||||
var biggest: u32 = 0;
|
||||
for (union_obj.fields.entries.items) |entry| {
|
||||
const field_ty = entry.value.ty;
|
||||
if (!field_ty.hasCodeGenBits()) continue;
|
||||
const field_align = field_ty.abiAlignment(target);
|
||||
if (field_align > biggest) {
|
||||
return field_align;
|
||||
}
|
||||
}
|
||||
assert(biggest != 0);
|
||||
return biggest;
|
||||
},
|
||||
.c_void,
|
||||
.void,
|
||||
.type,
|
||||
@@ -1411,6 +1476,9 @@ pub const Type = extern union {
|
||||
const int_tag_ty = self.intTagType(&buffer);
|
||||
return int_tag_ty.abiSize(target);
|
||||
},
|
||||
.@"union", .union_tagged => {
|
||||
@panic("TODO abiSize unions");
|
||||
},
|
||||
|
||||
.u8,
|
||||
.i8,
|
||||
@@ -1570,6 +1638,9 @@ pub const Type = extern union {
|
||||
const int_tag_ty = self.intTagType(&buffer);
|
||||
return int_tag_ty.bitSize(target);
|
||||
},
|
||||
.@"union", .union_tagged => {
|
||||
@panic("TODO bitSize unions");
|
||||
},
|
||||
|
||||
.u8, .i8 => 8,
|
||||
|
||||
@@ -2263,6 +2334,8 @@ pub const Type = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
/// During semantic analysis, instead call `Sema.typeHasOnePossibleValue` which
|
||||
/// resolves field types rather than asserting they are already resolved.
|
||||
pub fn onePossibleValue(starting_type: Type) ?Value {
|
||||
var ty = starting_type;
|
||||
while (true) switch (ty.tag()) {
|
||||
@@ -2330,10 +2403,18 @@ pub const Type = extern union {
|
||||
.extern_options,
|
||||
.@"anyframe",
|
||||
.anyframe_T,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.pointer,
|
||||
=> return null,
|
||||
|
||||
.@"struct" => {
|
||||
const s = ty.castTag(.@"struct").?.data;
|
||||
assert(s.haveFieldTypes());
|
||||
for (s.fields.entries.items) |entry| {
|
||||
const field_ty = entry.value.ty;
|
||||
if (field_ty.onePossibleValue() == null) {
|
||||
@@ -2359,6 +2440,12 @@ pub const Type = extern union {
|
||||
}
|
||||
},
|
||||
.enum_nonexhaustive => ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty,
|
||||
.@"union" => {
|
||||
return null; // TODO
|
||||
},
|
||||
.union_tagged => {
|
||||
return null; // TODO
|
||||
},
|
||||
|
||||
.empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value),
|
||||
.void => return Value.initTag(.void_value),
|
||||
@@ -2379,20 +2466,7 @@ pub const Type = extern union {
|
||||
ty = ty.elemType();
|
||||
continue;
|
||||
},
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
=> {
|
||||
ty = ty.castPointer().?.data;
|
||||
continue;
|
||||
},
|
||||
.pointer => {
|
||||
ty = ty.castTag(.pointer).?.data.pointee_type;
|
||||
continue;
|
||||
},
|
||||
|
||||
.inferred_alloc_const => unreachable,
|
||||
.inferred_alloc_mut => unreachable,
|
||||
};
|
||||
@@ -2412,6 +2486,8 @@ pub const Type = extern union {
|
||||
.enum_full => &self.castTag(.enum_full).?.data.namespace,
|
||||
.empty_struct => self.castTag(.empty_struct).?.data,
|
||||
.@"opaque" => &self.castTag(.@"opaque").?.data,
|
||||
.@"union" => &self.castTag(.@"union").?.data.namespace,
|
||||
.union_tagged => &self.castTag(.union_tagged).?.data.namespace,
|
||||
|
||||
else => null,
|
||||
};
|
||||
@@ -2612,6 +2688,10 @@ pub const Type = extern union {
|
||||
const error_set = ty.castTag(.error_set).?.data;
|
||||
return error_set.srcLoc();
|
||||
},
|
||||
.@"union", .union_tagged => {
|
||||
const union_obj = ty.cast(Payload.Union).?.data;
|
||||
return union_obj.srcLoc();
|
||||
},
|
||||
.atomic_ordering,
|
||||
.atomic_rmw_op,
|
||||
.calling_convention,
|
||||
@@ -2643,6 +2723,10 @@ pub const Type = extern union {
|
||||
const error_set = ty.castTag(.error_set).?.data;
|
||||
return error_set.owner_decl;
|
||||
},
|
||||
.@"union", .union_tagged => {
|
||||
const union_obj = ty.cast(Payload.Union).?.data;
|
||||
return union_obj.owner_decl;
|
||||
},
|
||||
.@"opaque" => @panic("TODO"),
|
||||
.atomic_ordering,
|
||||
.atomic_rmw_op,
|
||||
@@ -2801,6 +2885,8 @@ pub const Type = extern union {
|
||||
empty_struct,
|
||||
@"opaque",
|
||||
@"struct",
|
||||
@"union",
|
||||
union_tagged,
|
||||
enum_simple,
|
||||
enum_full,
|
||||
enum_nonexhaustive,
|
||||
@@ -2902,6 +2988,7 @@ pub const Type = extern union {
|
||||
.error_set_single => Payload.Name,
|
||||
.@"opaque" => Payload.Opaque,
|
||||
.@"struct" => Payload.Struct,
|
||||
.@"union", .union_tagged => Payload.Union,
|
||||
.enum_full, .enum_nonexhaustive => Payload.EnumFull,
|
||||
.enum_simple => Payload.EnumSimple,
|
||||
.empty_struct => Payload.ContainerScope,
|
||||
@@ -3040,6 +3127,11 @@ pub const Type = extern union {
|
||||
data: *Module.Struct,
|
||||
};
|
||||
|
||||
pub const Union = struct {
|
||||
base: Payload,
|
||||
data: *Module.Union,
|
||||
};
|
||||
|
||||
pub const EnumFull = struct {
|
||||
base: Payload,
|
||||
data: *Module.EnumFull,
|
||||
|
||||
@@ -104,6 +104,7 @@ pub const Value = extern union {
|
||||
/// Represents a pointer to a decl, not the value of the decl.
|
||||
decl_ref,
|
||||
elem_ptr,
|
||||
field_ptr,
|
||||
/// A slice of u8 whose memory is managed externally.
|
||||
bytes,
|
||||
/// This value is repeated some number of times. The amount of times to repeat
|
||||
@@ -223,6 +224,7 @@ pub const Value = extern union {
|
||||
.function => Payload.Function,
|
||||
.variable => Payload.Variable,
|
||||
.elem_ptr => Payload.ElemPtr,
|
||||
.field_ptr => Payload.FieldPtr,
|
||||
.float_16 => Payload.Float_16,
|
||||
.float_32 => Payload.Float_32,
|
||||
.float_64 => Payload.Float_64,
|
||||
@@ -414,6 +416,18 @@ pub const Value = extern union {
|
||||
};
|
||||
return Value{ .ptr_otherwise = &new_payload.base };
|
||||
},
|
||||
.field_ptr => {
|
||||
const payload = self.castTag(.field_ptr).?;
|
||||
const new_payload = try allocator.create(Payload.FieldPtr);
|
||||
new_payload.* = .{
|
||||
.base = payload.base,
|
||||
.data = .{
|
||||
.container_ptr = try payload.data.container_ptr.copy(allocator),
|
||||
.field_index = payload.data.field_index,
|
||||
},
|
||||
};
|
||||
return Value{ .ptr_otherwise = &new_payload.base };
|
||||
},
|
||||
.bytes => return self.copyPayloadShallow(allocator, Payload.Bytes),
|
||||
.repeated => {
|
||||
const payload = self.castTag(.repeated).?;
|
||||
@@ -569,6 +583,11 @@ pub const Value = extern union {
|
||||
try out_stream.print("&[{}] ", .{elem_ptr.index});
|
||||
val = elem_ptr.array_ptr;
|
||||
},
|
||||
.field_ptr => {
|
||||
const field_ptr = val.castTag(.field_ptr).?.data;
|
||||
try out_stream.print("fieldptr({d}) ", .{field_ptr.field_index});
|
||||
val = field_ptr.container_ptr;
|
||||
},
|
||||
.empty_array => return out_stream.writeAll(".{}"),
|
||||
.enum_literal => return out_stream.print(".{}", .{std.zig.fmtId(self.castTag(.enum_literal).?.data)}),
|
||||
.enum_field_index => return out_stream.print("(enum field {d})", .{self.castTag(.enum_field_index).?.data}),
|
||||
@@ -704,6 +723,7 @@ pub const Value = extern union {
|
||||
.ref_val,
|
||||
.decl_ref,
|
||||
.elem_ptr,
|
||||
.field_ptr,
|
||||
.bytes,
|
||||
.repeated,
|
||||
.float_16,
|
||||
@@ -1196,6 +1216,11 @@ pub const Value = extern union {
|
||||
std.hash.autoHash(&hasher, payload.array_ptr.hash());
|
||||
std.hash.autoHash(&hasher, payload.index);
|
||||
},
|
||||
.field_ptr => {
|
||||
const payload = self.castTag(.field_ptr).?.data;
|
||||
std.hash.autoHash(&hasher, payload.container_ptr.hash());
|
||||
std.hash.autoHash(&hasher, payload.field_index);
|
||||
},
|
||||
.decl_ref => {
|
||||
const decl = self.castTag(.decl_ref).?.data;
|
||||
std.hash.autoHash(&hasher, decl);
|
||||
@@ -1250,6 +1275,11 @@ pub const Value = extern union {
|
||||
const array_val = try elem_ptr.array_ptr.pointerDeref(allocator);
|
||||
return array_val.elemValue(allocator, elem_ptr.index);
|
||||
},
|
||||
.field_ptr => {
|
||||
const field_ptr = self.castTag(.field_ptr).?.data;
|
||||
const container_val = try field_ptr.container_ptr.pointerDeref(allocator);
|
||||
return container_val.fieldValue(allocator, field_ptr.field_index);
|
||||
},
|
||||
|
||||
else => unreachable,
|
||||
};
|
||||
@@ -1270,6 +1300,22 @@ pub const Value = extern union {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fieldValue(val: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value {
|
||||
switch (val.tag()) {
|
||||
.@"struct" => {
|
||||
const field_values = val.castTag(.@"struct").?.data;
|
||||
return field_values[index];
|
||||
},
|
||||
.@"union" => {
|
||||
const payload = val.castTag(.@"union").?.data;
|
||||
// TODO assert the tag is correct
|
||||
return payload.val;
|
||||
},
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a pointer to the element value at the index.
|
||||
pub fn elemPtr(self: Value, allocator: *Allocator, index: usize) !Value {
|
||||
if (self.castTag(.elem_ptr)) |elem_ptr| {
|
||||
@@ -1409,6 +1455,7 @@ pub const Value = extern union {
|
||||
.ref_val,
|
||||
.decl_ref,
|
||||
.elem_ptr,
|
||||
.field_ptr,
|
||||
.bytes,
|
||||
.repeated,
|
||||
.float_16,
|
||||
@@ -1496,6 +1543,16 @@ pub const Value = extern union {
|
||||
},
|
||||
};
|
||||
|
||||
pub const FieldPtr = struct {
|
||||
pub const base_tag = Tag.field_ptr;
|
||||
|
||||
base: Payload = Payload{ .tag = base_tag },
|
||||
data: struct {
|
||||
container_ptr: Value,
|
||||
field_index: usize,
|
||||
},
|
||||
};
|
||||
|
||||
pub const Bytes = struct {
|
||||
base: Payload,
|
||||
data: []const u8,
|
||||
|
||||
Reference in New Issue
Block a user