Commit 5c1fe58613

Vexu <git@vexu.eu>
2020-08-12 20:06:29
stage2: gen optional types
1 parent 75eaf15
Changed files (5)
src-self-hosted/astgen.zig
@@ -105,6 +105,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
         .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)),
         .BoolLiteral => return rlWrap(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)),
         .NullLiteral => return rlWrap(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)),
+        .OptionalType => return rlWrap(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)),
         else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}),
     }
 }
@@ -293,6 +294,17 @@ fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerErr
     return addZIRUnOp(mod, scope, src, .boolnot, operand);
 }
 
+fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
+    const tree = scope.tree();
+    const src = tree.token_locs[node.op_token].start;
+    const meta_type = try addZIRInstConst(mod, scope, src, .{
+        .ty = Type.initTag(.type),
+        .val = Value.initTag(.type_type),
+    });
+    const operand = try expr(mod, scope, .{ .ty = meta_type }, node.rhs);
+    return addZIRUnOp(mod, scope, src, .optional_type, operand);
+}
+
 /// Identifier token -> String (allocated in scope.arena())
 pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
     const tree = scope.tree();
src-self-hosted/Module.zig
@@ -2476,6 +2476,24 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
     }
     assert(inst.ty.zigTypeTag() != .Undefined);
 
+    // null to ?T
+    if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) {
+        return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = inst.ty.onePossibleValue().? });
+    }
+
+    // T to ?T
+    if (dest_type.zigTypeTag() == .Optional) {
+        const child_type = dest_type.elemType();
+        if (inst.value()) |val| {
+            if (child_type.eql(inst.ty)) {
+                return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
+            }
+            return self.fail(scope, inst.src, "TODO optional wrap {} to {}", .{ val, inst.ty });
+        } else if (child_type.eql(inst.ty)) {
+            return self.fail(scope, inst.src, "TODO optional wrap {}", .{inst.ty});
+        }
+    }
+
     // *[N]T to []T
     if (inst.ty.isSinglePointer() and dest_type.isSlice() and
         (!inst.ty.isConstPtr() or dest_type.isConstPtr()))
src-self-hosted/type.zig
@@ -569,16 +569,16 @@ pub const Type = extern union {
             .single_const_pointer_to_comptime_int,
             .const_slice_u8,
             .array_u8_sentinel_0,
+            .optional,
+            .optional_single_mut_pointer,
+            .optional_single_const_pointer,
+            => true,
             // TODO lazy types
             .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
             .single_const_pointer => self.elemType().hasCodeGenBits(),
             .single_mut_pointer => self.elemType().hasCodeGenBits(),
             .int_signed => self.cast(Payload.IntSigned).?.bits == 0,
             .int_unsigned => self.cast(Payload.IntUnsigned).?.bits == 0,
-            .optional,
-            .optional_single_mut_pointer,
-            .optional_single_const_pointer,
-            => true,
 
             .c_void,
             .void,
src-self-hosted/zir.zig
@@ -212,6 +212,8 @@ pub const Inst = struct {
         @"unreachable",
         /// Bitwise XOR. `^`
         xor,
+        /// Create an optional type '?T'
+        optional_type,
 
         pub fn Type(tag: Tag) type {
             return switch (tag) {
@@ -240,6 +242,7 @@ pub const Inst = struct {
                 .typeof,
                 .single_const_ptr_type,
                 .single_mut_ptr_type,
+                .optional_type,
                 => UnOp,
 
                 .add,
@@ -372,6 +375,7 @@ pub const Inst = struct {
                 .subwrap,
                 .typeof,
                 .xor,
+                .optional_type,
                 => false,
 
                 .@"break",
@@ -2242,6 +2246,20 @@ const EmitZIR = struct {
                         std.debug.panic("TODO implement emitType for {}", .{ty});
                     }
                 },
+                .Optional => {
+                    const inst = try self.arena.allocator.create(Inst.UnOp);
+                    inst.* = .{
+                        .base = .{
+                            .src = src,
+                            .tag = .optional_type,
+                        },
+                        .positionals = .{
+                            .operand = (try self.emitType(src, ty.elemType())).inst,
+                        },
+                        .kw_args = .{},
+                    };
+                    return self.emitUnnamedDecl(&inst.base);
+                },
                 else => std.debug.panic("TODO implement emitType for {}", .{ty}),
             },
         }
src-self-hosted/zir_sema.zig
@@ -106,6 +106,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
         .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false),
         .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?),
         .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?),
+        .optional_type => return analyzeInstOptionalType(mod, scope, old_inst.castTag(.optional_type).?),
     }
 }
 
@@ -620,6 +621,34 @@ fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) I
     return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{});
 }
 
+fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerError!*Inst {
+    const child_type = try resolveType(mod, scope, optional.positionals.operand);
+
+    return mod.constType(scope, optional.base.src, Type.initPayload(switch (child_type.tag()) {
+        .single_const_pointer => blk: {
+            const payload = try scope.arena().create(Type.Payload.OptionalSingleConstPointer);
+            payload.* = .{
+                .pointee_type = child_type.elemType(),
+            };
+            break :blk &payload.base;
+        },
+        .single_mut_pointer => blk: {
+            const payload = try scope.arena().create(Type.Payload.OptionalSingleMutPointer);
+            payload.* = .{
+                .pointee_type = child_type.elemType(),
+            };
+            break :blk &payload.base;
+        },
+        else => blk: {
+            const payload = try scope.arena().create(Type.Payload.Optional);
+            payload.* = .{
+                .child_type = child_type,
+            };
+            break :blk &payload.base;
+        },
+    }));
+}
+
 fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
     const return_type = try resolveType(mod, scope, fntype.positionals.return_type);