Skip to content

Commit 4723a9a

Browse files
Rollup merge of #108333 - compiler-errors:new-solver-object-sound, r=lcnr
Make object bound candidates sound in the new trait solver r? `@lcnr`
2 parents 0b6b373 + ed30eff commit 4723a9a

File tree

9 files changed

+309
-3
lines changed

9 files changed

+309
-3
lines changed

compiler/rustc_trait_selection/src/solve/assembly.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,15 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq {
9999
requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
100100
) -> QueryResult<'tcx>;
101101

102+
// Consider a clause specifically for a `dyn Trait` self type. This requires
103+
// additionally checking all of the supertraits and object bounds to hold,
104+
// since they're not implied by the well-formedness of the object type.
105+
fn consider_object_bound_candidate(
106+
ecx: &mut EvalCtxt<'_, 'tcx>,
107+
goal: Goal<'tcx, Self>,
108+
assumption: ty::Predicate<'tcx>,
109+
) -> QueryResult<'tcx>;
110+
102111
fn consider_impl_candidate(
103112
ecx: &mut EvalCtxt<'_, 'tcx>,
104113
goal: Goal<'tcx, Self>,
@@ -455,7 +464,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
455464
for assumption in
456465
elaborate_predicates(tcx, bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)))
457466
{
458-
match G::consider_implied_clause(self, goal, assumption.predicate, []) {
467+
match G::consider_object_bound_candidate(self, goal, assumption.predicate) {
459468
Ok(result) => {
460469
candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
461470
}

compiler/rustc_trait_selection/src/solve/project_goals.rs

+45
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,51 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
128128
}
129129
}
130130

131+
fn consider_object_bound_candidate(
132+
ecx: &mut EvalCtxt<'_, 'tcx>,
133+
goal: Goal<'tcx, Self>,
134+
assumption: ty::Predicate<'tcx>,
135+
) -> QueryResult<'tcx> {
136+
if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred()
137+
&& poly_projection_pred.projection_def_id() == goal.predicate.def_id()
138+
{
139+
ecx.probe(|ecx| {
140+
let assumption_projection_pred =
141+
ecx.instantiate_binder_with_infer(poly_projection_pred);
142+
let mut nested_goals = ecx.eq(
143+
goal.param_env,
144+
goal.predicate.projection_ty,
145+
assumption_projection_pred.projection_ty,
146+
)?;
147+
148+
let tcx = ecx.tcx();
149+
let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
150+
bug!("expected object type in `consider_object_bound_candidate`");
151+
};
152+
nested_goals.extend(
153+
structural_traits::predicates_for_object_candidate(
154+
ecx,
155+
goal.param_env,
156+
goal.predicate.projection_ty.trait_ref(tcx),
157+
bounds,
158+
)
159+
.into_iter()
160+
.map(|pred| goal.with(tcx, pred)),
161+
);
162+
163+
let subst_certainty = ecx.evaluate_all(nested_goals)?;
164+
165+
ecx.eq_term_and_make_canonical_response(
166+
goal,
167+
subst_certainty,
168+
assumption_projection_pred.term,
169+
)
170+
})
171+
} else {
172+
Err(NoSolution)
173+
}
174+
}
175+
131176
fn consider_impl_candidate(
132177
ecx: &mut EvalCtxt<'_, 'tcx>,
133178
goal: Goal<'tcx, ProjectionPredicate<'tcx>>,

compiler/rustc_trait_selection/src/solve/trait_goals.rs

+40
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,46 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
8686
}
8787
}
8888

89+
fn consider_object_bound_candidate(
90+
ecx: &mut EvalCtxt<'_, 'tcx>,
91+
goal: Goal<'tcx, Self>,
92+
assumption: ty::Predicate<'tcx>,
93+
) -> QueryResult<'tcx> {
94+
if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred()
95+
&& poly_trait_pred.def_id() == goal.predicate.def_id()
96+
{
97+
// FIXME: Constness and polarity
98+
ecx.probe(|ecx| {
99+
let assumption_trait_pred =
100+
ecx.instantiate_binder_with_infer(poly_trait_pred);
101+
let mut nested_goals = ecx.eq(
102+
goal.param_env,
103+
goal.predicate.trait_ref,
104+
assumption_trait_pred.trait_ref,
105+
)?;
106+
107+
let tcx = ecx.tcx();
108+
let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
109+
bug!("expected object type in `consider_object_bound_candidate`");
110+
};
111+
nested_goals.extend(
112+
structural_traits::predicates_for_object_candidate(
113+
ecx,
114+
goal.param_env,
115+
goal.predicate.trait_ref,
116+
bounds,
117+
)
118+
.into_iter()
119+
.map(|pred| goal.with(tcx, pred)),
120+
);
121+
122+
ecx.evaluate_all_and_make_canonical_response(nested_goals)
123+
})
124+
} else {
125+
Err(NoSolution)
126+
}
127+
}
128+
89129
fn consider_auto_trait_candidate(
90130
ecx: &mut EvalCtxt<'_, 'tcx>,
91131
goal: Goal<'tcx, Self>,

compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs

+112-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
use rustc_hir::{Movability, Mutability};
1+
use rustc_data_structures::fx::FxHashMap;
2+
use rustc_hir::{def_id::DefId, Movability, Mutability};
23
use rustc_infer::traits::query::NoSolution;
3-
use rustc_middle::ty::{self, Ty, TyCtxt};
4+
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable};
45

