authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-15 01:06:05-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-07-15 01:06:05-04:00
log4c7fe74b2c0e8279d0003f820a18d59e1237b74a
tree1b88748105f7b2aa25cd6aedc8565a0e1c34b7c7
parentdd70336f3ace541586210bd7bd061cc09a8c0e03
parent04572f6e341e6ff19877d1ae3b79e3baa653e652
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12124 from ziglang/stage2-coerce-result-ptr

Sema: fix coerce_result_ptr in case of inferred result type

5 files changed, 151 insertions(+), 22 deletions(-)

src/Sema.zig+111-16
......@@ -1974,8 +1974,6 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
19741974 defer trash_block.instructions.deinit(sema.gpa);
19751975 const operand = try trash_block.addBitCast(pointee_ty, .void_value);
19761976
1977 try inferred_alloc.stored_inst_list.append(sema.arena, operand);
1978
19791977 try sema.requireRuntimeBlock(block, src);
19801978 const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
19811979 .pointee_type = pointee_ty,
......@@ -1983,6 +1981,12 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
19831981 .@"addrspace" = addr_space,
19841982 });
19851983 const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr);
1984
1985 try inferred_alloc.prongs.append(sema.arena, .{
1986 .stored_inst = operand,
1987 .placeholder = Air.refToIndex(bitcasted_ptr).?,
1988 });
1989
19861990 return bitcasted_ptr;
19871991 },
19881992 .inferred_alloc_comptime => {
......@@ -2027,7 +2031,24 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
20272031
20282032 const dummy_ptr = try trash_block.addTy(.alloc, sema.typeOf(ptr));
20292033 const dummy_operand = try trash_block.addBitCast(pointee_ty, .void_value);
2030 try sema.storePtr2(&trash_block, src, dummy_ptr, src, dummy_operand, src, .bitcast);
2034 return coerceResultPtr(sema, block, src, ptr, dummy_ptr, dummy_operand, &trash_block);
2035}
2036
2037fn coerceResultPtr(
2038 sema: *Sema,
2039 block: *Block,
2040 src: LazySrcLoc,
2041 ptr: Air.Inst.Ref,
2042 dummy_ptr: Air.Inst.Ref,
2043 dummy_operand: Air.Inst.Ref,
2044 trash_block: *Block,
2045) CompileError!Air.Inst.Ref {
2046 const target = sema.mod.getTarget();
2047 const addr_space = target_util.defaultAddressSpace(target, .local);
2048 const pointee_ty = sema.typeOf(dummy_operand);
2049 const prev_trash_len = trash_block.instructions.items.len;
2050
2051 try sema.storePtr2(trash_block, src, dummy_ptr, src, dummy_operand, src, .bitcast);
20312052
20322053 {
20332054 const air_tags = sema.air_instructions.items(.tag);
......@@ -2059,15 +2080,24 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
20592080 while (true) {
20602081 const air_tags = sema.air_instructions.items(.tag);
20612082 const air_datas = sema.air_instructions.items(.data);
2083
2084 if (trash_block.instructions.items.len == prev_trash_len) {
2085 if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| {
2086 return sema.addConstant(ptr_ty, ptr_val);
2087 }
2088 if (pointee_ty.eql(Type.@"null", sema.mod)) {
2089 const opt_ty = sema.typeOf(new_ptr).childType();
2090 const null_inst = try sema.addConstant(opt_ty, Value.@"null");
2091 _ = try block.addBinOp(.store, new_ptr, null_inst);
2092 return Air.Inst.Ref.void_value;
2093 }
2094 return sema.bitCast(block, ptr_ty, new_ptr, src);
2095 }
2096
20622097 const trash_inst = trash_block.instructions.pop();
2098
20632099 switch (air_tags[trash_inst]) {
20642100 .bitcast => {
2065 if (Air.indexToRef(trash_inst) == dummy_operand) {
2066 if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| {
2067 return sema.addConstant(ptr_ty, ptr_val);
2068 }
2069 return sema.bitCast(block, ptr_ty, new_ptr, src);
2070 }
20712101 const ty_op = air_datas[trash_inst].ty_op;
20722102 const operand_ty = sema.typeOf(ty_op.operand);
20732103 const ptr_operand_ty = try Type.ptr(sema.arena, sema.mod, .{
......@@ -3141,7 +3171,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
31413171 },
31423172 .inferred_alloc => {
31433173 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
3144 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
3174 const peer_inst_list = inferred_alloc.data.prongs.items(.stored_inst);
31453175 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none);
31463176
31473177 const final_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
......@@ -3250,6 +3280,70 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
32503280 .tag = .alloc,
32513281 .data = .{ .ty = final_ptr_ty },
32523282 });
3283
3284 // Now we need to go back over all the coerce_result_ptr instructions, which
3285 // previously inserted a bitcast as a placeholder, and do the logic as if
3286 // the new result ptr type was available.
3287 const placeholders = inferred_alloc.data.prongs.items(.placeholder);
3288 const gpa = sema.gpa;
3289
3290 var trash_block = block.makeSubBlock();
3291 trash_block.is_comptime = false;
3292 trash_block.is_coerce_result_ptr = true;
3293 defer trash_block.instructions.deinit(gpa);
3294
3295 const mut_final_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
3296 .pointee_type = final_elem_ty,
3297 .mutable = true,
3298 .@"align" = inferred_alloc.data.alignment,
3299 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
3300 });
3301 const dummy_ptr = try trash_block.addTy(.alloc, mut_final_ptr_ty);
3302 const empty_trash_count = trash_block.instructions.items.len;
3303
3304 for (placeholders) |bitcast_inst, i| {
3305 const sub_ptr_ty = sema.typeOf(Air.indexToRef(bitcast_inst));
3306
3307 if (mut_final_ptr_ty.eql(sub_ptr_ty, sema.mod)) {
3308 // New result location type is the same as the old one; nothing
3309 // to do here.
3310 continue;
3311 }
3312
3313 var bitcast_block = block.makeSubBlock();
3314 defer bitcast_block.instructions.deinit(gpa);
3315
3316 trash_block.instructions.shrinkRetainingCapacity(empty_trash_count);
3317 const sub_ptr = try coerceResultPtr(sema, &bitcast_block, src, ptr, dummy_ptr, peer_inst_list[i], &trash_block);
3318
3319 assert(bitcast_block.instructions.items.len > 0);
3320 // If only one instruction is produced then we can replace the bitcast
3321 // placeholder instruction with this instruction; no need for an entire block.
3322 if (bitcast_block.instructions.items.len == 1) {
3323 const only_inst = bitcast_block.instructions.items[0];
3324 sema.air_instructions.set(bitcast_inst, sema.air_instructions.get(only_inst));
3325 continue;
3326 }
3327
3328 // Here we replace the placeholder bitcast instruction with a block
3329 // that does the coerce_result_ptr logic.
3330 _ = try bitcast_block.addBr(bitcast_inst, sub_ptr);
3331 const ty_inst = sema.air_instructions.items(.data)[bitcast_inst].ty_op.ty;
3332 try sema.air_extra.ensureUnusedCapacity(
3333 gpa,
3334 @typeInfo(Air.Block).Struct.fields.len + bitcast_block.instructions.items.len,
3335 );
3336 sema.air_instructions.set(bitcast_inst, .{
3337 .tag = .block,
3338 .data = .{ .ty_pl = .{
3339 .ty = ty_inst,
3340 .payload = sema.addExtraAssumeCapacity(Air.Block{
3341 .body_len = @intCast(u32, bitcast_block.instructions.items.len),
3342 }),
3343 } },
3344 });
3345 sema.air_extra.appendSliceAssumeCapacity(bitcast_block.instructions.items);
3346 }
32533347 },
32543348 else => unreachable,
32553349 }
......@@ -4086,9 +4180,6 @@ fn storeToInferredAlloc(
40864180 inferred_alloc: *Value.Payload.InferredAlloc,
40874181) CompileError!void {
40884182 const operand_ty = sema.typeOf(operand);
4089 // Add the stored instruction to the set we will use to resolve peer types
4090 // for the inferred allocation.
4091 try inferred_alloc.data.stored_inst_list.append(sema.arena, operand);
40924183 // Create a runtime bitcast instruction with exactly the type the pointer wants.
40934184 const target = sema.mod.getTarget();
40944185 const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
......@@ -4097,7 +4188,13 @@ fn storeToInferredAlloc(
40974188 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
40984189 });
40994190 const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr);
4100 return sema.storePtr(block, src, bitcasted_ptr, operand);
4191 // Add the stored instruction to the set we will use to resolve peer types
4192 // for the inferred allocation.
4193 try inferred_alloc.data.prongs.append(sema.arena, .{
4194 .stored_inst = operand,
4195 .placeholder = Air.refToIndex(bitcasted_ptr).?,
4196 });
4197 return sema.storePtr2(block, src, bitcasted_ptr, src, operand, src, .bitcast);
41014198}
41024199
41034200fn storeToInferredAllocComptime(
......@@ -21614,8 +21711,6 @@ fn storePtr2(
2161421711 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
2161521712 return;
2161621713
21617 // TODO handle if the element type requires comptime
21618
2161921714 if (air_tag == .bitcast) {
2162021715 // `air_tag == .bitcast` is used as a special case for `zirCoerceResultPtr`
2162121716 // to avoid calling `requireRuntimeBlock` for the dummy block.
src/print_zir.zig+1-1
......@@ -232,13 +232,13 @@ const Writer = struct {
232232 .validate_array_init_ty,
233233 .validate_struct_init_ty,
234234 .make_ptr_const,
235 .validate_deref,
235236 => try self.writeUnNode(stream, inst),
236237
237238 .ref,
238239 .ret_tok,
239240 .ensure_err_payload_void,
240241 .closure_capture,
241 .validate_deref,
242242 => try self.writeUnTok(stream, inst),
243243
244244 .bool_br_and,
src/value.zig+10-1
......@@ -4935,7 +4935,16 @@ pub const Value = extern union {
49354935 /// peer type resolution. This is stored in a separate list so that
49364936 /// the items are contiguous in memory and thus can be passed to
49374937 /// `Module.resolvePeerTypes`.
4938 stored_inst_list: std.ArrayListUnmanaged(Air.Inst.Ref) = .{},
4938 prongs: std.MultiArrayList(struct {
4939 /// The dummy instruction used as a peer to resolve the type.
4940 /// Although this has a redundant type with placeholder, this is
4941 /// needed in addition because it may be a constant value, which
4942 /// affects peer type resolution.
4943 stored_inst: Air.Inst.Ref,
4944 /// The bitcast instruction used as a placeholder when the
4945 /// new result pointer type is not yet known.
4946 placeholder: Air.Inst.Index,
4947 }) = .{},
49394948 /// 0 means ABI-aligned.
49404949 alignment: u32,
49414950 },
test/behavior/array.zig+1-4
......@@ -570,10 +570,7 @@ test "type coercion of pointer to anon struct literal to pointer to array" {
570570}
571571
572572test "array with comptime only element type" {
573 const a = [_]type{
574 u32,
575 i32,
576 };
573 const a = [_]type{ u32, i32 };
577574 try testing.expect(a[0] == u32);
578575 try testing.expect(a[1] == i32);
579576}
test/behavior/if.zig+28
......@@ -128,3 +128,31 @@ test "if peer expressions inferred optional type" {
128128 try expect(left.? == 98);
129129 try expect(right.? == 99);
130130}
131
132test "if-else expression with runtime condition result location is inferred optional" {
133 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
134 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
135 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
136 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
137
138 const A = struct { b: u64, c: u64 };
139 var d: bool = true;
140 const e = if (d) A{ .b = 15, .c = 30 } else null;
141 try expect(e != null);
142}
143
144test "result location with inferred type ends up being pointer to comptime_int" {
145 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
146 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
147 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
148 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
149 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
150
151 var a: ?u32 = 1234;
152 var b: u32 = 2000;
153 var c = if (a) |d| blk: {
154 if (d < b) break :blk @as(u32, 1);
155 break :blk 0;
156 } else @as(u32, 0);
157 try expect(c == 1);
158}