Skip to content

Commit f503f02

Browse files
committed
Canonicalize v2 of implied_bounds query too
1 parent ed20a3e commit f503f02

File tree

8 files changed

+183
-129
lines changed

8 files changed

+183
-129
lines changed

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ where
128128
let infcx_compat = infcx.fork();
129129

130130
debug!(?assumed_wf_types);
131-
let implied_bounds = infcx.implied_bounds_tys(param_env, &assumed_wf_types);
131+
let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, &assumed_wf_types);
132132
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
133133

134134
let errors = infcx.resolve_regions(&outlives_env);

compiler/rustc_middle/src/arena.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ macro_rules! arena_types {
5353
rustc_middle::traits::query::NormalizationResult<'tcx>
5454
>
5555
>,
56-
[] implied_outlives_bounds_compat:
56+
[] implied_outlives_bounds:
5757
rustc_middle::infer::canonical::Canonical<'tcx,
5858
rustc_middle::infer::canonical::QueryResponse<'tcx,
5959
Vec<rustc_middle::traits::query::OutlivesBound<'tcx>>

compiler/rustc_middle/src/query/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1963,12 +1963,12 @@ rustc_queries! {
19631963
}
19641964

19651965
query implied_outlives_bounds(
1966-
goal: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
1966+
goal: CanonicalTyGoal<'tcx>
19671967
) -> Result<
1968-
&'tcx [OutlivesBound<'tcx>],
1968+
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>,
19691969
NoSolution,
19701970
> {
1971-
desc { "computing implied outlives bounds v2 for `{}`", goal.value }
1971+
desc { "computing implied outlives bounds v2 for `{}`", goal.value.value }
19721972
}
19731973

19741974
/// Do not call this query directly:

compiler/rustc_trait_selection/src/traits/outlives_bounds.rs

