Skip to content

Commit a2f6273

Browse files
Rollup merge of rust-lang#152165 - JohnTitor:issue-151579, r=lcnr
Normalize capture place `ty`s to prevent ICE Fixes rust-lang#151579 Fixes rust-lang#120811 r? @lcnr
2 parents 9f383ce + dddbf96 commit a2f6273

5 files changed

Lines changed: 143 additions & 50 deletions

File tree

compiler/rustc_hir_typeck/src/upvar.rs

Lines changed: 79 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ use rustc_middle::ty::{
4949
use rustc_middle::{bug, span_bug};
5050
use rustc_session::lint;
5151
use rustc_span::{BytePos, Pos, Span, Symbol, sym};
52+
use rustc_trait_selection::error_reporting::InferCtxtErrorExt as _;
5253
use rustc_trait_selection::infer::InferCtxtExt;
54+
use rustc_trait_selection::solve;
5355
use tracing::{debug, instrument};
5456

5557
use super::FnCtxt;
@@ -196,17 +198,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
196198
let closure_def_id = closure_def_id.expect_local();
197199

198200
assert_eq!(self.tcx.hir_body_owner_def_id(body.id()), closure_def_id);
201+
202+
let closure_fcx = FnCtxt::new(self, self.tcx.param_env(closure_def_id), closure_def_id);
203+
199204
let mut delegate = InferBorrowKind {
205+
fcx: &closure_fcx,
200206
closure_def_id,
201207
capture_information: Default::default(),
202208
fake_reads: Default::default(),
203209
};
204210

205-
let _ = euv::ExprUseVisitor::new(
206-
&FnCtxt::new(self, self.tcx.param_env(closure_def_id), closure_def_id),
207-
&mut delegate,
208-
)
209-
.consume_body(body);
211+
let _ = euv::ExprUseVisitor::new(&closure_fcx, &mut delegate).consume_body(body);
210212

211213
// There are several curious situations with coroutine-closures where
212214
// analysis is too aggressive with borrows when the coroutine-closure is
@@ -286,7 +288,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
286288
let hir::def::Res::Local(local_id) = path.res else {
287289
bug!();
288290
};
289-
let place = self.place_for_root_variable(closure_def_id, local_id);
291+
let place = closure_fcx.place_for_root_variable(closure_def_id, local_id);
290292
delegate.capture_information.push((
291293
place,
292294
ty::CaptureInfo {
@@ -325,7 +327,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
325327

326328
if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
327329
for var_hir_id in upvars.keys() {
328-
let place = self.place_for_root_variable(closure_def_id, *var_hir_id);
330+
let place = closure_fcx.place_for_root_variable(closure_def_id, *var_hir_id);
329331

330332
debug!("seed place {:?}", place);
331333

@@ -559,17 +561,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
559561
bug!();
560562
};
561563

564+
let coroutine_fcx =
565+
FnCtxt::new(self, self.tcx.param_env(coroutine_def_id), coroutine_def_id);
566+
562567
let mut delegate = InferBorrowKind {
568+
fcx: &coroutine_fcx,
563569
closure_def_id: coroutine_def_id,
564570
capture_information: Default::default(),
565571
fake_reads: Default::default(),
566572
};
567573

568-
let _ = euv::ExprUseVisitor::new(
569-
&FnCtxt::new(self, self.tcx.param_env(coroutine_def_id), coroutine_def_id),
570-
&mut delegate,
571-
)
572-
.consume_expr(body);
574+
let _ = euv::ExprUseVisitor::new(&coroutine_fcx, &mut delegate).consume_expr(body);
573575

574576
let (_, kind, _) = self.process_collected_capture_information(
575577
hir::CaptureBy::Ref,
@@ -1125,6 +1127,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11251127
);
11261128
}
11271129
}
1130+
fn normalize_capture_place(&self, span: Span, place: Place<'tcx>) -> Place<'tcx> {
1131+
let mut place = self.resolve_vars_if_possible(place);
1132+
1133+
// In the new solver, types in HIR `Place`s can contain unnormalized aliases,
1134+
// which can ICE later (e.g. when projecting fields for diagnostics).
1135+
if self.next_trait_solver() {
1136+
let cause = self.misc(span);
1137+
let at = self.at(&cause, self.param_env);
1138+
match solve::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
1139+
at,
1140+
place.clone(),
1141+
vec![],
1142+
) {
1143+
Ok((normalized, goals)) => {
1144+
if !goals.is_empty() {
1145+
let mut typeck_results = self.typeck_results.borrow_mut();
1146+
typeck_results.coroutine_stalled_predicates.extend(
1147+
goals
1148+
.into_iter()
1149+
// FIXME: throwing away the param-env :(
1150+
.map(|goal| (goal.predicate, self.misc(span))),
1151+
);
1152+
}
1153+
normalized
1154+
}
1155+
Err(errors) => {
1156+
let guar = self.infcx.err_ctxt().report_fulfillment_errors(errors);
1157+
place.base_ty = Ty::new_error(self.tcx, guar);
1158+
for proj in &mut place.projections {
1159+
proj.ty = Ty::new_error(self.tcx, guar);
1160+
}
1161+
place
1162+
}
1163+
}
1164+
} else {
1165+
// For the old solver we can rely on `normalize` to eagerly normalize aliases.
1166+
self.normalize(span, place)
1167+
}
1168+
}
11281169

11291170
/// Combines all the reasons for 2229 migrations
11301171
fn compute_2229_migrations_reasons(
@@ -1734,11 +1775,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17341775
) -> Place<'tcx> {
17351776
let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id);
17361777

1737-
Place {
1778+
let place = Place {
17381779
base_ty: self.node_ty(var_hir_id),
17391780
base: PlaceBase::Upvar(upvar_id),
17401781
projections: Default::default(),
1741-
}
1782+
};
1783+
1784+
// Normalize eagerly when inserting into `capture_information`, so all downstream
1785+
// capture analysis can assume a normalized `Place`.
1786+
self.normalize_capture_place(self.tcx.hir_span(var_hir_id), place)
17421787
}
17431788

