Commit 2c11acf807

Andrew Kelley <andrew@ziglang.org>
2020-04-22 03:14:56
ir: analyze fieldptr instruction
1 parent c12bc86
Changed files (3)
src-self-hosted
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);
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.
 
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,