Skip to content

Commit 0110aa8

Browse files
committed
Auto merge of #119101 - compiler-errors:outlives, r=<try>
Normalize region obligation in lexical region resolution with next-gen solver This normalizes region obligations when we `resolve_regions`, since they may be unnormalized with deferred projection equality. It's pretty hard to add tests that exercise this without also triggering MIR borrowck errors (because we don't normalize there yet). I've added one test with two revisions that should test that we both 1. normalize region obligations in the param env, and 2. normalize registered region obligations during lexical region resolution.
2 parents d5fd099 + 9ae7edf commit 0110aa8

File tree

21 files changed

+164
-53
lines changed

21 files changed

+164
-53
lines changed

compiler/rustc_borrowck/src/type_check/constraint_conversion.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
157157
tcx,
158158
region_bound_pairs,
159159
Some(implicit_region_bound),
160-
param_env,
160+
// FIXME(-Znext-solver): These bounds are not normalized!
161+
param_env.caller_bounds(),
161162
)
162163
.type_must_outlive(origin, t1, r2, constraint_category);
163164
}

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use rustc_middle::ty::{
2020
};
2121
use rustc_middle::ty::{GenericParamDefKind, TyCtxt};
2222
use rustc_span::Span;
23+
use rustc_trait_selection::regions::InferCtxtRegionExt;
2324
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
2425
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
2526
use rustc_trait_selection::traits::{
@@ -380,7 +381,7 @@ fn compare_method_predicate_entailment<'tcx>(
380381
param_env,
381382
infcx.implied_bounds_tys(param_env, impl_m_def_id, &wf_tys),
382383
);
383-
let errors = infcx.resolve_regions(&outlives_env);
384+
let errors = infcx.resolve_regions_normalizing_outlives_obligations(&outlives_env);
384385
if !errors.is_empty() {
385386
return Err(infcx
386387
.tainted_by_errors()

compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_middle::ty::{
88
self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperVisitable, TypeVisitable, TypeVisitor,
99
};
1010
use rustc_span::Span;
11+
use rustc_trait_selection::regions::InferCtxtRegionExt;
1112
use rustc_trait_selection::traits::{
1213
elaborate, normalize_param_env_or_error, outlives_bounds::InferCtxtExt, ObligationCtxt,
1314
};
@@ -160,7 +161,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
160161
param_env,
161162
infcx.implied_bounds_tys(param_env, impl_m.def_id.expect_local(), &implied_wf_types),
162163
);
163-
let errors = infcx.resolve_regions(&outlives_env);
164+
let errors = infcx.resolve_regions_normalizing_outlives_obligations(&outlives_env);
164165
if !errors.is_empty() {
165166
tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (regions)");
166167
return;

compiler/rustc_hir_analysis/src/check/dropck.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
88
use rustc_middle::ty::util::CheckRegions;
99
use rustc_middle::ty::GenericArgsRef;
1010
use rustc_middle::ty::{self, TyCtxt};
11+
use rustc_trait_selection::regions::InferCtxtRegionExt;
1112
use rustc_trait_selection::traits::{self, ObligationCtxt};
1213

1314
use crate::errors;
@@ -173,7 +174,9 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
173174
return Err(guar.unwrap());
174175
}
175176

176-
let errors = ocx.infcx.resolve_regions(&OutlivesEnvironment::new(param_env));
177+
let errors = ocx
178+
.infcx
179+
.resolve_regions_normalizing_outlives_obligations(&OutlivesEnvironment::new(param_env));
177180
if !errors.is_empty() {
178181
let mut guar = None;
179182
for error in errors {
@@ -188,6 +191,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
188191
RegionResolutionError::UpperBoundUniverseConflict(a, _, _, _, b) => {
189192
format!("{b}: {a}", a = ty::Region::new_var(tcx, a))
190193
}
194+
RegionResolutionError::CannotNormalize(..) => todo!(),
191195
};
192196
guar = Some(
193197
struct_span_code_err!(

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+19-25
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ use rustc_hir as hir;
1010
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
1111
use rustc_hir::lang_items::LangItem;
1212
use rustc_hir::ItemKind;
13-
use rustc_infer::infer::outlives::env::{OutlivesEnvironment, RegionBoundPairs};
14-
use rustc_infer::infer::outlives::obligations::TypeOutlives;
13+
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
1514
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
16-
use rustc_middle::mir::ConstraintCategory;
1715
use rustc_middle::query::Providers;
1816
use rustc_middle::ty::print::with_no_trimmed_paths;
1917
use rustc_middle::ty::trait_def::TraitSpecializationKind;
@@ -26,6 +24,7 @@ use rustc_session::parse::feature_err;
2624
use rustc_span::symbol::{sym, Ident, Symbol};
2725
use rustc_span::{Span, DUMMY_SP};
2826
use rustc_target::spec::abi::Abi;
27+
use rustc_trait_selection::regions::InferCtxtRegionExt;
2928
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
3029
use rustc_trait_selection::traits::misc::{
3130
type_allowed_to_implement_const_param_ty, ConstParamTyImplementationError,
@@ -136,7 +135,7 @@ where
136135
infcx.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, false);
137136
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
138137

139-
let errors = infcx.resolve_regions(&outlives_env);
138+
let errors = infcx.resolve_regions_normalizing_outlives_obligations(&outlives_env);
140139
if errors.is_empty() {
141140
return Ok(());
142141
}
@@ -177,7 +176,8 @@ where
177176
let implied_bounds =
178177
infcx_compat.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, true);
179178
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
180-
let errors_compat = infcx_compat.resolve_regions(&outlives_env);
179+
let errors_compat =
180+
infcx_compat.resolve_regions_normalizing_outlives_obligations(&outlives_env);
181181
if errors_compat.is_empty() {
182182
Ok(())
183183
} else {
@@ -731,10 +731,12 @@ fn ty_known_to_outlive<'tcx>(
731731
ty: Ty<'tcx>,
732732
region: ty::Region<'tcx>,
733733
) -> bool {
734-
resolve_regions_with_wf_tys(tcx, id, param_env, wf_tys, |infcx, region_bound_pairs| {
735-
let origin = infer::RelateParamBound(DUMMY_SP, ty, None);
736-
let outlives = &mut TypeOutlives::new(infcx, tcx, region_bound_pairs, None, param_env);
737-
outlives.type_must_outlive(origin, ty, region, ConstraintCategory::BoringNoLocation);
734+
test_region_obligations(tcx, id, param_env, wf_tys, |infcx| {
735+
infcx.register_region_obligation(infer::RegionObligation {
736+
sub_region: region,
737+
sup_type: ty,
738+
origin: infer::RelateParamBound(DUMMY_SP, ty, None),
739+
});
738740
})
739741
}
740742

@@ -748,42 +750,34 @@ fn region_known_to_outlive<'tcx>(
748750
region_a: ty::Region<'tcx>,
749751
region_b: ty::Region<'tcx>,
750752
) -> bool {
751-
resolve_regions_with_wf_tys(tcx, id, param_env, wf_tys, |mut infcx, _| {
752-
use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate;
753-
let origin = infer::RelateRegionParamBound(DUMMY_SP);
754-
// `region_a: region_b` -> `region_b <= region_a`
755-
infcx.push_sub_region_constraint(
756-
origin,
757-
region_b,
758-
region_a,
759-
ConstraintCategory::BoringNoLocation,
760-
);
753+
test_region_obligations(tcx, id, param_env, wf_tys, |infcx| {
754+
infcx.sub_regions(infer::RelateRegionParamBound(DUMMY_SP), region_b, region_a);
761755
})
762756
}
763757

764758
/// Given a known `param_env` and a set of well formed types, set up an
765759
/// `InferCtxt`, call the passed function (to e.g. set up region constraints
766760
/// to be tested), then resolve region and return errors
767-
fn resolve_regions_with_wf_tys<'tcx>(
761+
fn test_region_obligations<'tcx>(
768762
tcx: TyCtxt<'tcx>,
769763
id: LocalDefId,
770764
param_env: ty::ParamEnv<'tcx>,
771765
wf_tys: &FxIndexSet<Ty<'tcx>>,
772-
add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'tcx>, &'a RegionBoundPairs<'tcx>),
766+
add_constraints: impl FnOnce(&InferCtxt<'tcx>),
773767
) -> bool {
774768
// Unfortunately, we have to use a new `InferCtxt` each call, because
775769
// region constraints get added and solved there and we need to test each
776770
// call individually.
777771
let infcx = tcx.infer_ctxt().build();
772+
773+
add_constraints(&infcx);
774+
778775
let outlives_environment = OutlivesEnvironment::with_bounds(
779776
param_env,
780777
infcx.implied_bounds_tys(param_env, id, wf_tys),
781778
);
782-
let region_bound_pairs = outlives_environment.region_bound_pairs();
783-
784-
add_constraints(&infcx, region_bound_pairs);
785779

786-
let errors = infcx.resolve_regions(&outlives_environment);
780+
let errors = infcx.resolve_regions_normalizing_outlives_obligations(&outlives_environment);
787781
debug!(?errors, "errors");
788782

789783
// If we were able to prove that the type outlives the region without

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
518518

519519
self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit();
520520
}
521+
522+
RegionResolutionError::CannotNormalize(ty, origin) => {
523+
self.tcx
524+
.dcx()
525+
.struct_span_err(origin.span(), format!("cannot normalize `{ty}`"))
526+
.emit();
527+
}
521528
}
522529
}
523530
}
@@ -559,7 +566,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
559566
RegionResolutionError::GenericBoundFailure(..) => true,
560567
RegionResolutionError::ConcreteFailure(..)
561568
| RegionResolutionError::SubSupConflict(..)
562-
| RegionResolutionError::UpperBoundUniverseConflict(..) => false,
569+
| RegionResolutionError::UpperBoundUniverseConflict(..)
570+
| RegionResolutionError::CannotNormalize(..) => false,
563571
};
564572

565573
let mut errors = if errors.iter().all(|e| is_bound_failure(e)) {
@@ -574,6 +582,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
574582
RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
575583
RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(),
576584
RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(),
585+
RegionResolutionError::CannotNormalize(_, ref sro) => sro.span(),
577586
});
578587
errors
579588
}

compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ pub enum RegionResolutionError<'tcx> {
9898
SubregionOrigin<'tcx>, // cause of the constraint
9999
Region<'tcx>, // the placeholder `'b`
100100
),
101+
102+
CannotNormalize(Ty<'tcx>, SubregionOrigin<'tcx>),
101103
}
102104

103105
impl<'tcx> RegionResolutionError<'tcx> {
@@ -106,7 +108,8 @@ impl<'tcx> RegionResolutionError<'tcx> {
106108
RegionResolutionError::ConcreteFailure(origin, _, _)
107109
| RegionResolutionError::GenericBoundFailure(origin, _, _)
108110
| RegionResolutionError::SubSupConflict(_, _, origin, _, _, _, _)
109-
| RegionResolutionError::UpperBoundUniverseConflict(_, _, _, origin, _) => origin,
111+
| RegionResolutionError::UpperBoundUniverseConflict(_, _, _, origin, _)
112+
| RegionResolutionError::CannotNormalize(_, origin) => origin,
110113
}
111114
}
112115
}

compiler/rustc_infer/src/infer/outlives/mod.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use super::{InferCtxt, RegionResolutionError};
55
use crate::infer::free_regions::RegionRelations;
66
use crate::infer::lexical_region_resolve;
77
use rustc_middle::traits::query::OutlivesBound;
8-
use rustc_middle::ty;
8+
use rustc_middle::ty::{self, Ty};
99

1010
pub mod components;
1111
pub mod env;
@@ -41,12 +41,22 @@ impl<'tcx> InferCtxt<'tcx> {
4141
/// result. After this, no more unification operations should be
4242
/// done -- or the compiler will panic -- but it is legal to use
4343
/// `resolve_vars_if_possible` as well as `fully_resolve`.
44+
///
45+
/// If you are in a crate that has access to `rustc_trai_selection`,
46+
/// then it's probably better to use `resolve_regions_normalizing_outlives_obligations`,
47+
/// which knows how to normalize registered region obligations.
4448
#[must_use]
4549
pub fn resolve_regions(
4650
&self,
4751
outlives_env: &OutlivesEnvironment<'tcx>,
52+
deeply_normalize_ty: impl Fn(Ty<'tcx>) -> Result<Ty<'tcx>, Ty<'tcx>>,
4853
) -> Vec<RegionResolutionError<'tcx>> {
49-
self.process_registered_region_obligations(outlives_env);
54+
match self.process_registered_region_obligations(outlives_env, deeply_normalize_ty) {
55+
Ok(()) => {}
56+
Err((ty, origin)) => {
57+
return vec![RegionResolutionError::CannotNormalize(ty, origin)];
58+
}
59+
};
5060

5161
let (var_infos, data) = {
5262
let mut inner = self.inner.borrow_mut();

compiler/rustc_infer/src/infer/outlives/obligations.rs

+33-7
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,9 @@ use crate::infer::{
6868
use crate::traits::{ObligationCause, ObligationCauseCode};
6969
use rustc_data_structures::undo_log::UndoLogs;
7070
use rustc_middle::mir::ConstraintCategory;
71-
use rustc_middle::ty::GenericArgKind;
7271
use rustc_middle::ty::{self, GenericArgsRef, Region, Ty, TyCtxt, TypeVisitableExt};
72+
use rustc_middle::ty::{GenericArgKind, ToPredicate};
73+
use rustc_span::DUMMY_SP;
7374
use smallvec::smallvec;
7475

7576
use super::env::OutlivesEnvironment;
@@ -123,26 +124,51 @@ impl<'tcx> InferCtxt<'tcx> {
123124
/// flow of the inferencer. The key point is that it is
124125
/// invoked after all type-inference variables have been bound --
125126
/// right before lexical region resolution.
126-
#[instrument(level = "debug", skip(self, outlives_env))]
127-
pub fn process_registered_region_obligations(&self, outlives_env: &OutlivesEnvironment<'tcx>) {
127+
#[instrument(level = "debug", skip(self, outlives_env, deeply_normalize_ty))]
128+
pub fn process_registered_region_obligations<E>(
129+
&self,
130+
outlives_env: &OutlivesEnvironment<'tcx>,
131+
mut deeply_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
132+
) -> Result<(), (E, SubregionOrigin<'tcx>)> {
128133
assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot");
129134

135+
let normalized_caller_bounds: Vec<_> = outlives_env
136+
.param_env
137+
.caller_bounds()
138+
.iter()
139+
.filter_map(|clause| {
140+
let bound_clause = clause.kind();
141+
let ty::ClauseKind::TypeOutlives(outlives) = bound_clause.skip_binder() else {
142+
return None;
143+
};
144+
Some(deeply_normalize_ty(outlives.0).map(|ty| {
145+
bound_clause
146+
.rebind(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, outlives.1)))
147+
.to_predicate(self.tcx)
148+
}))
149+
})
150+
// FIXME: How do we accurately report an error here :(
151+
.try_collect()
152+
.map_err(|e| (e, SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP)))?;
153+
130154
let my_region_obligations = self.take_registered_region_obligations();
131155

132156
for RegionObligation { sup_type, sub_region, origin } in my_region_obligations {
157+
let sup_type = deeply_normalize_ty(sup_type).map_err(|e| (e, origin.clone()))?;
133158
debug!(?sup_type, ?sub_region, ?origin);
134-
let sup_type = self.resolve_vars_if_possible(sup_type);
135159

136160
let outlives = &mut TypeOutlives::new(
137161
self,
138162
self.tcx,
139163
outlives_env.region_bound_pairs(),
140164
None,
141-
outlives_env.param_env,
165+
&normalized_caller_bounds,
142166
);
143167
let category = origin.to_constraint_category();
144168
outlives.type_must_outlive(origin, sup_type, sub_region, category);
145169
}
170+
171+
Ok(())
146172
}
147173
}
148174

@@ -190,7 +216,7 @@ where
190216
tcx: TyCtxt<'tcx>,
191217
region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
192218
implicit_region_bound: Option<ty::Region<'tcx>>,
193-
param_env: ty::ParamEnv<'tcx>,
219+
caller_bounds: &'cx [ty::Clause<'tcx>],
194220
) -> Self {
195221
Self {
196222
delegate,
@@ -199,7 +225,7 @@ where
199225
tcx,
200226
region_bound_pairs,
201227
implicit_region_bound,
202-
param_env,
228+
caller_bounds,
203229
),
204230
}
205231
}

compiler/rustc_infer/src/infer/outlives/verify.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,17 @@ pub struct VerifyBoundCx<'cx, 'tcx> {
2323
/// Outside of borrowck the only way to prove `T: '?0` is by
2424
/// setting `'?0` to `'empty`.
2525
implicit_region_bound: Option<ty::Region<'tcx>>,
26-
param_env: ty::ParamEnv<'tcx>,
26+
caller_bounds: &'cx [ty::Clause<'tcx>],
2727
}
2828

