Commit c1e19f4c0a

Vexu <git@vexu.eu>
2020-11-16 19:45:53
stage2: initial container astgen
1 parent f173d07
src/type/Enum.zig
@@ -1,8 +1,10 @@
 const std = @import("std");
+const zir = @import("../zir.zig");
 const Value = @import("../value.zig").Value;
 const Type = @import("../type.zig").Type;
 const Module = @import("../Module.zig");
 const Scope = Module.Scope;
+const Enum = @This();
 
 base: Type.Payload = .{ .tag = .@"enum" },
 
@@ -12,6 +14,7 @@ analysis: union(enum) {
     resolved: Size,
     failed,
 },
+scope: Scope.Container,
 
 pub const Field = struct {
     value: Value,
@@ -20,13 +23,10 @@ pub const Field = struct {
 pub const Zir = struct {
     body: zir.Module.Body,
     inst: *zir.Inst,
-    arena: std.heap.ArenaAllocator.State,
 };
 
 pub const Size = struct {
-    is_zero_bits: bool,
-    alignment: u32,
-    size: u32,
+    tag_type: Type,
     fields: std.AutoArrayHashMap([]const u8, Field),
 };
 
@@ -45,11 +45,11 @@ pub fn resolve(self: *Enum, mod: *Module, scope: *Scope) !void {
 }
 
 // TODO should this resolve the type or assert that it has already been resolved?
-pub fn abiAlignment(self: *Enum) u32 {
+pub fn abiAlignment(self: *Enum, target: std.Target) u32 {
     switch (self.analysis) {
         .queued => unreachable, // alignment has not been resolved
         .in_progress => unreachable, // alignment has not been resolved
         .failed => unreachable, // type resolution failed
-        .resolved => |r| return r.tag_type.abiAlignment(),
+        .resolved => |r| return r.tag_type.abiAlignment(target),
     }
 }
src/type/Struct.zig
@@ -1,8 +1,10 @@
 const std = @import("std");
+const zir = @import("../zir.zig");
 const Value = @import("../value.zig").Value;
 const Type = @import("../type.zig").Type;
 const Module = @import("../Module.zig");
 const Scope = Module.Scope;
+const Struct = @This();
 
 base: Type.Payload = .{ .tag = .@"struct" },
 
@@ -11,7 +13,7 @@ analysis: union(enum) {
     zero_bits_in_progress,
     zero_bits: Zero,
     in_progress,
-    alignment: Align,
+    // alignment: Align,
     resolved: Size,
     failed,
 },
@@ -24,7 +26,6 @@ pub const Field = struct {
 pub const Zir = struct {
     body: zir.Module.Body,
     inst: *zir.Inst,
-    arena: std.heap.ArenaAllocator.State,
 };
 
 pub const Zero = struct {
@@ -39,11 +40,11 @@ pub const Size = struct {
     fields: std.AutoArrayHashMap([]const u8, Field),
 };
 
-pub fn resolveZeroBits(self: *Enum, mod: *Module, scope: *Scope) !void {
+pub fn resolveZeroBits(self: *Struct, mod: *Module, scope: *Scope) !void {
     const zir = switch (self.analysis) {
         .failed => return error.AnalysisFail,
         .zero_bits_in_progress => {
-            return mod.fail(scope, src, "union '{}' depends on itself", .{});
+            return mod.fail(scope, src, "struct '{}' depends on itself", .{});
         },
         .queued => |zir| zir,
         else => return,
@@ -53,5 +54,3 @@ pub fn resolveZeroBits(self: *Enum, mod: *Module, scope: *Scope) !void {
 
     // TODO
 }
-
-pub fn resolveSize(self: *Enum,)
\ No newline at end of file
src/type/Union.zig
@@ -1,8 +1,10 @@
 const std = @import("std");
+const zir = @import("../zir.zig");
 const Value = @import("../value.zig").Value;
 const Type = @import("../type.zig").Type;
 const Module = @import("../Module.zig");
 const Scope = Module.Scope;
+const Union = @This();
 
 base: Type.Payload = .{ .tag = .@"struct" },
 
@@ -11,7 +13,7 @@ analysis: union(enum) {
     zero_bits_in_progress,
     zero_bits: Zero,
     in_progress,
-    alignment: Align,
+    // alignment: Align,
     resolved: Size,
     failed,
 },
@@ -24,7 +26,6 @@ pub const Field = struct {
 pub const Zir = struct {
     body: zir.Module.Body,
     inst: *zir.Inst,
-    arena: std.heap.ArenaAllocator.State,
 };
 
 pub const Zero = struct {
@@ -39,7 +40,7 @@ pub const Size = struct {
     fields: std.AutoArrayHashMap([]const u8, Field),
 };
 
-pub fn resolveZeroBits(self: *Enum, mod: *Module, scope: *Scope) !void {
+pub fn resolveZeroBits(self: *Union, mod: *Module, scope: *Scope) !void {
     const zir = switch (self.analysis) {
         .failed => return error.AnalysisFail,
         .zero_bits_in_progress => {
@@ -53,5 +54,3 @@ pub fn resolveZeroBits(self: *Enum, mod: *Module, scope: *Scope) !void {
 
     // TODO
 }
-
-pub fn resolveSize(self: *Enum,)
\ No newline at end of file
src/astgen.zig
@@ -281,6 +281,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
         .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),
         .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?),
         .Switch => return switchExpr(mod, scope, rl, node.castTag(.Switch).?),
+        .ContainerDecl => return containerDecl(mod, scope, rl, node.castTag(.ContainerDecl).?),
 
         .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
         .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),
@@ -294,7 +295,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
         .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}),
         .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
         .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),
-        .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}),
         .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}),
     }
 }
@@ -765,6 +765,168 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si
     return rlWrapPtr(mod, scope, rl, try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand));
 }
 
