diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 6abd4f51e1..110fe05b8e 100644 --- a/src-self-hosted/Module.zig +++ b/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())) diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 1827d53043..57f399d696 100644 --- a/src-self-hosted/astgen.zig +++ b/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(); diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index fbc34c27bc..eadb91ffea 100644 --- a/src-self-hosted/type.zig +++ b/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, diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index ba0c05d587..c98fcf4a74 100644 --- a/src-self-hosted/zir.zig +++ b/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}), }, } diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index e36487e8d5..e241caefc9 100644 --- a/src-self-hosted/zir_sema.zig +++ b/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);