| author | |
| committer | |
| log | 02e9d9b43b3b1cd9a4858a1f2bff302057dc2ee2 |
| tree | 211ff30043bc817e462a6845e2bf0437a7a179af |
| parent | c97c7f9e3bade44136f2bdf8ec4015f1b1b8303f |
I was able to get the backend implementation working on LLVM and the C
backend, but I'm going to ask for some help on the other backends.6 files changed, 98 insertions(+), 35 deletions(-)
src/arch/wasm/CodeGen.zig+13-10| ... | @@ -1386,7 +1386,7 @@ fn isByRef(ty: Type, target: std.Target) bool { | ... | @@ -1386,7 +1386,7 @@ fn isByRef(ty: Type, target: std.Target) bool { |
| 1386 | return true; | 1386 | return true; |
| 1387 | }, | 1387 | }, |
| 1388 | .Optional => { | 1388 | .Optional => { |
| 1389 | if (ty.isPtrLikeOptional()) return false; | 1389 | if (ty.optionalReprIsPayload()) return false; |
| 1390 | var buf: Type.Payload.ElemType = undefined; | 1390 | var buf: Type.Payload.ElemType = undefined; |
| 1391 | return ty.optionalChild(&buf).hasRuntimeBitsIgnoreComptime(); | 1391 | return ty.optionalChild(&buf).hasRuntimeBitsIgnoreComptime(); |
| 1392 | }, | 1392 | }, |
| ... | @@ -1832,6 +1832,9 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1832,6 +1832,9 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1832 | if (!pl_ty.hasRuntimeBitsIgnoreComptime()) { | 1832 | if (!pl_ty.hasRuntimeBitsIgnoreComptime()) { |
| 1833 | return self.store(lhs, rhs, Type.u8, 0); | 1833 | return self.store(lhs, rhs, Type.u8, 0); |
| 1834 | } | 1834 | } |
| 1835 | if (pl_ty.zigTypeTag() == .ErrorSet) { | ||
| 1836 | return self.store(lhs, rhs, Type.anyerror, 0); | ||
| 1837 | } | ||
| 1835 | 1838 | ||
| 1836 | const len = @intCast(u32, ty.abiSize(self.target)); | 1839 | const len = @intCast(u32, ty.abiSize(self.target)); |
| 1837 | return self.memcpy(lhs, rhs, .{ .imm32 = len }); | 1840 | return self.memcpy(lhs, rhs, .{ .imm32 = len }); |
| ... | @@ -2198,7 +2201,7 @@ fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WV | ... | @@ -2198,7 +2201,7 @@ fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WV |
| 2198 | const parent_ptr = try self.lowerParentPtr(payload_ptr.container_ptr, payload_ptr.container_ty); | 2201 | const parent_ptr = try self.lowerParentPtr(payload_ptr.container_ptr, payload_ptr.container_ty); |
| 2199 | var buf: Type.Payload.ElemType = undefined; | 2202 | var buf: Type.Payload.ElemType = undefined; |
| 2200 | const payload_ty = payload_ptr.container_ty.optionalChild(&buf); | 2203 | const payload_ty = payload_ptr.container_ty.optionalChild(&buf); |
| 2201 | if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.isPtrLikeOptional()) { | 2204 | if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.optionalReprIsPayload()) { |
| 2202 | return parent_ptr; | 2205 | return parent_ptr; |
| 2203 | } | 2206 | } |
| 2204 | 2207 | ||
| ... | @@ -2353,7 +2356,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { | ... | @@ -2353,7 +2356,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 2353 | const err_val = if (!is_pl) val else Value.initTag(.zero); | 2356 | const err_val = if (!is_pl) val else Value.initTag(.zero); |
| 2354 | return self.lowerConstant(err_val, error_type); | 2357 | return self.lowerConstant(err_val, error_type); |
| 2355 | }, | 2358 | }, |
| 2356 | .Optional => if (ty.isPtrLikeOptional()) { | 2359 | .Optional => if (ty.optionalReprIsPayload()) { |
| 2357 | var buf: Type.Payload.ElemType = undefined; | 2360 | var buf: Type.Payload.ElemType = undefined; |
| 2358 | const pl_ty = ty.optionalChild(&buf); | 2361 | const pl_ty = ty.optionalChild(&buf); |
| 2359 | if (val.castTag(.opt_payload)) |payload| { | 2362 | if (val.castTag(.opt_payload)) |payload| { |
| ... | @@ -2392,7 +2395,7 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!WValue { | ... | @@ -2392,7 +2395,7 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!WValue { |
| 2392 | .Optional => { | 2395 | .Optional => { |
| 2393 | var buf: Type.Payload.ElemType = undefined; | 2396 | var buf: Type.Payload.ElemType = undefined; |
| 2394 | const pl_ty = ty.optionalChild(&buf); | 2397 | const pl_ty = ty.optionalChild(&buf); |
| 2395 | if (ty.isPtrLikeOptional()) { | 2398 | if (ty.optionalReprIsPayload()) { |
| 2396 | return self.emitUndefined(pl_ty); | 2399 | return self.emitUndefined(pl_ty); |
| 2397 | } | 2400 | } |
| 2398 | return WValue{ .imm32 = 0xaaaaaaaa }; | 2401 | return WValue{ .imm32 = 0xaaaaaaaa }; |
| ... | @@ -2542,7 +2545,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner | ... | @@ -2542,7 +2545,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner |
| 2542 | } | 2545 | } |
| 2543 | 2546 | ||
| 2544 | fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOperator) InnerError!WValue { | 2547 | fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOperator) InnerError!WValue { |
| 2545 | if (ty.zigTypeTag() == .Optional and !ty.isPtrLikeOptional()) { | 2548 | if (ty.zigTypeTag() == .Optional and !ty.optionalReprIsPayload()) { |
| 2546 | var buf: Type.Payload.ElemType = undefined; | 2549 | var buf: Type.Payload.ElemType = undefined; |
| 2547 | const payload_ty = ty.optionalChild(&buf); | 2550 | const payload_ty = ty.optionalChild(&buf); |
| 2548 | if (payload_ty.hasRuntimeBitsIgnoreComptime()) { | 2551 | if (payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| ... | @@ -3120,7 +3123,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: en | ... | @@ -3120,7 +3123,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: en |
| 3120 | 3123 | ||
| 3121 | fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue { | 3124 | fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue { |
| 3122 | try self.emitWValue(operand); | 3125 | try self.emitWValue(operand); |
| 3123 | if (!optional_ty.isPtrLikeOptional()) { | 3126 | if (!optional_ty.optionalReprIsPayload()) { |
| 3124 | var buf: Type.Payload.ElemType = undefined; | 3127 | var buf: Type.Payload.ElemType = undefined; |
| 3125 | const payload_ty = optional_ty.optionalChild(&buf); | 3128 | const payload_ty = optional_ty.optionalChild(&buf); |
| 3126 | // When payload is zero-bits, we can treat operand as a value, rather than | 3129 | // When payload is zero-bits, we can treat operand as a value, rather than |
| ... | @@ -3146,7 +3149,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -3146,7 +3149,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3146 | const opt_ty = self.air.typeOf(ty_op.operand); | 3149 | const opt_ty = self.air.typeOf(ty_op.operand); |
| 3147 | const payload_ty = self.air.typeOfIndex(inst); | 3150 | const payload_ty = self.air.typeOfIndex(inst); |
| 3148 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} }; | 3151 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} }; |
| 3149 | if (opt_ty.isPtrLikeOptional()) return operand; | 3152 | if (opt_ty.optionalReprIsPayload()) return operand; |
| 3150 | 3153 | ||
| 3151 | const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target); | 3154 | const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target); |
| 3152 | 3155 | ||
| ... | @@ -3166,7 +3169,7 @@ fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -3166,7 +3169,7 @@ fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3166 | 3169 | ||
| 3167 | var buf: Type.Payload.ElemType = undefined; | 3170 | var buf: Type.Payload.ElemType = undefined; |
| 3168 | const payload_ty = opt_ty.optionalChild(&buf); | 3171 | const payload_ty = opt_ty.optionalChild(&buf); |
| 3169 | if (!payload_ty.hasRuntimeBitsIgnoreComptime() or opt_ty.isPtrLikeOptional()) { | 3172 | if (!payload_ty.hasRuntimeBitsIgnoreComptime() or opt_ty.optionalReprIsPayload()) { |
| 3170 | return operand; | 3173 | return operand; |
| 3171 | } | 3174 | } |
| 3172 | 3175 | ||
| ... | @@ -3184,7 +3187,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue | ... | @@ -3184,7 +3187,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue |
| 3184 | return self.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty.fmtDebug()}); | 3187 | return self.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty.fmtDebug()}); |
| 3185 | } | 3188 | } |
| 3186 | 3189 | ||
| 3187 | if (opt_ty.isPtrLikeOptional()) { | 3190 | if (opt_ty.optionalReprIsPayload()) { |
| 3188 | return operand; | 3191 | return operand; |
| 3189 | } | 3192 | } |
| 3190 | 3193 | ||
| ... | @@ -3215,7 +3218,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -3215,7 +3218,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3215 | 3218 | ||
| 3216 | const operand = try self.resolveInst(ty_op.operand); | 3219 | const operand = try self.resolveInst(ty_op.operand); |
| 3217 | const op_ty = self.air.typeOfIndex(inst); | 3220 | const op_ty = self.air.typeOfIndex(inst); |
| 3218 | if (op_ty.isPtrLikeOptional()) { | 3221 | if (op_ty.optionalReprIsPayload()) { |
| 3219 | return operand; | 3222 | return operand; |
| 3220 | } | 3223 | } |
| 3221 | const offset = std.math.cast(u32, op_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch { | 3224 | const offset = std.math.cast(u32, op_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch { |
src/codegen.zig+1-1| ... | @@ -654,7 +654,7 @@ pub fn generateSymbol( | ... | @@ -654,7 +654,7 @@ pub fn generateSymbol( |
| 654 | return Result{ .appended = {} }; | 654 | return Result{ .appended = {} }; |
| 655 | } | 655 | } |
| 656 | 656 | ||
| 657 | if (typed_value.ty.isPtrLikeOptional()) { | 657 | if (typed_value.ty.optionalReprIsPayload()) { |
| 658 | if (typed_value.val.castTag(.opt_payload)) |payload| { | 658 | if (typed_value.val.castTag(.opt_payload)) |payload| { |
| 659 | switch (try generateSymbol(bin_file, src_loc, .{ | 659 | switch (try generateSymbol(bin_file, src_loc, .{ |
| 660 | .ty = payload_type, | 660 | .ty = payload_type, |
src/codegen/c.zig+7-6| ... | @@ -712,7 +712,7 @@ pub const DeclGen = struct { | ... | @@ -712,7 +712,7 @@ pub const DeclGen = struct { |
| 712 | .Optional => { | 712 | .Optional => { |
| 713 | var opt_buf: Type.Payload.ElemType = undefined; | 713 | var opt_buf: Type.Payload.ElemType = undefined; |
| 714 | const payload_type = ty.optionalChild(&opt_buf); | 714 | const payload_type = ty.optionalChild(&opt_buf); |
| 715 | if (ty.isPtrLikeOptional()) { | 715 | if (ty.optionalReprIsPayload()) { |
| 716 | return dg.renderValue(writer, payload_type, val, location); | 716 | return dg.renderValue(writer, payload_type, val, location); |
| 717 | } | 717 | } |
| 718 | if (payload_type.abiSize(target) == 0) { | 718 | if (payload_type.abiSize(target) == 0) { |
| ... | @@ -1360,7 +1360,7 @@ pub const DeclGen = struct { | ... | @@ -1360,7 +1360,7 @@ pub const DeclGen = struct { |
| 1360 | var opt_buf: Type.Payload.ElemType = undefined; | 1360 | var opt_buf: Type.Payload.ElemType = undefined; |
| 1361 | const child_type = t.optionalChild(&opt_buf); | 1361 | const child_type = t.optionalChild(&opt_buf); |
| 1362 | 1362 | ||
| 1363 | if (t.isPtrLikeOptional()) { | 1363 | if (t.optionalReprIsPayload()) { |
| 1364 | return dg.renderType(w, child_type); | 1364 | return dg.renderType(w, child_type); |
| 1365 | } | 1365 | } |
| 1366 | 1366 | ||
| ... | @@ -3161,6 +3161,8 @@ fn airIsNull( | ... | @@ -3161,6 +3161,8 @@ fn airIsNull( |
| 3161 | if (ty.isPtrLikeOptional()) { | 3161 | if (ty.isPtrLikeOptional()) { |
| 3162 | // operand is a regular pointer, test `operand !=/== NULL` | 3162 | // operand is a regular pointer, test `operand !=/== NULL` |
| 3163 | try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator }); | 3163 | try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator }); |
| 3164 | } else if (payload_type.zigTypeTag() == .ErrorSet) { | ||
| 3165 | try writer.print("){s} {s} 0;\n", .{ deref_suffix, operator }); | ||
| 3164 | } else if (payload_type.abiSize(target) == 0) { | 3166 | } else if (payload_type.abiSize(target) == 0) { |
| 3165 | try writer.print("){s} {s} true;\n", .{ deref_suffix, operator }); | 3167 | try writer.print("){s} {s} true;\n", .{ deref_suffix, operator }); |
| 3166 | } else { | 3168 | } else { |
| ... | @@ -3183,7 +3185,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3183,7 +3185,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3183 | else | 3185 | else |
| 3184 | operand_ty; | 3186 | operand_ty; |
| 3185 | 3187 | ||
| 3186 | if (opt_ty.isPtrLikeOptional()) { | 3188 | if (opt_ty.optionalReprIsPayload()) { |
| 3187 | // the operand is just a regular pointer, no need to do anything special. | 3189 | // the operand is just a regular pointer, no need to do anything special. |
| 3188 | // *?*T -> **T and ?*T -> *T are **T -> **T and *T -> *T in C | 3190 | // *?*T -> **T and ?*T -> *T are **T -> **T and *T -> *T in C |
| 3189 | return operand; | 3191 | return operand; |
| ... | @@ -3209,7 +3211,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3209,7 +3211,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3209 | 3211 | ||
| 3210 | const opt_ty = operand_ty.elemType(); | 3212 | const opt_ty = operand_ty.elemType(); |
| 3211 | 3213 | ||
| 3212 | if (opt_ty.isPtrLikeOptional()) { | 3214 | if (opt_ty.optionalReprIsPayload()) { |
| 3213 | // The payload and the optional are the same value. | 3215 | // The payload and the optional are the same value. |
| 3214 | // Setting to non-null will be done when the payload is set. | 3216 | // Setting to non-null will be done when the payload is set. |
| 3215 | return operand; | 3217 | return operand; |
| ... | @@ -3419,8 +3421,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3419,8 +3421,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3419 | const operand = try f.resolveInst(ty_op.operand); | 3421 | const operand = try f.resolveInst(ty_op.operand); |
| 3420 | 3422 | ||
| 3421 | const inst_ty = f.air.typeOfIndex(inst); | 3423 | const inst_ty = f.air.typeOfIndex(inst); |
| 3422 | if (inst_ty.isPtrLikeOptional()) { | 3424 | if (inst_ty.optionalReprIsPayload()) { |
| 3423 | // the operand is just a regular pointer, no need to do anything special. | ||
| 3424 | return operand; | 3425 | return operand; |
| 3425 | } | 3426 | } |
| 3426 | 3427 |
src/codegen/llvm.zig+23-11| ... | @@ -1390,7 +1390,7 @@ pub const Object = struct { | ... | @@ -1390,7 +1390,7 @@ pub const Object = struct { |
| 1390 | gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_ty); | 1390 | gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_ty); |
| 1391 | return di_ty; | 1391 | return di_ty; |
| 1392 | } | 1392 | } |
| 1393 | if (ty.isPtrLikeOptional()) { | 1393 | if (ty.optionalReprIsPayload()) { |
| 1394 | const ptr_di_ty = try o.lowerDebugType(child_ty, resolve); | 1394 | const ptr_di_ty = try o.lowerDebugType(child_ty, resolve); |
| 1395 | // The recursive call to `lowerDebugType` means we can't use `gop` anymore. | 1395 | // The recursive call to `lowerDebugType` means we can't use `gop` anymore. |
| 1396 | try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .mod = o.module }); | 1396 | try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .mod = o.module }); |
| ... | @@ -1472,6 +1472,12 @@ pub const Object = struct { | ... | @@ -1472,6 +1472,12 @@ pub const Object = struct { |
| 1472 | .ErrorUnion => { | 1472 | .ErrorUnion => { |
| 1473 | const err_set_ty = ty.errorUnionSet(); | 1473 | const err_set_ty = ty.errorUnionSet(); |
| 1474 | const payload_ty = ty.errorUnionPayload(); | 1474 | const payload_ty = ty.errorUnionPayload(); |
| 1475 | if (err_set_ty.errorSetCardinality() == .zero) { | ||
| 1476 | const payload_di_ty = try o.lowerDebugType(payload_ty, .full); | ||
| 1477 | // The recursive call to `lowerDebugType` means we can't use `gop` anymore. | ||
| 1478 | try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(payload_di_ty), .{ .mod = o.module }); | ||
| 1479 | return payload_di_ty; | ||
| 1480 | } | ||
| 1475 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { | 1481 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 1476 | const err_set_di_ty = try o.lowerDebugType(err_set_ty, .full); | 1482 | const err_set_di_ty = try o.lowerDebugType(err_set_ty, .full); |
| 1477 | // The recursive call to `lowerDebugType` means we can't use `gop` anymore. | 1483 | // The recursive call to `lowerDebugType` means we can't use `gop` anymore. |
| ... | @@ -2439,7 +2445,7 @@ pub const DeclGen = struct { | ... | @@ -2439,7 +2445,7 @@ pub const DeclGen = struct { |
| 2439 | return dg.context.intType(1); | 2445 | return dg.context.intType(1); |
| 2440 | } | 2446 | } |
| 2441 | const payload_llvm_ty = try dg.llvmType(child_ty); | 2447 | const payload_llvm_ty = try dg.llvmType(child_ty); |
| 2442 | if (t.isPtrLikeOptional()) { | 2448 | if (t.optionalReprIsPayload()) { |
| 2443 | return payload_llvm_ty; | 2449 | return payload_llvm_ty; |
| 2444 | } | 2450 | } |
| 2445 | 2451 | ||
| ... | @@ -3058,7 +3064,7 @@ pub const DeclGen = struct { | ... | @@ -3058,7 +3064,7 @@ pub const DeclGen = struct { |
| 3058 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { | 3064 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 3059 | return non_null_bit; | 3065 | return non_null_bit; |
| 3060 | } | 3066 | } |
| 3061 | if (tv.ty.isPtrLikeOptional()) { | 3067 | if (tv.ty.optionalReprIsPayload()) { |
| 3062 | if (tv.val.castTag(.opt_payload)) |payload| { | 3068 | if (tv.val.castTag(.opt_payload)) |payload| { |
| 3063 | return dg.genTypedValue(.{ .ty = payload_ty, .val = payload.data }); | 3069 | return dg.genTypedValue(.{ .ty = payload_ty, .val = payload.data }); |
| 3064 | } else if (is_pl) { | 3070 | } else if (is_pl) { |
| ... | @@ -3557,7 +3563,9 @@ pub const DeclGen = struct { | ... | @@ -3557,7 +3563,9 @@ pub const DeclGen = struct { |
| 3557 | const payload_ty = opt_payload_ptr.container_ty.optionalChild(&buf); | 3563 | const payload_ty = opt_payload_ptr.container_ty.optionalChild(&buf); |
| 3558 | bitcast_needed = !payload_ty.eql(ptr_child_ty, dg.module); | 3564 | bitcast_needed = !payload_ty.eql(ptr_child_ty, dg.module); |
| 3559 | 3565 | ||
| 3560 | if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.isPtrLikeOptional()) { | 3566 | if (!payload_ty.hasRuntimeBitsIgnoreComptime() or |
| 3567 | payload_ty.optionalReprIsPayload()) | ||
| 3568 | { | ||
| 3561 | // In this case, we represent pointer to optional the same as pointer | 3569 | // In this case, we represent pointer to optional the same as pointer |
| 3562 | // to the payload. | 3570 | // to the payload. |
| 3563 | break :blk parent_llvm_ptr; | 3571 | break :blk parent_llvm_ptr; |
| ... | @@ -4461,7 +4469,9 @@ pub const FuncGen = struct { | ... | @@ -4461,7 +4469,9 @@ pub const FuncGen = struct { |
| 4461 | .Int, .Bool, .Pointer, .ErrorSet => scalar_ty, | 4469 | .Int, .Bool, .Pointer, .ErrorSet => scalar_ty, |
| 4462 | .Optional => blk: { | 4470 | .Optional => blk: { |
| 4463 | const payload_ty = operand_ty.optionalChild(&opt_buffer); | 4471 | const payload_ty = operand_ty.optionalChild(&opt_buffer); |
| 4464 | if (!payload_ty.hasRuntimeBitsIgnoreComptime() or operand_ty.isPtrLikeOptional()) { | 4472 | if (!payload_ty.hasRuntimeBitsIgnoreComptime() or |
| 4473 | operand_ty.optionalReprIsPayload()) | ||
| 4474 | { | ||
| 4465 | break :blk operand_ty; | 4475 | break :blk operand_ty; |
| 4466 | } | 4476 | } |
| 4467 | // We need to emit instructions to check for equality/inequality | 4477 | // We need to emit instructions to check for equality/inequality |
| ... | @@ -5414,7 +5424,7 @@ pub const FuncGen = struct { | ... | @@ -5414,7 +5424,7 @@ pub const FuncGen = struct { |
| 5414 | const operand = try self.resolveInst(un_op); | 5424 | const operand = try self.resolveInst(un_op); |
| 5415 | const operand_ty = self.air.typeOf(un_op); | 5425 | const operand_ty = self.air.typeOf(un_op); |
| 5416 | const optional_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; | 5426 | const optional_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; |
| 5417 | if (optional_ty.isPtrLikeOptional()) { | 5427 | if (optional_ty.optionalReprIsPayload()) { |
| 5418 | const optional_llvm_ty = try self.dg.llvmType(optional_ty); | 5428 | const optional_llvm_ty = try self.dg.llvmType(optional_ty); |
| 5419 | const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand; | 5429 | const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand; |
| 5420 | return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), ""); | 5430 | return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), ""); |
| ... | @@ -5499,7 +5509,7 @@ pub const FuncGen = struct { | ... | @@ -5499,7 +5509,7 @@ pub const FuncGen = struct { |
| 5499 | const res_ptr_ty = try self.dg.llvmType(result_ty); | 5509 | const res_ptr_ty = try self.dg.llvmType(result_ty); |
| 5500 | return self.builder.buildBitCast(operand, res_ptr_ty, ""); | 5510 | return self.builder.buildBitCast(operand, res_ptr_ty, ""); |
| 5501 | } | 5511 | } |
| 5502 | if (optional_ty.isPtrLikeOptional()) { | 5512 | if (optional_ty.optionalReprIsPayload()) { |
| 5503 | // The payload and the optional are the same value. | 5513 | // The payload and the optional are the same value. |
| 5504 | return operand; | 5514 | return operand; |
| 5505 | } | 5515 | } |
| ... | @@ -5527,7 +5537,7 @@ pub const FuncGen = struct { | ... | @@ -5527,7 +5537,7 @@ pub const FuncGen = struct { |
| 5527 | const res_ptr_ty = try self.dg.llvmType(result_ty); | 5537 | const res_ptr_ty = try self.dg.llvmType(result_ty); |
| 5528 | return self.builder.buildBitCast(operand, res_ptr_ty, ""); | 5538 | return self.builder.buildBitCast(operand, res_ptr_ty, ""); |
| 5529 | } | 5539 | } |
| 5530 | if (optional_ty.isPtrLikeOptional()) { | 5540 | if (optional_ty.optionalReprIsPayload()) { |
| 5531 | // The payload and the optional are the same value. | 5541 | // The payload and the optional are the same value. |
| 5532 | // Setting to non-null will be done when the payload is set. | 5542 | // Setting to non-null will be done when the payload is set. |
| 5533 | return operand; | 5543 | return operand; |
| ... | @@ -5561,7 +5571,7 @@ pub const FuncGen = struct { | ... | @@ -5561,7 +5571,7 @@ pub const FuncGen = struct { |
| 5561 | const payload_ty = self.air.typeOfIndex(inst); | 5571 | const payload_ty = self.air.typeOfIndex(inst); |
| 5562 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return null; | 5572 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return null; |
| 5563 | 5573 | ||
| 5564 | if (optional_ty.isPtrLikeOptional()) { | 5574 | if (optional_ty.optionalReprIsPayload()) { |
| 5565 | // Payload value is the same as the optional value. | 5575 | // Payload value is the same as the optional value. |
| 5566 | return operand; | 5576 | return operand; |
| 5567 | } | 5577 | } |
| ... | @@ -5702,7 +5712,9 @@ pub const FuncGen = struct { | ... | @@ -5702,7 +5712,9 @@ pub const FuncGen = struct { |
| 5702 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return non_null_bit; | 5712 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return non_null_bit; |
| 5703 | const operand = try self.resolveInst(ty_op.operand); | 5713 | const operand = try self.resolveInst(ty_op.operand); |
| 5704 | const optional_ty = self.air.typeOfIndex(inst); | 5714 | const optional_ty = self.air.typeOfIndex(inst); |
| 5705 | if (optional_ty.isPtrLikeOptional()) return operand; | 5715 | if (optional_ty.optionalReprIsPayload()) { |
| 5716 | return operand; | ||
| 5717 | } | ||
| 5706 | const llvm_optional_ty = try self.dg.llvmType(optional_ty); | 5718 | const llvm_optional_ty = try self.dg.llvmType(optional_ty); |
| 5707 | if (isByRef(optional_ty)) { | 5719 | if (isByRef(optional_ty)) { |
| 5708 | const optional_ptr = self.buildAlloca(llvm_optional_ty); | 5720 | const optional_ptr = self.buildAlloca(llvm_optional_ty); |
| ... | @@ -7038,7 +7050,7 @@ pub const FuncGen = struct { | ... | @@ -7038,7 +7050,7 @@ pub const FuncGen = struct { |
| 7038 | } | 7050 | } |
| 7039 | const success_bit = self.builder.buildExtractValue(result, 1, ""); | 7051 | const success_bit = self.builder.buildExtractValue(result, 1, ""); |
| 7040 | 7052 | ||
| 7041 | if (optional_ty.isPtrLikeOptional()) { | 7053 | if (optional_ty.optionalReprIsPayload()) { |
| 7042 | return self.builder.buildSelect(success_bit, payload.typeOf().constNull(), payload, ""); | 7054 | return self.builder.buildSelect(success_bit, payload.typeOf().constNull(), payload, ""); |
| 7043 | } | 7055 | } |
| 7044 | 7056 |
src/type.zig+53-5| ... | @@ -2916,8 +2916,10 @@ pub const Type = extern union { | ... | @@ -2916,8 +2916,10 @@ pub const Type = extern union { |
| 2916 | var buf: Payload.ElemType = undefined; | 2916 | var buf: Payload.ElemType = undefined; |
| 2917 | const child_type = ty.optionalChild(&buf); | 2917 | const child_type = ty.optionalChild(&buf); |
| 2918 | 2918 | ||
| 2919 | if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) { | 2919 | switch (child_type.zigTypeTag()) { |
| 2920 | return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; | 2920 | .Pointer => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, |
| 2921 | .ErrorSet => return abiAlignmentAdvanced(Type.anyerror, target, strat), | ||
| 2922 | else => {}, | ||
| 2921 | } | 2923 | } |
| 2922 | 2924 | ||
| 2923 | switch (strat) { | 2925 | switch (strat) { |
| ... | @@ -3365,14 +3367,29 @@ pub const Type = extern union { | ... | @@ -3365,14 +3367,29 @@ pub const Type = extern union { |
| 3365 | const child_type = ty.optionalChild(&buf); | 3367 | const child_type = ty.optionalChild(&buf); |
| 3366 | if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 }; | 3368 | if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 }; |
| 3367 | 3369 | ||
| 3368 | if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr() and !child_type.isSlice()) | 3370 | switch (child_type.zigTypeTag()) { |
| 3369 | return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; | 3371 | .Pointer => { |
| 3372 | const ptr_info = child_type.ptrInfo().data; | ||
| 3373 | const has_null = switch (ptr_info.size) { | ||
| 3374 | .Slice, .C => true, | ||
| 3375 | else => ptr_info.@"allowzero", | ||
| 3376 | }; | ||
| 3377 | if (!has_null) { | ||
| 3378 | const ptr_size_bytes = @divExact(target.cpu.arch.ptrBitWidth(), 8); | ||
| 3379 | return AbiSizeAdvanced{ .scalar = ptr_size_bytes }; | ||
| 3380 | } | ||
| 3381 | }, | ||
| 3382 | .ErrorSet => return abiSizeAdvanced(Type.anyerror, target, strat), | ||
| 3383 | else => {}, | ||
| 3384 | } | ||
| 3370 | 3385 | ||
| 3371 | // Optional types are represented as a struct with the child type as the first | 3386 | // Optional types are represented as a struct with the child type as the first |
| 3372 | // field and a boolean as the second. Since the child type's abi alignment is | 3387 | // field and a boolean as the second. Since the child type's abi alignment is |
| 3373 | // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal | 3388 | // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal |
| 3374 | // to the child type's ABI alignment. | 3389 | // to the child type's ABI alignment. |
| 3375 | return AbiSizeAdvanced{ .scalar = child_type.abiAlignment(target) + child_type.abiSize(target) }; | 3390 | return AbiSizeAdvanced{ |
| 3391 | .scalar = child_type.abiAlignment(target) + child_type.abiSize(target), | ||
| 3392 | }; | ||
| 3376 | }, | 3393 | }, |
| 3377 | 3394 | ||
| 3378 | .error_union => { | 3395 | .error_union => { |
| ... | @@ -3901,8 +3918,39 @@ pub const Type = extern union { | ... | @@ -3901,8 +3918,39 @@ pub const Type = extern union { |
| 3901 | return ty.ptrInfo().data.@"allowzero"; | 3918 | return ty.ptrInfo().data.@"allowzero"; |
| 3902 | } | 3919 | } |
| 3903 | 3920 | ||
| 3921 | /// See also `isPtrLikeOptional`. | ||
| 3922 | pub fn optionalReprIsPayload(ty: Type) bool { | ||
| 3923 | switch (ty.tag()) { | ||
| 3924 | .optional_single_const_pointer, | ||
| 3925 | .optional_single_mut_pointer, | ||
| 3926 | .c_const_pointer, | ||
| 3927 | .c_mut_pointer, | ||
| 3928 | => return true, | ||
| 3929 | |||
| 3930 | .optional => { | ||
| 3931 | const child_ty = ty.castTag(.optional).?.data; | ||
| 3932 | switch (child_ty.zigTypeTag()) { | ||
| 3933 | .Pointer => { | ||
| 3934 | const info = child_ty.ptrInfo().data; | ||
| 3935 | switch (info.size) { | ||
| 3936 | .Slice, .C => return false, | ||
| 3937 | .Many, .One => return !info.@"allowzero", | ||
| 3938 | } | ||
| 3939 | }, | ||
| 3940 | .ErrorSet => return true, | ||
| 3941 | else => return false, | ||
| 3942 | } | ||
| 3943 | }, | ||
| 3944 | |||
| 3945 | .pointer => return ty.castTag(.pointer).?.data.size == .C, | ||
| 3946 | |||
| 3947 | else => return false, | ||
| 3948 | } | ||
| 3949 | } | ||
| 3950 | |||
| 3904 | /// Returns true if the type is optional and would be lowered to a single pointer | 3951 | /// Returns true if the type is optional and would be lowered to a single pointer |
| 3905 | /// address value, using 0 for null. Note that this returns true for C pointers. | 3952 | /// address value, using 0 for null. Note that this returns true for C pointers. |
| 3953 | /// See also `hasOptionalRepr`. | ||
| 3906 | pub fn isPtrLikeOptional(self: Type) bool { | 3954 | pub fn isPtrLikeOptional(self: Type) bool { |
| 3907 | switch (self.tag()) { | 3955 | switch (self.tag()) { |
| 3908 | .optional_single_const_pointer, | 3956 | .optional_single_const_pointer, |
test/behavior/error.zig+1-2| ... | @@ -433,9 +433,8 @@ test "return function call to error set from error union function" { | ... | @@ -433,9 +433,8 @@ test "return function call to error set from error union function" { |
| 433 | } | 433 | } |
| 434 | 434 | ||
| 435 | test "optional error set is the same size as error set" { | 435 | test "optional error set is the same size as error set" { |
| 436 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 437 | |||
| 438 | comptime try expect(@sizeOf(?anyerror) == @sizeOf(anyerror)); | 436 | comptime try expect(@sizeOf(?anyerror) == @sizeOf(anyerror)); |
| 437 | comptime try expect(@alignOf(?anyerror) == @alignOf(anyerror)); | ||
| 439 | const S = struct { | 438 | const S = struct { |
| 440 | fn returnsOptErrSet() ?anyerror { | 439 | fn returnsOptErrSet() ?anyerror { |
| 441 | return null; | 440 | return null; |