Skip to content

Commit 795401e

Browse files
committed
correctly handle normalization in implied bounds
1 parent 8859fde commit 795401e

File tree

3 files changed

+68
-131
lines changed

3 files changed

+68
-131
lines changed

compiler/rustc_trait_selection/src/solve/mod.rs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -140,19 +140,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
140140
#[instrument(level = "debug", skip(self))]
141141
fn compute_well_formed_goal(
142142
&mut self,
143-
goal: Goal<'tcx, ty::GenericArg<'tcx>>,
143+
Goal { predicate, param_env }: Goal<'tcx, ty::GenericArg<'tcx>>,
144144
) -> QueryResult<'tcx> {
145-
match crate::traits::wf::unnormalized_obligations(
146-
self.infcx,
147-
goal.param_env,
148-
goal.predicate,
149-
) {
150-
Some(obligations) => {
151-
self.add_goals(obligations.into_iter().map(|o| o.into()));
152-
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
153-
}
154-
None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS),
155-
}
145+
debug_assert_eq!(predicate, self.infcx.resolve_vars_if_possible(predicate));
146+
let obligations =
147+
crate::traits::wf::unnormalized_obligations(self.tcx(), param_env, predicate);
148+
self.add_goals(obligations.into_iter().map(|o| o.into()));
149+
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
156150
}
157151

158152
#[instrument(level = "debug", skip(self), ret)]

compiler/rustc_trait_selection/src/traits/wf.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,21 +77,20 @@ pub fn obligations<'tcx>(
7777

7878
/// Compute the predicates that are required for a type to be well-formed.
7979
///
80-
/// This is only intended to be used in the new solver, since it does not
81-
/// take into account recursion depth or proper error-reporting spans.
80+
/// This is only intended to be used in implied bounds computation and in
81+
/// the new solver, since it does not take into account recursion depth or
82+
/// proper error-reporting spans.
8283
pub fn unnormalized_obligations<'tcx>(
83-
infcx: &InferCtxt<'tcx>,
84+
tcx: TyCtxt<'tcx>,
8485
param_env: ty::ParamEnv<'tcx>,
8586
arg: GenericArg<'tcx>,
86-
) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
87+
) -> Vec<traits::PredicateObligation<'tcx>> {
8788
if let ty::GenericArgKind::Lifetime(..) = arg.unpack() {
88-
return Some(vec![]);
89+
return vec![];
8990
}
9091

91-
debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));
92-
9392
let mut wf = WfPredicates {
94-
tcx: infcx.tcx,
93+
tcx,
9594
param_env,
9695
body_id: CRATE_DEF_ID,
9796
span: DUMMY_SP,
@@ -100,7 +99,7 @@ pub fn unnormalized_obligations<'tcx>(
10099
item: None,
101100
};
102101
wf.compute(arg);
103-
Some(wf.out)
102+
wf.out
104103
}
105104

106105
/// Returns the obligations that make this trait reference

compiler/rustc_traits/src/implied_outlives_bounds.rs

Lines changed: 54 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,16 @@
22
//! Do not call this query directory. See
33
//! [`rustc_trait_selection::traits::query::type_op::implied_outlives_bounds`].
44
5-
use rustc_infer::infer::canonical::{self, Canonical};
5+
use rustc_infer::infer::canonical;
66
use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
77
use rustc_infer::infer::TyCtxtInferExt;
88
use rustc_infer::traits::query::OutlivesBound;
99
use rustc_middle::ty::query::Providers;
1010
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
11-
use rustc_span::def_id::CRATE_DEF_ID;
12-
use rustc_span::source_map::DUMMY_SP;
1311
use rustc_trait_selection::infer::InferCtxtBuilderExt;
1412
use rustc_trait_selection::traits::query::{CanonicalTyGoal, Fallible, NoSolution};
1513
use rustc_trait_selection::traits::wf;
16-
use rustc_trait_selection::traits::ObligationCtxt;
14+
use rustc_trait_selection::traits::ObligationCause;
1715
use smallvec::{smallvec, SmallVec};
1816

