Skip to content

Commit c528357

Browse files
committed
Auto merge of #108012 - compiler-errors:issue-107999, r=oli-obk
Don't ICE in `might_permit_raw_init` if reference is polymorphic Emitting optimized MIR for a polymorphic function may require computing layout of a type that isn't (yet) known. This happens in the instcombine pass, for example. Let's fail gracefully in that condition. cc `@saethlin` fixes #107999
2 parents 2d14db3 + b096f0e commit c528357

File tree

11 files changed

+116
-56
lines changed

11 files changed

+116
-56
lines changed

compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,8 @@ fn codegen_regular_intrinsic_call<'tcx>(
640640
sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid => {
641641
intrinsic_args!(fx, args => (); intrinsic);
642642

643-
let layout = fx.layout_of(substs.type_at(0));
643+
let ty = substs.type_at(0);
644+
let layout = fx.layout_of(ty);
644645
if layout.abi.is_uninhabited() {
645646
with_no_trimmed_paths!({
646647
crate::base::codegen_panic_nounwind(
@@ -653,7 +654,10 @@ fn codegen_regular_intrinsic_call<'tcx>(
653654
}
654655

655656
if intrinsic == sym::assert_zero_valid
656-
&& !fx.tcx.permits_zero_init(fx.param_env().and(layout))
657+
&& !fx
658+
.tcx
659+
.permits_zero_init(fx.param_env().and(ty))
660+
.expect("expected to have layout during codegen")
657661
{
658662
with_no_trimmed_paths!({
659663
crate::base::codegen_panic_nounwind(
@@ -669,7 +673,10 @@ fn codegen_regular_intrinsic_call<'tcx>(
669673
}
670674

671675
if intrinsic == sym::assert_mem_uninitialized_valid
672-
&& !fx.tcx.permits_uninit_init(fx.param_env().and(layout))
676+
&& !fx
677+
.tcx
678+
.permits_uninit_init(fx.param_env().and(ty))
679+
.expect("expected to have layout during codegen")
673680
{
674681
with_no_trimmed_paths!({
675682
crate::base::codegen_panic_nounwind(

compiler/rustc_codegen_ssa/src/mir/block.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -674,8 +674,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
674674
let layout = bx.layout_of(ty);
675675
let do_panic = match intrinsic {
676676
Inhabited => layout.abi.is_uninhabited(),
677-
ZeroValid => !bx.tcx().permits_zero_init(bx.param_env().and(layout)),
678-
MemUninitializedValid => !bx.tcx().permits_uninit_init(bx.param_env().and(layout)),
677+
ZeroValid => !bx
678+
.tcx()
679+
.permits_zero_init(bx.param_env().and(ty))
680+
.expect("expected to have layout during codegen"),
681+
MemUninitializedValid => !bx
682+
.tcx()
683+
.permits_uninit_init(bx.param_env().and(ty))
684+
.expect("expected to have layout during codegen"),
679685
};
680686
Some(if do_panic {
681687
let msg_str = with_no_visible_paths!({

compiler/rustc_const_eval/src/interpret/intrinsics.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
448448
}
449449

450450
if intrinsic_name == sym::assert_zero_valid {
451-
let should_panic = !self.tcx.permits_zero_init(self.param_env.and(layout));
451+
let should_panic = !self
452+
.tcx
453+
.permits_zero_init(self.param_env.and(ty))
454+
.map_err(|_| err_inval!(TooGeneric))?;
452455

453456
if should_panic {
454457
M::abort(
@@ -462,7 +465,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
462465
}
463466

464467
if intrinsic_name == sym::assert_mem_uninitialized_valid {
465-
let should_panic = !self.tcx.permits_uninit_init(self.param_env.and(layout));
468+
let should_panic = !self
469+
.tcx
470+
.permits_uninit_init(self.param_env.and(ty))
471+
.map_err(|_| err_inval!(TooGeneric))?;
466472

467473
if should_panic {
468474
M::abort(

compiler/rustc_const_eval/src/lib.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,8 @@ pub fn provide(providers: &mut Providers) {
5959
const_eval::deref_mir_constant(tcx, param_env, value)
6060
};
6161
providers.permits_uninit_init = |tcx, param_env_and_ty| {
62-
let (param_env, ty) = param_env_and_ty.into_parts();
63-
util::might_permit_raw_init(tcx, param_env, ty, InitKind::UninitMitigated0x01Fill)
64-
};
65-
providers.permits_zero_init = |tcx, param_env_and_ty| {
66-
let (param_env, ty) = param_env_and_ty.into_parts();
67-
util::might_permit_raw_init(tcx, param_env, ty, InitKind::Zero)
62+
util::might_permit_raw_init(tcx, param_env_and_ty, InitKind::UninitMitigated0x01Fill)
6863
};
64+
providers.permits_zero_init =
65+
|tcx, param_env_and_ty| util::might_permit_raw_init(tcx, param_env_and_ty, InitKind::Zero);
6966
}

compiler/rustc_const_eval/src/util/might_permit_raw_init.rs

+17-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
2-
use rustc_middle::ty::{ParamEnv, TyCtxt};
1+
use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout};
2+
use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt};
33
use rustc_session::Limit;
44
use rustc_target::abi::{Abi, FieldsShape, InitKind, Scalar, Variants};
55

@@ -20,15 +20,14 @@ use crate::interpret::{InterpCx, MemoryKind, OpTy};
2020
/// to the full uninit check).
2121
pub fn might_permit_raw_init<'tcx>(
2222
tcx: TyCtxt<'tcx>,
23-
param_env: ParamEnv<'tcx>,
24-
ty: TyAndLayout<'tcx>,
23+
param_env_and_ty: ParamEnvAnd<'tcx, Ty<'tcx>>,
2524
kind: InitKind,
26-
) -> bool {
25+
) -> Result<bool, LayoutError<'tcx>> {
2726
if tcx.sess.opts.unstable_opts.strict_init_checks {
28-
might_permit_raw_init_strict(ty, tcx, kind)
27+
might_permit_raw_init_strict(tcx.layout_of(param_env_and_ty)?, tcx, kind)
2928
} else {
30-
let layout_cx = LayoutCx { tcx, param_env };
31-
might_permit_raw_init_lax(ty, &layout_cx, kind)
29+
let layout_cx = LayoutCx { tcx, param_env: param_env_and_ty.param_env };
30+
might_permit_raw_init_lax(tcx.layout_of(param_env_and_ty)?, &layout_cx, kind)
3231
}
3332
}
3433

@@ -38,7 +37,7 @@ fn might_permit_raw_init_strict<'tcx>(
3837
ty: TyAndLayout<'tcx>,
3938
tcx: TyCtxt<'tcx>,
4039
kind: InitKind,
41-
) -> bool {
40+
) -> Result<bool, LayoutError<'tcx>> {
4241
let machine = CompileTimeInterpreter::new(
4342
Limit::new(0),
4443
/*can_access_statics:*/ false,
@@ -65,7 +64,7 @@ fn might_permit_raw_init_strict<'tcx>(
6564
// This does *not* actually check that references are dereferenceable, but since all types that
6665
// require dereferenceability also require non-null, we don't actually get any false negatives
6766
// due to this.
68-
cx.validate_operand(&ot).is_ok()
67+
Ok(cx.validate_operand(&ot).is_ok())
6968
}
7069

7170
/// Implements the 'lax' (default) version of the `might_permit_raw_init` checks; see that function for
@@ -74,7 +73,7 @@ fn might_permit_raw_init_lax<'tcx>(
7473
this: TyAndLayout<'tcx>,
7574
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
7675
init_kind: InitKind,
77-
) -> bool {
76+
) -> Result<bool, LayoutError<'tcx>> {
7877
let scalar_allows_raw_init = move |s: Scalar| -> bool {
7978
match init_kind {
8079
InitKind::Zero => {
@@ -103,20 +102,20 @@ fn might_permit_raw_init_lax<'tcx>(
103102
};
104103
if !valid {
105104
// This is definitely not okay.
106-
return false;
105+
return Ok(false);
107106
}
108107

109108
// Special magic check for references and boxes (i.e., special pointer types).
110109
if let Some(pointee) = this.ty.builtin_deref(false) {
111-
let pointee = cx.layout_of(pointee.ty).expect("need to be able to compute layouts");
110+
let pointee = cx.layout_of(pointee.ty)?;
112111
// We need to ensure that the LLVM attributes `aligned` and `dereferenceable(size)` are satisfied.
113112
if pointee.align.abi.bytes() > 1 {
114113
// 0x01-filling is not aligned.
115-
return false;
114+
return Ok(false);
116115
}
117116
if pointee.size.bytes() > 0 {
118117
// A 'fake' integer pointer is not sufficiently dereferenceable.
119-
return false;
118+
return Ok(false);
120119
}
121120
}
122121

@@ -129,9 +128,9 @@ fn might_permit_raw_init_lax<'tcx>(
129128
}
130129
FieldsShape::Arbitrary { offsets, .. } => {
131130
for idx in 0..offsets.len() {
132-
if !might_permit_raw_init_lax(this.field(cx, idx), cx, init_kind) {
131+
if !might_permit_raw_init_lax(this.field(cx, idx), cx, init_kind)? {
133132
// We found a field that is unhappy with this kind of initialization.
134-
return false;
133+
return Ok(false);
135134
}
136135
}
137136
}
@@ -148,5 +147,5 @@ fn might_permit_raw_init_lax<'tcx>(
148147
}
149148
}
150149

151-
true
150+
Ok(true)
152151
}

compiler/rustc_middle/src/query/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -2143,12 +2143,12 @@ rustc_queries! {
21432143
separate_provide_extern
21442144
}
21452145

2146-
query permits_uninit_init(key: ty::ParamEnvAnd<'tcx, TyAndLayout<'tcx>>) -> bool {
2147-
desc { "checking to see if `{}` permits being left uninit", key.value.ty }
2146+
query permits_uninit_init(key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> Result<bool, ty::layout::LayoutError<'tcx>> {
2147+
desc { "checking to see if `{}` permits being left uninit", key.value }
21482148
}
21492149

2150-
query permits_zero_init(key: ty::ParamEnvAnd<'tcx, TyAndLayout<'tcx>>) -> bool {
2151-
desc { "checking to see if `{}` permits being left zeroed", key.value.ty }
2150+
query permits_zero_init(key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> Result<bool, ty::layout::LayoutError<'tcx>> {
2151+
desc { "checking to see if `{}` permits being left zeroed", key.value }
21522152
}
21532153

21542154
query compare_impl_const(

compiler/rustc_middle/src/ty/query.rs

-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ use crate::traits::specialization_graph;
3030
use crate::traits::{self, ImplSource};
3131
use crate::ty::context::TyCtxtFeed;
3232
use crate::ty::fast_reject::SimplifiedType;
33-
use crate::ty::layout::TyAndLayout;
3433
use crate::ty::subst::{GenericArg, SubstsRef};
3534
use crate::ty::util::AlwaysRequiresDrop;
3635
use crate::ty::GeneratorDiagnosticData;

compiler/rustc_mir_transform/src/instcombine.rs

+24-20
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ use rustc_middle::mir::{
66
BinOp, Body, Constant, ConstantKind, LocalDecls, Operand, Place, ProjectionElem, Rvalue,
77
SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UnOp,
88
};
9-
use rustc_middle::ty::{self, layout::TyAndLayout, ParamEnv, ParamEnvAnd, SubstsRef, Ty, TyCtxt};
9+
use rustc_middle::ty::layout::LayoutError;
10+
use rustc_middle::ty::{self, ParamEnv, ParamEnvAnd, SubstsRef, Ty, TyCtxt};
1011
use rustc_span::symbol::{sym, Symbol};
1112

1213
pub struct InstCombine;
@@ -230,38 +231,41 @@ impl<'tcx> InstCombineContext<'tcx, '_> {
230231

231232
// Check this is a foldable intrinsic before we query the layout of our generic parameter
232233
let Some(assert_panics) = intrinsic_assert_panics(intrinsic_name) else { return; };
233-
let Ok(layout) = self.tcx.layout_of(self.param_env.and(ty)) else { return; };
234-
if assert_panics(self.tcx, self.param_env.and(layout)) {
235-
// If we know the assert panics, indicate to later opts that the call diverges
236-
*target = None;
237-
} else {
238-
// If we know the assert does not panic, turn the call into a Goto
239-
terminator.kind = TerminatorKind::Goto { target: *target_block };
234+
match assert_panics(self.tcx, self.param_env.and(ty)) {
235+
// We don't know the layout, don't touch the assertion
236+
Err(_) => {}
237+
Ok(true) => {
238+
// If we know the assert panics, indicate to later opts that the call diverges
239+
*target = None;
240+
}
241+
Ok(false) => {
242+
// If we know the assert does not panic, turn the call into a Goto
243+
terminator.kind = TerminatorKind::Goto { target: *target_block };
244+
}
240245
}
241246
}
242247
}
243248

244249
fn intrinsic_assert_panics<'tcx>(
245250
intrinsic_name: Symbol,
246-
) -> Option<fn(TyCtxt<'tcx>, ParamEnvAnd<'tcx, TyAndLayout<'tcx>>) -> bool> {
251+
) -> Option<fn(TyCtxt<'tcx>, ParamEnvAnd<'tcx, Ty<'tcx>>) -> Result<bool, LayoutError<'tcx>>> {
247252
fn inhabited_predicate<'tcx>(
248-
_tcx: TyCtxt<'tcx>,
249-
param_env_and_layout: ParamEnvAnd<'tcx, TyAndLayout<'tcx>>,
250-
) -> bool {
251-
let (_param_env, layout) = param_env_and_layout.into_parts();
252-
layout.abi.is_uninhabited()
253+
tcx: TyCtxt<'tcx>,
254+
param_env_and_ty: ParamEnvAnd<'tcx, Ty<'tcx>>,
255+
) -> Result<bool, LayoutError<'tcx>> {
256+
Ok(tcx.layout_of(param_env_and_ty)?.abi.is_uninhabited())
253257
}
254258
fn zero_valid_predicate<'tcx>(
255259
tcx: TyCtxt<'tcx>,
256-
param_env_and_layout: ParamEnvAnd<'tcx, TyAndLayout<'tcx>>,
257-
) -> bool {
258-
!tcx.permits_zero_init(param_env_and_layout)
260+
param_env_and_ty: ParamEnvAnd<'tcx, Ty<'tcx>>,
261+
) -> Result<bool, LayoutError<'tcx>> {
262+
Ok(!tcx.permits_zero_init(param_env_and_ty)?)
259263
}
260264
fn mem_uninitialized_valid_predicate<'tcx>(
261265
tcx: TyCtxt<'tcx>,
262-
param_env_and_layout: ParamEnvAnd<'tcx, TyAndLayout<'tcx>>,
263-
) -> bool {
264-
!tcx.permits_uninit_init(param_env_and_layout)
266+
param_env_and_ty: ParamEnvAnd<'tcx, Ty<'tcx>>,
267+
) -> Result<bool, LayoutError<'tcx>> {
268+
Ok(!tcx.permits_uninit_init(param_env_and_ty)?)
265269
}
266270

267271
match intrinsic_name {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
- // MIR for `generic` before InstCombine
2+
+ // MIR for `generic` after InstCombine
3+
4+
fn generic() -> () {
5+
let mut _0: (); // return place in scope 0 at $DIR/dont_yeet_assert.rs:+0:21: +0:21
6+
let _1: (); // in scope 0 at $DIR/dont_yeet_assert.rs:+1:5: +1:61
7+
8+
bb0: {
9+
StorageLive(_1); // scope 0 at $DIR/dont_yeet_assert.rs:+1:5: +1:61
10+
_1 = assert_mem_uninitialized_valid::<&T>() -> bb1; // scope 0 at $DIR/dont_yeet_assert.rs:+1:5: +1:61
11+
// mir::Constant
12+
// + span: $DIR/dont_yeet_assert.rs:10:5: 10:59
13+
// + user_ty: UserType(0)
14+
// + literal: Const { ty: extern "rust-intrinsic" fn() {assert_mem_uninitialized_valid::<&T>}, val: Value(<ZST>) }
15+
}
16+
17+
bb1: {
18+
StorageDead(_1); // scope 0 at $DIR/dont_yeet_assert.rs:+1:61: +1:62
19+
_0 = const (); // scope 0 at $DIR/dont_yeet_assert.rs:+0:21: +2:2
20+
return; // scope 0 at $DIR/dont_yeet_assert.rs:+2:2: +2:2
21+
}
22+
}
23+

tests/mir-opt/dont_yeet_assert.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// compile-flags: --crate-type=lib
2+
// unit-test: InstCombine
3+
4+
#![feature(core_intrinsics)]
5+
6+
// Want to make sure this assertion isn't compiled away in generic code.
7+
8+
// EMIT_MIR dont_yeet_assert.generic.InstCombine.diff
9+
pub fn generic<T>() {
10+
core::intrinsics::assert_mem_uninitialized_valid::<&T>();
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// compile-flags: --crate-type=lib -Zmir-enable-passes=+InstCombine
2+
// build-pass
3+
4+
#![feature(core_intrinsics)]
5+
6+
pub fn generic<T>() {
7+
core::intrinsics::assert_mem_uninitialized_valid::<&T>();
8+
}

0 commit comments

Comments
 (0)