diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 24db3b035a..ed4d008934 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -382,7 +382,7 @@ const Analyze = struct { return self.constIntBig(old_inst.src, Type.initTag(.comptime_int), big_int); }, .ptrtoint => return self.analyzeInstPtrToInt(func, old_inst.cast(text.Inst.PtrToInt).?), - .fieldptr => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), + .fieldptr => return self.analyzeInstFieldPtr(func, old_inst.cast(text.Inst.FieldPtr).?), .deref => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), .as => return self.analyzeInstAs(func, old_inst.cast(text.Inst.As).?), .@"asm" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), @@ -470,6 +470,39 @@ const Analyze = struct { return self.addNewInstArgs(f, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr }); } + fn analyzeInstFieldPtr(self: *Analyze, func: ?*Fn, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst { + const object_ptr = try self.resolveInst(func, fieldptr.positionals.object_ptr); + const field_name = try self.resolveConstString(func, fieldptr.positionals.field_name); + + const elem_ty = switch (object_ptr.ty.zigTypeTag()) { + .Pointer => object_ptr.ty.elemType(), + else => return self.fail(fieldptr.base.src, "expected pointer, found '{}'", .{object_ptr.ty}), + }; + switch (elem_ty.zigTypeTag()) { + .Array => { + if (mem.eql(u8, field_name, "len")) { + const len_payload = try self.arena.allocator.create(Value.Payload.Int_u64); + len_payload.* = .{ .int = elem_ty.arrayLen() }; + + const ref_payload = try self.arena.allocator.create(Value.Payload.RefVal); + ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) }; + + return self.constInst(fieldptr.base.src, .{ + .ty = Type.initTag(.single_const_pointer_to_comptime_int), + .val = Value.initPayload(&ref_payload.base), + }); + } else { + return self.fail( + fieldptr.positionals.field_name.src, + "no member named '{}' in '{}'", + .{ field_name, elem_ty }, + ); + } + }, + else => return self.fail(fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}), + } + } + fn analyzeInstIntCast(self: *Analyze, func: ?*Fn, intcast: *text.Inst.IntCast) InnerError!*Inst { const dest_type = try self.resolveType(func, intcast.positionals.dest_type); const new_inst = try self.resolveInst(func, intcast.positionals.value); diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index f57bb58c71..2081f4ed5c 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -54,6 +54,7 @@ pub const Type = extern union { .array, .array_u8_sentinel_0 => return .Array, .single_const_pointer => return .Pointer, + .single_const_pointer_to_comptime_int => return .Pointer, .const_slice_u8 => return .Pointer, } } @@ -127,6 +128,7 @@ pub const Type = extern union { .const_slice_u8 => return out_stream.writeAll("[]const u8"), .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), + .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"), .array_u8_sentinel_0 => { const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise); @@ -177,6 +179,7 @@ pub const Type = extern union { .@"comptime_float" => return Value.initTag(.comptime_float_type), .@"noreturn" => return Value.initTag(.noreturn_type), .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type), + .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type), .const_slice_u8 => return Value.initTag(.const_slice_u8_type), else => { const ty_payload = try allocator.create(Value.Payload.Ty); @@ -219,7 +222,9 @@ pub const Type = extern union { .fn_naked_noreturn_no_args, => false, - .single_const_pointer => true, + .single_const_pointer, + .single_const_pointer_to_comptime_int, + => true, }; } @@ -253,6 +258,7 @@ pub const Type = extern union { .array, .array_u8_sentinel_0, .single_const_pointer, + .single_const_pointer_to_comptime_int, .fn_naked_noreturn_no_args, => false, @@ -293,7 +299,10 @@ pub const Type = extern union { .fn_naked_noreturn_no_args, => unreachable, - .single_const_pointer, .const_slice_u8 => true, + .single_const_pointer, + .single_const_pointer_to_comptime_int, + .const_slice_u8, + => true, }; } @@ -330,7 +339,47 @@ pub const Type = extern union { .array => self.cast(Payload.Array).?.elem_type, .single_const_pointer => self.cast(Payload.SingleConstPointer).?.pointee_type, - .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.@"u8"), + .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8), + .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int), + }; + } + + /// Asserts the type is an array. + pub fn arrayLen(self: Type) u64 { + return switch (self.tag()) { + .u8, + .i8, + .isize, + .usize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, + .f16, + .f32, + .f64, + .f128, + .c_void, + .bool, + .void, + .type, + .anyerror, + .comptime_int, + .comptime_float, + .noreturn, + .fn_naked_noreturn_no_args, + .single_const_pointer, + .single_const_pointer_to_comptime_int, + .const_slice_u8, + => unreachable, + + .array => self.cast(Payload.Array).?.len, + .array_u8_sentinel_0 => self.cast(Payload.Array_u8_Sentinel0).?.len, }; } @@ -353,6 +402,7 @@ pub const Type = extern union { .fn_naked_noreturn_no_args, .array, .single_const_pointer, + .single_const_pointer_to_comptime_int, .array_u8_sentinel_0, .const_slice_u8, => unreachable, @@ -380,32 +430,33 @@ pub const Type = extern union { /// See `zigTypeTag` for the function that corresponds to `std.builtin.TypeId`. pub const Tag = enum { // The first section of this enum are tags that require no payload. - @"u8", - @"i8", - @"isize", - @"usize", - @"c_short", - @"c_ushort", - @"c_int", - @"c_uint", - @"c_long", - @"c_ulong", - @"c_longlong", - @"c_ulonglong", - @"c_longdouble", - @"c_void", - @"f16", - @"f32", - @"f64", - @"f128", - @"bool", - @"void", - @"type", - @"anyerror", - @"comptime_int", - @"comptime_float", - @"noreturn", + u8, + i8, + isize, + usize, + c_short, + c_ushort, + c_int, + c_uint, + c_long, + c_ulong, + c_longlong, + c_ulonglong, + c_longdouble, + c_void, + f16, + f32, + f64, + f128, + bool, + void, + type, + anyerror, + comptime_int, + comptime_float, + noreturn, fn_naked_noreturn_no_args, + single_const_pointer_to_comptime_int, const_slice_u8, // See last_no_payload_tag below. // After this, the tag requires a payload. diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index 1c3dee0fff..086473246f 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -44,6 +44,7 @@ pub const Value = extern union { comptime_float_type, noreturn_type, fn_naked_noreturn_no_args_type, + single_const_pointer_to_comptime_int_type, const_slice_u8_type, void_value, @@ -58,6 +59,7 @@ pub const Value = extern union { int_big, function, ref, + ref_val, bytes, pub const last_no_payload_tag = Tag.bool_false; @@ -100,7 +102,8 @@ pub const Value = extern union { out_stream: var, ) !void { comptime assert(fmt.len == 0); - switch (self.tag()) { + var val = self; + while (true) switch (val.tag()) { .u8_type => return out_stream.writeAll("u8"), .i8_type => return out_stream.writeAll("i8"), .isize_type => return out_stream.writeAll("isize"), @@ -127,20 +130,26 @@ pub const Value = extern union { .comptime_float_type => return out_stream.writeAll("comptime_float"), .noreturn_type => return out_stream.writeAll("noreturn"), .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), + .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), .const_slice_u8_type => return out_stream.writeAll("[]const u8"), .void_value => return out_stream.writeAll("{}"), .noreturn_value => return out_stream.writeAll("unreachable"), .bool_true => return out_stream.writeAll("true"), .bool_false => return out_stream.writeAll("false"), - .ty => return self.cast(Payload.Ty).?.ty.format("", options, out_stream), - .int_u64 => return std.fmt.formatIntValue(self.cast(Payload.Int_u64).?.int, "", options, out_stream), - .int_i64 => return std.fmt.formatIntValue(self.cast(Payload.Int_i64).?.int, "", options, out_stream), - .int_big => return out_stream.print("{}", .{self.cast(Payload.IntBig).?.big_int}), + .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream), + .int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream), + .int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream), + .int_big => return out_stream.print("{}", .{val.cast(Payload.IntBig).?.big_int}), .function => return out_stream.writeAll("(function)"), .ref => return out_stream.writeAll("(ref)"), + .ref_val => { + try out_stream.writeAll("*const "); + val = val.cast(Payload.RefVal).?.val; + continue; + }, .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream), - } + }; } /// Asserts that the value is representable as an array of bytes. @@ -183,6 +192,7 @@ pub const Value = extern union { .comptime_float_type => Type.initTag(.@"comptime_float"), .noreturn_type => Type.initTag(.@"noreturn"), .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args), + .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), .const_slice_u8_type => Type.initTag(.const_slice_u8), .void_value, @@ -194,6 +204,7 @@ pub const Value = extern union { .int_big, .function, .ref, + .ref_val, .bytes, => unreachable, }; @@ -229,6 +240,7 @@ pub const Value = extern union { .comptime_float_type, .noreturn_type, .fn_naked_noreturn_no_args_type, + .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, .void_value, .noreturn_value, @@ -236,6 +248,7 @@ pub const Value = extern union { .bool_false, .function, .ref, + .ref_val, .bytes, => unreachable, @@ -311,6 +324,11 @@ pub const Value = extern union { pointee: *MemoryCell, }; + pub const RefVal = struct { + base: Payload = Payload{ .tag = .ref_val }, + val: Value, + }; + pub const Bytes = struct { base: Payload = Payload{ .tag = .bytes }, data: []const u8,