diff options
| author | Matthew Lugg <mlugg@mlugg.co.uk> | 2024-10-23 16:48:33 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-23 16:48:33 +0100 |
| commit | 6bf52b0505ad7317b5f0d6fa77b7c41318b9c73b (patch) | |
| tree | 22b5acedc288f3f2085fa4a170ee3d7629e8a9fe /src/Value.zig | |
| parent | 2d888a8e639856e8cb6e4c6f9e6a27647b464952 (diff) | |
| parent | f7d679ceae2403d4137d75d4afe32a3e8eb0cf16 (diff) | |
| download | zig-6bf52b0505ad7317b5f0d6fa77b7c41318b9c73b.tar.gz zig-6bf52b0505ad7317b5f0d6fa77b7c41318b9c73b.zip | |
Merge pull request #21697 from mlugg/callconv
Replace `std.builtin.CallingConvention` with a tagged union, eliminating `@setAlignStack`
Diffstat (limited to 'src/Value.zig')
| -rw-r--r-- | src/Value.zig | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/src/Value.zig b/src/Value.zig index 157f47bc7e..6c5c01e2cc 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -4490,3 +4490,159 @@ 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 { + 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); + return switch (tag) { + inline else => |tag_comptime| @unionInit( + T, + @tagName(tag_comptime), + try val.unionValue(zcu).interpret(@FieldType(T, @tagName(tag_comptime)), pt), + ), + }; + }, + + .@"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 { + 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 }, + } })); + }, + }; +} |
