Skip to content

Commit eac7da1

Browse files
committed
trait_sel: sizedness goals prefer alias candidates
Following the changes to lowering, sizedness supertraits are now elaborated on associated type bounds. In existing code where an item bound was used to prove a predicate, this elaboration can result in a param candidate being present which will now take precedence over the projection candidate. Evaluation of the param candidate results in types being equated and region constraints being created that would not have with the projection candidate - sometimes emitting an error as a consequence! For sizedness predicates, now prefer non-param candidates if any exist. So that the `issue-93262.rs` test continued to pass, this required removing normalisation of the predicates of projections when confirming projection candidates which permits more uses of GATs and changes the output of yet more tests.
1 parent f16fddc commit eac7da1

File tree

16 files changed

+156
-125
lines changed

16 files changed

+156
-125
lines changed

compiler/rustc_middle/src/ty/context.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
493493
self.is_default_trait(def_id)
494494
}
495495

496+
fn is_sizedness_trait(self, def_id: DefId) -> bool {
497+
self.is_sizedness_trait(def_id)
498+
}
499+
496500
fn as_lang_item(self, def_id: DefId) -> Option<TraitSolverLangItem> {
497501
lang_item_to_trait_lang_item(self.lang_items().from_def_id(def_id)?)
498502
}
@@ -1645,6 +1649,10 @@ impl<'tcx> TyCtxt<'tcx> {
16451649
.any(|&default_trait| self.lang_items().get(default_trait) == Some(def_id))
16461650
}
16471651

1652+
pub fn is_sizedness_trait(self, def_id: DefId) -> bool {
1653+
matches!(self.as_lang_item(def_id), Some(LangItem::Sized | LangItem::MetaSized))
1654+
}
1655+
16481656
/// Returns a range of the start/end indices specified with the
16491657
/// `rustc_layout_scalar_valid_range` attribute.
16501658
// FIXME(eddyb) this is an awkward spot for this method, maybe move it?

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,6 +1343,7 @@ where
13431343
#[instrument(level = "debug", skip(self), ret)]
13441344
pub(super) fn merge_trait_candidates(
13451345
&mut self,
1346+
prefer_alias_over_param_candidates: bool,
13461347
mut candidates: Vec<Candidate<I>>,
13471348
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
13481349
if let TypingMode::Coherence = self.typing_mode() {
@@ -1368,6 +1369,26 @@ where
13681369
return Ok((candidate.result, Some(TraitGoalProvenVia::Misc)));
13691370
}
13701371

1372+
let potential_alias_bound_response =
1373+
candidates.iter().any(|c| matches!(c.source, CandidateSource::AliasBound)).then(|| {
1374+
let alias_bounds: Vec<_> = candidates
1375+
.iter()
1376+
.filter(|c| matches!(c.source, CandidateSource::AliasBound))
1377+
.map(|c| c.result)
1378+
.collect();
1379+
if let Some(response) = self.try_merge_responses(&alias_bounds) {
1380+
(response, Some(TraitGoalProvenVia::AliasBound))
1381+
} else {
1382+
(self.bail_with_ambiguity(&alias_bounds), None)
1383+
}
1384+
});
1385+
1386+
if prefer_alias_over_param_candidates
1387+
&& let Some(alias_bound_response) = potential_alias_bound_response
1388+
{
1389+
return Ok(alias_bound_response);
1390+
}
1391+
13711392
// If there are non-global where-bounds, prefer where-bounds
13721393
// (including global ones) over everything else.
13731394
let has_non_global_where_bounds = candidates
@@ -1386,17 +1407,8 @@ where
13861407
};
13871408
}
13881409

1389-
if candidates.iter().any(|c| matches!(c.source, CandidateSource::AliasBound)) {
1390-
let alias_bounds: Vec<_> = candidates
1391-
.iter()
1392-
.filter(|c| matches!(c.source, CandidateSource::AliasBound))
1393-
.map(|c| c.result)
1394-
.collect();
1395-
return if let Some(response) = self.try_merge_responses(&alias_bounds) {
1396-
Ok((response, Some(TraitGoalProvenVia::AliasBound)))
1397-
} else {
1398-
Ok((self.bail_with_ambiguity(&alias_bounds), None))
1399-
};
1410+
if let Some(response) = potential_alias_bound_response {
1411+
return Ok(response);
14001412
}
14011413

14021414
self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates);
@@ -1431,7 +1443,12 @@ where
14311443
goal: Goal<I, TraitPredicate<I>>,
14321444
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
14331445
let candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
1434-
self.merge_trait_candidates(candidates)
1446+
1447+
let did = goal.predicate.def_id();
1448+
let is_sizedness_or_auto_or_default_goal = self.cx().is_sizedness_trait(did)
1449+
|| self.cx().trait_is_auto(did)
1450+
|| self.cx().is_default_trait(did);
1451+
self.merge_trait_candidates(is_sizedness_or_auto_or_default_goal, candidates)
14351452
}
14361453

