Skip to content

Commit 67b3e81

Browse files
committed
Auto merge of rust-lang#90887 - jackh726:issue-90729, r=nikomatsakis
Try to normalize associated types before processing obligations Closes rust-lang#90729 r? `@nikomatsakis`
2 parents 89adcc6 + 06067d9 commit 67b3e81

22 files changed

+383
-216
lines changed

Diff for: compiler/rustc_trait_selection/src/traits/fulfill.rs

+17
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,8 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
346346

347347
let obligation = &mut pending_obligation.obligation;
348348

349+
debug!(?obligation, "process_obligation pre-resolve");
350+
349351
if obligation.predicate.has_infer_types_or_consts() {
350352
obligation.predicate =
351353
self.selcx.infcx().resolve_vars_if_possible(obligation.predicate);
@@ -355,6 +357,21 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
355357

356358
let infcx = self.selcx.infcx();
357359

360+
if obligation.predicate.has_projections() {
361+
let mut obligations = Vec::new();
362+
let predicate = crate::traits::project::try_normalize_with_depth_to(
363+
self.selcx,
364+
obligation.param_env,
365+
obligation.cause.clone(),
366+
obligation.recursion_depth + 1,
367+
obligation.predicate,
368+
&mut obligations,
369+
);
370+
if predicate != obligation.predicate {
371+
obligations.push(obligation.with(predicate));
372+
return ProcessResult::Changed(mk_pending(obligations));
373+
}
374+
}
358375
let binder = obligation.predicate.kind();
359376
match binder.no_bound_vars() {
360377
None => match binder.skip_binder() {

Diff for: compiler/rustc_trait_selection/src/traits/project.rs

+80-10
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,32 @@ where
295295
result
296296
}
297297

298+
#[instrument(level = "info", skip(selcx, param_env, cause, obligations))]
299+
pub fn try_normalize_with_depth_to<'a, 'b, 'tcx, T>(
300+
selcx: &'a mut SelectionContext<'b, 'tcx>,
301+
param_env: ty::ParamEnv<'tcx>,
302+
cause: ObligationCause<'tcx>,
303+
depth: usize,
304+
value: T,
305+
obligations: &mut Vec<PredicateObligation<'tcx>>,
306+
) -> T
307+
where
308+
T: TypeFoldable<'tcx>,
309+
{
310+
debug!(obligations.len = obligations.len());
311+
let mut normalizer = AssocTypeNormalizer::new_without_eager_inference_replacement(
312+
selcx,
313+
param_env,
314+
cause,
315+
depth,
316+
obligations,
317+
);
318+
let result = ensure_sufficient_stack(|| normalizer.fold(value));
319+
debug!(?result, obligations.len = normalizer.obligations.len());
320+
debug!(?normalizer.obligations,);
321+
result
322+
}
323+
298324
pub(crate) fn needs_normalization<'tcx, T: TypeFoldable<'tcx>>(value: &T, reveal: Reveal) -> bool {
299325
match reveal {
300326
Reveal::UserFacing => value
@@ -314,6 +340,10 @@ struct AssocTypeNormalizer<'a, 'b, 'tcx> {
314340
obligations: &'a mut Vec<PredicateObligation<'tcx>>,
315341
depth: usize,
316342
universes: Vec<Option<ty::UniverseIndex>>,
343+
/// If true, when a projection is unable to be completed, an inference
344+
/// variable will be created and an obligation registered to project to that
345+
/// inference variable. Also, constants will be eagerly evaluated.
346+
eager_inference_replacement: bool,
317347
}
318348

319349
impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
@@ -324,7 +354,33 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
324354
depth: usize,
325355
obligations: &'a mut Vec<PredicateObligation<'tcx>>,
326356
) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
327-
AssocTypeNormalizer { selcx, param_env, cause, obligations, depth, universes: vec![] }
357+
AssocTypeNormalizer {
358+
selcx,
359+
param_env,
360+
cause,
361+
obligations,
362+
depth,
363+
universes: vec![],
364+
eager_inference_replacement: true,
365+
}
366+
}
367+
368+
fn new_without_eager_inference_replacement(
369+
selcx: &'a mut SelectionContext<'b, 'tcx>,
370+
param_env: ty::ParamEnv<'tcx>,
371+
cause: ObligationCause<'tcx>,
372+
depth: usize,
373+
obligations: &'a mut Vec<PredicateObligation<'tcx>>,
374+
) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
375+
AssocTypeNormalizer {
376+
selcx,
377+
param_env,
378+
cause,
379+
obligations,
380+
depth,
381+
universes: vec![],
382+
eager_inference_replacement: false,
383+
}
328384
}
329385