56
use crate::solve::EvalCtxt;
67

@@ -231,3 +232,112 @@ pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
231232
}
232233
}
233234
}
235+
236+
/// Assemble a list of predicates that would be present on a theoretical
237+
/// user impl for an object type. These predicates must be checked any time
238+
/// we assemble a built-in object candidate for an object type, since they
239+
/// are not implied by the well-formedness of the type.
240+
///
241+
/// For example, given the following traits:
242+
///
243+
/// ```rust,ignore (theoretical code)
244+
/// trait Foo: Baz {
245+
/// type Bar: Copy;
246+
/// }
247+
///
248+
/// trait Baz {}
249+
/// ```
250+
///
251+
/// For the dyn type `dyn Foo<Item = Ty>`, we can imagine there being a
252+
/// pair of theoretical impls:
253+
///
254+
/// ```rust,ignore (theoretical code)
255+
/// impl Foo for dyn Foo<Item = Ty>
256+
/// where
257+
/// Self: Baz,
258+
/// <Self as Foo>::Bar: Copy,
259+
/// {
260+
/// type Bar = Ty;
261+
/// }
262+
///
263+
/// impl Baz for dyn Foo<Item = Ty> {}
264+
/// ```
265+
///
266+
/// However, in order to make such impls well-formed, we need to do an
267+
/// additional step of eagerly folding the associated types in the where
268+
/// clauses of the impl. In this example, that means replacing
269+
/// `<Self as Foo>::Bar` with `Ty` in the first impl.
270+
pub(crate) fn predicates_for_object_candidate<'tcx>(
271+
ecx: &EvalCtxt<'_, 'tcx>,
272+
param_env: ty::ParamEnv<'tcx>,
273+
trait_ref: ty::TraitRef<'tcx>,
274+
object_bound: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
275+
) -> Vec<ty::Predicate<'tcx>> {
276+
let tcx = ecx.tcx();
277+
let mut requirements = vec![];
278+
requirements.extend(
279+
tcx.super_predicates_of(trait_ref.def_id).instantiate(tcx, trait_ref.substs).predicates,
280+
);
281+
for item in tcx.associated_items(trait_ref.def_id).in_definition_order() {
282+
// FIXME(associated_const_equality): Also add associated consts to
283+
// the requirements here.
284+
if item.kind == ty::AssocKind::Type {
285+
requirements.extend(tcx.item_bounds(item.def_id).subst(tcx, trait_ref.substs));
286+
}
287+
}
288+
289+
let mut replace_projection_with = FxHashMap::default();
290+
for bound in object_bound {
291+
if let ty::ExistentialPredicate::Projection(proj) = bound.skip_binder() {
292+
let proj = proj.with_self_ty(tcx, trait_ref.self_ty());
293+
let old_ty = replace_projection_with.insert(proj.def_id(), bound.rebind(proj));
294+
assert_eq!(
295+
old_ty,
296+
None,
297+
"{} has two substitutions: {} and {}",
298+
proj.projection_ty,
299+
proj.term,
300+
old_ty.unwrap()
301+
);
302+
}
303+
}
304+
305+
requirements.fold_with(&mut ReplaceProjectionWith {
306+
ecx,
307+
param_env,
308+
mapping: replace_projection_with,
309+
})
310+
}
311+
312+
struct ReplaceProjectionWith<'a, 'tcx> {
313+
ecx: &'a EvalCtxt<'a, 'tcx>,
314+
param_env: ty::ParamEnv<'tcx>,
315+
mapping: FxHashMap<DefId, ty::PolyProjectionPredicate<'tcx>>,
316+
}
317+
318+
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceProjectionWith<'_, 'tcx> {
319+
fn interner(&self) -> TyCtxt<'tcx> {
320+
self.ecx.tcx()
321+
}
322+
323+
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
324+
if let ty::Alias(ty::Projection, alias_ty) = *ty.kind()
325+
&& let Some(replacement) = self.mapping.get(&alias_ty.def_id)
326+
{
327+
// We may have a case where our object type's projection bound is higher-ranked,
328+
// but the where clauses we instantiated are not. We can solve this by instantiating
329+
// the binder at the usage site.
330+
let proj = self.ecx.instantiate_binder_with_infer(*replacement);
331+
// FIXME: Technically this folder could be fallible?
332+
let nested = self
333+
.ecx
334+
.eq(self.param_env, alias_ty, proj.projection_ty)
335+
.expect("expected to be able to unify goal projection with dyn's projection");
336+
// FIXME: Technically we could register these too..
337+
assert!(nested.is_empty(), "did not expect unification to have any nested goals");
338+
proj.term.ty().unwrap()
339+
} else {
340+
ty.super_fold_with(self)
341+
}
342+
}
343+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// compile-flags: -Ztrait-solver=next
2+
// check-pass
3+
4+
trait Trait<'a> {
5+
type Item: for<'b> Trait2<'b>;
6+
}
7+
8+
trait Trait2<'a> {}
9+
impl Trait2<'_> for () {}
10+
11+
fn needs_trait(_: Box<impl for<'a> Trait<'a> + ?Sized>) {}
12+
13+
fn foo(x: Box<dyn for<'a> Trait<'a, Item = ()>>) {
14+
needs_trait(x);
15+
}
16+
17+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// compile-flags: -Ztrait-solver=next
2+
// From #80800
3+
4+
trait SuperTrait {
5+
type A;
6+
type B;
7+
}
8+
9+
trait Trait: SuperTrait<A = <Self as SuperTrait>::B> {}
10+
11+
fn transmute<A, B>(x: A) -> B {
12+
foo::<A, B, dyn Trait<A = A, B = B>>(x)
13+
//~^ ERROR type annotations needed: cannot satisfy `dyn Trait<A = A, B = B>: Trait`
14+
}
15+
16+
fn foo<A, B, T: ?Sized>(x: T::A) -> B
17+
where
18+
T: Trait<B = B>,
19+
{
20+
x
21+
}
22+
23+
static X: u8 = 0;
24+
fn main() {
25+
let x = transmute::<&u8, &[u8; 1_000_000]>(&X);
26+
println!("{:?}", x[100_000]);
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error[E0283]: type annotations needed: cannot satisfy `dyn Trait<A = A, B = B>: Trait`
2+
--> $DIR/more-object-bound.rs:12:5
3+
|
4+
LL | foo::<A, B, dyn Trait<A = A, B = B>>(x)
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: cannot satisfy `dyn Trait<A = A, B = B>: Trait`
8+
note: required by a bound in `foo`
9+
--> $DIR/more-object-bound.rs:18:8
10+
|
11+
LL | fn foo<A, B, T: ?Sized>(x: T::A) -> B
12+
| --- required by a bound in this function
13+
LL | where
14+
LL | T: Trait<B = B>,
15+
| ^^^^^^^^^^^^ required by this bound in `foo`
16+
17+
error: aborting due to previous error
18+
19+
For more information about this error, try `rustc --explain E0283`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// compile-flags: -Ztrait-solver=next
2+
3+
trait Setup {
4+
type From: Copy;
5+
}
6+
7+
fn copy<U: Setup + ?Sized>(from: &U::From) -> U::From {
8+
*from
9+
}
10+
11+
pub fn copy_any<T>(t: &T) -> T {
12+
copy::<dyn Setup<From=T>>(t)
13+
//~^ ERROR the trait bound `dyn Setup<From = T>: Setup` is not satisfied
14+
}
15+
16+
fn main() {
17+
let x = String::from("Hello, world");
18+
let y = copy_any(&x);
19+
println!("{y}");
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error[E0277]: the trait bound `dyn Setup<From = T>: Setup` is not satisfied
2+
--> $DIR/object-unsafety.rs:12:12
3+
|
4+
LL | copy::<dyn Setup<From=T>>(t)
5+
| ^^^^^^^^^^^^^^^^^ the trait `Setup` is not implemented for `dyn Setup<From = T>`
6+
|
7+
note: required by a bound in `copy`
8+
--> $DIR/object-unsafety.rs:7:12
9+
|
10+
LL | fn copy<U: Setup + ?Sized>(from: &U::From) -> U::From {
11+
| ^^^^^ required by this bound in `copy`
12+
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
13+
|
14+
LL | pub fn copy_any<T>(t: &T) -> T where dyn Setup<From = T>: Setup {
15+
| ++++++++++++++++++++++++++++++++
16+
17+
error: aborting due to previous error
18+
19+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)