skip CI
All checks were successful
Build / build (push) Successful in 5m9s
Test / build (push) Successful in 10m43s

This commit is contained in:
2026-03-29 00:48:33 +01:00
parent 3c7054fae0
commit fbaf85a849
2 changed files with 95 additions and 61 deletions

View File

@@ -70,16 +70,22 @@ pub const Value = union(Type) {
stride: SpvWord, stride: SpvWord,
data: []u8, data: []u8,
pub inline fn createValueFromIndex(self: *const @This(), allocator: std.mem.Allocator, results: []Result, index: usize) RuntimeError!*Value { pub inline fn createValueFromIndex(self: *const @This(), allocator: std.mem.Allocator, results: []const Result, index: usize) RuntimeError!*Value {
const value = allocator.create(Value) catch return RuntimeError.OutOfMemory; const value = allocator.create(Value) catch return RuntimeError.OutOfMemory;
errdefer allocator.destroy(value); errdefer allocator.destroy(value);
value.* = try Value.init(allocator, results, self.type_word); value.* = try Value.init(allocator, results, self.type_word, false);
_ = try value.writeConst(self.data[self.getOffsetOfIndex(index)..]); _ = try value.writeConst(self.data[self.getOffsetOfIndex(index)..]);
return value; return value;
} }
pub inline fn createLocalValueFromIndex(self: *const @This(), allocator: std.mem.Allocator, results: []const Result, index: usize) RuntimeError!Value {
var value = try Value.init(allocator, results, self.type_word, false);
_ = try value.writeConst(self.data[self.getOffsetOfIndex(index)..]);
return value;
}
pub inline fn getOffsetOfIndex(self: *const @This(), index: usize) usize { pub inline fn getOffsetOfIndex(self: *const @This(), index: usize) usize {
return self.stride * index; return self.stride * index;
} }
@@ -116,7 +122,7 @@ pub const Value = union(Type) {
}; };
} }
pub fn init(allocator: std.mem.Allocator, results: []const Result, target_type: SpvWord) RuntimeError!Self { pub fn init(allocator: std.mem.Allocator, results: []const Result, target_type: SpvWord, is_externally_visible: bool) RuntimeError!Self {
const resolved = results[target_type].resolveType(results); const resolved = results[target_type].resolveType(results);
const member_count = resolved.getMemberCounts(); const member_count = resolved.getMemberCounts();
@@ -137,7 +143,7 @@ pub const Value = union(Type) {
errdefer self.deinit(allocator); errdefer self.deinit(allocator);
for (self.Vector) |*value| { for (self.Vector) |*value| {
value.* = try Self.init(allocator, results, v.components_type_word); value.* = try Self.init(allocator, results, v.components_type_word, is_externally_visible);
} }
break :blk self; break :blk self;
}, },
@@ -155,11 +161,22 @@ pub const Value = union(Type) {
errdefer self.deinit(allocator); errdefer self.deinit(allocator);
for (self.Matrix) |*value| { for (self.Matrix) |*value| {
value.* = try Self.init(allocator, results, m.column_type_word); value.* = try Self.init(allocator, results, m.column_type_word, is_externally_visible);
} }
break :blk self; break :blk self;
}, },
.Array => |a| blk: { .Array => |a| blk: {
// If an array is in externally visible storage we treat it as a runtime array
if (is_externally_visible) {
break :blk .{
.RuntimeArray = .{
.type_word = a.components_type_word,
.stride = a.stride,
.data = &.{},
},
};
}
var self: Self = .{ var self: Self = .{
.Array = .{ .Array = .{
.stride = a.stride, .stride = a.stride,
@@ -169,7 +186,7 @@ pub const Value = union(Type) {
errdefer self.deinit(allocator); errdefer self.deinit(allocator);
for (self.Array.values) |*value| { for (self.Array.values) |*value| {
value.* = try Self.init(allocator, results, a.components_type_word); value.* = try Self.init(allocator, results, a.components_type_word, is_externally_visible);
} }
break :blk self; break :blk self;
}, },
@@ -183,7 +200,7 @@ pub const Value = union(Type) {
errdefer self.deinit(allocator); errdefer self.deinit(allocator);
for (self.Structure.values, s.members_type_word) |*value, type_word| { for (self.Structure.values, s.members_type_word) |*value, type_word| {
value.* = try Self.init(allocator, results, type_word); value.* = try Self.init(allocator, results, type_word, is_externally_visible);
} }
break :blk self; break :blk self;
}, },

View File

@@ -1150,11 +1150,11 @@ fn opBitcast(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeErro
_ = try to_value.write(bytes); _ = try to_value.write(bytes);
} }
fn copyValue(dst: *Value, src: *const Value) void { fn copyValue(dst: *Value, src: *const Value) RuntimeError!void {
const helpers = struct { const helpers = struct {
inline fn copySlice(dst_slice: []Value, src_slice: []const Value) void { inline fn copySlice(dst_slice: []Value, src_slice: []const Value) RuntimeError!void {
for (0..@min(dst_slice.len, src_slice.len)) |i| { for (0..@min(dst_slice.len, src_slice.len)) |i| {
copyValue(&dst_slice[i], &src_slice[i]); try copyValue(&dst_slice[i], &src_slice[i]);
} }
} }
@@ -1167,75 +1167,75 @@ fn copyValue(dst: *Value, src: *const Value) void {
}; };
} }
inline fn writeF32(dst_f32_ptr: *f32, src_v: *const Value) void { inline fn writeF32(dst_f32_ptr: *f32, src_v: *const Value) RuntimeError!void {
switch (src_v.*) { switch (src_v.*) {
.Pointer => |src_ptr| switch (src_ptr.ptr) { .Pointer => |src_ptr| switch (src_ptr.ptr) {
.f32_ptr => |src_f32_ptr| dst_f32_ptr.* = src_f32_ptr.*, .f32_ptr => |src_f32_ptr| dst_f32_ptr.* = src_f32_ptr.*,
.common => |src_val_ptr| dst_f32_ptr.* = src_val_ptr.Float.value.float32, .common => |src_val_ptr| dst_f32_ptr.* = src_val_ptr.Float.value.float32,
else => unreachable, else => return RuntimeError.InvalidSpirV,
}, },
.Float => |f| dst_f32_ptr.* = f.value.float32, .Float => |f| dst_f32_ptr.* = f.value.float32,
else => unreachable, else => return RuntimeError.InvalidSpirV,
} }
} }
inline fn writeI32(dst_i32_ptr: *i32, src_v: *const Value) void { inline fn writeI32(dst_i32_ptr: *i32, src_v: *const Value) RuntimeError!void {
switch (src_v.*) { switch (src_v.*) {
.Pointer => |src_ptr| switch (src_ptr.ptr) { .Pointer => |src_ptr| switch (src_ptr.ptr) {
.i32_ptr => |src_i32_ptr| dst_i32_ptr.* = src_i32_ptr.*, .i32_ptr => |src_i32_ptr| dst_i32_ptr.* = src_i32_ptr.*,
.common => |src_val_ptr| dst_i32_ptr.* = src_val_ptr.Int.value.sint32, .common => |src_val_ptr| dst_i32_ptr.* = src_val_ptr.Int.value.sint32,
else => unreachable, else => return RuntimeError.InvalidSpirV,
}, },
.Int => |i| dst_i32_ptr.* = i.value.sint32, .Int => |i| dst_i32_ptr.* = i.value.sint32,
else => unreachable, else => return RuntimeError.InvalidSpirV,
} }
} }
inline fn writeU32(dst_u32_ptr: *u32, src_v: *const Value) void { inline fn writeU32(dst_u32_ptr: *u32, src_v: *const Value) RuntimeError!void {
switch (src_v.*) { switch (src_v.*) {
.Pointer => |src_ptr| switch (src_ptr.ptr) { .Pointer => |src_ptr| switch (src_ptr.ptr) {
.u32_ptr => |src_u32_ptr| dst_u32_ptr.* = src_u32_ptr.*, .u32_ptr => |src_u32_ptr| dst_u32_ptr.* = src_u32_ptr.*,
.common => |src_val_ptr| dst_u32_ptr.* = src_val_ptr.Int.value.uint32, .common => |src_val_ptr| dst_u32_ptr.* = src_val_ptr.Int.value.uint32,
else => unreachable, else => return RuntimeError.InvalidSpirV,
}, },
.Int => |i| dst_u32_ptr.* = i.value.uint32, .Int => |i| dst_u32_ptr.* = i.value.uint32,
else => unreachable, else => return RuntimeError.InvalidSpirV,
} }
} }
inline fn readF32(dst_v: *Value, src_f32_ptr: *const f32) void { inline fn readF32(dst_v: *Value, src_f32_ptr: *const f32) RuntimeError!void {
switch (dst_v.*) { switch (dst_v.*) {
.Pointer => |dst_ptr| switch (dst_ptr.ptr) { .Pointer => |dst_ptr| switch (dst_ptr.ptr) {
.f32_ptr => |dst_f32_ptr| dst_f32_ptr.* = src_f32_ptr.*, .f32_ptr => |dst_f32_ptr| dst_f32_ptr.* = src_f32_ptr.*,
.common => |dst_val_ptr| dst_val_ptr.Float.value.float32 = src_f32_ptr.*, .common => |dst_val_ptr| dst_val_ptr.Float.value.float32 = src_f32_ptr.*,
else => unreachable, else => return RuntimeError.InvalidSpirV,
}, },
.Float => |*f| f.value.float32 = src_f32_ptr.*, .Float => |*f| f.value.float32 = src_f32_ptr.*,
else => unreachable, else => return RuntimeError.InvalidSpirV,
} }
} }
inline fn readI32(dst_v: *Value, src_i32_ptr: *const i32) void { inline fn readI32(dst_v: *Value, src_i32_ptr: *const i32) RuntimeError!void {
switch (dst_v.*) { switch (dst_v.*) {
.Pointer => |dst_ptr| switch (dst_ptr.ptr) { .Pointer => |dst_ptr| switch (dst_ptr.ptr) {
.i32_ptr => |dst_i32_ptr| dst_i32_ptr.* = src_i32_ptr.*, .i32_ptr => |dst_i32_ptr| dst_i32_ptr.* = src_i32_ptr.*,
.common => |dst_val_ptr| dst_val_ptr.Int.value.sint32 = src_i32_ptr.*, .common => |dst_val_ptr| dst_val_ptr.Int.value.sint32 = src_i32_ptr.*,
else => unreachable, else => return RuntimeError.InvalidSpirV,
}, },
.Int => |*i| i.value.sint32 = src_i32_ptr.*, .Int => |*i| i.value.sint32 = src_i32_ptr.*,
else => unreachable, else => return RuntimeError.InvalidSpirV,
} }
} }
inline fn readU32(dst_v: *Value, src_u32_ptr: *const u32) void { inline fn readU32(dst_v: *Value, src_u32_ptr: *const u32) RuntimeError!void {
switch (dst_v.*) { switch (dst_v.*) {
.Pointer => |dst_ptr| switch (dst_ptr.ptr) { .Pointer => |dst_ptr| switch (dst_ptr.ptr) {
.u32_ptr => |dst_u32_ptr| dst_u32_ptr.* = src_u32_ptr.*, .u32_ptr => |dst_u32_ptr| dst_u32_ptr.* = src_u32_ptr.*,
.common => |dst_val_ptr| dst_val_ptr.Int.value.uint32 = src_u32_ptr.*, .common => |dst_val_ptr| dst_val_ptr.Int.value.uint32 = src_u32_ptr.*,
else => unreachable, else => return RuntimeError.InvalidSpirV,
}, },
.Int => |*i| i.value.uint32 = src_u32_ptr.*, .Int => |*i| i.value.uint32 = src_u32_ptr.*,
else => unreachable, else => return RuntimeError.InvalidSpirV,
} }
} }
}; };
@@ -1244,35 +1244,46 @@ fn copyValue(dst: *Value, src: *const Value) void {
switch (dst.Pointer.ptr) { switch (dst.Pointer.ptr) {
.common => |dst_val_ptr| return switch (src.*) { .common => |dst_val_ptr| return switch (src.*) {
.Pointer => |src_ptr| switch (src_ptr.ptr) { .Pointer => |src_ptr| switch (src_ptr.ptr) {
.common => |src_val_ptr| copyValue(dst_val_ptr, src_val_ptr), .common => |src_val_ptr| try copyValue(dst_val_ptr, src_val_ptr),
else => dst_val_ptr.* = src.*, else => dst_val_ptr.* = src.*,
}, },
else => copyValue(dst_val_ptr, src), else => try copyValue(dst_val_ptr, src),
}, },
.f32_ptr => |dst_f32_ptr| helpers.writeF32(dst_f32_ptr, src), .f32_ptr => |dst_f32_ptr| try helpers.writeF32(dst_f32_ptr, src),
.i32_ptr => |dst_i32_ptr| helpers.writeI32(dst_i32_ptr, src), .i32_ptr => |dst_i32_ptr| try helpers.writeI32(dst_i32_ptr, src),
.u32_ptr => |dst_u32_ptr| helpers.writeU32(dst_u32_ptr, src), .u32_ptr => |dst_u32_ptr| try helpers.writeU32(dst_u32_ptr, src),
} }
} }
switch (src.*) { switch (src.*) {
.Vector, .Matrix => |src_slice| { .Vector, .Matrix => |src_slice| {
const dst_slice = helpers.getDstSlice(dst); const dst_slice = helpers.getDstSlice(dst);
helpers.copySlice(dst_slice.?, src_slice); try helpers.copySlice(dst_slice.?, src_slice);
}, },
.Array => |a| { .Array => |a| {
const dst_slice = helpers.getDstSlice(dst); const dst_slice = helpers.getDstSlice(dst);
helpers.copySlice(dst_slice.?, a.values); try helpers.copySlice(dst_slice.?, a.values);
}, },
.Structure => |s| { .Structure => |s| {
const dst_slice = helpers.getDstSlice(dst); const dst_slice = helpers.getDstSlice(dst);
helpers.copySlice(dst_slice.?, s.values); try helpers.copySlice(dst_slice.?, s.values);
}, },
.Pointer => |ptr| switch (ptr.ptr) { .Pointer => |ptr| switch (ptr.ptr) {
.common => |src_val_ptr| copyValue(dst, src_val_ptr), .common => |src_val_ptr| try copyValue(dst, src_val_ptr),
.f32_ptr => |src_f32_ptr| helpers.readF32(dst, src_f32_ptr), .f32_ptr => |src_f32_ptr| try helpers.readF32(dst, src_f32_ptr),
.i32_ptr => |src_i32_ptr| helpers.readI32(dst, src_i32_ptr), .i32_ptr => |src_i32_ptr| try helpers.readI32(dst, src_i32_ptr),
.u32_ptr => |src_u32_ptr| helpers.readU32(dst, src_u32_ptr), .u32_ptr => |src_u32_ptr| try helpers.readU32(dst, src_u32_ptr),
},
.RuntimeArray => |src_arr| switch (dst.*) {
.RuntimeArray => |dst_arr| @memcpy(dst_arr.data, src_arr.data),
.Pointer => |dst_ptr| switch (dst_ptr.ptr) {
.common => |dst_ptr_ptr| switch (dst_ptr_ptr.*) {
.RuntimeArray => |dst_arr| @memcpy(dst_arr.data, src_arr.data),
else => return RuntimeError.InvalidSpirV,
},
else => return RuntimeError.InvalidSpirV,
},
else => return RuntimeError.InvalidSpirV,
}, },
else => dst.* = src.*, else => dst.* = src.*,
} }
@@ -1541,10 +1552,7 @@ fn opCompositeExtract(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Ru
continue; continue;
} }
switch (composite) { switch (composite) {
.RuntimeArray => |arr| { .RuntimeArray => |arr| composite = try arr.createLocalValueFromIndex(arena_allocator, rt.results, member_id),
composite = try Value.init(arena_allocator, rt.results, arr.type_word);
_ = try composite.writeConst(arr.data[arr.getOffsetOfIndex(member_id)..]);
},
.Vector4f32 => |v| break :blk .{ .Float = .{ .bit_count = 32, .value = .{ .float32 = v[member_id] } } }, .Vector4f32 => |v| break :blk .{ .Float = .{ .bit_count = 32, .value = .{ .float32 = v[member_id] } } },
.Vector3f32 => |v| break :blk .{ .Float = .{ .bit_count = 32, .value = .{ .float32 = v[member_id] } } }, .Vector3f32 => |v| break :blk .{ .Float = .{ .bit_count = 32, .value = .{ .float32 = v[member_id] } } },
.Vector2f32 => |v| break :blk .{ .Float = .{ .bit_count = 32, .value = .{ .float32 = v[member_id] } } }, .Vector2f32 => |v| break :blk .{ .Float = .{ .bit_count = 32, .value = .{ .float32 = v[member_id] } } },
@@ -1571,7 +1579,7 @@ fn opCompositeInsert(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Run
const target = try rt.results[id].getValue(); const target = try rt.results[id].getValue();
copyValue(target, composite); try copyValue(target, composite);
const index_count = word_count - 4; const index_count = word_count - 4;
@@ -1587,7 +1595,7 @@ fn opCompositeInsert(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Run
indices: []const SpvWord, indices: []const SpvWord,
) RuntimeError!void { ) RuntimeError!void {
if (indices.len == 0) { if (indices.len == 0) {
copyValue(current, object_value); try copyValue(current, object_value);
return; return;
} }
@@ -1614,8 +1622,7 @@ fn opCompositeInsert(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Run
return; return;
} }
var elem_value = try Value.init(alloc, results, arr.type_word); var elem_value = try arr.createLocalValueFromIndex(alloc, results, elem_offset);
_ = try elem_value.writeConst(arr.data[elem_offset..]);
try insertAt(alloc, results, &elem_value, object_value, indices[1..]); try insertAt(alloc, results, &elem_value, object_value, indices[1..]);
_ = try elem_value.read(arr.data[elem_offset..]); _ = try elem_value.read(arr.data[elem_offset..]);
}, },
@@ -1700,7 +1707,7 @@ fn opConstantComposite(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) R
const target_value = try target.getValue(); const target_value = try target.getValue();
if (target_value.getCompositeDataOrNull()) |*values| { if (target_value.getCompositeDataOrNull()) |*values| {
for (values.*) |*element| { for (values.*) |*element| {
copyValue(element, try rt.mod.results[try rt.it.next()].getValue()); try copyValue(element, try rt.mod.results[try rt.it.next()].getValue());
} }
return; return;
} }
@@ -1740,7 +1747,7 @@ fn opConstantComposite(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) R
fn opCopyMemory(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opCopyMemory(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
const target = try rt.it.next(); const target = try rt.it.next();
const source = try rt.it.next(); const source = try rt.it.next();
copyValue(try rt.results[target].getValue(), try rt.results[source].getValue()); try copyValue(try rt.results[target].getValue(), try rt.results[source].getValue());
} }
fn opDecorate(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opDecorate(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
@@ -2000,7 +2007,7 @@ fn opLoad(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
_ = rt.it.skip(); _ = rt.it.skip();
const id = try rt.it.next(); const id = try rt.it.next();
const ptr_id = try rt.it.next(); const ptr_id = try rt.it.next();
copyValue(try rt.results[id].getValue(), try rt.results[ptr_id].getValue()); try copyValue(try rt.results[id].getValue(), try rt.results[ptr_id].getValue());
} }
fn opMemberName(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { fn opMemberName(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
@@ -2061,7 +2068,7 @@ fn opReturn(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
fn opReturnValue(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opReturnValue(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
if (rt.function_stack.getLastOrNull()) |function| { if (rt.function_stack.getLastOrNull()) |function| {
var ret_res = rt.results[try rt.it.next()]; var ret_res = rt.results[try rt.it.next()];
copyValue(try function.ret.getValue(), try ret_res.getValue()); try copyValue(try function.ret.getValue(), try ret_res.getValue());
} else { } else {
return RuntimeError.InvalidSpirV; // No current function ??? return RuntimeError.InvalidSpirV; // No current function ???
} }
@@ -2095,13 +2102,13 @@ fn opSelect(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
obj1_val.getCompositeDataOrNull().?, obj1_val.getCompositeDataOrNull().?,
obj2_val.getCompositeDataOrNull().?, obj2_val.getCompositeDataOrNull().?,
) |*t, c, o1, o2| { ) |*t, c, o1, o2| {
copyValue(t, if (c.Bool) &o1 else &o2); try copyValue(t, if (c.Bool) &o1 else &o2);
} }
return; return;
} }
switch (target_val.*) { switch (target_val.*) {
.Bool, .Int, .Float => copyValue(target_val, if (cond_val.Bool) obj1_val else obj2_val), .Bool, .Int, .Float => try copyValue(target_val, if (cond_val.Bool) obj1_val else obj2_val),
.Vector4f32 => |*v| { .Vector4f32 => |*v| {
const cond_vec = @Vector(4, bool){ const cond_vec = @Vector(4, bool){
@@ -2188,7 +2195,7 @@ fn opSourceExtension(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Run
fn opStore(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opStore(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
const ptr_id = try rt.it.next(); const ptr_id = try rt.it.next();
const val_id = try rt.it.next(); const val_id = try rt.it.next();
copyValue(try rt.results[ptr_id].getValue(), try rt.results[val_id].getValue()); try copyValue(try rt.results[ptr_id].getValue(), try rt.results[val_id].getValue());
} }
fn opTypeArray(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opTypeArray(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
@@ -2425,15 +2432,25 @@ fn opVariable(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) R
const resolved_word = if (rt.mod.results[var_type].resolveTypeWordOrNull()) |word| word else var_type; const resolved_word = if (rt.mod.results[var_type].resolveTypeWordOrNull()) |word| word else var_type;
const resolved = &rt.mod.results[resolved_word]; const resolved = &rt.mod.results[resolved_word];
const resolved_type = switch ((try resolved.getConstVariant()).*) {
.Type => |t| @as(Result.Type, t),
else => return RuntimeError.InvalidSpirV,
};
const externally_visible_data_storages = [_]spv.SpvStorageClass{
.Uniform,
.StorageBuffer,
};
const is_externally_visible = std.mem.containsAtLeastScalar(spv.SpvStorageClass, &externally_visible_data_storages, 1, storage_class);
target.variant = .{ target.variant = .{
.Variable = .{ .Variable = .{
.storage_class = storage_class, .storage_class = storage_class,
.type_word = resolved_word, .type_word = resolved_word,
.type = switch ((try resolved.getConstVariant()).*) { .type = resolved_type,
.Type => |t| @as(Result.Type, t), .value = try Value.init(allocator, rt.mod.results, resolved_word, is_externally_visible),
else => return RuntimeError.InvalidSpirV,
},
.value = try Value.init(allocator, rt.mod.results, resolved_word),
}, },
}; };
@@ -2476,7 +2493,7 @@ fn setupConstant(allocator: std.mem.Allocator, rt: *Runtime) RuntimeError!*Resul
const resolved = rt.mod.results[res_type].resolveType(rt.mod.results); const resolved = rt.mod.results[res_type].resolveType(rt.mod.results);
target.variant = .{ target.variant = .{
.Constant = .{ .Constant = .{
.value = try Value.init(allocator, rt.mod.results, res_type), .value = try Value.init(allocator, rt.mod.results, res_type, false),
.type_word = res_type, .type_word = res_type,
.type = switch ((try resolved.getConstVariant()).*) { .type = switch ((try resolved.getConstVariant()).*) {
.Type => |t| @as(Result.Type, t), .Type => |t| @as(Result.Type, t),