1917
pub(crate) fn provide(p: &mut Providers) {
@@ -23,79 +21,52 @@ pub(crate) fn provide(p: &mut Providers) {
2321
fn implied_outlives_bounds<'tcx>(
2422
tcx: TyCtxt<'tcx>,
2523
goal: CanonicalTyGoal<'tcx>,
26-
) -> Result<
27-
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>,
28-
NoSolution,
29-
> {
24+
) -> Fallible<canonical::CanonicalQueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>> {
3025
tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| {
3126
let (param_env, ty) = key.into_parts();
32-
compute_implied_outlives_bounds(ocx, param_env, ty)
27+
28+
compute_implied_outlives_bounds(tcx, param_env, ty, |ty| {
29+
let ty = ocx.normalize(&ObligationCause::dummy(), param_env, ty);
30+
if !ocx.select_all_or_error().is_empty() {
31+
return Err(NoSolution);
32+
}
33+
let ty = ocx.infcx.resolve_vars_if_possible(ty);
34+
assert!(!ty.has_non_region_infer());
35+
Ok(ty)
36+
})
3337
})
3438
}
3539

40+
/// For the sake of completeness, we should be careful when dealing with inference artifacts:
41+
/// - This function shouldn't access an InferCtxt.
42+
/// - `ty` must be fully resolved.
43+
/// - `normalize_op` must return a fully resolved type.
3644
fn compute_implied_outlives_bounds<'tcx>(
37-
ocx: &ObligationCtxt<'_, 'tcx>,
45+
tcx: TyCtxt<'tcx>,
3846
param_env: ty::ParamEnv<'tcx>,
3947
ty: Ty<'tcx>,
48+
normalize_op: impl Fn(Ty<'tcx>) -> Fallible<Ty<'tcx>>,
4049
) -> Fallible<Vec<OutlivesBound<'tcx>>> {
41-
let tcx = ocx.infcx.tcx;
42-
4350
// Sometimes when we ask what it takes for T: WF, we get back that
4451
// U: WF is required; in that case, we push U onto this stack and
4552
// process it next. Because the resulting predicates aren't always
4653
// guaranteed to be a subset of the original type, so we need to store the
4754
// WF args we've computed in a set.
4855
let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
49-
let mut wf_args = vec![ty.into()];
56+
let mut wf_args = vec![ty.into(), normalize_op(ty)?.into()];
5057

51-
let mut outlives_bounds: Vec<ty::OutlivesPredicate<ty::GenericArg<'tcx>, ty::Region<'tcx>>> =
52-
vec![];
58+
let mut outlives_bounds: Vec<OutlivesBound<'tcx>> = vec![];
5359

5460
while let Some(arg) = wf_args.pop() {
5561
if !checked_wf_args.insert(arg) {
5662
continue;
5763
}
5864

59-
// Compute the obligations for `arg` to be well-formed. If `arg` is
60-
// an unresolved inference variable, just substituted an empty set
61-
// -- because the return type here is going to be things we *add*
62-
// to the environment, it's always ok for this set to be smaller
63-
// than the ultimate set. (Note: normally there won't be
64-
// unresolved inference variables here anyway, but there might be
65-
// during typeck under some circumstances.)
66-
//
67-
// FIXME(@lcnr): It's not really "always fine", having fewer implied
68-
// bounds can be backward incompatible, e.g. #101951 was caused by
69-
// us not dealing with inference vars in `TypeOutlives` predicates.
70-
let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP)
71-
.unwrap_or_default();
72-
73-
for obligation in obligations {
74-
debug!(?obligation);
65+
// From the full set of obligations, just filter down to the region relationships.
66+
for obligation in wf::unnormalized_obligations(tcx, param_env, arg) {
7567
assert!(!obligation.has_escaping_bound_vars());
76-
77-
// While these predicates should all be implied by other parts of
78-
// the program, they are still relevant as they may constrain
79-
// inference variables, which is necessary to add the correct
80-
// implied bounds in some cases, mostly when dealing with projections.
81-
//
82-
// Another important point here: we only register `Projection`
83-
// predicates, since otherwise we might register outlives
84-
// predicates containing inference variables, and we don't
85-
// learn anything new from those.
86-
if obligation.predicate.has_non_region_infer() {
87-
match obligation.predicate.kind().skip_binder() {
88-
ty::PredicateKind::Clause(ty::Clause::Projection(..))
89-
| ty::PredicateKind::AliasEq(..) => {
90-
ocx.register_obligation(obligation.clone());
91-
}
92-
_ => {}
93-
}
94-
}
95-
96-
let pred = match obligation.predicate.kind().no_bound_vars() {
97-
None => continue,
98-
Some(pred) => pred,
68+
let Some(pred) = obligation.predicate.kind().no_bound_vars() else {
69+
continue;
9970
};
10071
match pred {
10172
ty::PredicateKind::Clause(ty::Clause::Trait(..))
@@ -114,48 +85,27 @@ fn compute_implied_outlives_bounds<'tcx>(
11485
| ty::PredicateKind::TypeWellFormedFromEnv(..) => {}
11586

11687
// We need to search through *all* WellFormed predicates
117-
ty::PredicateKind::WellFormed(arg) => {
118-
wf_args.push(arg);
119-
}
88+
ty::PredicateKind::WellFormed(arg) => wf_args.push(arg),
12089

12190
// We need to register region relationships
122-
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(ty::OutlivesPredicate(
123-
r_a,
124-
r_b,
125-
))) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)),
91+
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(
92+
ty::OutlivesPredicate(r_a, r_b),
93+
)) => outlives_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a)),
12694

