Commit 8671e8d6d4

Andrew Kelley <andrew@ziglang.org>
2020-04-21 23:06:09
ir: analyze fntype instruction
1 parent 22e7ca5
Changed files (3)
src-self-hosted
src-self-hosted/ir.zig
@@ -168,7 +168,7 @@ const Analyze = struct {
         } else if (self.decl_table.get(old_inst)) |kv| {
             return kv.value.ptr orelse return error.AnalysisFail;
         } else {
-            const new_inst = self.analyzeInst(old_inst, null) catch |err| switch (err) {
+            const new_inst = self.analyzeInst(null, old_inst) catch |err| switch (err) {
                 error.AnalysisFail => {
                     try self.decl_table.putNoClobber(old_inst, .{ .ptr = null });
                     return error.AnalysisFail;
@@ -256,7 +256,14 @@ const Analyze = struct {
         });
     }
 
-    fn analyzeInst(self: *Analyze, old_inst: *text.Inst, opt_func: ?*Fn) InnerError!*Inst {
+    fn constType(self: *Analyze, src: usize, ty: Type) !*Inst {
+        return self.constInst(src, .{
+            .ty = Type.initTag(.@"type"),
+            .val = try ty.toValue(&self.arena.allocator),
+        });
+    }
+
+    fn analyzeInst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst {
         switch (old_inst.tag) {
             .str => {
                 // We can use this reference because Inst.Const's Value is arena-allocated.
@@ -271,49 +278,63 @@ const Analyze = struct {
             .as => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
             .@"asm" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
             .@"unreachable" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
-            .@"fn" => {
-                const fn_inst = old_inst.cast(text.Inst.Fn).?;
-                const fn_type = try self.resolveType(opt_func, fn_inst.positionals.fn_type);
-
-                var new_func: Fn = .{
-                    .body = std.ArrayList(*Inst).init(self.allocator),
-                    .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator),
-                    .fn_index = self.fns.items.len,
-                };
-                defer new_func.body.deinit();
-                defer new_func.inst_table.deinit();
-                // Don't hang on to a reference to this when analyzing body instructions, since the memory
-                // could become invalid.
-                (try self.fns.addOne()).* = .{
-                    .analysis_status = .in_progress,
-                    .body = undefined,
-                };
-
-                for (fn_inst.positionals.body.instructions) |src_inst| {
-                    const new_inst = self.analyzeInst(src_inst, &new_func) catch |err| {
-                        self.fns.items[new_func.fn_index].analysis_status = .failure;
-                        return err;
-                    };
-                    try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst });
-                }
-
-                self.fns.items[new_func.fn_index] = .{
-                    .analysis_status = .success,
-                    .body = new_func.body.toOwnedSlice(),
-                };
-
-                const fn_payload = try self.arena.allocator.create(Value.Payload.Function);
-                fn_payload.* = .{ .index = new_func.fn_index };
-
-                return self.constInst(old_inst.src, .{
-                    .ty = fn_type,
-                    .val = Value.initPayload(&fn_payload.base),
-                });
-            },
+            .@"fn" => return self.analyzeInstFn(func, old_inst.cast(text.Inst.Fn).?),
             .@"export" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
             .primitive => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
-            .fntype => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
+            .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?),
+        }
+    }
+
+    fn analyzeInstFn(self: *Analyze, opt_func: ?*Fn, fn_inst: *text.Inst.Fn) InnerError!*Inst {
+        const fn_type = try self.resolveType(opt_func, fn_inst.positionals.fn_type);
+
+        var new_func: Fn = .{
+            .body = std.ArrayList(*Inst).init(self.allocator),
+            .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator),
+            .fn_index = self.fns.items.len,
+        };
+        defer new_func.body.deinit();
+        defer new_func.inst_table.deinit();
+        // Don't hang on to a reference to this when analyzing body instructions, since the memory
+        // could become invalid.
+        (try self.fns.addOne()).* = .{
+            .analysis_status = .in_progress,
+            .body = undefined,
+        };
+
+        for (fn_inst.positionals.body.instructions) |src_inst| {
+            const new_inst = self.analyzeInst(&new_func, src_inst) catch |err| {
+                self.fns.items[new_func.fn_index].analysis_status = .failure;
+                return err;
+            };
+            try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst });
+        }
+
+        self.fns.items[new_func.fn_index] = .{
+            .analysis_status = .success,
+            .body = new_func.body.toOwnedSlice(),
+        };
+
+        const fn_payload = try self.arena.allocator.create(Value.Payload.Function);
+        fn_payload.* = .{ .index = new_func.fn_index };
+
+        return self.constInst(fn_inst.base.src, .{
+            .ty = fn_type,
+            .val = Value.initPayload(&fn_payload.base),
+        });
+    }
+
+    fn analyzeInstFnType(self: *Analyze, opt_func: ?*Fn, fntype: *text.Inst.FnType) InnerError!*Inst {
+        const return_type = try self.resolveType(opt_func, fntype.positionals.return_type);
+
+        if (return_type.zigTypeTag() == .NoReturn and
+            fntype.positionals.param_types.len == 0 and
+            fntype.kw_args.cc == .Naked)
+        {
+            return self.constType(fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
         }
+
+        return self.fail(fntype.base.src, "TODO implement fntype instruction more", .{});
     }
 
     fn coerce(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
src-self-hosted/type.zig
@@ -1,6 +1,7 @@
 const std = @import("std");
 const Value = @import("value.zig").Value;
 const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
 
 /// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
 /// It's important for this struct to be small.
@@ -48,6 +49,8 @@ pub const Type = extern union {
             .@"comptime_float" => return .ComptimeFloat,
             .@"noreturn" => return .NoReturn,
 
+            .fn_naked_noreturn_no_args => return .Fn,
+
             .array, .array_u8_sentinel_0 => return .Array,
             .single_const_pointer => return .Pointer,
             .const_slice_u8 => return .Pointer,
@@ -122,6 +125,7 @@ pub const Type = extern union {
                 => return out_stream.writeAll(@tagName(t)),
 
                 .const_slice_u8 => return out_stream.writeAll("[]const u8"),
+                .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
 
                 .array_u8_sentinel_0 => {
                     const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise);
@@ -144,6 +148,43 @@ pub const Type = extern union {
         }
     }
 
+    pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value {
+        switch (self.tag()) {
+            .@"u8" => return Value.initTag(.u8_type),
+            .@"i8" => return Value.initTag(.i8_type),
+            .@"isize" => return Value.initTag(.isize_type),
+            .@"usize" => return Value.initTag(.usize_type),
+            .@"c_short" => return Value.initTag(.c_short_type),
+            .@"c_ushort" => return Value.initTag(.c_ushort_type),
+            .@"c_int" => return Value.initTag(.c_int_type),
+            .@"c_uint" => return Value.initTag(.c_uint_type),
+            .@"c_long" => return Value.initTag(.c_long_type),
+            .@"c_ulong" => return Value.initTag(.c_ulong_type),
+            .@"c_longlong" => return Value.initTag(.c_longlong_type),
+            .@"c_ulonglong" => return Value.initTag(.c_ulonglong_type),
+            .@"c_longdouble" => return Value.initTag(.c_longdouble_type),
+            .@"c_void" => return Value.initTag(.c_void_type),
+            .@"f16" => return Value.initTag(.f16_type),
+            .@"f32" => return Value.initTag(.f32_type),
+            .@"f64" => return Value.initTag(.f64_type),
+            .@"f128" => return Value.initTag(.f128_type),
+            .@"bool" => return Value.initTag(.bool_type),
+            .@"void" => return Value.initTag(.void_type),
+            .@"type" => return Value.initTag(.type_type),
+            .@"anyerror" => return Value.initTag(.anyerror_type),
+            .@"comptime_int" => return Value.initTag(.comptime_int_type),
+            .@"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),
+            .const_slice_u8 => return Value.initTag(.const_slice_u8_type),
+            else => {
+                const ty_payload = try allocator.create(Value.Payload.Ty);
+                ty_payload.* = .{ .ty = self };
+                return Value.initPayload(&ty_payload.base);
+            },
+        }
+    }
+
     pub fn isSinglePointer(self: Type) bool {
         return switch (self.tag()) {
             .@"u8",
@@ -174,6 +215,7 @@ pub const Type = extern union {
             .array,
             .array_u8_sentinel_0,
             .const_slice_u8,
+            .fn_naked_noreturn_no_args,
             => false,
 
             .single_const_pointer => true,
@@ -210,6 +252,7 @@ pub const Type = extern union {
             .array,
             .array_u8_sentinel_0,
             .single_const_pointer,
+            .fn_naked_noreturn_no_args,
             => false,
 
             .const_slice_u8 => true,
@@ -246,6 +289,7 @@ pub const Type = extern union {
             .@"noreturn",
             .array,
             .array_u8_sentinel_0,
+            .fn_naked_noreturn_no_args,
             => unreachable,
 
             .single_const_pointer, .const_slice_u8 => true,
@@ -280,6 +324,7 @@ pub const Type = extern union {
             .@"comptime_int",
             .@"comptime_float",
             .@"noreturn",
+            .fn_naked_noreturn_no_args,
             => unreachable,
 
             .array => self.cast(Payload.Array).?.elem_type,
@@ -296,7 +341,6 @@ 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.
-        const_slice_u8,
         @"u8",
         @"i8",
         @"isize",
@@ -321,14 +365,16 @@ pub const Type = extern union {
         @"anyerror",
         @"comptime_int",
         @"comptime_float",
-        @"noreturn", // See last_no_payload_tag below.
+        @"noreturn",
+        fn_naked_noreturn_no_args,
+        const_slice_u8, // See last_no_payload_tag below.
         // After this, the tag requires a payload.
 
         array_u8_sentinel_0,
         array,
         single_const_pointer,
 
-        pub const last_no_payload_tag = Tag.@"noreturn";
+        pub const last_no_payload_tag = Tag.const_slice_u8;
         pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
     };
 
src-self-hosted/value.zig
@@ -16,10 +16,34 @@ pub const Value = extern union {
 
     pub const Tag = enum {
         // The first section of this enum are tags that require no payload.
+        u8_type,
+        i8_type,
+        isize_type,
+        usize_type,
+        c_short_type,
+        c_ushort_type,
+        c_int_type,
+        c_uint_type,
+        c_long_type,
+        c_ulong_type,
+        c_longlong_type,
+        c_ulonglong_type,
+        c_longdouble_type,
+        f16_type,
+        f32_type,
+        f64_type,
+        f128_type,
+        c_void_type,
+        bool_type,
         void_type,
+        type_type,
+        anyerror_type,
+        comptime_int_type,
+        comptime_float_type,
         noreturn_type,
-        bool_type,
-        usize_type,
+        fn_naked_noreturn_no_args_type,
+        const_slice_u8_type,
+
         void_value,
         noreturn_value,
         bool_true,
@@ -74,10 +98,34 @@ pub const Value = extern union {
     ) !void {
         comptime assert(fmt.len == 0);
         switch (self.tag()) {
+            .u8_type => return out_stream.writeAll("u8"),
+            .i8_type => return out_stream.writeAll("i8"),
+            .isize_type => return out_stream.writeAll("isize"),
+            .usize_type => return out_stream.writeAll("usize"),
+            .c_short_type => return out_stream.writeAll("c_short"),
+            .c_ushort_type => return out_stream.writeAll("c_ushort"),
+            .c_int_type => return out_stream.writeAll("c_int"),
+            .c_uint_type => return out_stream.writeAll("c_uint"),
+            .c_long_type => return out_stream.writeAll("c_long"),
+            .c_ulong_type => return out_stream.writeAll("c_ulong"),
+            .c_longlong_type => return out_stream.writeAll("c_longlong"),
+            .c_ulonglong_type => return out_stream.writeAll("c_ulonglong"),
+            .c_longdouble_type => return out_stream.writeAll("c_longdouble"),
+            .f16_type => return out_stream.writeAll("f16"),
+            .f32_type => return out_stream.writeAll("f32"),
+            .f64_type => return out_stream.writeAll("f64"),
+            .f128_type => return out_stream.writeAll("f128"),
+            .c_void_type => return out_stream.writeAll("c_void"),
+            .bool_type => return out_stream.writeAll("bool"),
             .void_type => return out_stream.writeAll("void"),
+            .type_type => return out_stream.writeAll("type"),
+            .anyerror_type => return out_stream.writeAll("anyerror"),
+            .comptime_int_type => return out_stream.writeAll("comptime_int"),
+            .comptime_float_type => return out_stream.writeAll("comptime_float"),
             .noreturn_type => return out_stream.writeAll("noreturn"),
-            .bool_type => return out_stream.writeAll("bool"),
-            .usize_type => return out_stream.writeAll("usize"),
+            .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
+            .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"),
@@ -105,10 +153,33 @@ pub const Value = extern union {
         return switch (self.tag()) {
             .ty => self.cast(Payload.Ty).?.ty,
 
+            .u8_type => Type.initTag(.@"u8"),
+            .i8_type => Type.initTag(.@"i8"),
+            .isize_type => Type.initTag(.@"isize"),
+            .usize_type => Type.initTag(.@"usize"),
+            .c_short_type => Type.initTag(.@"c_short"),
+            .c_ushort_type => Type.initTag(.@"c_ushort"),
+            .c_int_type => Type.initTag(.@"c_int"),
+            .c_uint_type => Type.initTag(.@"c_uint"),
+            .c_long_type => Type.initTag(.@"c_long"),
+            .c_ulong_type => Type.initTag(.@"c_ulong"),
+            .c_longlong_type => Type.initTag(.@"c_longlong"),
+            .c_ulonglong_type => Type.initTag(.@"c_ulonglong"),
+            .c_longdouble_type => Type.initTag(.@"c_longdouble"),
+            .f16_type => Type.initTag(.@"f16"),
+            .f32_type => Type.initTag(.@"f32"),
+            .f64_type => Type.initTag(.@"f64"),
+            .f128_type => Type.initTag(.@"f128"),
+            .c_void_type => Type.initTag(.@"c_void"),
+            .bool_type => Type.initTag(.@"bool"),
             .void_type => Type.initTag(.@"void"),
+            .type_type => Type.initTag(.@"type"),
+            .anyerror_type => Type.initTag(.@"anyerror"),
+            .comptime_int_type => Type.initTag(.@"comptime_int"),
+            .comptime_float_type => Type.initTag(.@"comptime_float"),
             .noreturn_type => Type.initTag(.@"noreturn"),
-            .bool_type => Type.initTag(.@"bool"),
-            .usize_type => Type.initTag(.@"usize"),
+            .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
+            .const_slice_u8_type => Type.initTag(.const_slice_u8),
 
             .void_value,
             .noreturn_value,