+103-113
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,6 @@ pub use rustc_middle::traits::query::OutlivesBound;
1212
pub type BoundsCompat<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
1313
pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
1414
pub trait InferCtxtExt<'a, 'tcx> {
15-
fn implied_outlives_bounds_compat(
16-
&self,
17-
param_env: ty::ParamEnv<'tcx>,
18-
body_id: LocalDefId,
19-
ty: Ty<'tcx>,
20-
) -> Vec<OutlivesBound<'tcx>>;
21-
2215
fn implied_bounds_tys_compat(
2316
&'a self,
2417
param_env: ty::ParamEnv<'tcx>,
@@ -29,130 +22,127 @@ pub trait InferCtxtExt<'a, 'tcx> {
2922
fn implied_bounds_tys(
3023
&'a self,
3124
param_env: ty::ParamEnv<'tcx>,
25+
body_id: LocalDefId,
3226
tys: &'a FxIndexSet<Ty<'tcx>>,
3327
) -> Bounds<'a, 'tcx>;
3428
}
3529

36-
impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
37-
/// Implied bounds are region relationships that we deduce
38-
/// automatically. The idea is that (e.g.) a caller must check that a
39-
/// function's argument types are well-formed immediately before
40-
/// calling that fn, and hence the *callee* can assume that its
41-
/// argument types are well-formed. This may imply certain relationships
42-
/// between generic parameters. For example:
43-
/// ```
44-
/// fn foo<T>(x: &T) {}
45-
/// ```
46-
/// can only be called with a `'a` and `T` such that `&'a T` is WF.
47-
/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
48-
///
49-
/// # Parameters
50-
///
51-
/// - `param_env`, the where-clauses in scope
52-
/// - `body_id`, the body-id to use when normalizing assoc types.
53-
/// Note that this may cause outlives obligations to be injected
54-
/// into the inference context with this body-id.
55-
/// - `ty`, the type that we are supposed to assume is WF.
56-
#[instrument(level = "debug", skip(self, param_env, body_id), ret)]
57-
fn implied_outlives_bounds_compat(
58-
&self,
59-
param_env: ty::ParamEnv<'tcx>,
60-
body_id: LocalDefId,
61-
ty: Ty<'tcx>,
62-
) -> Vec<OutlivesBound<'tcx>> {
63-
let ty = self.resolve_vars_if_possible(ty);
64-
let ty = OpportunisticRegionResolver::new(self).fold_ty(ty);
65-
66-
// We do not expect existential variables in implied bounds.
67-
// We may however encounter unconstrained lifetime variables
68-
// in very rare cases.
69-
//
70-
// See `ui/implied-bounds/implied-bounds-unconstrained-2.rs` for
71-
// an example.
72-
assert!(!ty.has_non_region_infer());
73-
74-
let mut canonical_var_values = OriginalQueryValues::default();
75-
let canonical_ty =
76-
self.canonicalize_query_keep_static(param_env.and(ty), &mut canonical_var_values);
77-
let Ok(canonical_result) = self.tcx.implied_outlives_bounds_compat(canonical_ty) else {
78-
return vec![];
79-
};
80-
81-
let mut constraints = QueryRegionConstraints::default();
82-
let Ok(InferOk { value: mut bounds, obligations }) = self
83-
.instantiate_nll_query_response_and_region_obligations(
84-
&ObligationCause::dummy(),
30+
/// Implied bounds are region relationships that we deduce
31+
/// automatically. The idea is that (e.g.) a caller must check that a
32+
/// function's argument types are well-formed immediately before
33+
/// calling that fn, and hence the *callee* can assume that its
34+
/// argument types are well-formed. This may imply certain relationships
35+
/// between generic parameters. For example:
36+
/// ```
37+
/// fn foo<T>(x: &T) {}
38+
/// ```
39+
/// can only be called with a `'a` and `T` such that `&'a T` is WF.
40+
/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
41+
///
42+
/// # Parameters
43+
///
44+
/// - `param_env`, the where-clauses in scope
45+
/// - `body_id`, the body-id to use when normalizing assoc types.
46+
/// Note that this may cause outlives obligations to be injected
47+
/// into the inference context with this body-id.
48+
/// - `ty`, the type that we are supposed to assume is WF.
49+
#[instrument(level = "debug", skip(infcx, param_env, body_id), ret)]
50+
fn implied_outlives_bounds<'a, 'tcx>(
51+
infcx: &'a InferCtxt<'tcx>,
52+
param_env: ty::ParamEnv<'tcx>,
53+
body_id: LocalDefId,
54+
ty: Ty<'tcx>,
55+
compat: bool,
56+
) -> Vec<OutlivesBound<'tcx>> {
57+
let ty = infcx.resolve_vars_if_possible(ty);
58+
let ty = OpportunisticRegionResolver::new(infcx).fold_ty(ty);
59+
60+
// We do not expect existential variables in implied bounds.
61+
// We may however encounter unconstrained lifetime variables
62+
// in very rare cases.
63+
//
64+
// See `ui/implied-bounds/implied-bounds-unconstrained-2.rs` for
65+
// an example.
66+
assert!(!ty.has_non_region_infer());
67+
68+
let mut canonical_var_values = OriginalQueryValues::default();
69+
let canonical_ty =
70+
infcx.canonicalize_query_keep_static(param_env.and(ty), &mut canonical_var_values);
71+
let implied_bounds_result = if compat {
72+
infcx.tcx.implied_outlives_bounds_compat(canonical_ty)
73+
} else {
74+
infcx.tcx.implied_outlives_bounds(canonical_ty)
75+
};
76+
let Ok(canonical_result) = implied_bounds_result else {
77+
return vec![];
78+
};
79+
80+
let mut constraints = QueryRegionConstraints::default();
81+
let Ok(InferOk { value: mut bounds, obligations }) = infcx
82+
.instantiate_nll_query_response_and_region_obligations(
83+
&ObligationCause::dummy(),
84+
param_env,
85+
&canonical_var_values,
86+
canonical_result,
87+
&mut constraints,
88+
)
89+
else {
90+
return vec![];
91+
};
92+
assert_eq!(&obligations, &[]);
93+
94+
// Because of #109628, we may have unexpected placeholders. Ignore them!
95+
// FIXME(#109628): panic in this case once the issue is fixed.
96+
bounds.retain(|bound| !bound.has_placeholders());
97+
98+
if !constraints.is_empty() {
99+
let span = infcx.tcx.def_span(body_id);
100+
101+
debug!(?constraints);
102+
if !constraints.member_constraints.is_empty() {
103+
span_bug!(span, "{:#?}", constraints.member_constraints);
104+
}
105+
106+
// Instantiation may have produced new inference variables and constraints on those
107+
// variables. Process these constraints.
108+
let ocx = ObligationCtxt::new(infcx);
109+
let cause = ObligationCause::misc(span, body_id);
110+
for &constraint in &constraints.outlives {
111+
ocx.register_obligation(infcx.query_outlives_constraint_to_obligation(
112+
constraint,
113+
cause.clone(),
85114
param_env,
86-
&canonical_var_values,
87-
canonical_result,
88-
&mut constraints,
89-
)
90-
else {
91-
return vec![];
92-
};
93-
assert_eq!(&obligations, &[]);
94-
95-
// Because of #109628, we may have unexpected placeholders. Ignore them!
96-
// FIXME(#109628): panic in this case once the issue is fixed.
97-
bounds.retain(|bound| !bound.has_placeholders());
98-
99-
if !constraints.is_empty() {
100-
let span = self.tcx.def_span(body_id);
101-
102-
debug!(?constraints);
103-
if !constraints.member_constraints.is_empty() {
104-
span_bug!(span, "{:#?}", constraints.member_constraints);
105-
}
106-
107-
// Instantiation may have produced new inference variables and constraints on those
108-
// variables. Process these constraints.
109-
let ocx = ObligationCtxt::new(self);
110-
let cause = ObligationCause::misc(span, body_id);
111-
for &constraint in &constraints.outlives {
112-
ocx.register_obligation(self.query_outlives_constraint_to_obligation(
113-
constraint,
114-
cause.clone(),
115-
param_env,
116-
));
117-
}
118-
119-
let errors = ocx.select_all_or_error();
120-
if !errors.is_empty() {
121-
self.tcx.sess.span_delayed_bug(
122-
span,
123-
"implied_outlives_bounds_compat failed to solve obligations from instantiation",
124-
);
125-
}
126-
};
127-
128-
bounds
129-
}
115+
));
116+
}
117+
118+
let errors = ocx.select_all_or_error();
119+
if !errors.is_empty() {
120+
infcx.tcx.sess.span_delayed_bug(
121+
span,
122+
"implied_outlives_bounds_compat failed to solve obligations from instantiation",
123+
);
124+
}
125+
};
126+
127+
bounds
128+
}
130129