14371454
fn try_stall_coroutine_witness(

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
146146
obligation: &PolyTraitObligation<'tcx>,
147147
idx: usize,
148148
) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
149-
let tcx = self.tcx();
150-
151149
let placeholder_trait_predicate =
152150
self.infcx.enter_forall_and_leak_universe(obligation.predicate).trait_ref;
153151
let placeholder_self_ty = self.infcx.shallow_resolve(placeholder_trait_predicate.self_ty());
@@ -196,28 +194,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
196194
.map_err(|_| SelectionError::Unimplemented)?,
197195
);
198196

199-
// FIXME(compiler-errors): I don't think this is needed.
200-
if let ty::Alias(ty::Projection, alias_ty) = placeholder_self_ty.kind() {
201-
let predicates = tcx.predicates_of(alias_ty.def_id).instantiate_own(tcx, alias_ty.args);
202-
for (predicate, _) in predicates {
203-
let normalized = normalize_with_depth_to(
204-
self,
205-
obligation.param_env,
206-
obligation.cause.clone(),
207-
obligation.recursion_depth + 1,
208-
predicate,
209-
&mut obligations,
210-
);
211-
obligations.push(Obligation::with_depth(
212-
self.tcx(),
213-
obligation.cause.clone(),
214-
obligation.recursion_depth + 1,
215-
obligation.param_env,
216-
normalized,
217-
));
218-
}
219-
}
220-
221197
Ok(obligations)
222198
}
223199

