| 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 | 1386 | return true; |
| 1387 | 1387 | }, |
| 1388 | 1388 | .Optional => { |
| 1389 | if (ty.isPtrLikeOptional()) return false; | |
| 1389 | if (ty.optionalReprIsPayload()) return false; | |
| 1390 | 1390 | var buf: Type.Payload.ElemType = undefined; |
| 1391 | 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 | 1832 | if (!pl_ty.hasRuntimeBitsIgnoreComptime()) { |
| 1833 | 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 | 1839 | const len = @intCast(u32, ty.abiSize(self.target)); |
| 1837 | 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 | 2201 | const parent_ptr = try self.lowerParentPtr(payload_ptr.container_ptr, payload_ptr.container_ty); |
| 2199 | 2202 | var buf: Type.Payload.ElemType = undefined; |
| 2200 | 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 | 2205 | return parent_ptr; |
| 2203 | 2206 | } |
| 2204 | 2207 | |
| ... | ... | @@ -2353,7 +2356,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 2353 | 2356 | const err_val = if (!is_pl) val else Value.initTag(.zero); |
| 2354 | 2357 | return self.lowerConstant(err_val, error_type); |
| 2355 | 2358 | }, |
| 2356 | .Optional => if (ty.isPtrLikeOptional()) { | |
| 2359 | .Optional => if (ty.optionalReprIsPayload()) { | |
| 2357 | 2360 | var buf: Type.Payload.ElemType = undefined; |
| 2358 | 2361 | const pl_ty = ty.optionalChild(&buf); |
| 2359 | 2362 | if (val.castTag(.opt_payload)) |payload| { |
| ... | ... | @@ -2392,7 +2395,7 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!WValue { |
| 2392 | 2395 | .Optional => { |
| 2393 | 2396 | var buf: Type.Payload.ElemType = undefined; |
| 2394 | 2397 | const pl_ty = ty.optionalChild(&buf); |
| 2395 | if (ty.isPtrLikeOptional()) { | |
| 2398 | if (ty.optionalReprIsPayload()) { | |
| 2396 | 2399 | return self.emitUndefined(pl_ty); |
| 2397 | 2400 | } |
| 2398 | 2401 | return WValue{ .imm32 = 0xaaaaaaaa }; |
| ... | ... | @@ -2542,7 +2545,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner |
| 2542 | 2545 | } |
| 2543 | 2546 | |
| 2544 | 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 | 2549 | var buf: Type.Payload.ElemType = undefined; |
| 2547 | 2550 | const payload_ty = ty.optionalChild(&buf); |
| 2548 | 2551 | if (payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| ... | ... | @@ -3120,7 +3123,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: en |
| 3120 | 3123 | |
| 3121 | 3124 | fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue { |
| 3122 | 3125 | try self.emitWValue(operand); |
| 3123 | if (!optional_ty.isPtrLikeOptional()) { | |
| 3126 | if (!optional_ty.optionalReprIsPayload()) { | |
| 3124 | 3127 | var buf: Type.Payload.ElemType = undefined; |
| 3125 | 3128 | const payload_ty = optional_ty.optionalChild(&buf); |
| 3126 | 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 | 3149 | const opt_ty = self.air.typeOf(ty_op.operand); |
| 3147 | 3150 | const payload_ty = self.air.typeOfIndex(inst); |
| 3148 | 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 | 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 | 3169 | |
| 3167 | 3170 | var buf: Type.Payload.ElemType = undefined; |
| 3168 | 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 | 3173 | return operand; |
| 3171 | 3174 | } |
| 3172 | 3175 | |
| ... | ... | @@ -3184,7 +3187,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue |
| 3184 | 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 | 3191 | return operand; |
| 3189 | 3192 | } |
| 3190 | 3193 | |
| ... | ... | @@ -3215,7 +3218,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3215 | 3218 | |
| 3216 | 3219 | const operand = try self.resolveInst(ty_op.operand); |
| 3217 | 3220 | const op_ty = self.air.typeOfIndex(inst); |
| 3218 | if (op_ty.isPtrLikeOptional()) { | |
| 3221 | if (op_ty.optionalReprIsPayload()) { | |
| 3219 | 3222 | return operand; |
| 3220 | 3223 | } |
| 3221 | 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 | 654 | return Result{ .appended = {} }; |
| 655 | 655 | } |
| 656 | 656 | |
| 657 | if (typed_value.ty.isPtrLikeOptional()) { | |
| 657 | if (typed_value.ty.optionalReprIsPayload()) { | |
| 658 | 658 | if (typed_value.val.castTag(.opt_payload)) |payload| { |
| 659 | 659 | switch (try generateSymbol(bin_file, src_loc, .{ |
| 660 | 660 | .ty = payload_type, |
src/codegen/c.zig+7-6| ... | ... | @@ -712,7 +712,7 @@ pub const DeclGen = struct { |
| 712 | 712 | .Optional => { |
| 713 | 713 | var opt_buf: Type.Payload.ElemType = undefined; |
| 714 | 714 | const payload_type = ty.optionalChild(&opt_buf); |
| 715 | if (ty.isPtrLikeOptional()) { | |
| 715 | if (ty.optionalReprIsPayload()) { | |
| 716 | 716 | return dg.renderValue(writer, payload_type, val, location); |
| 717 | 717 | } |
| 718 | 718 | if (payload_type.abiSize(target) == 0) { |
| ... | ... | @@ -1360,7 +1360,7 @@ pub const DeclGen = struct { |
| 1360 | 1360 | var opt_buf: Type.Payload.ElemType = undefined; |
| 1361 | 1361 | const child_type = t.optionalChild(&opt_buf); |
| 1362 | 1362 | |
| 1363 | if (t.isPtrLikeOptional()) { | |
| 1363 | if (t.optionalReprIsPayload()) { | |
| 1364 | 1364 | return dg.renderType(w, child_type); |
| 1365 | 1365 | } |
| 1366 | 1366 | |
| ... | ... | @@ -3161,6 +3161,8 @@ fn airIsNull( |
| 3161 | 3161 | if (ty.isPtrLikeOptional()) { |
| 3162 | 3162 | // operand is a regular pointer, test `operand !=/== NULL` |
| 3163 | 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 | 3166 | } else if (payload_type.abiSize(target) == 0) { |
| 3165 | 3167 | try writer.print("){s} {s} true;\n", .{ deref_suffix, operator }); |
| 3166 | 3168 | } else { |
| ... | ... | @@ -3183,7 +3185,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3183 | 3185 | else |
| 3184 | 3186 | operand_ty; |
| 3185 | 3187 | |
| 3186 | if (opt_ty.isPtrLikeOptional()) { | |
| 3188 | if (opt_ty.optionalReprIsPayload()) { | |
| 3187 | 3189 | // the operand is just a regular pointer, no need to do anything special. |
| 3188 | 3190 | // *?*T -> **T and ?*T -> *T are **T -> **T and *T -> *T in C |
| 3189 | 3191 | return operand; |
| ... | ... | @@ -3209,7 +3211,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3209 | 3211 | |
| 3210 | 3212 | const opt_ty = operand_ty.elemType(); |
| 3211 | 3213 | |
| 3212 | if (opt_ty.isPtrLikeOptional()) { | |
| 3214 | if (opt_ty.optionalReprIsPayload()) { | |
| 3213 | 3215 | // The payload and the optional are the same value. |
| 3214 | 3216 | // Setting to non-null will be done when the payload is set. |
| 3215 | 3217 | return operand; |
| ... | ... | @@ -3419,8 +3421,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3419 | 3421 | const operand = try f.resolveInst(ty_op.operand); |
| 3420 | 3422 | |
| 3421 | 3423 | const inst_ty = f.air.typeOfIndex(inst); |
| 3422 | if (inst_ty.isPtrLikeOptional()) { | |
| 3423 | // the operand is just a regular pointer, no need to do anything special. | |
| 3424 | if (inst_ty.optionalReprIsPayload()) { | |
| 3424 | 3425 | return operand; |
| 3425 | 3426 | } |
| 3426 | 3427 |
src/codegen/llvm.zig+23-11| ... | ... | @@ -1390,7 +1390,7 @@ pub const Object = struct { |
| 1390 | 1390 | gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_ty); |
| 1391 | 1391 | return di_ty; |
| 1392 | 1392 | } |
| 1393 | if (ty.isPtrLikeOptional()) { | |
| 1393 | if (ty.optionalReprIsPayload()) { | |
| 1394 | 1394 | const ptr_di_ty = try o.lowerDebugType(child_ty, resolve); |
| 1395 | 1395 | // The recursive call to `lowerDebugType` means we can't use `gop` anymore. |
| 1396 | 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 | 1472 | .ErrorUnion => { |
| 1473 | 1473 | const err_set_ty = ty.errorUnionSet(); |
| 1474 | 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 | 1481 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 1476 | 1482 | const err_set_di_ty = try o.lowerDebugType(err_set_ty, .full); |
| 1477 | 1483 | // The recursive call to `lowerDebugType` means we can't use `gop` anymore. |
| ... | ... | @@ -2439,7 +2445,7 @@ pub const DeclGen = struct { |
| 2439 | 2445 | return dg.context.intType(1); |
| 2440 | 2446 | } |
| 2441 | 2447 | const payload_llvm_ty = try dg.llvmType(child_ty); |
| 2442 | if (t.isPtrLikeOptional()) { | |
| 2448 | if (t.optionalReprIsPayload()) { | |
| 2443 | 2449 | return payload_llvm_ty; |
| 2444 | 2450 | } |
| 2445 | 2451 | |
| ... | ... | @@ -3058,7 +3064,7 @@ pub const DeclGen = struct { |
| 3058 | 3064 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 3059 | 3065 | return non_null_bit; |
| 3060 | 3066 | } |
| 3061 | if (tv.ty.isPtrLikeOptional()) { | |
| 3067 | if (tv.ty.optionalReprIsPayload()) { | |
| 3062 | 3068 | if (tv.val.castTag(.opt_payload)) |payload| { |
| 3063 | 3069 | return dg.genTypedValue(.{ .ty = payload_ty, .val = payload.data }); |
| 3064 | 3070 | } else if (is_pl) { |
| ... | ... | @@ -3557,7 +3563,9 @@ pub const DeclGen = struct { |
| 3557 | 3563 | const payload_ty = opt_payload_ptr.container_ty.optionalChild(&buf); |
| 3558 | 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 | 3569 | // In this case, we represent pointer to optional the same as pointer |
| 3562 | 3570 | // to the payload. |
| 3563 | 3571 | break :blk parent_llvm_ptr; |
| ... | ... | @@ -4461,7 +4469,9 @@ pub const FuncGen = struct { |
| 4461 | 4469 | .Int, .Bool, .Pointer, .ErrorSet => scalar_ty, |
| 4462 | 4470 | .Optional => blk: { |
| 4463 | 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 | 4475 | break :blk operand_ty; |
| 4466 | 4476 | } |
| 4467 | 4477 | // We need to emit instructions to check for equality/inequality |
| ... | ... | @@ -5414,7 +5424,7 @@ pub const FuncGen = struct { |
| 5414 | 5424 | const operand = try self.resolveInst(un_op); |
| 5415 | 5425 | const operand_ty = self.air.typeOf(un_op); |
| 5416 | 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 | 5428 | const optional_llvm_ty = try self.dg.llvmType(optional_ty); |
| 5419 | 5429 | const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand; |
| 5420 | 5430 | return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), ""); |
| ... | ... | @@ -5499,7 +5509,7 @@ pub const FuncGen = struct { |
| 5499 | 5509 | const res_ptr_ty = try self.dg.llvmType(result_ty); |
| 5500 | 5510 | return self.builder.buildBitCast(operand, res_ptr_ty, ""); |
| 5501 | 5511 | } |
| 5502 | if (optional_ty.isPtrLikeOptional()) { | |
| 5512 | if (optional_ty.optionalReprIsPayload()) { | |
| 5503 | 5513 | // The payload and the optional are the same value. |
| 5504 | 5514 | return operand; |
| 5505 | 5515 | } |
| ... | ... | @@ -5527,7 +5537,7 @@ pub const FuncGen = struct { |
| 5527 | 5537 | const res_ptr_ty = try self.dg.llvmType(result_ty); |
| 5528 | 5538 | return self.builder.buildBitCast(operand, res_ptr_ty, ""); |
| 5529 | 5539 | } |
| 5530 | if (optional_ty.isPtrLikeOptional()) { | |
| 5540 | if (optional_ty.optionalReprIsPayload()) { | |
| 5531 | 5541 | // The payload and the optional are the same value. |
| 5532 | 5542 | // Setting to non-null will be done when the payload is set. |
| 5533 | 5543 | return operand; |
| ... | ... | @@ -5561,7 +5571,7 @@ pub const FuncGen = struct { |
| 5561 | 5571 | const payload_ty = self.air.typeOfIndex(inst); |
| 5562 | 5572 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return null; |
| 5563 | 5573 | |
| 5564 | if (optional_ty.isPtrLikeOptional()) { | |
| 5574 | if (optional_ty.optionalReprIsPayload()) { | |
| 5565 | 5575 | // Payload value is the same as the optional value. |
| 5566 | 5576 | return operand; |
| 5567 | 5577 | } |
| ... | ... | @@ -5702,7 +5712,9 @@ pub const FuncGen = struct { |
| 5702 | 5712 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return non_null_bit; |
| 5703 | 5713 | const operand = try self.resolveInst(ty_op.operand); |
| 5704 | 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 | 5718 | const llvm_optional_ty = try self.dg.llvmType(optional_ty); |
| 5707 | 5719 | if (isByRef(optional_ty)) { |
| 5708 | 5720 | const optional_ptr = self.buildAlloca(llvm_optional_ty); |
| ... | ... | @@ -7038,7 +7050,7 @@ pub const FuncGen = struct { |
| 7038 | 7050 | } |
| 7039 | 7051 | const success_bit = self.builder.buildExtractValue(result, 1, ""); |
| 7040 | 7052 | |
| 7041 | if (optional_ty.isPtrLikeOptional()) { | |
| 7053 | if (optional_ty.optionalReprIsPayload()) { | |
| 7042 | 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 | 2916 | var buf: Payload.ElemType = undefined; |
| 2917 | 2917 | const child_type = ty.optionalChild(&buf); |
| 2918 | 2918 | |
| 2919 | if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) { | |
| 2920 | return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; | |
| 2919 | switch (child_type.zigTypeTag()) { | |
| 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 | 2925 | switch (strat) { |
| ... | ... | @@ -3365,14 +3367,29 @@ pub const Type = extern union { |
| 3365 | 3367 | const child_type = ty.optionalChild(&buf); |
| 3366 | 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()) | |
| 3369 | return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; | |
| 3370 | switch (child_type.zigTypeTag()) { | |
| 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 | 3386 | // Optional types are represented as a struct with the child type as the first |
| 3372 | 3387 | // field and a boolean as the second. Since the child type's abi alignment is |
| 3373 | 3388 | // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal |
| 3374 | 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 | 3395 | .error_union => { |
| ... | ... | @@ -3901,8 +3918,39 @@ pub const Type = extern union { |
| 3901 | 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 | 3951 | /// Returns true if the type is optional and would be lowered to a single pointer |
| 3905 | 3952 | /// address value, using 0 for null. Note that this returns true for C pointers. |
| 3953 | /// See also `hasOptionalRepr`. | |
| 3906 | 3954 | pub fn isPtrLikeOptional(self: Type) bool { |
| 3907 | 3955 | switch (self.tag()) { |
| 3908 | 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 | 433 | } |
| 434 | 434 | |
| 435 | 435 | test "optional error set is the same size as error set" { |
| 436 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 437 | ||
| 438 | 436 | comptime try expect(@sizeOf(?anyerror) == @sizeOf(anyerror)); |
| 437 | comptime try expect(@alignOf(?anyerror) == @alignOf(anyerror)); | |
| 439 | 438 | const S = struct { |
| 440 | 439 | fn returnsOptErrSet() ?anyerror { |
| 441 | 440 | return null; |