17441789
fn should_log_capture_analysis(&self, closure_def_id: LocalDefId) -> bool {
@@ -1994,7 +2039,8 @@ fn drop_location_span(tcx: TyCtxt<'_>, hir_id: HirId) -> Span {
19942039
tcx.sess.source_map().end_point(owner_span)
19952040
}
19962041

1997-
struct InferBorrowKind<'tcx> {
2042+
struct InferBorrowKind<'fcx, 'a, 'tcx> {
2043+
fcx: &'fcx FnCtxt<'a, 'tcx>,
19982044
// The def-id of the closure whose kind and upvar accesses are being inferred.
19992045
closure_def_id: LocalDefId,
20002046

@@ -2028,7 +2074,7 @@ struct InferBorrowKind<'tcx> {
20282074
fake_reads: Vec<(Place<'tcx>, FakeReadCause, HirId)>,
20292075
}
20302076

2031-
impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> {
2077+
impl<'fcx, 'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'fcx, 'a, 'tcx> {
20322078
#[instrument(skip(self), level = "debug")]
20332079
fn fake_read(
20342080
&mut self,
@@ -2042,8 +2088,10 @@ impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> {
20422088
// such as deref of a raw pointer.
20432089
let dummy_capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::Immutable);
20442090

2045-
let (place, _) =
2046-
restrict_capture_precision(place_with_id.place.clone(), dummy_capture_kind);
2091+
let span = self.fcx.tcx.hir_span(diag_expr_id);
2092+
let place = self.fcx.normalize_capture_place(span, place_with_id.place.clone());
2093+
2094+
let (place, _) = restrict_capture_precision(place, dummy_capture_kind);
20472095

20482096
let (place, _) = restrict_repr_packed_field_ref_capture(place, dummy_capture_kind);
20492097
self.fake_reads.push((place, cause, diag_expr_id));
@@ -2054,8 +2102,11 @@ impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> {
20542102
let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return };
20552103
assert_eq!(self.closure_def_id, upvar_id.closure_expr_id);
20562104

2105+
let span = self.fcx.tcx.hir_span(diag_expr_id);
2106+
let place = self.fcx.normalize_capture_place(span, place_with_id.place.clone());
2107+
20572108
self.capture_information.push((
2058-
place_with_id.place.clone(),
2109+
place,
20592110
ty::CaptureInfo {
20602111
capture_kind_expr_id: Some(diag_expr_id),
20612112
path_expr_id: Some(diag_expr_id),
@@ -2069,8 +2120,11 @@ impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> {
20692120
let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return };
20702121
assert_eq!(self.closure_def_id, upvar_id.closure_expr_id);
20712122

2123+
let span = self.fcx.tcx.hir_span(diag_expr_id);
2124+
let place = self.fcx.normalize_capture_place(span, place_with_id.place.clone());
2125+
20722126
self.capture_information.push((
2073-
place_with_id.place.clone(),
2127+
place,
20742128
ty::CaptureInfo {
20752129
capture_kind_expr_id: Some(diag_expr_id),
20762130
path_expr_id: Some(diag_expr_id),
@@ -2092,14 +2146,16 @@ impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> {
20922146
// The region here will get discarded/ignored
20932147
let capture_kind = ty::UpvarCapture::ByRef(bk);
20942148

2149+
let span = self.fcx.tcx.hir_span(diag_expr_id);
2150+
let place = self.fcx.normalize_capture_place(span, place_with_id.place.clone());
2151+
20952152
// We only want repr packed restriction to be applied to reading references into a packed
20962153
// struct, and not when the data is being moved. Therefore we call this method here instead
20972154
// of in `restrict_capture_precision`.
2098-
let (place, mut capture_kind) =
2099-
restrict_repr_packed_field_ref_capture(place_with_id.place.clone(), capture_kind);
2155+
let (place, mut capture_kind) = restrict_repr_packed_field_ref_capture(place, capture_kind);
21002156

21012157
// Raw pointers don't inherit mutability
2102-
if place_with_id.place.deref_tys().any(Ty::is_raw_ptr) {
2158+
if place.deref_tys().any(Ty::is_raw_ptr) {
21032159
capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::Immutable);
21042160
}
21052161

tests/crashes/120911.rs

Lines changed: 0 additions & 26 deletions
This file was deleted.

tests/crashes/120811.rs renamed to tests/ui/closures/closure-capture-hrtb-gat-no-ice-120811.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,34 @@
1-
//@ known-bug: #120811
1+
//@ check-pass
2+
3+
// Regression test for #120811.
4+
// Used to ICE during closure capture analysis.
25

36
trait Container {
47
type Item<'a>;
58
}
9+
610
impl Container for () {
711
type Item<'a> = ();
812
}
13+
914
struct Exchange<C, F> {
1015
_marker: std::marker::PhantomData<(C, F)>,
1116
}
17+
1218
fn exchange<C, F>(_: F) -> Exchange<C, F>
1319
where
1420
C: Container,
1521
for<'a> F: FnMut(&C::Item<'a>),
1622
{
1723
unimplemented!()
1824
}
25+
1926
trait Parallelization<C> {}
27+
2028
impl<C, F> Parallelization<C> for Exchange<C, F> {}
29+
2130
fn unary_frontier<P: Parallelization<()>>(_: P) {}
31+
2232
fn main() {
2333
let exchange = exchange(|_| ());
2434
let _ = || {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Regression test for #151579
2+
//@ compile-flags: -Znext-solver=globally
3+
//@ edition:2018
4+
5+
#![deny(rust_2021_incompatible_closure_captures)]
6+
7+
struct Dummy;
8+
9+
trait Trait {
10+
type Assoc;
11+
}
12+
13+
impl Trait for Dummy {
14+
type Assoc = (*mut i32,);
15+
}
16+
17+
struct SyncPointer(<Dummy as Trait>::Assoc);
18+
unsafe impl Sync for SyncPointer {}
19+
20+
fn test_assoc_capture(a: SyncPointer) {
21+
let _ = move || {
22+
//~^ ERROR: changes to closure capture
23+
let _x = a.0.0;
24+
};
25+
}
26+
27+
fn main() {
28+
let ptr = SyncPointer((std::ptr::null_mut(),));
29+
test_assoc_capture(ptr);
30+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error: changes to closure capture in Rust 2021 will affect which traits the closure implements
2+
--> $DIR/normalize-capture-place-151579.rs:21:13
3+
|
4+
LL | let _ = move || {
5+
| ^^^^^^^ in Rust 2018, this closure implements `Sync` as `a` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `a` is not fully captured and `a.0.0` does not implement `Sync`
6+
LL |
7+
LL | let _x = a.0.0;
8+
| ----- in Rust 2018, this closure captures all of `a`, but in Rust 2021, it will only capture `a.0.0`
9+
|
10+
= note: for more information, see <https://doc.rust-lang.org/edition-guide/rust-2021/disjoint-capture-in-closures.html>
11+
note: the lint level is defined here
12+
--> $DIR/normalize-capture-place-151579.rs:5:9
13+
|
14+
LL | #![deny(rust_2021_incompatible_closure_captures)]
15+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16+
help: add a dummy let to cause `a` to be fully captured
17+
|
18+
LL ~ let _ = move || {
19+
LL + let _ = &a;
20+
|
21+
22+
error: aborting due to 1 previous error
23+

0 commit comments

Comments
 (0)