aboutsummaryrefslogtreecommitdiff
path: root/src/Value.zig
diff options
context:
space:
mode:
authormlugg <mlugg@mlugg.co.uk>2024-09-28 02:10:25 +0100
committermlugg <mlugg@mlugg.co.uk>2024-10-19 19:08:59 +0100
commit51706af908e0c6acb822ef36760b7fe31faf62a6 (patch)
treed814bcfcfd83ebc5fd50da11f18a9a6427a54859 /src/Value.zig
parent8573836892ba1b7cd34d377b46258930161256c3 (diff)
downloadzig-51706af908e0c6acb822ef36760b7fe31faf62a6.tar.gz
zig-51706af908e0c6acb822ef36760b7fe31faf62a6.zip
compiler: introduce new `CallingConvention`
This commit begins implementing accepted proposal #21209 by making `std.builtin.CallingConvention` a tagged union. The stage1 dance here is a little convoluted. This commit introduces the new type as `NewCallingConvention`, keeping the old `CallingConvention` around. The compiler uses `std.builtin.NewCallingConvention` exclusively, but when fetching the type from `std` when running the compiler (e.g. with `getBuiltinType`), the name `CallingConvention` is used. This allows a prior build of Zig to be used to build this commit. The next commit will update `zig1.wasm`, and then the compiler and standard library can be updated to completely replace `CallingConvention` with `NewCallingConvention`. The second half of #21209 is to remove `@setAlignStack`, which will be implemented in another commit after updating `zig1.wasm`.
Diffstat (limited to 'src/Value.zig')
-rw-r--r--src/Value.zig160
1 files changed, 160 insertions, 0 deletions
diff --git a/src/Value.zig b/src/Value.zig
index 157f47bc7e..5fadb1a5c5 100644
--- a/src/Value.zig
+++ b/src/Value.zig
@@ -4490,3 +4490,163 @@ pub fn resolveLazy(
else => return val,
}
}
+
+/// Given a `Value` representing a comptime-known value of type `T`, unwrap it into an actual `T` known to the compiler.
+/// This is useful for accessing `std.builtin` structures received from comptime logic.
+/// `val` must be fully resolved.
+pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMemory, UndefinedValue, TypeMismatch }!T {
+ @setEvalBranchQuota(400_000);
+
+ const zcu = pt.zcu;
+ const ip = &zcu.intern_pool;
+ const ty = val.typeOf(zcu);
+ if (ty.zigTypeTag(zcu) != @typeInfo(T)) return error.TypeMismatch;
+ if (val.isUndef(zcu)) return error.UndefinedValue;
+
+ return switch (@typeInfo(T)) {
+ .type,
+ .noreturn,
+ .comptime_float,
+ .comptime_int,
+ .undefined,
+ .null,
+ .@"fn",
+ .@"opaque",
+ .enum_literal,
+ => comptime unreachable, // comptime-only or otherwise impossible
+
+ .pointer,
+ .array,
+ .error_union,
+ .error_set,
+ .frame,
+ .@"anyframe",
+ .vector,
+ => comptime unreachable, // unsupported
+
+ .void => {},
+
+ .bool => switch (val.toIntern()) {
+ .bool_false => false,
+ .bool_true => true,
+ else => unreachable,
+ },
+
+ .int => switch (ip.indexToKey(val.toIntern()).int.storage) {
+ .lazy_align, .lazy_size => unreachable, // `val` is fully resolved
+ inline .u64, .i64 => |x| std.math.cast(T, x) orelse return error.TypeMismatch,
+ .big_int => |big| big.to(T) catch return error.TypeMismatch,
+ },
+
+ .float => val.toFloat(T, zcu),
+
+ .optional => |opt| if (val.optionalValue(zcu)) |unwrapped|
+ try unwrapped.interpret(opt.child, pt)
+ else
+ null,
+
+ .@"enum" => zcu.toEnum(T, val),
+
+ .@"union" => |@"union"| {
+ const union_obj = zcu.typeToUnion(ty) orelse return error.TypeMismatch;
+ if (union_obj.field_types.len != @"union".fields.len) return error.TypeMismatch;
+ const tag_val = val.unionTag(zcu) orelse return error.TypeMismatch;
+ const tag = try tag_val.interpret(@"union".tag_type.?, pt);
+ switch (tag) {
+ inline else => |tag_comptime| {
+ const Payload = std.meta.FieldType(T, tag_comptime);
+ const payload = try val.unionValue(zcu).interpret(Payload, pt);
+ return @unionInit(T, @tagName(tag_comptime), payload);
+ },
+ }
+ },
+
+ .@"struct" => |@"struct"| {
+ if (ty.structFieldCount(zcu) != @"struct".fields.len) return error.TypeMismatch;
+ var result: T = undefined;
+ inline for (@"struct".fields, 0..) |field, field_idx| {
+ const field_val = try val.fieldValue(pt, field_idx);
+ @field(result, field.name) = try field_val.interpret(field.type, pt);
+ }
+ return result;
+ },
+ };
+}
+
+/// Given any `val` and a `Type` corresponding `@TypeOf(val)`, construct a `Value` representing it which can be used
+/// within the compilation. This is useful for passing `std.builtin` structures in the compiler back to the compilation.
+/// This is the inverse of `interpret`.
+pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory, TypeMismatch }!Value {
+ @setEvalBranchQuota(400_000);
+
+ const T = @TypeOf(val);
+
+ const zcu = pt.zcu;
+ if (ty.zigTypeTag(zcu) != @typeInfo(T)) return error.TypeMismatch;
+
+ return switch (@typeInfo(T)) {
+ .type,
+ .noreturn,
+ .comptime_float,
+ .comptime_int,
+ .undefined,
+ .null,
+ .@"fn",
+ .@"opaque",
+ .enum_literal,
+ => comptime unreachable, // comptime-only or otherwise impossible
+
+ .pointer,
+ .array,
+ .error_union,
+ .error_set,
+ .frame,
+ .@"anyframe",
+ .vector,
+ => comptime unreachable, // unsupported
+
+ .void => .void,
+
+ .bool => if (val) .true else .false,
+
+ .int => try pt.intValue(ty, val),
+
+ .float => try pt.floatValue(ty, val),
+
+ .optional => if (val) |some|
+ .fromInterned(try pt.intern(.{ .opt = .{
+ .ty = ty.toIntern(),
+ .val = (try uninterpret(some, ty.optionalChild(zcu), pt)).toIntern(),
+ } }))
+ else
+ try pt.nullValue(ty),
+
+ .@"enum" => try pt.enumValue(ty, (try uninterpret(@intFromEnum(val), ty.intTagType(zcu), pt)).toIntern()),
+
+ .@"union" => |@"union"| {
+ const tag: @"union".tag_type.? = val;
+ const tag_val = try uninterpret(tag, ty.unionTagType(zcu).?, pt);
+ const field_ty = ty.unionFieldType(tag_val, zcu) orelse return error.TypeMismatch;
+ return switch (val) {
+ inline else => |payload| try pt.unionValue(
+ ty,
+ tag_val,
+ try uninterpret(payload, field_ty, pt),
+ ),
+ };
+ },
+
+ .@"struct" => |@"struct"| {
+ if (ty.structFieldCount(zcu) != @"struct".fields.len) return error.TypeMismatch;
+ var field_vals: [@"struct".fields.len]InternPool.Index = undefined;
+ inline for (&field_vals, @"struct".fields, 0..) |*field_val, field, field_idx| {
+ const field_ty = ty.fieldType(field_idx, zcu);
+ field_val.* = (try uninterpret(@field(val, field.name), field_ty, pt)).toIntern();
+ }
+ return .fromInterned(try pt.intern(.{ .aggregate = .{
+ .ty = ty.toIntern(),
+ .storage = .{ .elems = &field_vals },
+ } }));
+ },
+ };
+}