12795
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
12896
ty_a,
12997
r_b,
130-
))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)),
98+
))) => {
99+
let ty_a = normalize_op(ty_a)?;
100+
let mut components = smallvec![];
101+
push_outlives_components(tcx, ty_a, &mut components);
102+
outlives_bounds.extend(implied_bounds_from_components(r_b, components))
103+
}
131104
}
132105
}
133106
}
134107

135-
// This call to `select_all_or_error` is necessary to constrain inference variables, which we
136-
// use further down when computing the implied bounds.
137-
match ocx.select_all_or_error().as_slice() {
138-
[] => (),
139-
_ => return Err(NoSolution),
140-
}
141-
142-
// We lazily compute the outlives components as
143-
// `select_all_or_error` constrains inference variables.
144-
let implied_bounds = outlives_bounds
145-
.into_iter()
146-
.flat_map(|ty::OutlivesPredicate(a, r_b)| match a.unpack() {
147-
ty::GenericArgKind::Lifetime(r_a) => vec![OutlivesBound::RegionSubRegion(r_b, r_a)],
148-
ty::GenericArgKind::Type(ty_a) => {
149-
let ty_a = ocx.infcx.resolve_vars_if_possible(ty_a);
150-
let mut components = smallvec![];
151-
push_outlives_components(tcx, ty_a, &mut components);
152-
implied_bounds_from_components(r_b, components)
153-
}
154-
ty::GenericArgKind::Const(_) => unreachable!(),
155-
})
156-
.collect();
157-
158-
Ok(implied_bounds)
108+
Ok(outlives_bounds)
159109
}
160110

161111
/// When we have an implied bound that `T: 'a`, we can further break
@@ -165,28 +115,22 @@ fn compute_implied_outlives_bounds<'tcx>(
165115
fn implied_bounds_from_components<'tcx>(
166116
sub_region: ty::Region<'tcx>,
167117
sup_components: SmallVec<[Component<'tcx>; 4]>,
168-
) -> Vec<OutlivesBound<'tcx>> {
169-
sup_components
170-
.into_iter()
171-
.filter_map(|component| {
172-
match component {
173-
Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)),
174-
Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)),
175-
Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)),
176-
Component::EscapingAlias(_) =>
177-
// If the projection has escaping regions, don't
178-
// try to infer any implied bounds even for its
179-
// free components. This is conservative, because
180-
// the caller will still have to prove that those
181-
// free components outlive `sub_region`. But the
182-
// idea is that the WAY that the caller proves
183-
// that may change in the future and we want to
184-
// give ourselves room to get smarter here.
185-
{
186-
None
187-
}
188-
Component::UnresolvedInferenceVariable(..) => None,
189-
}
190-
})
191-
.collect()
118+
) -> impl Iterator<Item = OutlivesBound<'tcx>> {
119+
sup_components.into_iter().filter_map(move |component| {
120+
match component {
121+
Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)),
122+
Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)),
123+
Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)),
124+
// If the projection has escaping regions, don't
125+
// try to infer any implied bounds even for its
126+
// free components. This is conservative, because
127+
// the caller will still have to prove that those
128+
// free components outlive `sub_region`. But the
129+
// idea is that the WAY that the caller proves
130+
// that may change in the future and we want to
131+
// give ourselves room to get smarter here.
132+
Component::EscapingAlias(_) => None,
133+
Component::UnresolvedInferenceVariable(..) => bug!("inference var in implied bounds"),
134+
}
135+
})
192136
}

0 commit comments

Comments
 (0)