Skip to content

Commit

Permalink
Auto merge of #90887 - jackh726:issue-90729, r=nikomatsakis
Browse files Browse the repository at this point in the history
Try to normalize associated types before processing obligations

Closes #90729

r? `@nikomatsakis`
  • Loading branch information
bors committed Mar 8, 2022
2 parents 89adcc6 + 06067d9 commit 67b3e81
Show file tree
Hide file tree
Showing 22 changed files with 383 additions and 216 deletions.
17 changes: 17 additions & 0 deletions compiler/rustc_trait_selection/src/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,8 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {

let obligation = &mut pending_obligation.obligation;

debug!(?obligation, "process_obligation pre-resolve");

if obligation.predicate.has_infer_types_or_consts() {
obligation.predicate =
self.selcx.infcx().resolve_vars_if_possible(obligation.predicate);
Expand All @@ -355,6 +357,21 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {

let infcx = self.selcx.infcx();

if obligation.predicate.has_projections() {
let mut obligations = Vec::new();
let predicate = crate::traits::project::try_normalize_with_depth_to(
self.selcx,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
obligation.predicate,
&mut obligations,
);
if predicate != obligation.predicate {
obligations.push(obligation.with(predicate));
return ProcessResult::Changed(mk_pending(obligations));
}
}
let binder = obligation.predicate.kind();
match binder.no_bound_vars() {
None => match binder.skip_binder() {
Expand Down
90 changes: 80 additions & 10 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,32 @@ where
result
}

#[instrument(level = "info", skip(selcx, param_env, cause, obligations))]
pub fn try_normalize_with_depth_to<'a, 'b, 'tcx, T>(
selcx: &'a mut SelectionContext<'b, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
cause: ObligationCause<'tcx>,
depth: usize,
value: T,
obligations: &mut Vec<PredicateObligation<'tcx>>,
) -> T
where
T: TypeFoldable<'tcx>,
{
debug!(obligations.len = obligations.len());
let mut normalizer = AssocTypeNormalizer::new_without_eager_inference_replacement(
selcx,
param_env,
cause,
depth,
obligations,
);
let result = ensure_sufficient_stack(|| normalizer.fold(value));
debug!(?result, obligations.len = normalizer.obligations.len());
debug!(?normalizer.obligations,);
result
}

pub(crate) fn needs_normalization<'tcx, T: TypeFoldable<'tcx>>(value: &T, reveal: Reveal) -> bool {
match reveal {
Reveal::UserFacing => value
Expand All @@ -314,6 +340,10 @@ struct AssocTypeNormalizer<'a, 'b, 'tcx> {
obligations: &'a mut Vec<PredicateObligation<'tcx>>,
depth: usize,
universes: Vec<Option<ty::UniverseIndex>>,
/// If true, when a projection is unable to be completed, an inference
/// variable will be created and an obligation registered to project to that
/// inference variable. Also, constants will be eagerly evaluated.
eager_inference_replacement: bool,
}

impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
Expand All @@ -324,7 +354,33 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
depth: usize,
obligations: &'a mut Vec<PredicateObligation<'tcx>>,
) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
AssocTypeNormalizer { selcx, param_env, cause, obligations, depth, universes: vec![] }
AssocTypeNormalizer {
selcx,
param_env,
cause,
obligations,
depth,
universes: vec![],
eager_inference_replacement: true,
}
}

fn new_without_eager_inference_replacement(
selcx: &'a mut SelectionContext<'b, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
cause: ObligationCause<'tcx>,
depth: usize,
obligations: &'a mut Vec<PredicateObligation<'tcx>>,
) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
AssocTypeNormalizer {
selcx,
param_env,
cause,
obligations,
depth,
universes: vec![],
eager_inference_replacement: false,
}
}

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

let data = data.super_fold_with(self);
let normalized_ty = normalize_projection_type(
self.selcx,
self.param_env,
data,
self.cause.clone(),
self.depth,
&mut self.obligations,
);
let normalized_ty = if self.eager_inference_replacement {
normalize_projection_type(
self.selcx,
self.param_env,
data,
self.cause.clone(),
self.depth,
&mut self.obligations,
)
} else {
opt_normalize_projection_type(
self.selcx,
self.param_env,
data,
self.cause.clone(),
self.depth,
&mut self.obligations,
)
.ok()
.flatten()
.unwrap_or_else(|| ty::Term::Ty(ty.super_fold_with(self)))
};
debug!(
?self.depth,
?ty,
Expand Down Expand Up @@ -501,7 +571,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
}

fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
if self.selcx.tcx().lazy_normalization() {
if self.selcx.tcx().lazy_normalization() || !self.eager_inference_replacement {
constant
} else {
let constant = constant.super_fold_with(self);
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/associated-types/issue-59324.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ pub trait ThriftService<Bug: NotFoo>:
{
fn get_service(
//~^ ERROR the trait bound `Bug: Foo` is not satisfied
//~| ERROR the trait bound `Bug: Foo` is not satisfied
&self,
) -> Self::AssocType;
//~^ the trait bound `Bug: Foo` is not satisfied
}

fn with_factory<H>(factory: dyn ThriftService<()>) {}
Expand Down
11 changes: 5 additions & 6 deletions src/test/ui/associated-types/issue-59324.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ LL | |
LL | |
LL | | Service<AssocType = <Bug as Foo>::OnlyFoo>
... |
LL | | ) -> Self::AssocType;
LL | |
LL | | }
| |_^ the trait `Foo` is not implemented for `Bug`
|
Expand All @@ -23,7 +23,7 @@ LL | |
LL | |
LL | | Service<AssocType = <Bug as Foo>::OnlyFoo>
... |
LL | | ) -> Self::AssocType;
LL | |
LL | | }
| |_^ the trait `Foo` is not implemented for `Bug`
|
Expand All @@ -37,7 +37,6 @@ error[E0277]: the trait bound `Bug: Foo` is not satisfied
|
LL | / fn get_service(
LL | |
LL | |
LL | | &self,
LL | | ) -> Self::AssocType;
| |_________________________^ the trait `Foo` is not implemented for `Bug`
Expand All @@ -48,10 +47,10 @@ LL | pub trait ThriftService<Bug: NotFoo + Foo>:
| +++++

