authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-23 18:48:10-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-24 15:34:52-07:00
log02e9d9b43b3b1cd9a4858a1f2bff302057dc2ee2
tree211ff30043bc817e462a6845e2bf0437a7a179af
parentc97c7f9e3bade44136f2bdf8ec4015f1b1b8303f

stage2: make `?anyerror` represented the same as `anyerror`

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 }
18351838
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 }
22042207
...@@ -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}
25432546
2544fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOperator) InnerError!WValue {2547fn 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
31203123
3121fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue {3124fn 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 than3129 // 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;
31503153
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);
31523155
...@@ -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 {
31663169
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 }
31723175
...@@ -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 }
31863189
3187 if (opt_ty.isPtrLikeOptional()) {3190 if (opt_ty.optionalReprIsPayload()) {
3188 return operand;3191 return operand;
3189 }3192 }
31903193
...@@ -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 {
32153218
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 }
656656
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);
13621362
1363 if (t.isPtrLikeOptional()) {1363 if (t.optionalReprIsPayload()) {
1364 return dg.renderType(w, child_type);1364 return dg.renderType(w, child_type);
1365 }1365 }
13661366
...@@ -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 else3185 else
3184 operand_ty;3186 operand_ty;
31853187
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 C3190 // *?*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 {
32093211
3210 const opt_ty = operand_ty.elemType();3212 const opt_ty = operand_ty.elemType();
32113213
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);
34203422
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 }
34263427
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 }
24452451
...@@ -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);
35593565
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 pointer3569 // 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/inequality4477 // 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;
55635573
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, "");
70407052
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 }
70447056
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);
29182918
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 }
29222924
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 };
33673369
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 }
33703385
3371 // Optional types are represented as a struct with the child type as the first3386 // 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 is3387 // 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 equal3388 // 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 },
33773394
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 }
39033920
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 pointer3951 /// 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}
434434
435test "optional error set is the same size as error set" {435test "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;