authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-08-14 20:25:06+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-08-17 14:26:32+03:00
log012fac255f35b9cdbe18c14753a195de89d07d28
tree6c7a8290ea5cae3da198bc4c9d97b2ce19478a42
parentc52513e25b69cdd28c5af567b8d3d6a0d9fb979e
signature Commit is signed but in an unrecognized format.

stage2: fix optimization causing wrong optional child types


7 files changed, 199 insertions(+), 115 deletions(-)

src-self-hosted/Module.zig+65-52
...@@ -2200,8 +2200,11 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn...@@ -2200,8 +2200,11 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn
2200 };2200 };
22012201
2202 const decl_tv = try decl.typedValue();2202 const decl_tv = try decl.typedValue();
2203 const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer);2203 const ty_payload = try scope.arena().create(Type.Payload.Pointer);
2204 ty_payload.* = .{ .pointee_type = decl_tv.ty };2204 ty_payload.* = .{
2205 .base = .{ .tag = .single_const_pointer },
2206 .pointee_type = decl_tv.ty,
2207 };
2205 const val_payload = try scope.arena().create(Value.Payload.DeclRef);2208 const val_payload = try scope.arena().create(Value.Payload.DeclRef);
2206 val_payload.* = .{ .decl = decl };2209 val_payload.* = .{ .decl = decl };
22072210
...@@ -2425,6 +2428,16 @@ pub fn cmpNumeric(...@@ -2425,6 +2428,16 @@ pub fn cmpNumeric(
2425 return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);2428 return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
2426}2429}
24272430
2431fn wrapOptional(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2432 if (inst.value()) |val| {
2433 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2434 }
2435
2436 // TODO how do we get the result location
2437 const b = try self.requireRuntimeBlock(scope, inst.src);
2438 return self.addUnOp(b, inst.src, dest_type, .wrap_optional, inst);
2439}
2440
2428fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {2441fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
2429 if (signed) {2442 if (signed) {
2430 const int_payload = try scope.arena().create(Type.Payload.IntSigned);2443 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...@@ -2502,14 +2515,12 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
25022515
2503 // T to ?T2516 // T to ?T
2504 if (dest_type.zigTypeTag() == .Optional) {2517 if (dest_type.zigTypeTag() == .Optional) {
2505 const child_type = dest_type.elemType();2518 var buf: Type.Payload.Pointer = undefined;
2506 if (inst.value()) |val| {2519 const child_type = dest_type.optionalChild(&buf);
2507 if (child_type.eql(inst.ty)) {2520 if (child_type.eql(inst.ty)) {
2508 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });2521 return self.wrapOptional(scope, dest_type, inst);
2509 }2522 } else if (try self.coerceNum(scope, child_type, inst)) |some| {
2510 return self.fail(scope, inst.src, "TODO optional wrap {} to {}", .{ val, dest_type });2523 return self.wrapOptional(scope, dest_type, some);
2511 } else if (child_type.eql(inst.ty)) {
2512 return self.fail(scope, inst.src, "TODO optional wrap {}", .{dest_type});
2513 }2524 }
2514 }2525 }
25152526
...@@ -2527,39 +2538,8 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst...@@ -2527,39 +2538,8 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
2527 }2538 }
25282539
2529 // comptime known number to other number2540 // comptime known number to other number
2530 if (inst.value()) |val| {2541 if (try self.coerceNum(scope, dest_type, inst)) |some|
2531 const src_zig_tag = inst.ty.zigTypeTag();2542 return some;
2532 const dst_zig_tag = dest_type.zigTypeTag();
2533
2534 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
2535 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
2536 if (val.floatHasFraction()) {
2537 return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty });
2538 }
2539 return self.fail(scope, inst.src, "TODO float to int", .{});
2540 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
2541 if (!val.intFitsInType(dest_type, self.target())) {
2542 return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
2543 }
2544 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2545 }
2546 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
2547 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
2548 const res = val.floatCast(scope.arena(), dest_type, self.target()) catch |err| switch (err) {
2549 error.Overflow => return self.fail(
2550 scope,
2551 inst.src,
2552 "cast of value {} to type '{}' loses information",
2553 .{ val, dest_type },
2554 ),
2555 error.OutOfMemory => return error.OutOfMemory,
2556 };
2557 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res });
2558 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
2559 return self.fail(scope, inst.src, "TODO int to float", .{});
2560 }
2561 }
2562 }
25632543
2564 // integer widening2544 // integer widening
2565 if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {2545 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...@@ -2591,6 +2571,42 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
2591 return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty });2571 return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty });
2592}2572}
25932573
2574pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?*Inst {
2575 const val = inst.value() orelse return null;
2576 const src_zig_tag = inst.ty.zigTypeTag();
2577 const dst_zig_tag = dest_type.zigTypeTag();
2578
2579 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
2580 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
2581 if (val.floatHasFraction()) {
2582 return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty });
2583 }
2584 return self.fail(scope, inst.src, "TODO float to int", .{});
2585 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
2586 if (!val.intFitsInType(dest_type, self.target())) {
2587 return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
2588 }
2589 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2590 }
2591 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
2592 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
2593 const res = val.floatCast(scope.arena(), dest_type, self.target()) catch |err| switch (err) {
2594 error.Overflow => return self.fail(
2595 scope,
2596 inst.src,
2597 "cast of value {} to type '{}' loses information",
2598 .{ val, dest_type },
2599 ),
2600 error.OutOfMemory => return error.OutOfMemory,
2601 };
2602 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res });
2603 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
2604 return self.fail(scope, inst.src, "TODO int to float", .{});
2605 }
2606 }
2607 return null;
2608}
2609
2594pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst {2610pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst {
2595 if (ptr.ty.isConstPtr())2611 if (ptr.ty.isConstPtr())
2596 return self.fail(scope, src, "cannot assign to constant", .{});2612 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:...@@ -2878,15 +2894,12 @@ pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs:
2878 return Value.initPayload(val_payload);2894 return Value.initPayload(val_payload);
2879}2895}
28802896
2881pub fn singleMutPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type) error{OutOfMemory}!Type {2897pub fn singlePtrType(self: *Module, scope: *Scope, src: usize, mutable: bool, elem_ty: Type) error{OutOfMemory}!Type {
2882 const type_payload = try scope.arena().create(Type.Payload.SingleMutPointer);2898 const type_payload = try scope.arena().create(Type.Payload.Pointer);
2883 type_payload.* = .{ .pointee_type = elem_ty };2899 type_payload.* = .{
2884 return Type.initPayload(&type_payload.base);2900 .base = .{ .tag = if (mutable) .single_mut_pointer else .single_const_pointer },
2885}2901 .pointee_type = elem_ty,
28862902 };
2887pub fn singleConstPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type) error{OutOfMemory}!Type {
2888 const type_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
2889 type_payload.* = .{ .pointee_type = elem_ty };
2890 return Type.initPayload(&type_payload.base);2903 return Type.initPayload(&type_payload.base);
2891}2904}
28922905
src-self-hosted/astgen.zig+1-1
...@@ -870,7 +870,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo...@@ -870,7 +870,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
870 const int_type_payload = try scope.arena().create(Value.Payload.IntType);870 const int_type_payload = try scope.arena().create(Value.Payload.IntType);
871 int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };871 int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };
872 const result = try addZIRInstConst(mod, scope, src, .{872 const result = try addZIRInstConst(mod, scope, src, .{
873 .ty = Type.initTag(.comptime_int),873 .ty = Type.initTag(.type),
874 .val = Value.initPayload(&int_type_payload.base),874 .val = Value.initPayload(&int_type_payload.base),
875 });875 });
876 return rlWrap(mod, scope, rl, result);876 return rlWrap(mod, scope, rl, result);
src-self-hosted/codegen.zig+34-2
...@@ -682,6 +682,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -682,6 +682,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
682 .sub => return self.genSub(inst.castTag(.sub).?),682 .sub => return self.genSub(inst.castTag(.sub).?),
683 .unreach => return MCValue{ .unreach = {} },683 .unreach => return MCValue{ .unreach = {} },
684 .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?),684 .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?),
685 .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
685 }686 }
686 }687 }
687688
...@@ -840,6 +841,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -840,6 +841,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
840 }841 }
841 }842 }
842843
844 fn genWrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
845 const optional_ty = inst.base.ty;
846
847 // No side effects, so if it's unreferenced, do nothing.
848 if (inst.base.isUnused())
849 return MCValue.dead;
850
851 // Optional type is just a boolean true
852 if (optional_ty.abiSize(self.target.*) == 1)
853 return MCValue{ .immediate = 1 };
854
855 switch (arch) {
856 else => return self.fail(inst.base.src, "TODO implement wrap optional for {}", .{self.target.cpu.arch}),
857 }
858 }
859
843 fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {860 fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
844 const elem_ty = inst.base.ty;861 const elem_ty = inst.base.ty;
845 if (!elem_ty.hasCodeGenBits())862 if (!elem_ty.hasCodeGenBits())
...@@ -2028,9 +2045,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2028,9 +2045,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2028 return mcv;2045 return mcv;
2029 }2046 }
20302047
2031 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) !MCValue {2048 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) error{ CodegenFail, OutOfMemory }!MCValue {
2032 if (typed_value.val.isUndef())2049 if (typed_value.val.isUndef())
2033 return MCValue.undef;2050 return MCValue{ .undef = {} };
2034 const ptr_bits = self.target.cpu.arch.ptrBitWidth();2051 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
2035 const ptr_bytes: u64 = @divExact(ptr_bits, 8);2052 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
2036 switch (typed_value.ty.zigTypeTag()) {2053 switch (typed_value.ty.zigTypeTag()) {
...@@ -2055,6 +2072,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2055,6 +2072,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2055 },2072 },
2056 .ComptimeInt => unreachable, // semantic analysis prevents this2073 .ComptimeInt => unreachable, // semantic analysis prevents this
2057 .ComptimeFloat => unreachable, // semantic analysis prevents this2074 .ComptimeFloat => unreachable, // semantic analysis prevents this
2075 .Optional => {
2076 if (typed_value.ty.isPtrLikeOptional()) {
2077 if (typed_value.val.isNull())
2078 return MCValue{ .immediate = 0 };
2079
2080 var buf: Type.Payload.Pointer = undefined;
2081 return self.genTypedValue(src, .{
2082 .ty = typed_value.ty.optionalChild(&buf),
2083 .val = typed_value.val,
2084 });
2085 } else if (typed_value.ty.abiSize(self.target.*) == 1) {
2086 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
2087 }
2088 return self.fail(src, "TODO non pointer optionals", .{});
2089 },
2058 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),2090 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
2059 }2091 }
2060 }2092 }
src-self-hosted/ir.zig+2
...@@ -83,6 +83,7 @@ pub const Inst = struct {...@@ -83,6 +83,7 @@ pub const Inst = struct {
83 floatcast,83 floatcast,
84 intcast,84 intcast,
85 unwrap_optional,85 unwrap_optional,
86 wrap_optional,
8687
87 pub fn Type(tag: Tag) type {88 pub fn Type(tag: Tag) type {
88 return switch (tag) {89 return switch (tag) {
...@@ -104,6 +105,7 @@ pub const Inst = struct {...@@ -104,6 +105,7 @@ pub const Inst = struct {
104 .intcast,105 .intcast,
105 .load,106 .load,
106 .unwrap_optional,107 .unwrap_optional,
108 .wrap_optional,
107 => UnOp,109 => UnOp,
108110
109 .add,111 .add,
src-self-hosted/type.zig+78-46
...@@ -107,6 +107,17 @@ pub const Type = extern union {...@@ -107,6 +107,17 @@ pub const Type = extern union {
107 return @fieldParentPtr(T, "base", self.ptr_otherwise);107 return @fieldParentPtr(T, "base", self.ptr_otherwise);
108 }108 }
109109
110 pub fn castPointer(self: Type) ?*Payload.Pointer {
111 return switch (self.tag()) {
112 .single_const_pointer,
113 .single_mut_pointer,
114 .optional_single_const_pointer,
115 .optional_single_mut_pointer,
116 => @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise),
117 else => null,
118 };
119 }
120
110 pub fn eql(a: Type, b: Type) bool {121 pub fn eql(a: Type, b: Type) bool {
111 // As a shortcut, if the small tags / addresses match, we're done.122 // As a shortcut, if the small tags / addresses match, we're done.
112 if (a.tag_if_small_enough == b.tag_if_small_enough)123 if (a.tag_if_small_enough == b.tag_if_small_enough)
...@@ -126,8 +137,8 @@ pub const Type = extern union {...@@ -126,8 +137,8 @@ pub const Type = extern union {
126 .Null => return true,137 .Null => return true,
127 .Pointer => {138 .Pointer => {
128 // Hot path for common case:139 // Hot path for common case:
129 if (a.cast(Payload.SingleConstPointer)) |a_payload| {140 if (a.castPointer()) |a_payload| {
130 if (b.cast(Payload.SingleConstPointer)) |b_payload| {141 if (b.castPointer()) |b_payload| {
131 return eql(a_payload.pointee_type, b_payload.pointee_type);142 return eql(a_payload.pointee_type, b_payload.pointee_type);
132 }143 }
133 }144 }
...@@ -185,7 +196,9 @@ pub const Type = extern union {...@@ -185,7 +196,9 @@ pub const Type = extern union {
185 return true;196 return true;
186 },197 },
187 .Optional => {198 .Optional => {
188 return a.elemType().eql(b.elemType());199 var buf_a: Payload.Pointer = undefined;
200 var buf_b: Payload.Pointer = undefined;
201 return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b));
189 },202 },
190 .Float,203 .Float,
191 .Struct,204 .Struct,
...@@ -249,7 +262,8 @@ pub const Type = extern union {...@@ -249,7 +262,8 @@ pub const Type = extern union {
249 }262 }
250 },263 },
251 .Optional => {264 .Optional => {
252 std.hash.autoHash(&hasher, self.elemType().hash());265 var buf: Payload.Pointer = undefined;
266 std.hash.autoHash(&hasher, self.optionalChild(&buf).hash());
253 },267 },
254 .Float,268 .Float,
255 .Struct,269 .Struct,
...@@ -326,8 +340,6 @@ pub const Type = extern union {...@@ -326,8 +340,6 @@ pub const Type = extern union {
326 };340 };
327 return Type{ .ptr_otherwise = &new_payload.base };341 return Type{ .ptr_otherwise = &new_payload.base };
328 },342 },
329 .single_const_pointer => return self.copyPayloadSingleField(allocator, Payload.SingleConstPointer, "pointee_type"),
330 .single_mut_pointer => return self.copyPayloadSingleField(allocator, Payload.SingleMutPointer, "pointee_type"),
331 .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),343 .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),
332 .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),344 .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),
333 .function => {345 .function => {
...@@ -346,8 +358,11 @@ pub const Type = extern union {...@@ -346,8 +358,11 @@ pub const Type = extern union {
346 return Type{ .ptr_otherwise = &new_payload.base };358 return Type{ .ptr_otherwise = &new_payload.base };
347 },359 },
348 .optional => return self.copyPayloadSingleField(allocator, Payload.Optional, "child_type"),360 .optional => return self.copyPayloadSingleField(allocator, Payload.Optional, "child_type"),
349 .optional_single_mut_pointer => return self.copyPayloadSingleField(allocator, Payload.OptionalSingleMutPointer, "pointee_type"),361 .single_const_pointer,
350 .optional_single_const_pointer => return self.copyPayloadSingleField(allocator, Payload.OptionalSingleConstPointer, "pointee_type"),362 .single_mut_pointer,
363 .optional_single_mut_pointer,
364 .optional_single_const_pointer,
365 => return self.copyPayloadSingleField(allocator, Payload.Pointer, "pointee_type"),
351 }366 }
352 }367 }
353368
...@@ -441,13 +456,13 @@ pub const Type = extern union {...@@ -441,13 +456,13 @@ pub const Type = extern union {
441 continue;456 continue;
442 },457 },
443 .single_const_pointer => {458 .single_const_pointer => {
444 const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", ty.ptr_otherwise);459 const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
445 try out_stream.writeAll("*const ");460 try out_stream.writeAll("*const ");
446 ty = payload.pointee_type;461 ty = payload.pointee_type;
447 continue;462 continue;
448 },463 },
449 .single_mut_pointer => {464 .single_mut_pointer => {
450 const payload = @fieldParentPtr(Payload.SingleMutPointer, "base", ty.ptr_otherwise);465 const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
451 try out_stream.writeAll("*");466 try out_stream.writeAll("*");
452 ty = payload.pointee_type;467 ty = payload.pointee_type;
453 continue;468 continue;
...@@ -467,13 +482,13 @@ pub const Type = extern union {...@@ -467,13 +482,13 @@ pub const Type = extern union {
467 continue;482 continue;
468 },483 },
469 .optional_single_const_pointer => {484 .optional_single_const_pointer => {
470 const payload = @fieldParentPtr(Payload.OptionalSingleConstPointer, "base", ty.ptr_otherwise);485 const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
471 try out_stream.writeAll("?*const ");486 try out_stream.writeAll("?*const ");
472 ty = payload.pointee_type;487 ty = payload.pointee_type;
473 continue;488 continue;
474 },489 },
475 .optional_single_mut_pointer => {490 .optional_single_mut_pointer => {
476 const payload = @fieldParentPtr(Payload.OptionalSingleMutPointer, "base", ty.ptr_otherwise);491 const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
477 try out_stream.writeAll("?*");492 try out_stream.writeAll("?*");
478 ty = payload.pointee_type;493 ty = payload.pointee_type;
479 continue;494 continue;
...@@ -658,7 +673,8 @@ pub const Type = extern union {...@@ -658,7 +673,8 @@ pub const Type = extern union {
658 },673 },
659674
660 .optional => {675 .optional => {
661 const child_type = self.cast(Payload.Optional).?.child_type;676 var buf: Payload.Pointer = undefined;
677 const child_type = self.optionalChild(&buf);
662 if (!child_type.hasCodeGenBits()) return 1;678 if (!child_type.hasCodeGenBits()) return 1;
663679
664 if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr())680 if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr())
...@@ -750,7 +766,8 @@ pub const Type = extern union {...@@ -750,7 +766,8 @@ pub const Type = extern union {
750 },766 },
751767
752 .optional => {768 .optional => {
753 const child_type = self.cast(Payload.Optional).?.child_type;769 var buf: Payload.Pointer = undefined;
770 const child_type = self.optionalChild(&buf);
754 if (!child_type.hasCodeGenBits()) return 1;771 if (!child_type.hasCodeGenBits()) return 1;
755772
756 if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr())773 if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr())
...@@ -990,7 +1007,23 @@ pub const Type = extern union {...@@ -990,7 +1007,23 @@ pub const Type = extern union {
990 };1007 };
991 }1008 }
9921009
993 /// Asserts the type is a pointer, optional or array type.1010 /// Asserts that the type is an optional
1011 pub fn isPtrLikeOptional(self: Type) bool {
1012 switch (self.tag()) {
1013 .optional_single_const_pointer, .optional_single_mut_pointer => return true,
1014 .optional => {
1015 var buf: Payload.Pointer = undefined;
1016 const child_type = self.optionalChild(&buf);
1017 // optionals of zero sized pointers behave like bools
1018 if (!child_type.hasCodeGenBits()) return false;
1019
1020 return child_type.zigTypeTag() == .Pointer and !child_type.isCPtr();
1021 },
1022 else => unreachable,
1023 }
1024 }
1025
1026 /// Asserts the type is a pointer or array type.
994 pub fn elemType(self: Type) Type {1027 pub fn elemType(self: Type) Type {
995 return switch (self.tag()) {1028 return switch (self.tag()) {
996 .u8,1029 .u8,
...@@ -1033,16 +1066,38 @@ pub const Type = extern union {...@@ -1033,16 +1066,38 @@ pub const Type = extern union {
1033 .function,1066 .function,
1034 .int_unsigned,1067 .int_unsigned,
1035 .int_signed,1068 .int_signed,
1069 .optional,
1070 .optional_single_const_pointer,
1071 .optional_single_mut_pointer,
1036 => unreachable,1072 => unreachable,
10371073
1038 .array => self.cast(Payload.Array).?.elem_type,1074 .array => self.cast(Payload.Array).?.elem_type,
1039 .single_const_pointer => self.cast(Payload.SingleConstPointer).?.pointee_type,1075 .single_const_pointer => self.castPointer().?.pointee_type,
1040 .single_mut_pointer => self.cast(Payload.SingleMutPointer).?.pointee_type,1076 .single_mut_pointer => self.castPointer().?.pointee_type,
1041 .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),1077 .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
1042 .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),1078 .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
1079 };
1080 }
1081
1082 /// Asserts that the type is an optional.
1083 pub fn optionalChild(self: Type, buf: *Payload.Pointer) Type {
1084 return switch (self.tag()) {
1043 .optional => self.cast(Payload.Optional).?.child_type,1085 .optional => self.cast(Payload.Optional).?.child_type,
1044 .optional_single_mut_pointer => self.cast(Payload.OptionalSingleMutPointer).?.pointee_type,1086 .optional_single_mut_pointer => {
1045 .optional_single_const_pointer => self.cast(Payload.OptionalSingleConstPointer).?.pointee_type,1087 buf.* = .{
1088 .base = .{ .tag = .single_mut_pointer },
1089 .pointee_type = self.castPointer().?.pointee_type
1090 };
1091 return Type.initPayload(&buf.base);
1092 },
1093 .optional_single_const_pointer => {
1094 buf.* = .{
1095 .base = .{ .tag = .single_const_pointer },
1096 .pointee_type = self.castPointer().?.pointee_type
1097 };
1098 return Type.initPayload(&buf.base);
1099 },
1100 else => unreachable,
1046 };1101 };
1047 }1102 }
10481103
...@@ -1901,13 +1956,8 @@ pub const Type = extern union {...@@ -1901,13 +1956,8 @@ pub const Type = extern union {
1901 ty = array.elem_type;1956 ty = array.elem_type;
1902 continue;1957 continue;
1903 },1958 },
1904 .single_const_pointer => {1959 .single_const_pointer, .single_mut_pointer => {
1905 const ptr = ty.cast(Payload.SingleConstPointer).?;1960 const ptr = ty.castPointer().?;
1906 ty = ptr.pointee_type;
1907 continue;
1908 },
1909 .single_mut_pointer => {
1910 const ptr = ty.cast(Payload.SingleMutPointer).?;
1911 ty = ptr.pointee_type;1961 ty = ptr.pointee_type;
1912 continue;1962 continue;
1913 },1963 },
...@@ -2049,14 +2099,8 @@ pub const Type = extern union {...@@ -2049,14 +2099,8 @@ pub const Type = extern union {
2049 len: u64,2099 len: u64,
2050 };2100 };
20512101
2052 pub const SingleConstPointer = struct {2102 pub const Pointer = struct {
2053 base: Payload = Payload{ .tag = .single_const_pointer },2103 base: Payload,
2054
2055 pointee_type: Type,
2056 };
2057
2058 pub const SingleMutPointer = struct {
2059 base: Payload = Payload{ .tag = .single_mut_pointer },
20602104
2061 pointee_type: Type,2105 pointee_type: Type,
2062 };2106 };
...@@ -2086,18 +2130,6 @@ pub const Type = extern union {...@@ -2086,18 +2130,6 @@ pub const Type = extern union {
20862130
2087 child_type: Type,2131 child_type: Type,
2088 };2132 };
2089
2090 pub const OptionalSingleConstPointer = struct {
2091 base: Payload = Payload{ .tag = .optional_single_const_pointer },
2092
2093 pointee_type: Type,
2094 };
2095
2096 pub const OptionalSingleMutPointer = struct {
2097 base: Payload = Payload{ .tag = .optional_single_mut_pointer },
2098
2099 pointee_type: Type,
2100 };
2101 };2133 };
2102};2134};
21032135
src-self-hosted/zir.zig+3-1
...@@ -2017,6 +2017,7 @@ const EmitZIR = struct {...@@ -2017,6 +2017,7 @@ const EmitZIR = struct {
2017 .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref),2017 .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref),
2018 .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref),2018 .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref),
2019 .unwrap_optional => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional).?, .unwrap_optional_unsafe),2019 .unwrap_optional => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional).?, .unwrap_optional_unsafe),
2020 .wrap_optional => try self.emitCast(inst.src, new_body, inst.castTag(.wrap_optional).?, .as),
20202021
2021 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),2022 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),
2022 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),2023 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),
...@@ -2360,6 +2361,7 @@ const EmitZIR = struct {...@@ -2360,6 +2361,7 @@ const EmitZIR = struct {
2360 }2361 }
2361 },2362 },
2362 .Optional => {2363 .Optional => {
2364 var buf: Type.Payload.Pointer = undefined;
2363 const inst = try self.arena.allocator.create(Inst.UnOp);2365 const inst = try self.arena.allocator.create(Inst.UnOp);
2364 inst.* = .{2366 inst.* = .{
2365 .base = .{2367 .base = .{
...@@ -2367,7 +2369,7 @@ const EmitZIR = struct {...@@ -2367,7 +2369,7 @@ const EmitZIR = struct {
2367 .tag = .optional_type,2369 .tag = .optional_type,
2368 },2370 },
2369 .positionals = .{2371 .positionals = .{
2370 .operand = (try self.emitType(src, ty.elemType())).inst,2372 .operand = (try self.emitType(src, ty.optionalChild(&buf))).inst,
2371 },2373 },
2372 .kw_args = .{},2374 .kw_args = .{},
2373 };2375 };
src-self-hosted/zir_sema.zig+16-13
...@@ -317,7 +317,7 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr...@@ -317,7 +317,7 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr
317317
318fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {318fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
319 const operand = try resolveInst(mod, scope, inst.positionals.operand);319 const operand = try resolveInst(mod, scope, inst.positionals.operand);
320 const ptr_type = try mod.singleConstPtrType(scope, inst.base.src, operand.ty);320 const ptr_type = try mod.singlePtrType(scope, inst.base.src, false, operand.ty);
321321
322 if (operand.value()) |val| {322 if (operand.value()) |val| {
323 const ref_payload = try scope.arena().create(Value.Payload.RefVal);323 const ref_payload = try scope.arena().create(Value.Payload.RefVal);
...@@ -358,7 +358,7 @@ fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst....@@ -358,7 +358,7 @@ fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst.
358358
359fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {359fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
360 const var_type = try resolveType(mod, scope, inst.positionals.operand);360 const var_type = try resolveType(mod, scope, inst.positionals.operand);
361 const ptr_type = try mod.singleMutPtrType(scope, inst.base.src, var_type);361 const ptr_type = try mod.singlePtrType(scope, inst.base.src, true, var_type);
362 const b = try mod.requireRuntimeBlock(scope, inst.base.src);362 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
363 return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);363 return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);
364}364}
...@@ -674,15 +674,17 @@ fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp...@@ -674,15 +674,17 @@ fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp
674674
675 return mod.constType(scope, optional.base.src, Type.initPayload(switch (child_type.tag()) {675 return mod.constType(scope, optional.base.src, Type.initPayload(switch (child_type.tag()) {
676 .single_const_pointer => blk: {676 .single_const_pointer => blk: {
677 const payload = try scope.arena().create(Type.Payload.OptionalSingleConstPointer);677 const payload = try scope.arena().create(Type.Payload.Pointer);
678 payload.* = .{678 payload.* = .{
679 .base = .{ .tag = .optional_single_const_pointer },
679 .pointee_type = child_type.elemType(),680 .pointee_type = child_type.elemType(),
680 };681 };
681 break :blk &payload.base;682 break :blk &payload.base;
682 },683 },
683 .single_mut_pointer => blk: {684 .single_mut_pointer => blk: {
684 const payload = try scope.arena().create(Type.Payload.OptionalSingleMutPointer);685 const payload = try scope.arena().create(Type.Payload.Pointer);
685 payload.* = .{686 payload.* = .{
687 .base = .{ .tag = .optional_single_mut_pointer },
686 .pointee_type = child_type.elemType(),688 .pointee_type = child_type.elemType(),
687 };689 };
688 break :blk &payload.base;690 break :blk &payload.base;
...@@ -705,11 +707,9 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp...@@ -705,11 +707,9 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp
705 return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{operand.ty.elemType()});707 return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{operand.ty.elemType()});
706 }708 }
707709
708 const child_type = operand.ty.elemType().elemType();710 var buf: Type.Payload.Pointer = undefined;
709 const child_pointer = if (operand.ty.isConstPtr())711 const child_type = try operand.ty.elemType().optionalChild(&buf).copy(scope.arena());
710 try mod.singleConstPtrType(scope, unwrap.base.src, child_type)712 const child_pointer = try mod.singlePtrType(scope, unwrap.base.src, operand.ty.isConstPtr(), child_type);
711 else
712 try mod.singleMutPtrType(scope, unwrap.base.src, child_type);
713713
714 if (operand.value()) |val| {714 if (operand.value()) |val| {
715 if (val.isNull()) {715 if (val.isNull()) {
...@@ -913,8 +913,11 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne...@@ -913,8 +913,11 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne
913 // required a larger index.913 // required a larger index.
914 const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64));914 const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64));
915915
916 const type_payload = try scope.arena().create(Type.Payload.SingleConstPointer);916 const type_payload = try scope.arena().create(Type.Payload.Pointer);
917 type_payload.* = .{ .pointee_type = array_ptr.ty.elemType().elemType() };917 type_payload.* = .{
918 .base = .{ .tag = .single_const_pointer },
919 .pointee_type = array_ptr.ty.elemType().elemType(),
920 };
918921
919 return mod.constInst(scope, inst.base.src, .{922 return mod.constInst(scope, inst.base.src, .{
920 .ty = Type.initPayload(&type_payload.base),923 .ty = Type.initPayload(&type_payload.base),
...@@ -1279,13 +1282,13 @@ fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerErr...@@ -1279,13 +1282,13 @@ fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerErr
12791282
1280fn analyzeInstSingleConstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {1283fn analyzeInstSingleConstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1281 const elem_type = try resolveType(mod, scope, inst.positionals.operand);1284 const elem_type = try resolveType(mod, scope, inst.positionals.operand);
1282 const ty = try mod.singleConstPtrType(scope, inst.base.src, elem_type);1285 const ty = try mod.singlePtrType(scope, inst.base.src, false, elem_type);
1283 return mod.constType(scope, inst.base.src, ty);1286 return mod.constType(scope, inst.base.src, ty);
1284}1287}
12851288
1286fn analyzeInstSingleMutPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {1289fn analyzeInstSingleMutPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1287 const elem_type = try resolveType(mod, scope, inst.positionals.operand);1290 const elem_type = try resolveType(mod, scope, inst.positionals.operand);
1288 const ty = try mod.singleMutPtrType(scope, inst.base.src, elem_type);1291 const ty = try mod.singlePtrType(scope, inst.base.src, true, elem_type);
1289 return mod.constType(scope, inst.base.src, ty);1292 return mod.constType(scope, inst.base.src, ty);
1290}1293}
12911294