330386
fn fold<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T {
@@ -428,14 +484,28 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
428484
// there won't be bound vars there.
429485

430486
let data = data.super_fold_with(self);
431-
let normalized_ty = normalize_projection_type(
432-
self.selcx,
433-
self.param_env,
434-
data,
435-
self.cause.clone(),
436-
self.depth,
437-
&mut self.obligations,
438-
);
487+
let normalized_ty = if self.eager_inference_replacement {
488+
normalize_projection_type(
489+
self.selcx,
490+
self.param_env,
491+
data,
492+
self.cause.clone(),
493+
self.depth,
494+
&mut self.obligations,
495+
)
496+
} else {
497+
opt_normalize_projection_type(
498+
self.selcx,
499+
self.param_env,
500+
data,
501+
self.cause.clone(),
502+
self.depth,
503+
&mut self.obligations,
504+
)
505+
.ok()
506+
.flatten()
507+
.unwrap_or_else(|| ty::Term::Ty(ty.super_fold_with(self)))
508+
};
439509
debug!(
440510
?self.depth,
441511
?ty,
@@ -501,7 +571,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
501571
}
502572

503573
fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
504-
if self.selcx.tcx().lazy_normalization() {
574+
if self.selcx.tcx().lazy_normalization() || !self.eager_inference_replacement {
505575
constant
506576
} else {
507577
let constant = constant.super_fold_with(self);

Diff for: src/test/ui/associated-types/issue-59324.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ pub trait ThriftService<Bug: NotFoo>:
1515
{
1616
fn get_service(
1717
//~^ ERROR the trait bound `Bug: Foo` is not satisfied
18-
//~| ERROR the trait bound `Bug: Foo` is not satisfied
1918
&self,
2019
) -> Self::AssocType;
20+
//~^ the trait bound `Bug: Foo` is not satisfied
2121
}
2222

2323
fn with_factory<H>(factory: dyn ThriftService<()>) {}

Diff for: src/test/ui/associated-types/issue-59324.stderr

+5-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | |
66
LL | |
77
LL | | Service<AssocType = <Bug as Foo>::OnlyFoo>
88
... |
9-
LL | | ) -> Self::AssocType;
9+
LL | |
1010
LL | | }
1111
| |_^ the trait `Foo` is not implemented for `Bug`
1212
|
@@ -23,7 +23,7 @@ LL | |
2323
LL | |
2424
LL | | Service<AssocType = <Bug as Foo>::OnlyFoo>
2525
... |
26-
LL | | ) -> Self::AssocType;
26+
LL | |
2727
LL | | }
2828
| |_^ the trait `Foo` is not implemented for `Bug`
2929
|
@@ -37,7 +37,6 @@ error[E0277]: the trait bound `Bug: Foo` is not satisfied
3737
|
3838
LL | / fn get_service(
3939
LL | |
40-
LL | |
4140
LL | | &self,
4241
LL | | ) -> Self::AssocType;
4342
| |_________________________^ the trait `Foo` is not implemented for `Bug`
@@ -48,10 +47,10 @@ LL | pub trait ThriftService<Bug: NotFoo + Foo>:
4847
| +++++
4948

5049
error[E0277]: the trait bound `Bug: Foo` is not satisfied
51-
--> $DIR/issue-59324.rs:16:8
50+
--> $DIR/issue-59324.rs:19:10
5251
|
53-
LL | fn get_service(
54-
| ^^^^^^^^^^^ the trait `Foo` is not implemented for `Bug`
52+
LL | ) -> Self::AssocType;
53+
| ^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `Bug`
5554
|
5655
help: consider further restricting this bound
5756
|