compiler/rustc_trait_selection/src/traits/select/mod.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
485485
}
486486
} else {
487487
let has_non_region_infer = stack.obligation.predicate.has_non_region_infer();
488-
if let Some(candidate) = self.winnow_candidates(has_non_region_infer, candidates) {
488+
let did = stack.obligation.predicate.def_id();
489+
let is_sizedness_or_auto_or_default_predicate = self.tcx().is_sizedness_trait(did)
490+
|| self.tcx().trait_is_auto(did)
491+
|| self.tcx().is_default_trait(did);
492+
if let Some(candidate) = self.winnow_candidates(
493+
has_non_region_infer,
494+
is_sizedness_or_auto_or_default_predicate,
495+
candidates,
496+
) {
489497
self.filter_reservation_impls(candidate)
490498
} else {
491499
Ok(None)
@@ -1826,6 +1834,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
18261834
fn winnow_candidates(
18271835
&mut self,
18281836
has_non_region_infer: bool,
1837+
prefer_alias_over_param_candidates: bool,
18291838
mut candidates: Vec<EvaluatedCandidate<'tcx>>,
18301839
) -> Option<SelectionCandidate<'tcx>> {
18311840
if candidates.len() == 1 {
@@ -1879,6 +1888,19 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
18791888
break;
18801889
}
18811890

1891+
let alias_bound = candidates
1892+
.iter()
1893+
.filter_map(|c| if let ProjectionCandidate(i) = c.candidate { Some(i) } else { None })
1894+
.try_reduce(|c1, c2| if has_non_region_infer { None } else { Some(c1.min(c2)) });
1895+
1896+
if prefer_alias_over_param_candidates {
1897+
match alias_bound {
1898+
Some(Some(index)) => return Some(ProjectionCandidate(index)),
1899+
Some(None) => {}
1900+
None => return None,
1901+
}
1902+
}
1903+
18821904
// The next highest priority is for non-global where-bounds. However, while we don't
18831905
// prefer global where-clauses here, we do bail with ambiguity when encountering both
18841906
// a global and a non-global where-clause.
@@ -1912,10 +1934,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
19121934
// fairly arbitrary but once again necessary for backwards compatibility.
19131935
// If there are multiple applicable candidates which don't affect type inference,
19141936
// choose the one with the lowest index.
1915-
let alias_bound = candidates
1916-
.iter()
1917-
.filter_map(|c| if let ProjectionCandidate(i) = c.candidate { Some(i) } else { None })
1918-
.try_reduce(|c1, c2| if has_non_region_infer { None } else { Some(c1.min(c2)) });
19191937
match alias_bound {
19201938
Some(Some(index)) => return Some(ProjectionCandidate(index)),
19211939
Some(None) => {}

compiler/rustc_type_ir/src/interner.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,8 @@ pub trait Interner:
300300

301301
fn is_default_trait(self, def_id: Self::DefId) -> bool;
302302

303+
fn is_sizedness_trait(self, def_id: Self::DefId) -> bool;
304+
303305
fn as_lang_item(self, def_id: Self::DefId) -> Option<TraitSolverLangItem>;
304306

305307
fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator<Item = Self::DefId>;

tests/ui/dyn-compatibility/assoc_type_bounds_sized_used.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ trait Bop {
99

1010
fn bop<T: Bop + ?Sized>() {
1111
let _ = <T as Bop>::Bar::default();
12-
//~^ ERROR: trait bounds were not satisfied
12+
//~^ ERROR: the size for values of type `T` cannot be known at compilation time
13+
//~| ERROR: the size for values of type `T` cannot be known at compilation time
1314
//~| ERROR: the size for values of type `T` cannot be known at compilation time
1415
}
1516

tests/ui/dyn-compatibility/assoc_type_bounds_sized_used.stderr

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,58 @@ help: consider relaxing the implicit `Sized` restriction
2424
LL | type Bar: Default + ?Sized
2525
| ++++++++
2626

27-
error[E0599]: the function or associated item `default` exists for associated type `<T as Bop>::Bar`, but its trait bounds were not satisfied
28-
--> $DIR/assoc_type_bounds_sized_used.rs:11:30
27+
error[E0277]: the size for values of type `T` cannot be known at compilation time
28+
--> $DIR/assoc_type_bounds_sized_used.rs:11:13
2929
|
30+
LL | fn bop<T: Bop + ?Sized>() {
31+
| - this type parameter needs to be `Sized`
3032
LL | let _ = <T as Bop>::Bar::default();
31-
| ^^^^^^^ function or associated item cannot be called on `<T as Bop>::Bar` due to unsatisfied trait bounds
33+
| ^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
34+
|
35+
note: required by a bound in `Bop::Bar`
36+
--> $DIR/assoc_type_bounds_sized_used.rs:7:15
37+
|
38+
LL | type Bar: Default
39+
| --- required by a bound in this associated type
40+
LL | where
41+
LL | Self: Sized;
42+
| ^^^^^ required by this bound in `Bop::Bar`
43+
help: consider removing the `?Sized` bound to make the type parameter `Sized`
44+
|
45+
LL - fn bop<T: Bop + ?Sized>() {
46+
LL + fn bop<T: Bop>() {
3247
|
33-
= note: the following trait bounds were not satisfied:
34-
`T: Sized`
35-
which is required by `<T as Bop>::Bar: Default`
36-
help: consider restricting the type parameter to satisfy the trait bound
48+
help: consider relaxing the implicit `Sized` restriction
49+
|
50+
LL | type Bar: Default + ?Sized
51+
| ++++++++
52+
53+
error[E0277]: the size for values of type `T` cannot be known at compilation time
54+
--> $DIR/assoc_type_bounds_sized_used.rs:11:13
3755
|
38-
LL | fn bop<T: Bop + ?Sized>() where T: Sized {
39-
| ++++++++++++++
56+
LL | fn bop<T: Bop + ?Sized>() {
57+
| - this type parameter needs to be `Sized`
58+
LL | let _ = <T as Bop>::Bar::default();
59+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
60+
|
61+
note: required by a bound in `Bop::Bar`
62+
--> $DIR/assoc_type_bounds_sized_used.rs:7:15
63+
|
64+
LL | type Bar: Default
65+
| --- required by a bound in this associated type
66+
LL | where
67+
LL | Self: Sized;
68+
| ^^^^^ required by this bound in `Bop::Bar`
69+
help: consider removing the `?Sized` bound to make the type parameter `Sized`
70+
|
71+
LL - fn bop<T: Bop + ?Sized>() {
72+
LL + fn bop<T: Bop>() {
73+
|
74+
help: consider relaxing the implicit `Sized` restriction
75+
|
76+
LL | type Bar: Default + ?Sized
77+
| ++++++++
4078

41-
error: aborting due to 2 previous errors
79+
error: aborting due to 3 previous errors
4280

43-
Some errors have detailed explanations: E0277, E0599.
44-
For more information about an error, try `rustc --explain E0277`.
81+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,3 @@
1-
error: lifetime bound not satisfied
2-
--> $DIR/issue-100013.rs:15:5
3-
|
4-
LL | / async { // a coroutine checked for autotrait impl `Send`
5-
LL | | let x = None::<I::Future<'_, '_>>; // a type referencing GAT
6-
LL | | async {}.await; // a yield point
7-
LL | | }
8-
| |_____^
9-
|
10-
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
11-
12-
error: lifetime bound not satisfied
13-
--> $DIR/issue-100013.rs:22:5
14-
|
15-
LL | / async { // a coroutine checked for autotrait impl `Send`
16-
LL | | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
17-
LL | | async {}.await; // a yield point
18-
LL | | }
19-
| |_____^
20-
|
21-
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
22-
231
error: lifetime may not live long enough
242
--> $DIR/issue-100013.rs:23:17
253
|
@@ -33,16 +11,5 @@ LL | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
3311
|
3412
= help: consider adding the following bound: `'a: 'b`
3513

36-
error: lifetime bound not satisfied
37-
--> $DIR/issue-100013.rs:29:5
38-
|
39-
LL | / async { // a coroutine checked for autotrait impl `Send`
40-
LL | | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
41-
LL | | async {}.await; // a yield point
42-
LL | | }
43-
| |_____^
44-
|
45-
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
46-
47-
error: aborting due to 4 previous errors
14+
error: aborting due to 1 previous error
4815

tests/ui/generic-associated-types/issue-92096.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@ check-pass
12
//@ edition:2018
23

34
use std::future::Future;
@@ -15,14 +16,6 @@ where
1516
C: Client + Send + Sync,
1617
{
1718
async move { c.connect().await }
18-
//~^ ERROR `C` does not live long enough
19-
//
20-
// FIXME(#71723). This is because we infer at some point a value of
21-
//
22-
// impl Future<Output = <C as Client>::Connection<'_>>
23-
//
24-
// and then we somehow fail the WF check because `where C: 'a` is not known,
25-
// but I'm not entirely sure how that comes about.
2619
}
2720

2821
fn main() {}

tests/ui/generic-associated-types/issue-92096.stderr

Lines changed: 0 additions & 8 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0282]: type annotations needed for `<X as Trait<'static>>::Out<_>`
2+
--> $DIR/norm-before-method-resolution-opaque-type.rs:21:9
3+
|
4+
LL | let x = *x;
5+
| ^
6+
|
7+
help: consider giving `x` an explicit type, where the placeholders `_` are specified
8+
|
9+
LL | let x: <_ as Trait<'static>>::Out<_> = *x;
10+
| +++++++++++++++++++++++++++++++
11+
12+
error: aborting due to 1 previous error
13+
14+
For more information about this error, try `rustc --explain E0282`.

tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.old.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0507]: cannot move out of `*x` which is behind a shared reference
2-
--> $DIR/norm-before-method-resolution-opaque-type.rs:22:13
2+
--> $DIR/norm-before-method-resolution-opaque-type.rs:21:13
33
|
44
LL | let x = *x;
55
| ^^ move occurs because `*x` has type `<X as Trait<'_>>::Out<Foo>`, which does not implement the `Copy` trait

tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//@ revisions: old next
22
//@[next] compile-flags: -Znext-solver
3-
//@[next] check-pass
43

54
#![feature(type_alias_impl_trait)]
65
trait Trait<'a> {
@@ -19,7 +18,9 @@ where
1918
for<'a> X: Trait<'a>,
2019
for<'a> <X as Trait<'a>>::Out<()>: Copy,
2120
{
22-
let x = *x; //[old]~ ERROR: cannot move out of `*x`
21+
let x = *x;
22+
//[old]~^ ERROR: cannot move out of `*x`
23+
//[next]~^^ ERROR: type annotations needed for `<X as Trait<'static>>::Out<_>`
2324
todo!();
2425
}
2526

tests/ui/nll/issue-50716.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ trait A {
55
type X: ?Sized;
66
}
77

8-
fn foo<'a, T: 'static>(s: Box<<&'a T as A>::X>) //~ ERROR
8+
fn foo<'a, T: 'static>(s: Box<<&'a T as A>::X>)
99
where
1010
for<'b> &'b T: A,
1111
<&'static T as A>::X: Sized

0 commit comments

Comments
 (0)