Skip to content

Commit

Permalink
Normalize obligations for closure confirmation
Browse files Browse the repository at this point in the history
  • Loading branch information
jackh726 committed Aug 28, 2021
1 parent ad02dc4 commit 0900c2f
Show file tree
Hide file tree
Showing 18 changed files with 349 additions and 79 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,7 @@ pub trait PrettyPrinter<'tcx>:
p!(print_def_path(did, substs));
if !substs.as_closure().is_valid() {
p!(" closure_substs=(unavailable)");
p!(write(" substs={:?}", substs));
} else {
p!(" closure_kind_ty=", print(substs.as_closure().kind_ty()));
p!(
Expand Down
16 changes: 14 additions & 2 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1747,7 +1747,7 @@ fn confirm_callable_candidate<'cx, 'tcx>(
ty: ret_type,
});

confirm_param_env_candidate(selcx, obligation, predicate, false)
confirm_param_env_candidate(selcx, obligation, predicate, true)
}

fn confirm_param_env_candidate<'cx, 'tcx>(
Expand All @@ -1767,8 +1767,18 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
);

let cache_projection = cache_entry.projection_ty;
let obligation_projection = obligation.predicate;
let mut nested_obligations = Vec::new();
let obligation_projection = obligation.predicate;
let obligation_projection = ensure_sufficient_stack(|| {
normalize_with_depth_to(
selcx,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
obligation_projection,
&mut nested_obligations,
)
});
let cache_projection = if potentially_unnormalized_candidate {
ensure_sufficient_stack(|| {
normalize_with_depth_to(
Expand All @@ -1784,6 +1794,8 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
cache_projection
};

debug!(?cache_projection, ?obligation_projection);

match infcx.at(cause, param_env).eq(cache_projection, obligation_projection) {
Ok(InferOk { value: _, obligations }) => {
nested_obligations.extend(obligations);
Expand Down
34 changes: 24 additions & 10 deletions compiler/rustc_trait_selection/src/traits/select/confirmation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -619,23 +619,37 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
_ => bug!("closure candidate for non-closure {:?}", obligation),
};

let obligation_predicate = obligation.predicate.to_poly_trait_ref();
let Normalized { value: obligation_predicate, mut obligations } =
ensure_sufficient_stack(|| {
normalize_with_depth(
self,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
obligation_predicate,
)
});

let trait_ref = self.closure_trait_ref_unnormalized(obligation, substs);
let Normalized { value: trait_ref, mut obligations } = ensure_sufficient_stack(|| {
normalize_with_depth(
self,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
trait_ref,
)
});
let Normalized { value: trait_ref, obligations: trait_ref_obligations } =
ensure_sufficient_stack(|| {
normalize_with_depth(
self,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
trait_ref,
)
});

debug!(?closure_def_id, ?trait_ref, ?obligations, "confirm closure candidate obligations");

obligations.extend(trait_ref_obligations);
obligations.extend(self.confirm_poly_trait_refs(
obligation.cause.clone(),
obligation.param_env,
obligation.predicate.to_poly_trait_ref(),
obligation_predicate,
trait_ref,
)?);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ LL | let c1 : () = c;
| expected due to this
|
= note: expected unit type `()`
found closure `[mod1::f<T>::{closure#0} closure_substs=(unavailable)]`
found closure `[mod1::f<T>::{closure#0} closure_substs=(unavailable) substs=[T, _#27t, extern "rust-call" fn(()), _#28t]]`
help: use parentheses to call this closure
|
LL | let c1 : () = c();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ LL | let c1 : () = c;
| expected due to this
|
= note: expected unit type `()`
found closure `[f<T>::{closure#0} closure_substs=(unavailable)]`
found closure `[f<T>::{closure#0} closure_substs=(unavailable) substs=[T, _#27t, extern "rust-call" fn(()), _#28t]]`
help: use parentheses to call this closure
|
LL | let c1 : () = c();
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/closures/print/closure-print-verbose.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
| expected due to this
|
= note: expected fn pointer `fn(u8) -> u8`
found closure `[main::{closure#0} closure_substs=(unavailable)]`
found closure `[main::{closure#0} closure_substs=(unavailable) substs=[i8, extern "rust-call" fn((u8,)) -> u8, _#6t]]`
note: closures can only be coerced to `fn` types if they do not capture any variables
--> $DIR/closure-print-verbose.rs:10:39
|
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// check-pass

pub trait Foo<'a> {
type Bar;
fn foo(&'a self) -> Self::Bar;
Expand All @@ -24,7 +26,6 @@ pub fn catalyst(x: &i32) {

pub fn broken<F: Fn(&i32)>(x: &i32, f: F) {
uncallable(x, |y| f(y));
//~^ type mismatch
}

fn main() {}
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ where P: Execute + 'static {
}

fn main() {
task(annotate( //~ type mismatch
task(annotate(
//~^ the size
//~^^ the trait bound
Annotate::<RefMutFamily<usize>>::new(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,3 @@
error[E0631]: type mismatch in closure arguments
--> $DIR/issue-62529-1.rs:80:10
|
LL | task(annotate(
| ^^^^^^^^ expected signature of `for<'r> fn(<RefMutFamily<usize> as FamilyLt<'r>>::Out) -> _`
...
LL | |value: &mut usize| {
| ------------------- found signature of `for<'r> fn(&'r mut usize) -> _`
|
note: required by a bound in `annotate`
--> $DIR/issue-62529-1.rs:44:8
|
LL | fn annotate<F, Q>(_q: Annotate<Q>, func: F) -> impl Execute + 'static
| -------- required by a bound in this
LL | where
LL | F: for<'r> FnOnce(<<Q as Inject>::I as FamilyLt<'r>>::Out) + 'static,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `annotate`

error[E0277]: the size for values of type `impl Execute` cannot be known at compilation time
--> $DIR/issue-62529-1.rs:80:10
|
Expand Down Expand Up @@ -61,7 +43,6 @@ LL | fn task<P>(processor: P) -> Task
LL | where P: Execute + 'static {
| ^^^^^^^ required by this bound in `task`

error: aborting due to 3 previous errors
error: aborting due to 2 previous errors

Some errors have detailed explanations: E0277, E0631.
For more information about an error, try `rustc --explain E0277`.
For more information about this error, try `rustc --explain E0277`.
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// check-pass

pub trait MyTrait<'a> {
type Output: 'a;
fn gimme_value(&self) -> Self::Output;
Expand All @@ -23,7 +25,7 @@ where

fn main() {
let struc = MyStruct;
meow(struc, |foo| { //~ type mismatch
meow(struc, |foo| {
println!("{:?}", foo);
})
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// check-pass

use std::marker::PhantomData;

trait A<'a> {
type B;
fn b(self) -> Self::B;
}

struct T;
struct S<'a>(PhantomData<&'a ()>);

impl<'a> A<'a> for T {
type B = S<'a>;
fn b(self) -> Self::B {
S(PhantomData)
}
}

fn s<TT, F>(t: TT, f: F)
where
TT: for<'a> A<'a>,
F: for<'a> FnOnce(<TT as A<'a>>::B)
{
f(t.b());
}

fn main() {
s(T, |_| {});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
error: implementation of `Parser` is not general enough
--> $DIR/normalization-under-binders-2.rs:53:5
|
LL | foo(bar, "string", |s| s.len() == 5);
| ^^^ implementation of `Parser` is not general enough
|
= note: `for<'a> fn(&'a str) -> (&'a str, &'a str) {bar}` must implement `Parser<'0>`, for any lifetime `'0`...
= note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1`

error: implementation of `Parser` is not general enough
--> $DIR/normalization-under-binders-2.rs:53:5
|
LL | foo(bar, "string", |s| s.len() == 5);
| ^^^ implementation of `Parser` is not general enough
|
= note: `for<'a> fn(&'a str) -> (&'a str, &'a str) {bar}` must implement `Parser<'0>`, for any lifetime `'0`...
= note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1`

error: implementation of `Parser` is not general enough
--> $DIR/normalization-under-binders-2.rs:53:5
|
LL | foo(bar, "string", |s| s.len() == 5);
| ^^^ implementation of `Parser` is not general enough
|
= note: `for<'a> fn(&'a str) -> (&'a str, &'a str) {bar}` must implement `Parser<'0>`, for any lifetime `'0`...
= note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1`

error: implementation of `Parser` is not general enough
--> $DIR/normalization-under-binders-2.rs:53:5
|
LL | foo(bar, "string", |s| s.len() == 5);
| ^^^ implementation of `Parser` is not general enough
|
= note: `for<'a> fn(&'a str) -> (&'a str, &'a str) {bar}` must implement `Parser<'0>`, for any lifetime `'0`...
= note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1`

error: implementation of `Parser` is not general enough
--> $DIR/normalization-under-binders-2.rs:53:5
|
LL | foo(bar, "string", |s| s.len() == 5);
| ^^^ implementation of `Parser` is not general enough
|
= note: `for<'a> fn(&'a str) -> (&'a str, &'a str) {bar}` must implement `Parser<'0>`, for any lifetime `'0`...
= note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1`

error: implementation of `Parser` is not general enough
--> $DIR/normalization-under-binders-2.rs:59:5
|
LL | foo(baz, "string", |s| s.0.len() == 5);
| ^^^ implementation of `Parser` is not general enough
|
= note: `for<'a> fn(&'a str) -> (&'a str, Wrapper<'a>) {baz}` must implement `Parser<'0>`, for any lifetime `'0`...
= note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1`

error: implementation of `Parser` is not general enough
--> $DIR/normalization-under-binders-2.rs:59:5
|
LL | foo(baz, "string", |s| s.0.len() == 5);
| ^^^ implementation of `Parser` is not general enough
|
= note: `for<'a> fn(&'a str) -> (&'a str, Wrapper<'a>) {baz}` must implement `Parser<'0>`, for any lifetime `'0`...
= note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1`

error: implementation of `Parser` is not general enough
--> $DIR/normalization-under-binders-2.rs:59:5
|
LL | foo(baz, "string", |s| s.0.len() == 5);
| ^^^ implementation of `Parser` is not general enough
|
= note: `for<'a> fn(&'a str) -> (&'a str, Wrapper<'a>) {baz}` must implement `Parser<'0>`, for any lifetime `'0`...
= note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1`

error: implementation of `Parser` is not general enough
--> $DIR/normalization-under-binders-2.rs:59:5
|
LL | foo(baz, "string", |s| s.0.len() == 5);
| ^^^ implementation of `Parser` is not general enough
|
= note: `for<'a> fn(&'a str) -> (&'a str, Wrapper<'a>) {baz}` must implement `Parser<'0>`, for any lifetime `'0`...
= note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1`

error: implementation of `Parser` is not general enough
--> $DIR/normalization-under-binders-2.rs:59:5
|
LL | foo(baz, "string", |s| s.0.len() == 5);
| ^^^ implementation of `Parser` is not general enough
|
= note: `for<'a> fn(&'a str) -> (&'a str, Wrapper<'a>) {baz}` must implement `Parser<'0>`, for any lifetime `'0`...
= note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1`

error: aborting due to 10 previous errors

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// ignore-compare-mode-nll
// fails in migrate, passes in nll

trait Parser<'s> {
type Output;

fn call(&self, input: &'s str) -> (&'s str, Self::Output);
}

impl<'s, F, T> Parser<'s> for F
where F: Fn(&'s str) -> (&'s str, T) {
type Output = T;
fn call(&self, input: &'s str) -> (&'s str, T) {
self(input)
}
}

fn foo<F1, F2>(
f1: F1,
base: &'static str,
f2: F2
)
where
F1: for<'a> Parser<'a>,
F2: FnOnce(&<F1 as Parser>::Output) -> bool
{
let s: String = base.to_owned();
let str_ref = s.as_ref();
let (remaining, produced) = f1.call(str_ref);
assert!(f2(&produced));
assert_eq!(remaining.len(), 0);
}

struct Wrapper<'a>(&'a str);

fn main() {
fn bar<'a>(s: &'a str) -> (&'a str, &'a str) {
(&s[..1], &s[..])
}

fn baz<'a>(s: &'a str) -> (&'a str, Wrapper<'a>) {
(&s[..1], Wrapper(&s[..]))
}

foo(bar, "string", |s| s.len() == 5);
//~^ ERROR implementation of `Parser` is not general enough
//~| ERROR implementation of `Parser` is not general enough
//~| ERROR implementation of `Parser` is not general enough
//~| ERROR implementation of `Parser` is not general enough
//~| ERROR implementation of `Parser` is not general enough
foo(baz, "string", |s| s.0.len() == 5);
//~^ ERROR implementation of `Parser` is not general enough
//~| ERROR implementation of `Parser` is not general enough
//~| ERROR implementation of `Parser` is not general enough
//~| ERROR implementation of `Parser` is not general enough
//~| ERROR implementation of `Parser` is not general enough
}
Loading

0 comments on commit 0900c2f

Please sign in to comment.