error[E0277]: the trait bound `Bug: Foo` is not satisfied
--> $DIR/issue-59324.rs:16:8
--> $DIR/issue-59324.rs:19:10
|
LL | fn get_service(
| ^^^^^^^^^^^ the trait `Foo` is not implemented for `Bug`
LL | ) -> Self::AssocType;
| ^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `Bug`
|
help: consider further restricting this bound
|
Expand Down
38 changes: 38 additions & 0 deletions src/test/ui/generic-associated-types/issue-90729.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// check-pass

#![feature(generic_associated_types)]

use std::marker::PhantomData;

pub trait Type {
type Ref<'a>;
}

pub trait AsBytes {}

impl AsBytes for &str {}

pub struct Utf8;

impl Type for Utf8 {
type Ref<'a> = &'a str;
}

pub struct Bytes<T: Type> {
_marker: PhantomData<T>,
}

impl<T: Type> Bytes<T>
where
for<'a> T::Ref<'a>: AsBytes,
{
pub fn new() -> Self {
Self {
_marker: PhantomData,
}
}
}

fn main() {
let _b = Bytes::<Utf8>::new();
}
10 changes: 10 additions & 0 deletions src/test/ui/generic-associated-types/issue-91139.migrate.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error[E0311]: the parameter type `T` may not live long enough
--> $DIR/issue-91139.rs:27:12
|
LL | fn foo<T>() {
| - help: consider adding an explicit lifetime bound...: `T: 'a`
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds

error: aborting due to previous error

12 changes: 11 additions & 1 deletion src/test/ui/generic-associated-types/issue-91139.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
// check-pass
// revisions: migrate nll
//[nll]compile-flags: -Z borrowck=mir

// Since we are testing nll (and migration) explicitly as a separate
// revisions, don't worry about the --compare-mode=nll on this test.

// ignore-compare-mode-nll

//[nll] check-pass
//[migrate] check-fail

#![feature(generic_associated_types)]

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

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

pub fn main() {}
55 changes: 55 additions & 0 deletions src/test/ui/generic-associated-types/issue-93341.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// check-pass

#![feature(generic_associated_types)]
use std::marker::PhantomData;

pub struct Id<'id>(PhantomData<fn(&'id ()) -> &'id ()>);

fn new_id() -> Id<'static> {
Id(PhantomData)
}

pub trait HasLifetime where {
type AtLifetime<'a>;
}

pub struct ExistentialLifetime<S: HasLifetime>(S::AtLifetime<'static>);

impl<S: HasLifetime> ExistentialLifetime<S> {
pub fn new<F>(f: F) -> ExistentialLifetime<S>
where for<'id> F: FnOnce(Id<'id>) -> S::AtLifetime<'id> {
ExistentialLifetime(f(new_id()))
}
}


struct ExampleS<'id>(Id<'id>);

struct ExampleMarker;

impl HasLifetime for ExampleMarker {
type AtLifetime<'id> = ExampleS<'id>;
}


fn broken0() -> ExistentialLifetime<ExampleMarker> {
fn new_helper<'id>(id: Id<'id>) -> ExampleS<'id> {
ExampleS(id)
}

ExistentialLifetime::<ExampleMarker>::new(new_helper)
}

fn broken1() -> ExistentialLifetime<ExampleMarker> {
fn new_helper<'id>(id: Id<'id>) -> <ExampleMarker as HasLifetime>::AtLifetime<'id> {
ExampleS(id)
}

ExistentialLifetime::<ExampleMarker>::new(new_helper)
}

fn broken2() -> ExistentialLifetime<ExampleMarker> {
ExistentialLifetime::<ExampleMarker>::new(|id| ExampleS(id))
}

fn main() {}
57 changes: 57 additions & 0 deletions src/test/ui/generic-associated-types/issue-93342.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// check-pass

#![feature(generic_associated_types)]

use std::marker::PhantomData;

pub trait Scalar: 'static {
type RefType<'a>: ScalarRef<'a>;
}

pub trait ScalarRef<'a>: 'a {}

impl Scalar for i32 {
type RefType<'a> = i32;
}

impl Scalar for String {
type RefType<'a> = &'a str;
}

impl Scalar for bool {
type RefType<'a> = i32;
}

impl<'a> ScalarRef<'a> for bool {}

impl<'a> ScalarRef<'a> for i32 {}

impl<'a> ScalarRef<'a> for &'a str {}

fn str_contains(a: &str, b: &str) -> bool {
a.contains(b)
}

pub struct BinaryExpression<A: Scalar, B: Scalar, O: Scalar, F>
where
F: Fn(A::RefType<'_>, B::RefType<'_>) -> O,
{
f: F,
_phantom: PhantomData<(A, B, O)>,
}

impl<A: Scalar, B: Scalar, O: Scalar, F> BinaryExpression<A, B, O, F>
where
F: Fn(A::RefType<'_>, B::RefType<'_>) -> O,
{
pub fn new(f: F) -> Self {
Self {
f,
_phantom: PhantomData,
}
}
}

fn main() {
BinaryExpression::<String, String, bool, _>::new(str_contains);
}
Loading

0 comments on commit 67b3e81

Please sign in to comment.