diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-05-23 18:48:10 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2022-05-24 15:34:52 -0700 |
| commit | 02e9d9b43b3b1cd9a4858a1f2bff302057dc2ee2 (patch) | |
| tree | 211ff30043bc817e462a6845e2bf0437a7a179af /src | |
| parent | c97c7f9e3bade44136f2bdf8ec4015f1b1b8303f (diff) | |
| download | zig-02e9d9b43b3b1cd9a4858a1f2bff302057dc2ee2.tar.gz zig-02e9d9b43b3b1cd9a4858a1f2bff302057dc2ee2.zip | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 23 | ||||
| -rw-r--r-- | src/codegen.zig | 2 | ||||
| -rw-r--r-- | src/codegen/c.zig | 13 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 34 | ||||
| -rw-r--r-- | src/type.zig | 58 |
5 files changed, 97 insertions, 33 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index b74651859c..6d0f3a9d23 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1386,7 +1386,7 @@ fn isByRef(ty: Type, target: std.Target) bool { return true; }, .Optional => { - if (ty.isPtrLikeOptional()) return false; + if (ty.optionalReprIsPayload()) return false; var buf: Type.Payload.ElemType = undefined; return ty.optionalChild(&buf).hasRuntimeBitsIgnoreComptime(); }, @@ -1832,6 +1832,9 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro if (!pl_ty.hasRuntimeBitsIgnoreComptime()) { return self.store(lhs, rhs, Type.u8, 0); } + if (pl_ty.zigTypeTag() == .ErrorSet) { + return self.store(lhs, rhs, Type.anyerror, 0); + } const len = @intCast(u32, ty.abiSize(self.target)); return self.memcpy(lhs, rhs, .{ .imm32 = len }); @@ -2198,7 +2201,7 @@ fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WV const parent_ptr = try self.lowerParentPtr(payload_ptr.container_ptr, payload_ptr.container_ty); var buf: Type.Payload.ElemType = undefined; const payload_ty = payload_ptr.container_ty.optionalChild(&buf); - if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.isPtrLikeOptional()) { + if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.optionalReprIsPayload()) { return parent_ptr; } @@ -2353,7 +2356,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { const err_val = if (!is_pl) val else Value.initTag(.zero); return self.lowerConstant(err_val, error_type); }, - .Optional => if (ty.isPtrLikeOptional()) { + .Optional => if (ty.optionalReprIsPayload()) { var buf: Type.Payload.ElemType = undefined; const pl_ty = ty.optionalChild(&buf); if (val.castTag(.opt_payload)) |payload| { @@ -2392,7 +2395,7 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!WValue { .Optional => { var buf: Type.Payload.ElemType = undefined; const pl_ty = ty.optionalChild(&buf); - if (ty.isPtrLikeOptional()) { + if (ty.optionalReprIsPayload()) { return self.emitUndefined(pl_ty); } return WValue{ .imm32 = 0xaaaaaaaa }; @@ -2542,7 +2545,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner } fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOperator) InnerError!WValue { - if (ty.zigTypeTag() == .Optional and !ty.isPtrLikeOptional()) { + if (ty.zigTypeTag() == .Optional and !ty.optionalReprIsPayload()) { var buf: Type.Payload.ElemType = undefined; const payload_ty = ty.optionalChild(&buf); if (payload_ty.hasRuntimeBitsIgnoreComptime()) { @@ -3120,7 +3123,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: en fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue { try self.emitWValue(operand); - if (!optional_ty.isPtrLikeOptional()) { + if (!optional_ty.optionalReprIsPayload()) { var buf: Type.Payload.ElemType = undefined; const payload_ty = optional_ty.optionalChild(&buf); // 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 { const opt_ty = self.air.typeOf(ty_op.operand); const payload_ty = self.air.typeOfIndex(inst); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} }; - if (opt_ty.isPtrLikeOptional()) return operand; + if (opt_ty.optionalReprIsPayload()) return operand; const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target); @@ -3166,7 +3169,7 @@ fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { var buf: Type.Payload.ElemType = undefined; const payload_ty = opt_ty.optionalChild(&buf); - if (!payload_ty.hasRuntimeBitsIgnoreComptime() or opt_ty.isPtrLikeOptional()) { + if (!payload_ty.hasRuntimeBitsIgnoreComptime() or opt_ty.optionalReprIsPayload()) { return operand; } @@ -3184,7 +3187,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue return self.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty.fmtDebug()}); } - if (opt_ty.isPtrLikeOptional()) { + if (opt_ty.optionalReprIsPayload()) { return operand; } @@ -3215,7 +3218,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const operand = try self.resolveInst(ty_op.operand); const op_ty = self.air.typeOfIndex(inst); - if (op_ty.isPtrLikeOptional()) { + if (op_ty.optionalReprIsPayload()) { return operand; } const offset = std.math.cast(u32, op_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch { diff --git a/src/codegen.zig b/src/codegen.zig index 0f411dc481..eea8095a62 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -654,7 +654,7 @@ pub fn generateSymbol( return Result{ .appended = {} }; } - if (typed_value.ty.isPtrLikeOptional()) { + if (typed_value.ty.optionalReprIsPayload()) { if (typed_value.val.castTag(.opt_payload)) |payload| { switch (try generateSymbol(bin_file, src_loc, .{ .ty = payload_type, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 63082d46be..1b6708c1cf 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -712,7 +712,7 @@ pub const DeclGen = struct { .Optional => { var opt_buf: Type.Payload.ElemType = undefined; const payload_type = ty.optionalChild(&opt_buf); - if (ty.isPtrLikeOptional()) { + if (ty.optionalReprIsPayload()) { return dg.renderValue(writer, payload_type, val, location); } if (payload_type.abiSize(target) == 0) { @@ -1360,7 +1360,7 @@ pub const DeclGen = struct { var opt_buf: Type.Payload.ElemType = undefined; const child_type = t.optionalChild(&opt_buf); - if (t.isPtrLikeOptional()) { + if (t.optionalReprIsPayload()) { return dg.renderType(w, child_type); } @@ -3161,6 +3161,8 @@ fn airIsNull( if (ty.isPtrLikeOptional()) { // operand is a regular pointer, test `operand !=/== NULL` try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator }); + } else if (payload_type.zigTypeTag() == .ErrorSet) { + try writer.print("){s} {s} 0;\n", .{ deref_suffix, operator }); } else if (payload_type.abiSize(target) == 0) { try writer.print("){s} {s} true;\n", .{ deref_suffix, operator }); } else { @@ -3183,7 +3185,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { else operand_ty; - if (opt_ty.isPtrLikeOptional()) { + if (opt_ty.optionalReprIsPayload()) { // the operand is just a regular pointer, no need to do anything special. // *?*T -> **T and ?*T -> *T are **T -> **T and *T -> *T in C return operand; @@ -3209,7 +3211,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { const opt_ty = operand_ty.elemType(); - if (opt_ty.isPtrLikeOptional()) { + if (opt_ty.optionalReprIsPayload()) { // The payload and the optional are the same value. // Setting to non-null will be done when the payload is set. return operand; @@ -3419,8 +3421,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(ty_op.operand); const inst_ty = f.air.typeOfIndex(inst); - if (inst_ty.isPtrLikeOptional()) { - // the operand is just a regular pointer, no need to do anything special. + if (inst_ty.optionalReprIsPayload()) { return operand; } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 95d12dff3a..e76b2941c2 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1390,7 +1390,7 @@ pub const Object = struct { gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_ty); return di_ty; } - if (ty.isPtrLikeOptional()) { + if (ty.optionalReprIsPayload()) { const ptr_di_ty = try o.lowerDebugType(child_ty, resolve); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .mod = o.module }); @@ -1472,6 +1472,12 @@ pub const Object = struct { .ErrorUnion => { const err_set_ty = ty.errorUnionSet(); const payload_ty = ty.errorUnionPayload(); + if (err_set_ty.errorSetCardinality() == .zero) { + const payload_di_ty = try o.lowerDebugType(payload_ty, .full); + // The recursive call to `lowerDebugType` means we can't use `gop` anymore. + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(payload_di_ty), .{ .mod = o.module }); + return payload_di_ty; + } if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { const err_set_di_ty = try o.lowerDebugType(err_set_ty, .full); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. @@ -2439,7 +2445,7 @@ pub const DeclGen = struct { return dg.context.intType(1); } const payload_llvm_ty = try dg.llvmType(child_ty); - if (t.isPtrLikeOptional()) { + if (t.optionalReprIsPayload()) { return payload_llvm_ty; } @@ -3058,7 +3064,7 @@ pub const DeclGen = struct { if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { return non_null_bit; } - if (tv.ty.isPtrLikeOptional()) { + if (tv.ty.optionalReprIsPayload()) { if (tv.val.castTag(.opt_payload)) |payload| { return dg.genTypedValue(.{ .ty = payload_ty, .val = payload.data }); } else if (is_pl) { @@ -3557,7 +3563,9 @@ pub const DeclGen = struct { const payload_ty = opt_payload_ptr.container_ty.optionalChild(&buf); bitcast_needed = !payload_ty.eql(ptr_child_ty, dg.module); - if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.isPtrLikeOptional()) { + if (!payload_ty.hasRuntimeBitsIgnoreComptime() or + payload_ty.optionalReprIsPayload()) + { // In this case, we represent pointer to optional the same as pointer // to the payload. break :blk parent_llvm_ptr; @@ -4461,7 +4469,9 @@ pub const FuncGen = struct { .Int, .Bool, .Pointer, .ErrorSet => scalar_ty, .Optional => blk: { const payload_ty = operand_ty.optionalChild(&opt_buffer); - if (!payload_ty.hasRuntimeBitsIgnoreComptime() or operand_ty.isPtrLikeOptional()) { + if (!payload_ty.hasRuntimeBitsIgnoreComptime() or + operand_ty.optionalReprIsPayload()) + { break :blk operand_ty; } // We need to emit instructions to check for equality/inequality @@ -5414,7 +5424,7 @@ pub const FuncGen = struct { const operand = try self.resolveInst(un_op); const operand_ty = self.air.typeOf(un_op); const optional_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; - if (optional_ty.isPtrLikeOptional()) { + if (optional_ty.optionalReprIsPayload()) { const optional_llvm_ty = try self.dg.llvmType(optional_ty); const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand; return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), ""); @@ -5499,7 +5509,7 @@ pub const FuncGen = struct { const res_ptr_ty = try self.dg.llvmType(result_ty); return self.builder.buildBitCast(operand, res_ptr_ty, ""); } - if (optional_ty.isPtrLikeOptional()) { + if (optional_ty.optionalReprIsPayload()) { // The payload and the optional are the same value. return operand; } @@ -5527,7 +5537,7 @@ pub const FuncGen = struct { const res_ptr_ty = try self.dg.llvmType(result_ty); return self.builder.buildBitCast(operand, res_ptr_ty, ""); } - if (optional_ty.isPtrLikeOptional()) { + if (optional_ty.optionalReprIsPayload()) { // The payload and the optional are the same value. // Setting to non-null will be done when the payload is set. return operand; @@ -5561,7 +5571,7 @@ pub const FuncGen = struct { const payload_ty = self.air.typeOfIndex(inst); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return null; - if (optional_ty.isPtrLikeOptional()) { + if (optional_ty.optionalReprIsPayload()) { // Payload value is the same as the optional value. return operand; } @@ -5702,7 +5712,9 @@ pub const FuncGen = struct { if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return non_null_bit; const operand = try self.resolveInst(ty_op.operand); const optional_ty = self.air.typeOfIndex(inst); - if (optional_ty.isPtrLikeOptional()) return operand; + if (optional_ty.optionalReprIsPayload()) { + return operand; + } const llvm_optional_ty = try self.dg.llvmType(optional_ty); if (isByRef(optional_ty)) { const optional_ptr = self.buildAlloca(llvm_optional_ty); @@ -7038,7 +7050,7 @@ pub const FuncGen = struct { } const success_bit = self.builder.buildExtractValue(result, 1, ""); - if (optional_ty.isPtrLikeOptional()) { + if (optional_ty.optionalReprIsPayload()) { return self.builder.buildSelect(success_bit, payload.typeOf().constNull(), payload, ""); } diff --git a/src/type.zig b/src/type.zig index 4b8a41915f..1c59cf9e59 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2916,8 +2916,10 @@ pub const Type = extern union { var buf: Payload.ElemType = undefined; const child_type = ty.optionalChild(&buf); - if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) { - return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; + switch (child_type.zigTypeTag()) { + .Pointer => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, + .ErrorSet => return abiAlignmentAdvanced(Type.anyerror, target, strat), + else => {}, } switch (strat) { @@ -3365,14 +3367,29 @@ pub const Type = extern union { const child_type = ty.optionalChild(&buf); if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 }; - if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr() and !child_type.isSlice()) - return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; + switch (child_type.zigTypeTag()) { + .Pointer => { + const ptr_info = child_type.ptrInfo().data; + const has_null = switch (ptr_info.size) { + .Slice, .C => true, + else => ptr_info.@"allowzero", + }; + if (!has_null) { + const ptr_size_bytes = @divExact(target.cpu.arch.ptrBitWidth(), 8); + return AbiSizeAdvanced{ .scalar = ptr_size_bytes }; + } + }, + .ErrorSet => return abiSizeAdvanced(Type.anyerror, target, strat), + else => {}, + } // Optional types are represented as a struct with the child type as the first // field and a boolean as the second. Since the child type's abi alignment is // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal // to the child type's ABI alignment. - return AbiSizeAdvanced{ .scalar = child_type.abiAlignment(target) + child_type.abiSize(target) }; + return AbiSizeAdvanced{ + .scalar = child_type.abiAlignment(target) + child_type.abiSize(target), + }; }, .error_union => { @@ -3901,8 +3918,39 @@ pub const Type = extern union { return ty.ptrInfo().data.@"allowzero"; } + /// See also `isPtrLikeOptional`. + pub fn optionalReprIsPayload(ty: Type) bool { + switch (ty.tag()) { + .optional_single_const_pointer, + .optional_single_mut_pointer, + .c_const_pointer, + .c_mut_pointer, + => return true, + + .optional => { + const child_ty = ty.castTag(.optional).?.data; + switch (child_ty.zigTypeTag()) { + .Pointer => { + const info = child_ty.ptrInfo().data; + switch (info.size) { + .Slice, .C => return false, + .Many, .One => return !info.@"allowzero", + } + }, + .ErrorSet => return true, + else => return false, + } + }, + + .pointer => return ty.castTag(.pointer).?.data.size == .C, + + else => return false, + } + } + /// Returns true if the type is optional and would be lowered to a single pointer /// address value, using 0 for null. Note that this returns true for C pointers. + /// See also `hasOptionalRepr`. pub fn isPtrLikeOptional(self: Type) bool { switch (self.tag()) { .optional_single_const_pointer, |
