diff options
| author | mlugg <mlugg@mlugg.co.uk> | 2024-09-28 02:10:25 +0100 |
|---|---|---|
| committer | mlugg <mlugg@mlugg.co.uk> | 2024-10-19 19:08:59 +0100 |
| commit | 51706af908e0c6acb822ef36760b7fe31faf62a6 (patch) | |
| tree | d814bcfcfd83ebc5fd50da11f18a9a6427a54859 /src/Value.zig | |
| parent | 8573836892ba1b7cd34d377b46258930161256c3 (diff) | |
| download | zig-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.zig | 160 |
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 }, + } })); + }, + }; +} |
