authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-14 16:48:44-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-14 16:48:44-07:00
log667ad9250f7250670778beedcebc45f5c0284446
tree76ac3d73766d13be52afa570111e4071d8d19cf9
parent1653a9b2597c66cbcc88ea75d8a4b88c163584a5

Sema: fix coerce_result_ptr in case of inferred result type

Previously, the logic for analyzing coerce_result_ptr would generate invalid bitcast instructions which did not include coercion logic, such as optional wrapping, resulting in miscompilations. Now, the logic of resolve_inferred_alloc goes back over all the placeholders inserted by coerce_result_ptr, and replaces them with logic doing the proper coercions. Closes #12045

3 files changed, 122 insertions(+), 8 deletions(-)

src/Sema.zig+100-7
......@@ -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 => {
......@@ -2026,8 +2030,23 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
20262030 defer trash_block.instructions.deinit(sema.gpa);
20272031
20282032 const dummy_ptr = try trash_block.addTy(.alloc, sema.typeOf(ptr));
2033 return coerceResultPtr(sema, block, src, ptr, dummy_ptr, pointee_ty, &trash_block);
2034}
2035
2036fn coerceResultPtr(
2037 sema: *Sema,
2038 block: *Block,
2039 src: LazySrcLoc,
2040 ptr: Air.Inst.Ref,
2041 dummy_ptr: Air.Inst.Ref,
2042 pointee_ty: Type,
2043 trash_block: *Block,
2044) CompileError!Air.Inst.Ref {
2045 const target = sema.mod.getTarget();
2046 const addr_space = target_util.defaultAddressSpace(target, .local);
2047
20292048 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);
2049 try sema.storePtr2(trash_block, src, dummy_ptr, src, dummy_operand, src, .bitcast);
20312050
20322051 {
20332052 const air_tags = sema.air_instructions.items(.tag);
......@@ -2066,6 +2085,12 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
20662085 if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| {
20672086 return sema.addConstant(ptr_ty, ptr_val);
20682087 }
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 }
20692094 return sema.bitCast(block, ptr_ty, new_ptr, src);
20702095 }
20712096 const ty_op = air_datas[trash_inst].ty_op;
......@@ -3141,7 +3166,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
31413166 },
31423167 .inferred_alloc => {
31433168 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
3144 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
3169 const peer_inst_list = inferred_alloc.data.prongs.items(.stored_inst);
31453170 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none);
31463171
31473172 const final_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
......@@ -3250,6 +3275,71 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
32503275 .tag = .alloc,
32513276 .data = .{ .ty = final_ptr_ty },
32523277 });
3278
3279 // Now we need to go back over all the coerce_result_ptr instructions, which
3280 // previously inserted a bitcast as a placeholder, and do the logic as if
3281 // the new result ptr type was available.
3282 const placeholders = inferred_alloc.data.prongs.items(.placeholder);
3283 const gpa = sema.gpa;
3284
3285 var trash_block = block.makeSubBlock();
3286 trash_block.is_comptime = false;
3287 trash_block.is_coerce_result_ptr = true;
3288 defer trash_block.instructions.deinit(gpa);
3289
3290 const mut_final_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
3291 .pointee_type = final_elem_ty,
3292 .mutable = true,
3293 .@"align" = inferred_alloc.data.alignment,
3294 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
3295 });
3296 const dummy_ptr = try trash_block.addTy(.alloc, mut_final_ptr_ty);
3297 const empty_trash_count = trash_block.instructions.items.len;
3298
3299 for (placeholders) |bitcast_inst, i| {
3300 const sub_ptr_ty = sema.typeOf(Air.indexToRef(bitcast_inst));
3301
3302 if (mut_final_ptr_ty.eql(sub_ptr_ty, sema.mod)) {
3303 // New result location type is the same as the old one; nothing
3304 // to do here.
3305 continue;
3306 }
3307
3308 var bitcast_block = block.makeSubBlock();
3309 defer bitcast_block.instructions.deinit(gpa);
3310
3311 trash_block.instructions.shrinkRetainingCapacity(empty_trash_count);
3312 const pointee_ty = sema.typeOf(peer_inst_list[i]);
3313 const sub_ptr = try coerceResultPtr(sema, &bitcast_block, src, ptr, dummy_ptr, pointee_ty, &trash_block);
3314
3315 assert(bitcast_block.instructions.items.len > 0);
3316 // If only one instruction is produced then we can replace the bitcast
3317 // placeholder instruction with this instruction; no need for an entire block.
3318 if (bitcast_block.instructions.items.len == 1) {
3319 const only_inst = bitcast_block.instructions.items[0];
3320 sema.air_instructions.set(bitcast_inst, sema.air_instructions.get(only_inst));
3321 continue;
3322 }
3323
3324 // Here we replace the placeholder bitcast instruction with a block
3325 // that does the coerce_result_ptr logic.
3326 _ = try bitcast_block.addBr(bitcast_inst, sub_ptr);
3327 const ty_inst = sema.air_instructions.items(.data)[bitcast_inst].ty_op.ty;
3328 try sema.air_extra.ensureUnusedCapacity(
3329 gpa,
3330 @typeInfo(Air.Block).Struct.fields.len + bitcast_block.instructions.items.len,
3331 );
3332 sema.air_instructions.set(bitcast_inst, .{
3333 .tag = .block,
3334 .data = .{ .ty_pl = .{
3335 .ty = ty_inst,
3336 .payload = sema.addExtraAssumeCapacity(Air.Block{
3337 .body_len = @intCast(u32, bitcast_block.instructions.items.len),
3338 }),
3339 } },
3340 });
3341 sema.air_extra.appendSliceAssumeCapacity(bitcast_block.instructions.items);
3342 }
32533343 },
32543344 else => unreachable,
32553345 }
......@@ -4086,9 +4176,6 @@ fn storeToInferredAlloc(
40864176 inferred_alloc: *Value.Payload.InferredAlloc,
40874177) CompileError!void {
40884178 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);
40924179 // Create a runtime bitcast instruction with exactly the type the pointer wants.
40934180 const target = sema.mod.getTarget();
40944181 const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
......@@ -4097,6 +4184,12 @@ fn storeToInferredAlloc(
40974184 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
40984185 });
40994186 const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr);
4187 // Add the stored instruction to the set we will use to resolve peer types
4188 // for the inferred allocation.
4189 try inferred_alloc.data.prongs.append(sema.arena, .{
4190 .stored_inst = operand,
4191 .placeholder = Air.refToIndex(bitcasted_ptr).?,
4192 });
41004193 return sema.storePtr(block, src, bitcasted_ptr, operand);
41014194}
41024195
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/if.zig+12
......@@ -128,3 +128,15 @@ 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}