From 012fac255f35b9cdbe18c14753a195de89d07d28 Mon Sep 17 00:00:00 2001 From: Vexu Date: Fri, 14 Aug 2020 20:25:06 +0300 Subject: [PATCH] stage2: fix optimization causing wrong optional child types --- src-self-hosted/Module.zig | 117 ++++++++++++++++++--------------- src-self-hosted/astgen.zig | 2 +- src-self-hosted/codegen.zig | 36 +++++++++- src-self-hosted/ir.zig | 2 + src-self-hosted/type.zig | 124 ++++++++++++++++++++++------------- src-self-hosted/zir.zig | 4 +- src-self-hosted/zir_sema.zig | 29 ++++---- 7 files changed, 199 insertions(+), 115 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 2fad7e7a00..2584066499 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -2200,8 +2200,11 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn }; const decl_tv = try decl.typedValue(); - const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer); - ty_payload.* = .{ .pointee_type = decl_tv.ty }; + const ty_payload = try scope.arena().create(Type.Payload.Pointer); + ty_payload.* = .{ + .base = .{ .tag = .single_const_pointer }, + .pointee_type = decl_tv.ty, + }; const val_payload = try scope.arena().create(Value.Payload.DeclRef); val_payload.* = .{ .decl = decl }; @@ -2425,6 +2428,16 @@ pub fn cmpNumeric( return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs); } +fn wrapOptional(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { + if (inst.value()) |val| { + return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); + } + + // TODO how do we get the result location + const b = try self.requireRuntimeBlock(scope, inst.src); + return self.addUnOp(b, inst.src, dest_type, .wrap_optional, inst); +} + fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type { if (signed) { const int_payload = try scope.arena().create(Type.Payload.IntSigned); @@ -2502,14 +2515,12 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst // 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, dest_type }); - } else if (child_type.eql(inst.ty)) { - return self.fail(scope, inst.src, "TODO optional wrap {}", .{dest_type}); + var buf: Type.Payload.Pointer = undefined; + const child_type = dest_type.optionalChild(&buf); + if (child_type.eql(inst.ty)) { + return self.wrapOptional(scope, dest_type, inst); + } else if (try self.coerceNum(scope, child_type, inst)) |some| { + return self.wrapOptional(scope, dest_type, some); } } @@ -2527,39 +2538,8 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst } // comptime known number to other number - if (inst.value()) |val| { - const src_zig_tag = inst.ty.zigTypeTag(); - const dst_zig_tag = dest_type.zigTypeTag(); - - if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) { - if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { - if (val.floatHasFraction()) { - return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty }); - } - return self.fail(scope, inst.src, "TODO float to int", .{}); - } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { - if (!val.intFitsInType(dest_type, self.target())) { - return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); - } - return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); - } - } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { - if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { - const res = val.floatCast(scope.arena(), dest_type, self.target()) catch |err| switch (err) { - error.Overflow => return self.fail( - scope, - inst.src, - "cast of value {} to type '{}' loses information", - .{ val, dest_type }, - ), - error.OutOfMemory => return error.OutOfMemory, - }; - return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res }); - } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { - return self.fail(scope, inst.src, "TODO int to float", .{}); - } - } - } + if (try self.coerceNum(scope, dest_type, inst)) |some| + return some; // integer widening if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) { @@ -2591,6 +2571,42 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty }); } +pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?*Inst { + const val = inst.value() orelse return null; + const src_zig_tag = inst.ty.zigTypeTag(); + const dst_zig_tag = dest_type.zigTypeTag(); + + if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) { + if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { + if (val.floatHasFraction()) { + return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty }); + } + return self.fail(scope, inst.src, "TODO float to int", .{}); + } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { + if (!val.intFitsInType(dest_type, self.target())) { + return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); + } + return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); + } + } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { + if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { + const res = val.floatCast(scope.arena(), dest_type, self.target()) catch |err| switch (err) { + error.Overflow => return self.fail( + scope, + inst.src, + "cast of value {} to type '{}' loses information", + .{ val, dest_type }, + ), + error.OutOfMemory => return error.OutOfMemory, + }; + return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res }); + } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { + return self.fail(scope, inst.src, "TODO int to float", .{}); + } + } + return null; +} + pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst { if (ptr.ty.isConstPtr()) return self.fail(scope, src, "cannot assign to constant", .{}); @@ -2878,15 +2894,12 @@ pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: return Value.initPayload(val_payload); } -pub fn singleMutPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type) error{OutOfMemory}!Type { - const type_payload = try scope.arena().create(Type.Payload.SingleMutPointer); - type_payload.* = .{ .pointee_type = elem_ty }; - return Type.initPayload(&type_payload.base); -} - -pub fn singleConstPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type) error{OutOfMemory}!Type { - const type_payload = try scope.arena().create(Type.Payload.SingleConstPointer); - type_payload.* = .{ .pointee_type = elem_ty }; +pub fn singlePtrType(self: *Module, scope: *Scope, src: usize, mutable: bool, elem_ty: Type) error{OutOfMemory}!Type { + const type_payload = try scope.arena().create(Type.Payload.Pointer); + type_payload.* = .{ + .base = .{ .tag = if (mutable) .single_mut_pointer else .single_const_pointer }, + .pointee_type = elem_ty, + }; return Type.initPayload(&type_payload.base); } diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 795aeda5ed..fd4c5c2864 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -870,7 +870,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo const int_type_payload = try scope.arena().create(Value.Payload.IntType); int_type_payload.* = .{ .signed = is_signed, .bits = bit_count }; const result = try addZIRInstConst(mod, scope, src, .{ - .ty = Type.initTag(.comptime_int), + .ty = Type.initTag(.type), .val = Value.initPayload(&int_type_payload.base), }); return rlWrap(mod, scope, rl, result); diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 12d3884308..58b7c97f7b 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -682,6 +682,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .sub => return self.genSub(inst.castTag(.sub).?), .unreach => return MCValue{ .unreach = {} }, .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?), + .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?), } } @@ -840,6 +841,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } } + fn genWrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue { + const optional_ty = inst.base.ty; + + // No side effects, so if it's unreferenced, do nothing. + if (inst.base.isUnused()) + return MCValue.dead; + + // Optional type is just a boolean true + if (optional_ty.abiSize(self.target.*) == 1) + return MCValue{ .immediate = 1 }; + + switch (arch) { + else => return self.fail(inst.base.src, "TODO implement wrap optional for {}", .{self.target.cpu.arch}), + } + } + fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue { const elem_ty = inst.base.ty; if (!elem_ty.hasCodeGenBits()) @@ -2028,9 +2045,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return mcv; } - fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) !MCValue { + fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) error{ CodegenFail, OutOfMemory }!MCValue { if (typed_value.val.isUndef()) - return MCValue.undef; + return MCValue{ .undef = {} }; const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); switch (typed_value.ty.zigTypeTag()) { @@ -2055,6 +2072,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }, .ComptimeInt => unreachable, // semantic analysis prevents this .ComptimeFloat => unreachable, // semantic analysis prevents this + .Optional => { + if (typed_value.ty.isPtrLikeOptional()) { + if (typed_value.val.isNull()) + return MCValue{ .immediate = 0 }; + + var buf: Type.Payload.Pointer = undefined; + return self.genTypedValue(src, .{ + .ty = typed_value.ty.optionalChild(&buf), + .val = typed_value.val, + }); + } else if (typed_value.ty.abiSize(self.target.*) == 1) { + return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; + } + return self.fail(src, "TODO non pointer optionals", .{}); + }, else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}), } } diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 9ae9518efe..4f83fa7030 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -83,6 +83,7 @@ pub const Inst = struct { floatcast, intcast, unwrap_optional, + wrap_optional, pub fn Type(tag: Tag) type { return switch (tag) { @@ -104,6 +105,7 @@ pub const Inst = struct { .intcast, .load, .unwrap_optional, + .wrap_optional, => UnOp, .add, diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index ef78b046e6..cc3f665c4c 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -107,6 +107,17 @@ pub const Type = extern union { return @fieldParentPtr(T, "base", self.ptr_otherwise); } + pub fn castPointer(self: Type) ?*Payload.Pointer { + return switch (self.tag()) { + .single_const_pointer, + .single_mut_pointer, + .optional_single_const_pointer, + .optional_single_mut_pointer, + => @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise), + else => null, + }; + } + pub fn eql(a: Type, b: Type) bool { // As a shortcut, if the small tags / addresses match, we're done. if (a.tag_if_small_enough == b.tag_if_small_enough) @@ -126,8 +137,8 @@ pub const Type = extern union { .Null => return true, .Pointer => { // Hot path for common case: - if (a.cast(Payload.SingleConstPointer)) |a_payload| { - if (b.cast(Payload.SingleConstPointer)) |b_payload| { + if (a.castPointer()) |a_payload| { + if (b.castPointer()) |b_payload| { return eql(a_payload.pointee_type, b_payload.pointee_type); } } @@ -185,7 +196,9 @@ pub const Type = extern union { return true; }, .Optional => { - return a.elemType().eql(b.elemType()); + var buf_a: Payload.Pointer = undefined; + var buf_b: Payload.Pointer = undefined; + return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b)); }, .Float, .Struct, @@ -249,7 +262,8 @@ pub const Type = extern union { } }, .Optional => { - std.hash.autoHash(&hasher, self.elemType().hash()); + var buf: Payload.Pointer = undefined; + std.hash.autoHash(&hasher, self.optionalChild(&buf).hash()); }, .Float, .Struct, @@ -326,8 +340,6 @@ pub const Type = extern union { }; return Type{ .ptr_otherwise = &new_payload.base }; }, - .single_const_pointer => return self.copyPayloadSingleField(allocator, Payload.SingleConstPointer, "pointee_type"), - .single_mut_pointer => return self.copyPayloadSingleField(allocator, Payload.SingleMutPointer, "pointee_type"), .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned), .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned), .function => { @@ -346,8 +358,11 @@ pub const Type = extern union { return Type{ .ptr_otherwise = &new_payload.base }; }, .optional => return self.copyPayloadSingleField(allocator, Payload.Optional, "child_type"), - .optional_single_mut_pointer => return self.copyPayloadSingleField(allocator, Payload.OptionalSingleMutPointer, "pointee_type"), - .optional_single_const_pointer => return self.copyPayloadSingleField(allocator, Payload.OptionalSingleConstPointer, "pointee_type"), + .single_const_pointer, + .single_mut_pointer, + .optional_single_mut_pointer, + .optional_single_const_pointer, + => return self.copyPayloadSingleField(allocator, Payload.Pointer, "pointee_type"), } } @@ -441,13 +456,13 @@ pub const Type = extern union { continue; }, .single_const_pointer => { - const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", ty.ptr_otherwise); + const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise); try out_stream.writeAll("*const "); ty = payload.pointee_type; continue; }, .single_mut_pointer => { - const payload = @fieldParentPtr(Payload.SingleMutPointer, "base", ty.ptr_otherwise); + const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise); try out_stream.writeAll("*"); ty = payload.pointee_type; continue; @@ -467,13 +482,13 @@ pub const Type = extern union { continue; }, .optional_single_const_pointer => { - const payload = @fieldParentPtr(Payload.OptionalSingleConstPointer, "base", ty.ptr_otherwise); + const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise); try out_stream.writeAll("?*const "); ty = payload.pointee_type; continue; }, .optional_single_mut_pointer => { - const payload = @fieldParentPtr(Payload.OptionalSingleMutPointer, "base", ty.ptr_otherwise); + const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise); try out_stream.writeAll("?*"); ty = payload.pointee_type; continue; @@ -658,7 +673,8 @@ pub const Type = extern union { }, .optional => { - const child_type = self.cast(Payload.Optional).?.child_type; + var buf: Payload.Pointer = undefined; + const child_type = self.optionalChild(&buf); if (!child_type.hasCodeGenBits()) return 1; if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) @@ -750,7 +766,8 @@ pub const Type = extern union { }, .optional => { - const child_type = self.cast(Payload.Optional).?.child_type; + var buf: Payload.Pointer = undefined; + const child_type = self.optionalChild(&buf); if (!child_type.hasCodeGenBits()) return 1; if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) @@ -990,7 +1007,23 @@ pub const Type = extern union { }; } - /// Asserts the type is a pointer, optional or array type. + /// Asserts that the type is an optional + pub fn isPtrLikeOptional(self: Type) bool { + switch (self.tag()) { + .optional_single_const_pointer, .optional_single_mut_pointer => return true, + .optional => { + var buf: Payload.Pointer = undefined; + const child_type = self.optionalChild(&buf); + // optionals of zero sized pointers behave like bools + if (!child_type.hasCodeGenBits()) return false; + + return child_type.zigTypeTag() == .Pointer and !child_type.isCPtr(); + }, + else => unreachable, + } + } + + /// Asserts the type is a pointer or array type. pub fn elemType(self: Type) Type { return switch (self.tag()) { .u8, @@ -1033,16 +1066,38 @@ pub const Type = extern union { .function, .int_unsigned, .int_signed, + .optional, + .optional_single_const_pointer, + .optional_single_mut_pointer, => unreachable, .array => self.cast(Payload.Array).?.elem_type, - .single_const_pointer => self.cast(Payload.SingleConstPointer).?.pointee_type, - .single_mut_pointer => self.cast(Payload.SingleMutPointer).?.pointee_type, + .single_const_pointer => self.castPointer().?.pointee_type, + .single_mut_pointer => self.castPointer().?.pointee_type, .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8), .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int), + }; + } + + /// Asserts that the type is an optional. + pub fn optionalChild(self: Type, buf: *Payload.Pointer) Type { + return switch (self.tag()) { .optional => self.cast(Payload.Optional).?.child_type, - .optional_single_mut_pointer => self.cast(Payload.OptionalSingleMutPointer).?.pointee_type, - .optional_single_const_pointer => self.cast(Payload.OptionalSingleConstPointer).?.pointee_type, + .optional_single_mut_pointer => { + buf.* = .{ + .base = .{ .tag = .single_mut_pointer }, + .pointee_type = self.castPointer().?.pointee_type + }; + return Type.initPayload(&buf.base); + }, + .optional_single_const_pointer => { + buf.* = .{ + .base = .{ .tag = .single_const_pointer }, + .pointee_type = self.castPointer().?.pointee_type + }; + return Type.initPayload(&buf.base); + }, + else => unreachable, }; } @@ -1901,13 +1956,8 @@ pub const Type = extern union { ty = array.elem_type; continue; }, - .single_const_pointer => { - const ptr = ty.cast(Payload.SingleConstPointer).?; - ty = ptr.pointee_type; - continue; - }, - .single_mut_pointer => { - const ptr = ty.cast(Payload.SingleMutPointer).?; + .single_const_pointer, .single_mut_pointer => { + const ptr = ty.castPointer().?; ty = ptr.pointee_type; continue; }, @@ -2049,14 +2099,8 @@ pub const Type = extern union { len: u64, }; - pub const SingleConstPointer = struct { - base: Payload = Payload{ .tag = .single_const_pointer }, - - pointee_type: Type, - }; - - pub const SingleMutPointer = struct { - base: Payload = Payload{ .tag = .single_mut_pointer }, + pub const Pointer = struct { + base: Payload, pointee_type: Type, }; @@ -2086,18 +2130,6 @@ pub const Type = extern union { child_type: Type, }; - - pub const OptionalSingleConstPointer = struct { - base: Payload = Payload{ .tag = .optional_single_const_pointer }, - - pointee_type: Type, - }; - - pub const OptionalSingleMutPointer = struct { - base: Payload = Payload{ .tag = .optional_single_mut_pointer }, - - pointee_type: Type, - }; }; }; diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 25b3b171b4..695cf0013f 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -2017,6 +2017,7 @@ const EmitZIR = struct { .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref), .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref), .unwrap_optional => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional).?, .unwrap_optional_unsafe), + .wrap_optional => try self.emitCast(inst.src, new_body, inst.castTag(.wrap_optional).?, .as), .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add), .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub), @@ -2360,6 +2361,7 @@ const EmitZIR = struct { } }, .Optional => { + var buf: Type.Payload.Pointer = undefined; const inst = try self.arena.allocator.create(Inst.UnOp); inst.* = .{ .base = .{ @@ -2367,7 +2369,7 @@ const EmitZIR = struct { .tag = .optional_type, }, .positionals = .{ - .operand = (try self.emitType(src, ty.elemType())).inst, + .operand = (try self.emitType(src, ty.optionalChild(&buf))).inst, }, .kw_args = .{}, }; diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index 5047c61687..281bc75b1d 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -317,7 +317,7 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const operand = try resolveInst(mod, scope, inst.positionals.operand); - const ptr_type = try mod.singleConstPtrType(scope, inst.base.src, operand.ty); + const ptr_type = try mod.singlePtrType(scope, inst.base.src, false, operand.ty); if (operand.value()) |val| { const ref_payload = try scope.arena().create(Value.Payload.RefVal); @@ -358,7 +358,7 @@ fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst. fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const var_type = try resolveType(mod, scope, inst.positionals.operand); - const ptr_type = try mod.singleMutPtrType(scope, inst.base.src, var_type); + const ptr_type = try mod.singlePtrType(scope, inst.base.src, true, var_type); const b = try mod.requireRuntimeBlock(scope, inst.base.src); return mod.addNoOp(b, inst.base.src, ptr_type, .alloc); } @@ -674,15 +674,17 @@ fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp 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); + const payload = try scope.arena().create(Type.Payload.Pointer); payload.* = .{ + .base = .{ .tag = .optional_single_const_pointer }, .pointee_type = child_type.elemType(), }; break :blk &payload.base; }, .single_mut_pointer => blk: { - const payload = try scope.arena().create(Type.Payload.OptionalSingleMutPointer); + const payload = try scope.arena().create(Type.Payload.Pointer); payload.* = .{ + .base = .{ .tag = .optional_single_mut_pointer }, .pointee_type = child_type.elemType(), }; break :blk &payload.base; @@ -705,11 +707,9 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{operand.ty.elemType()}); } - const child_type = operand.ty.elemType().elemType(); - const child_pointer = if (operand.ty.isConstPtr()) - try mod.singleConstPtrType(scope, unwrap.base.src, child_type) - else - try mod.singleMutPtrType(scope, unwrap.base.src, child_type); + var buf: Type.Payload.Pointer = undefined; + const child_type = try operand.ty.elemType().optionalChild(&buf).copy(scope.arena()); + const child_pointer = try mod.singlePtrType(scope, unwrap.base.src, operand.ty.isConstPtr(), child_type); if (operand.value()) |val| { if (val.isNull()) { @@ -913,8 +913,11 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne // required a larger index. const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64)); - const type_payload = try scope.arena().create(Type.Payload.SingleConstPointer); - type_payload.* = .{ .pointee_type = array_ptr.ty.elemType().elemType() }; + const type_payload = try scope.arena().create(Type.Payload.Pointer); + type_payload.* = .{ + .base = .{ .tag = .single_const_pointer }, + .pointee_type = array_ptr.ty.elemType().elemType(), + }; return mod.constInst(scope, inst.base.src, .{ .ty = Type.initPayload(&type_payload.base), @@ -1279,13 +1282,13 @@ fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerErr fn analyzeInstSingleConstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const elem_type = try resolveType(mod, scope, inst.positionals.operand); - const ty = try mod.singleConstPtrType(scope, inst.base.src, elem_type); + const ty = try mod.singlePtrType(scope, inst.base.src, false, elem_type); return mod.constType(scope, inst.base.src, ty); } fn analyzeInstSingleMutPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const elem_type = try resolveType(mod, scope, inst.positionals.operand); - const ty = try mod.singleMutPtrType(scope, inst.base.src, elem_type); + const ty = try mod.singlePtrType(scope, inst.base.src, true, elem_type); return mod.constType(scope, inst.base.src, ty); }