130+
impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
131131
fn implied_bounds_tys_compat(
132132
&'a self,
133133
param_env: ParamEnv<'tcx>,
134134
body_id: LocalDefId,
135135
tys: &'a FxIndexSet<Ty<'tcx>>,
136136
) -> BoundsCompat<'a, 'tcx> {
137-
tys.iter().flat_map(move |ty| self.implied_outlives_bounds_compat(param_env, body_id, *ty))
137+
tys.iter().flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, *ty, true))
138138
}
139139

140140
fn implied_bounds_tys(
141141
&'a self,
142142
param_env: ParamEnv<'tcx>,
143+
body_id: LocalDefId,
143144
tys: &'a FxIndexSet<Ty<'tcx>>,
144145
) -> Bounds<'a, 'tcx> {
145-
tys.iter()
146-
.flat_map(move |&ty| {
147-
let ty = self.resolve_vars_if_possible(ty);
148-
let ty = OpportunisticRegionResolver::new(self).fold_ty(ty);
149-
150-
if ty.has_infer() {
151-
return &[] as &[OutlivesBound<'_>];
152-
}
153-
154-
self.tcx.implied_outlives_bounds(param_env.and(ty)).unwrap_or(&[])
155-
})
156-
.copied()
146+
tys.iter().flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, *ty, false))
157147
}
158148
}

compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,14 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
6666
ocx: &ObligationCtxt<'_, 'tcx>,
6767
param_env: ty::ParamEnv<'tcx>,
6868
ty: Ty<'tcx>,
69-
) -> Result<&'tcx [OutlivesBound<'tcx>], NoSolution> {
69+
) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
7070
let normalize_op = |ty| {
7171
let ty = ocx.normalize(&ObligationCause::dummy(), param_env, ty);
7272
if !ocx.select_all_or_error().is_empty() {
7373
return Err(NoSolution);
7474
}
7575
let ty = ocx.infcx.resolve_vars_if_possible(ty);
7676
let ty = OpportunisticRegionResolver::new(&ocx.infcx).fold_ty(ty);
77-
assert!(!ty.has_infer());
7877
Ok(ty)
7978
};
8079

@@ -139,7 +138,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
139138
}
140139
}
141140

142-
Ok(ocx.infcx.tcx.arena.alloc_slice(&outlives_bounds))
141+
Ok(outlives_bounds)
143142
}
144143

145144
pub fn compute_implied_outlives_bounds_compat_inner<'tcx>(

compiler/rustc_traits/src/implied_outlives_bounds.rs

+10-8
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@ use rustc_infer::infer::canonical::{self, Canonical};
66
use rustc_infer::infer::TyCtxtInferExt;
77
use rustc_infer::traits::query::OutlivesBound;
88
use rustc_middle::query::Providers;
9-
use rustc_middle::ty::{self, Ty, TyCtxt};
9+
use rustc_middle::ty::TyCtxt;
1010
use rustc_trait_selection::infer::InferCtxtBuilderExt;
1111
use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::{
1212
compute_implied_outlives_bounds_compat_inner, compute_implied_outlives_bounds_inner,
1313
};
1414
use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution};
15-
use rustc_trait_selection::traits::ObligationCtxt;
1615

1716
pub(crate) fn provide(p: &mut Providers) {
1817
*p = Providers { implied_outlives_bounds_compat, ..*p };
@@ -34,10 +33,13 @@ fn implied_outlives_bounds_compat<'tcx>(
3433

3534
fn implied_outlives_bounds<'tcx>(
3635
tcx: TyCtxt<'tcx>,
37-
goal: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
38-
) -> Result<&'tcx [OutlivesBound<'tcx>], NoSolution> {
39-
let (param_env, goal_ty) = goal.into_parts();
40-
let infcx = tcx.infer_ctxt().build();
41-
let ocx = ObligationCtxt::new(&infcx);
42-
compute_implied_outlives_bounds_inner(&ocx, param_env, goal_ty)
36+
goal: CanonicalTyGoal<'tcx>,
37+
) -> Result<
38+
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>,
39+
NoSolution,
40+
> {
41+
tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| {
42+
let (param_env, ty) = key.into_parts();
43+
compute_implied_outlives_bounds_inner(&ocx, param_env, ty)
44+
})
4345
}
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// check-pass
2+
3+
pub trait QueryBase {
4+
type Db;
5+
}
6+
7+
pub trait AsyncQueryFunction<'f>: // 'f is important
8+
QueryBase<Db = <Self as AsyncQueryFunction<'f>>::SendDb> // bound is important
9+
{
10+
type SendDb;
11+
}
12+
13+
pub struct QueryTable<'me, Q, DB> {
14+
_q: Option<Q>,
15+
_db: Option<DB>,
16+
_marker: Option<&'me ()>,
17+
}
18+
19+
impl<'me, Q> QueryTable<'me, Q, <Q as QueryBase>::Db> // projection is important
20+
// ^^^ removing 'me (and in QueryTable) gives a different error
21+
where
22+
Q: for<'f> AsyncQueryFunction<'f>,
23+
{
24+
pub fn get_async<'a>(&'a mut self) {
25+
panic!();
26+
}
27+
}
28+
29+
fn main() {}

0 commit comments

Comments
 (0)