2929
impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
3030
pub fn new(
3131
tcx: TyCtxt<'tcx>,
3232
region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
3333
implicit_region_bound: Option<ty::Region<'tcx>>,
34-
param_env: ty::ParamEnv<'tcx>,
34+
caller_bounds: &'cx [ty::Clause<'tcx>],
3535
) -> Self {
36-
Self { tcx, region_bound_pairs, implicit_region_bound, param_env }
36+
Self { tcx, region_bound_pairs, implicit_region_bound, caller_bounds }
3737
}
3838

3939
#[instrument(level = "debug", skip(self))]
@@ -219,8 +219,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
219219
// To start, collect bounds from user environment. Note that
220220
// parameter environments are already elaborated, so we don't
221221
// have to worry about that.
222-
let c_b = self.param_env.caller_bounds();
223-
let param_bounds = self.collect_outlives_from_clause_list(erased_ty, c_b.into_iter());
222+
let param_bounds =
223+
self.collect_outlives_from_clause_list(erased_ty, self.caller_bounds.iter().copied());
224224

225225
// Next, collect regions we scraped from the well-formedness
226226
// constraints in the fn signature. To do that, we walk the list

compiler/rustc_infer/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#![feature(extend_one)]
2323
#![feature(let_chains)]
2424
#![feature(if_let_guard)]
25+
#![feature(iterator_try_collect)]
2526
#![feature(min_specialization)]
2627
#![feature(never_type)]
2728
#![feature(try_blocks)]

compiler/rustc_trait_selection/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ extern crate smallvec;
4040

4141
pub mod errors;
4242
pub mod infer;
43+
pub mod regions;
4344
pub mod solve;
4445
pub mod traits;
4546

0 commit comments

Comments
 (0)