Diff for: src/test/ui/generic-associated-types/issue-90729.rs

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// check-pass
2+
3+
#![feature(generic_associated_types)]
4+
5+
use std::marker::PhantomData;
6+
7+
pub trait Type {
8+
type Ref<'a>;
9+
}
10+
11+
pub trait AsBytes {}
12+
13+
impl AsBytes for &str {}
14+
15+
pub struct Utf8;
16+
17+
impl Type for Utf8 {
18+
type Ref<'a> = &'a str;
19+
}
20+
21+
pub struct Bytes<T: Type> {
22+
_marker: PhantomData<T>,
23+
}
24+
25+
impl<T: Type> Bytes<T>
26+
where
27+
for<'a> T::Ref<'a>: AsBytes,
28+
{
29+
pub fn new() -> Self {
30+
Self {
31+
_marker: PhantomData,
32+
}
33+
}
34+
}
35+
36+
fn main() {
37+
let _b = Bytes::<Utf8>::new();
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error[E0311]: the parameter type `T` may not live long enough
2+
--> $DIR/issue-91139.rs:27:12
3+
|
4+
LL | fn foo<T>() {
5+
| - help: consider adding an explicit lifetime bound...: `T: 'a`
6+
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
7+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
8+
9+
error: aborting due to previous error
10+

Diff for: src/test/ui/generic-associated-types/issue-91139.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
// check-pass
1+
// revisions: migrate nll
2+
//[nll]compile-flags: -Z borrowck=mir
3+
4+
// Since we are testing nll (and migration) explicitly as a separate
5+
// revisions, don't worry about the --compare-mode=nll on this test.
6+
7+
// ignore-compare-mode-nll
8+
9+
//[nll] check-pass
10+
//[migrate] check-fail
211

312
#![feature(generic_associated_types)]
413

@@ -16,6 +25,7 @@ impl<T> Foo<T> for () {
1625

1726
fn foo<T>() {
1827
let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
28+
//[migrate]~^ the parameter type `T` may not live long enough
1929
}
2030

2131
pub fn main() {}

Diff for: src/test/ui/generic-associated-types/issue-93341.rs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// check-pass
2+
3+
#![feature(generic_associated_types)]
4+
use std::marker::PhantomData;
5+
6+
pub struct Id<'id>(PhantomData<fn(&'id ()) -> &'id ()>);
7+
8+
fn new_id() -> Id<'static> {
9+
Id(PhantomData)
10+
}
11+
12+
pub trait HasLifetime where {
13+
type AtLifetime<'a>;
14+
}
15+
16+
pub struct ExistentialLifetime<S: HasLifetime>(S::AtLifetime<'static>);
17+
18+
impl<S: HasLifetime> ExistentialLifetime<S> {
19+
pub fn new<F>(f: F) -> ExistentialLifetime<S>
20+
where for<'id> F: FnOnce(Id<'id>) -> S::AtLifetime<'id> {
21+
ExistentialLifetime(f(new_id()))
22+
}
23+
}
24+
25+
26+
struct ExampleS<'id>(Id<'id>);
27+
28+
struct ExampleMarker;
29+
30+
impl HasLifetime for ExampleMarker {
31+
type AtLifetime<'id> = ExampleS<'id>;
32+
}
33+
34+
35+
fn broken0() -> ExistentialLifetime<ExampleMarker> {
36+
fn new_helper<'id>(id: Id<'id>) -> ExampleS<'id> {
37+
ExampleS(id)
38+
}
39+
40+
ExistentialLifetime::<ExampleMarker>::new(new_helper)
41+
}
42+
43+
fn broken1() -> ExistentialLifetime<ExampleMarker> {
44+
fn new_helper<'id>(id: Id<'id>) -> <ExampleMarker as HasLifetime>::AtLifetime<'id> {
45+
ExampleS(id)
46+
}
47+
48+
ExistentialLifetime::<ExampleMarker>::new(new_helper)
49+
}
50+
51+
fn broken2() -> ExistentialLifetime<ExampleMarker> {
52+
ExistentialLifetime::<ExampleMarker>::new(|id| ExampleS(id))
53+
}
54+
55+
fn main() {}

Diff for: src/test/ui/generic-associated-types/issue-93342.rs

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// check-pass
2+
3+
#![feature(generic_associated_types)]
4+
5+
use std::marker::PhantomData;
6+
7+
pub trait Scalar: 'static {
8+
type RefType<'a>: ScalarRef<'a>;
9+
}
10+
11+
pub trait ScalarRef<'a>: 'a {}
12+
13+
impl Scalar for i32 {
14+
type RefType<'a> = i32;
15+
}
16+
17+
impl Scalar for String {
18+
type RefType<'a> = &'a str;
19+
}
20+
21+
impl Scalar for bool {
22+
type RefType<'a> = i32;
23+
}
24+
25+
impl<'a> ScalarRef<'a> for bool {}
26+
27+
impl<'a> ScalarRef<'a> for i32 {}
28+
29+
impl<'a> ScalarRef<'a> for &'a str {}
30+
31+
fn str_contains(a: &str, b: &str) -> bool {
32+
a.contains(b)
33+
}
34+
35+
pub struct BinaryExpression<A: Scalar, B: Scalar, O: Scalar, F>
36+
where
37+
F: Fn(A::RefType<'_>, B::RefType<'_>) -> O,
38+
{
39+
f: F,
40+
_phantom: PhantomData<(A, B, O)>,
41+
}
42+
43+
impl<A: Scalar, B: Scalar, O: Scalar, F> BinaryExpression<A, B, O, F>
44+
where
45+
F: Fn(A::RefType<'_>, B::RefType<'_>) -> O,
46+
{
47+
pub fn new(f: F) -> Self {
48+
Self {
49+
f,
50+
_phantom: PhantomData,
51+
}
52+
}
53+
}
54+
55+
fn main() {
56+
BinaryExpression::<String, String, bool, _>::new(str_contains);
57+
}

0 commit comments

Comments
 (0)