+fn containerField(mod: *Module, scope: *Scope, node: *ast.Node.ContainerField) InnerError!*zir.Inst {
+    const tree = scope.tree();
+    const src = tree.token_locs[node.firstToken()].start;
+    const name = try identifierTokenString(mod, scope, node.name_token);
+
+    if (node.comptime_token == null and node.value_expr == null and node.align_expr == null) {
+        if (node.type_expr) |some| {
+            const ty = try typeExpr(mod, scope, some);
+            return addZIRInst(mod, scope, src, zir.Inst.ContainerFieldTyped, .{
+                .bytes = name,
+                .ty = ty,
+            }, .{});
+        } else {
+            return addZIRInst(mod, scope, src, zir.Inst.ContainerFieldNamed, .{
+                .bytes = name,
+            }, .{});
+        }
+    }
+
+    const ty = if (node.type_expr) |some| try typeExpr(mod, scope, some) else null;
+    const alignment = if (node.align_expr) |some| try expr(mod, scope, .none, some) else null;
+    const init = if (node.value_expr) |some| try expr(mod, scope, .none, some) else null;
+
+    return addZIRInst(mod, scope, src, zir.Inst.ContainerField, .{
+        .bytes = name,
+    }, .{
+        .ty = ty,
+        .init = init,
+        .alignment = alignment,
+        .is_comptime = node.comptime_token != null,
+    });
+}
+
+fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ContainerDecl) InnerError!*zir.Inst {
+    const tree = scope.tree();
+    const src = tree.token_locs[node.kind_token].start;
+
+    var gen_scope: Scope.GenZIR = .{
+        .parent = scope,
+        .decl = scope.decl().?,
+        .arena = scope.arena(),
+        .instructions = .{},
+    };
+    defer gen_scope.instructions.deinit(mod.gpa);
+
+    var fields = std.ArrayList(*zir.Inst).init(mod.gpa);
+    defer fields.deinit();
+
+    for (node.fieldsAndDecls()) |fd| {
+        if (fd.castTag(.ContainerField)) |f| {
+            try fields.append(try containerField(mod, &gen_scope.base, f));
+        }
+    }
+
+    var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
+    errdefer decl_arena.deinit();
+    const arena = &decl_arena.allocator;
+
+    var layout: std.builtin.TypeInfo.ContainerLayout = .Auto;
+    if (node.layout_token) |some| switch (tree.token_ids[some]) {
+        .Keyword_extern => layout = .Extern,
+        .Keyword_packed => layout = .Packed,
+        else => unreachable,
+    };
+
+    const container_type = switch (tree.token_ids[node.kind_token]) {
+        .Keyword_enum => blk: {
+            const tag_type: ?*zir.Inst = switch (node.init_arg_expr) {
+                .Type => |t| try typeExpr(mod, &gen_scope.base, t),
+                .None => null,
+                .Enum => unreachable,
+            };
+            const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.EnumType, .{
+                .fields = try arena.dupe(*zir.Inst, fields.items),
+            }, .{
+                .layout = layout,
+                .tag_type = tag_type,
+            });
+            const enum_type = try arena.create(Type.Payload.Enum);
+            enum_type.* = .{
+                .analysis = .{
+                    .queued = .{
+                        .body = .{ .instructions = try arena.dupe(*zir.Inst, gen_scope.instructions.items) },
+                        .inst = inst,
+                    },
+                },
+                .scope = .{
+                    .file_scope = scope.getFileScope(),
+                    .ty = Type.initPayload(&enum_type.base),
+                },
+            };
+            break :blk Type.initPayload(&enum_type.base);
+        },
+        .Keyword_struct => blk: {
+            assert(node.init_arg_expr == .None);
+            const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.StructType, .{
+                .fields = try arena.dupe(*zir.Inst, fields.items),
+            }, .{
+                .layout = layout,
+            });
+            const struct_type = try arena.create(Type.Payload.Struct);
+            struct_type.* = .{
+                .analysis = .{
+                    .queued = .{
+                        .body = .{ .instructions = try arena.dupe(*zir.Inst, gen_scope.instructions.items) },
+                        .inst = inst,
+                    },
+                },
+                .scope = .{
+                    .file_scope = scope.getFileScope(),
+                    .ty = Type.initPayload(&struct_type.base),
+                },
+            };
+            break :blk Type.initPayload(&struct_type.base);
+        },
+        .Keyword_union => blk: {
+            const init_inst = switch (node.init_arg_expr) {
+                .Enum => |e| if (e) |t| try typeExpr(mod, &gen_scope.base, t) else null,
+                .None => null,
+                .Type => |t| try typeExpr(mod, &gen_scope.base, t),
+            };
+            const init_kind: zir.Inst.UnionType.InitKind = switch (node.init_arg_expr) {
+                .Enum => .enum_type,
+                .None => .none,
+                .Type => .tag_type,
+            };
+            const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.UnionType, .{
+                .fields = try arena.dupe(*zir.Inst, fields.items),
+            }, .{
+                .layout = layout,
+                .init_kind = init_kind,
+                .init_inst = init_inst,
+            });
+            const union_type = try arena.create(Type.Payload.Union);
+            union_type.* = .{
+                .analysis = .{
+                    .queued = .{
+                        .body = .{ .instructions = try arena.dupe(*zir.Inst, gen_scope.instructions.items) },
+                        .inst = inst,
+                    },
+                },
+                .scope = .{
+                    .file_scope = scope.getFileScope(),
+                    .ty = Type.initPayload(&union_type.base),
+                },
+            };
+            break :blk Type.initPayload(&union_type.base);
+        },
+        .Keyword_opaque => return mod.fail(scope, src, "TODO opaque containers", .{}),
+        else => unreachable,
+    };
+    const type_payload = try arena.create(Value.Payload.Ty);
+    type_payload.* = .{
+        .ty = container_type,
+    };
+    const decl = try mod.createContainerDecl(scope, node.kind_token, &decl_arena, .{
+        .ty = Type.initTag(.type),
+        .val = Value.initPayload(&type_payload.base),
+    });
+    return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{}));
+}
+
 fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst {
     const tree = scope.tree();
     const src = tree.token_locs[node.error_token].start;
src/Module.zig
@@ -469,12 +469,12 @@ pub const Scope = struct {
         }
     }
 
-    pub fn getOwnerPkg(base: *Scope) *Package {
+    pub fn getFileScope(base: *Scope) *Scope.File {
         var cur = base;
         while (true) {
             cur = switch (cur.tag) {
-                .container => return @fieldParentPtr(Container, "base", cur).file_scope.pkg,
-                .file => return @fieldParentPtr(File, "base", cur).pkg,
+                .container => return @fieldParentPtr(Container, "base", cur).file_scope,
+                .file => return @fieldParentPtr(File, "base", cur),
                 .zir_module => unreachable, // TODO are zir modules allowed to import packages?
                 .gen_zir => @fieldParentPtr(GenZIR, "base", cur).parent,
                 .local_val => @fieldParentPtr(LocalVal, "base", cur).parent,
@@ -550,7 +550,7 @@ pub const Scope = struct {
         file_scope: *Scope.File,
 
         /// Direct children of the file.
-        decls: std.AutoArrayHashMapUnmanaged(*Decl, void),
+        decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},
         ty: Type,
 
         pub fn deinit(self: *Container, gpa: *Allocator) void {
@@ -2273,8 +2273,15 @@ pub fn createAnonymousDecl(
     return new_decl;
 }
 
-fn createContainerDecl(self: *Module, scope: *Scope, container_node: *std.zig.ast.Node.ContainerDecl) !*Decl {
-    const name = try self.getAnonTypeName(scope, container_node.kind_token);
+pub fn createContainerDecl(
+    self: *Module,
+    scope: *Scope,
+    base_token: std.zig.ast.TokenIndex,
+    decl_arena: *std.heap.ArenaAllocator,
+    typed_value: TypedValue,
+) !*Decl {
+    const scope_decl = scope.decl().?;
+    const name = try self.getAnonTypeName(scope, base_token);
     defer self.gpa.free(name);
     const name_hash = scope.namespace().fullyQualifiedNameHash(name);
     const src_hash: std.zig.SrcHash = undefined;
@@ -2282,18 +2289,25 @@ fn createContainerDecl(self: *Module, scope: *Scope, container_node: *std.zig.as
     const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
 
     decl_arena_state.* = decl_arena.state;
+    new_decl.typed_value = .{
+        .most_recent = .{
+            .typed_value = typed_value,
+            .arena = decl_arena_state,
+        },
+    };
+    new_decl.analysis = .complete;
     new_decl.generation = self.generation;
 
     return new_decl;
 }
 
 fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 {
-    const container = scope.getContainer();
-    const tree = self.getAstTree(container);
+    const tree = scope.tree();
     const base_name = switch (tree.token_ids[base_token]) {
         .Keyword_struct => "struct",
         .Keyword_enum => "enum",
         .Keyword_union => "union",
+        .Keyword_opaque => "opaque",
         else => unreachable,
     };
     const loc = tree.tokenLocationLoc(0, tree.token_locs[base_token]);
@@ -2487,7 +2501,7 @@ pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst,
 }
 
 pub fn analyzeImport(self: *Module, scope: *Scope, src: usize, target_string: []const u8) !*Scope.File {
-    const cur_pkg = scope.getOwnerPkg();
+    const cur_pkg = scope.getFileScope().pkg;
     const cur_pkg_dir_path = cur_pkg.root_src_directory.path orelse ".";
     const found_pkg = cur_pkg.table.get(target_string);
 
src/type.zig
@@ -923,7 +923,7 @@ pub const Type = extern union {
                 @panic("TODO abiAlignment error union");
             },
 
-            .@"enum" => self.cast(Payload.Enum).?.abiAlignment(),
+            .@"enum" => self.cast(Payload.Enum).?.abiAlignment(target),
             .@"struct" => @panic("TODO"),
             .@"union" => @panic("TODO"),
 
@@ -3221,9 +3221,9 @@ pub const Type = extern union {
             scope: *Module.Scope.Container,
         };
 
-        pub const Enum = @import("value/Enum.zig");
-        pub const Struct = @import("value/Struct.zig");
-        pub const Union = @import("value/Union.zig");
+        pub const Enum = @import("type/Enum.zig");
+        pub const Struct = @import("type/Struct.zig");
+        pub const Union = @import("type/Union.zig");
     };
 };
 
src/zir.zig
@@ -1084,12 +1084,13 @@ pub const Inst = struct {
 
         positionals: struct {
             bytes: []const u8,
-            ty: ?*Inst,
-            init: ?*Inst,
-            alignment: ?*Inst,
-            is_comptime: bool,
         },
-        kw_args: struct {},
+        kw_args: struct {
+            ty: ?*Inst = null,
+            init: ?*Inst = null,
+            alignment: ?*Inst = null,
+            is_comptime: bool = false,
+        },
     };
 
     pub const EnumType = struct {
@@ -1125,13 +1126,17 @@ pub const Inst = struct {
             fields: []*Inst,
         },
         kw_args: struct {
-            init_expr: union(enum) {
-                enum_type: ?*Inst,
-                tag_type: *Inst,
-                none,
-            },
+            init_inst: ?*Inst = null,
+            init_kind: InitKind = .none,
             layout: std.builtin.TypeInfo.ContainerLayout = .Auto,
         },
+
+        // TODO error: values of type '(enum literal)' must be comptime known
+        pub const InitKind = enum {
+            enum_type,
+            tag_type,
+            none,
+        };
     };
 };
 
src/zir_sema.zig
@@ -139,6 +139,14 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
         .switch_range => return analyzeInstSwitchRange(mod, scope, old_inst.castTag(.switch_range).?),
         .booland => return analyzeInstBoolOp(mod, scope, old_inst.castTag(.booland).?),
         .boolor => return analyzeInstBoolOp(mod, scope, old_inst.castTag(.boolor).?),
+
+        .container_field_named,
+        .container_field_typed,
+        .container_field,
+        .enum_type,
+        .union_type,
+        .struct_type,
+        => return mod.fail(scope, old_inst.src, "TODO analyze container instructions", .{}),
     }
 }