diff options
| author | bors <bors@rust-lang.org> | 2024-04-02 05:17:28 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-04-02 05:17:28 +0000 |
| commit | e2cf2cb30388385f0fe6b406a31a3f9841a72a62 (patch) | |
| tree | aab0bcc516128fd2357ce0e143e585b75d98cd2b | |
| parent | 6bbd8c519af69ebc30486d6cf02b7f7a52113950 (diff) | |
| parent | 09ea3f93ee6497247172a80ba17493748d08b64c (diff) | |
Auto merge of #122267 - compiler-errors:closure-like-goals, r=lcnr
Eagerly instantiate closure/coroutine-like bounds with placeholders to deal with binders correctly A follow-up to #119849, however it aims to fix a different set of issues. Currently, we have trouble confirming goals where built-in closure/fnptr/coroutine signatures are compared against higher-ranked goals. Currently, we don't support goals like `for<'a> fn(&'a ()): Fn(&'a ())` because we don't expect the self type of goal to reference any bound regions from the goal, because we don't really know how to deal with the double binder of predicate + self type. However, this definitely can be reached (#121653) -- and in fact, it results in post-mono errors in the case of #112347 where the builtin type (e.g. a coroutine) is hidden behind a TAIT. The proper fix here is to instantiate the goal before trying to extract the signature from the self type. See final two commits. r? lcnr
12 files changed, 158 insertions, 141 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 6f512a1173f..0459246553b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -28,7 +28,7 @@ use crate::traits::{ BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource, ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause, PolyTraitObligation, PredicateObligation, Selection, SelectionError, SignatureMismatch, TraitNotObjectSafe, - Unimplemented, + TraitObligation, Unimplemented, }; use super::BuiltinImplConditions; @@ -678,17 +678,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn_host_effect: ty::Const<'tcx>, ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { debug!(?obligation, "confirm_fn_pointer_candidate"); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let tcx = self.tcx(); - - let Some(self_ty) = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars()) else { - // FIXME: Ideally we'd support `for<'a> fn(&'a ()): Fn(&'a ())`, - // but we do not currently. Luckily, such a bound is not - // particularly useful, so we don't expect users to write - // them often. - return Err(SelectionError::Unimplemented); - }; - let sig = self_ty.fn_sig(tcx); let trait_ref = closure_trait_ref_and_return_type( tcx, @@ -700,7 +693,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) .map_bound(|(trait_ref, _)| trait_ref); - let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + let mut nested = + self.equate_trait_refs(obligation.with(tcx, placeholder_predicate), trait_ref)?; let cause = obligation.derived_cause(BuiltinDerivedObligation); // Confirm the `type Output: Sized;` bound that is present on `FnOnce` @@ -748,10 +742,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { - // Okay to skip binder because the args on coroutine types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { bug!("closure candidate for non-closure {:?}", obligation); }; @@ -760,15 +752,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let coroutine_sig = args.as_coroutine().sig(); - // NOTE: The self-type is a coroutine type and hence is - // in fact unparameterized (or at least does not reference any - // regions bound in the obligation). - let self_ty = obligation - .predicate - .self_ty() - .no_bound_vars() - .expect("unboxed closure type should not capture bound vars from the predicate"); - let (trait_ref, _, _) = super::util::coroutine_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), @@ -776,7 +759,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { coroutine_sig, ); - let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + let nested = self.equate_trait_refs( + obligation.with(self.tcx(), placeholder_predicate), + ty::Binder::dummy(trait_ref), + )?; debug!(?trait_ref, ?nested, "coroutine candidate obligations"); Ok(nested) @@ -786,10 +772,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { - // Okay to skip binder because the args on coroutine types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { bug!("closure candidate for non-closure {:?}", obligation); }; @@ -801,11 +785,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let (trait_ref, _) = super::util::future_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), - obligation.predicate.no_bound_vars().expect("future has no bound vars").self_ty(), + self_ty, coroutine_sig, ); - let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + let nested = self.equate_trait_refs( + obligation.with(self.tcx(), placeholder_predicate), + ty::Binder::dummy(trait_ref), + )?; debug!(?trait_ref, ?nested, "future candidate obligations"); Ok(nested) @@ -815,10 +802,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { - // Okay to skip binder because the args on coroutine types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { bug!("closure candidate for non-closure {:?}", obligation); }; @@ -830,11 +815,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let (trait_ref, _) = super::util::iterator_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), - obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(), + self_ty, gen_sig, ); - let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + let nested = self.equate_trait_refs( + obligation.with(self.tcx(), placeholder_predicate), + ty::Binder::dummy(trait_ref), + )?; debug!(?trait_ref, ?nested, "iterator candidate obligations"); Ok(nested) @@ -844,10 +832,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { - // Okay to skip binder because the args on coroutine types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { bug!("closure candidate for non-closure {:?}", obligation); }; @@ -859,11 +845,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let (trait_ref, _) = super::util::async_iterator_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), - obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(), + self_ty, gen_sig, ); - let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + let nested = self.equate_trait_refs( + obligation.with(self.tcx(), placeholder_predicate), + ty::Binder::dummy(trait_ref), + )?; debug!(?trait_ref, ?nested, "iterator candidate obligations"); Ok(nested) @@ -874,14 +863,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { - // Okay to skip binder because the args on closure types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty: Ty<'_> = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); + let trait_ref = match *self_ty.kind() { - ty::Closure(_, args) => { - self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_) - } + ty::Closure(..) => self.closure_trait_ref_unnormalized( + self_ty, + obligation.predicate.def_id(), + self.tcx().consts.true_, + ), ty::CoroutineClosure(_, args) => { args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| { ty::TraitRef::new( @@ -896,7 +886,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } }; - self.confirm_poly_trait_refs(obligation, trait_ref) + self.equate_trait_refs(obligation.with(self.tcx(), placeholder_predicate), trait_ref) } #[instrument(skip(self), level = "debug")] @@ -904,8 +894,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); + let tcx = self.tcx(); - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); let mut nested = vec![]; let (trait_ref, kind_ty) = match *self_ty.kind() { @@ -972,7 +964,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => bug!("expected callable type for AsyncFn candidate"), }; - nested.extend(self.confirm_poly_trait_refs(obligation, trait_ref)?); + nested.extend( + self.equate_trait_refs(obligation.with(tcx, placeholder_predicate), trait_ref)?, + ); let goal_kind = self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap(); @@ -1025,34 +1019,32 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// selection of the impl. Therefore, if there is a mismatch, we /// report an error to the user. #[instrument(skip(self), level = "trace")] - fn confirm_poly_trait_refs( + fn equate_trait_refs( &mut self, - obligation: &PolyTraitObligation<'tcx>, - self_ty_trait_ref: ty::PolyTraitRef<'tcx>, + obligation: TraitObligation<'tcx>, + found_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { - let obligation_trait_ref = - self.infcx.enter_forall_and_leak_universe(obligation.predicate.to_poly_trait_ref()); - let self_ty_trait_ref = self.infcx.instantiate_binder_with_fresh_vars( + let found_trait_ref = self.infcx.instantiate_binder_with_fresh_vars( obligation.cause.span, HigherRankedType, - self_ty_trait_ref, + found_trait_ref, ); // Normalize the obligation and expected trait refs together, because why not - let Normalized { obligations: nested, value: (obligation_trait_ref, expected_trait_ref) } = + let Normalized { obligations: nested, value: (obligation_trait_ref, found_trait_ref) } = ensure_sufficient_stack(|| { normalize_with_depth( self, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, - (obligation_trait_ref, self_ty_trait_ref), + (obligation.predicate.trait_ref, found_trait_ref), ) }); // needed to define opaque types for tests/ui/type-alias-impl-trait/assoc-projection-ice.rs self.infcx .at(&obligation.cause, obligation.param_env) - .eq(DefineOpaqueTypes::Yes, obligation_trait_ref, expected_trait_ref) + .eq(DefineOpaqueTypes::Yes, obligation_trait_ref, found_trait_ref) .map(|InferOk { mut obligations, .. }| { obligations.extend(nested); obligations @@ -1060,7 +1052,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map_err(|terr| { SignatureMismatch(Box::new(SignatureMismatchData { expected_trait_ref: ty::Binder::dummy(obligation_trait_ref), - found_trait_ref: ty::Binder::dummy(expected_trait_ref), + found_trait_ref: ty::Binder::dummy(found_trait_ref), terr, })) }) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 1894fbba302..084f7b1a8c2 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2679,26 +2679,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> { #[instrument(skip(self), level = "debug")] fn closure_trait_ref_unnormalized( &mut self, - obligation: &PolyTraitObligation<'tcx>, - args: GenericArgsRef<'tcx>, + self_ty: Ty<'tcx>, + fn_trait_def_id: DefId, fn_host_effect: ty::Const<'tcx>, ) -> ty::PolyTraitRef<'tcx> { + let ty::Closure(_, args) = *self_ty.kind() else { + bug!("expected closure, found {self_ty}"); + }; let closure_sig = args.as_closure().sig(); - debug!(?closure_sig); - - // NOTE: The self-type is an unboxed closure type and hence is - // in fact unparameterized (or at least does not reference any - // regions bound in the obligation). - let self_ty = obligation - .predicate - .self_ty() - .no_bound_vars() - .expect("unboxed closure type should not capture bound vars from the predicate"); - closure_trait_ref_and_return_type( self.tcx(), - obligation.predicate.def_id(), + fn_trait_def_id, self_ty, closure_sig, util::TupleArgumentsFlag::No, diff --git a/tests/ui/higher-ranked/builtin-closure-like-bounds.rs b/tests/ui/higher-ranked/builtin-closure-like-bounds.rs new file mode 100644 index 00000000000..dee290cc439 --- /dev/null +++ b/tests/ui/higher-ranked/builtin-closure-like-bounds.rs @@ -0,0 +1,58 @@ +//@ edition:2024 +//@ compile-flags: -Zunstable-options +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Makes sure that we support closure/coroutine goals where the signature of +// the item references higher-ranked lifetimes from the *predicate* binder, +// not its own internal signature binder. +// +// This was fixed in <https://github.com/rust-lang/rust/pull/122267>. + +#![feature(unboxed_closures, gen_blocks)] + +trait Dispatch { + fn dispatch(self); +} + +struct Fut<T>(T); +impl<T: for<'a> Fn<(&'a (),)>> Dispatch for Fut<T> +where + for<'a> <T as FnOnce<(&'a (),)>>::Output: Future, +{ + fn dispatch(self) { + (self.0)(&()); + } +} + +struct Gen<T>(T); +impl<T: for<'a> Fn<(&'a (),)>> Dispatch for Gen<T> +where + for<'a> <T as FnOnce<(&'a (),)>>::Output: Iterator, +{ + fn dispatch(self) { + (self.0)(&()); + } +} + +struct Closure<T>(T); +impl<T: for<'a> Fn<(&'a (),)>> Dispatch for Closure<T> +where + for<'a> <T as FnOnce<(&'a (),)>>::Output: Fn<(&'a (),)>, +{ + fn dispatch(self) { + (self.0)(&())(&()); + } +} + +fn main() { + async fn foo(_: &()) {} + Fut(foo).dispatch(); + + gen fn bar(_: &()) {} + Gen(bar).dispatch(); + + fn uwu<'a>(x: &'a ()) -> impl Fn(&'a ()) { |_| {} } + Closure(uwu).dispatch(); +} diff --git a/tests/ui/higher-ranked/closure-bound-codegen-ice.rs b/tests/ui/higher-ranked/closure-bound-codegen-ice.rs new file mode 100644 index 00000000000..4d7ae12d7a7 --- /dev/null +++ b/tests/ui/higher-ranked/closure-bound-codegen-ice.rs @@ -0,0 +1,33 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ build-pass + +// Regression test for incomplete handling of Fn-trait goals, +// fixed in #122267. + +trait Trait { + type Assoc<'a>: FnOnce(&'a ()); +} + +impl Trait for () { + type Assoc<'a> = fn(&'a ()); +} + +trait Indir { + fn break_me() {} +} + +impl<F: Trait> Indir for F +where + for<'a> F::Assoc<'a>: FnOnce(&'a ()), +{ + fn break_me() {} +} + +fn foo<F: Trait>() { + F::break_me() +} + +fn main() { + foo::<()>(); +} diff --git a/tests/ui/higher-ranked/trait-bounds/fn-ptr.classic.stderr b/tests/ui/higher-ranked/trait-bounds/fn-ptr.classic.stderr deleted file mode 100644 index b322ea41c43..00000000000 --- a/tests/ui/higher-ranked/trait-bounds/fn-ptr.classic.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0277]: expected a `Fn(&'w ())` closure, found `fn(&'w ())` - --> $DIR/fn-ptr.rs:12:5 - | -LL | ice(); - | ^^^^^ expected an `Fn(&'w ())` closure, found `fn(&'w ())` - | - = help: the trait `for<'w> Fn<(&'w (),)>` is not implemented for `fn(&'w ())` -note: required by a bound in `ice` - --> $DIR/fn-ptr.rs:7:25 - | -LL | fn ice() - | --- required by a bound in this function -LL | where -LL | for<'w> fn(&'w ()): Fn(&'w ()), - | ^^^^^^^^^^ required by this bound in `ice` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/higher-ranked/trait-bounds/fn-ptr.current.stderr b/tests/ui/higher-ranked/trait-bounds/fn-ptr.current.stderr deleted file mode 100644 index f3583cd218b..00000000000 --- a/tests/ui/higher-ranked/trait-bounds/fn-ptr.current.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0277]: expected a `Fn(&'w ())` closure, found `fn(&'w ())` - --> $DIR/fn-ptr.rs:13:5 - | -LL | ice(); - | ^^^^^ expected an `Fn(&'w ())` closure, found `fn(&'w ())` - | - = help: the trait `for<'w> Fn<(&'w (),)>` is not implemented for `fn(&'w ())` -note: required by a bound in `ice` - --> $DIR/fn-ptr.rs:8:25 - | -LL | fn ice() - | --- required by a bound in this function -LL | where -LL | for<'w> fn(&'w ()): Fn(&'w ()), - | ^^^^^^^^^^ required by this bound in `ice` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/higher-ranked/trait-bounds/fn-ptr.rs b/tests/ui/higher-ranked/trait-bounds/fn-ptr.rs index 9298c10c341..7a4c15f4d4b 100644 --- a/tests/ui/higher-ranked/trait-bounds/fn-ptr.rs +++ b/tests/ui/higher-ranked/trait-bounds/fn-ptr.rs @@ -1,7 +1,7 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] check-pass +//@ check-pass fn ice() where @@ -11,5 +11,4 @@ where fn main() { ice(); - //[current]~^ ERROR expected a `Fn(&'w ())` closure, found `fn(&'w ())` } diff --git a/tests/ui/higher-ranked/trait-bounds/future.classic.stderr b/tests/ui/higher-ranked/trait-bounds/future.classic.stderr deleted file mode 100644 index ef31b7266c7..00000000000 --- a/tests/ui/higher-ranked/trait-bounds/future.classic.stderr +++ /dev/null @@ -1,6 +0,0 @@ -error: the compiler unexpectedly panicked. this is a bug. - -query stack during panic: -#0 [evaluate_obligation] evaluating trait selection obligation `for<'a> {async fn body@$DIR/future.rs:32:35: 34:2}: core::future::future::Future` -#1 [codegen_select_candidate] computing candidate for `<strlen as Trait>` -end of query stack diff --git a/tests/ui/higher-ranked/trait-bounds/future.current.stderr b/tests/ui/higher-ranked/trait-bounds/future.current.stderr deleted file mode 100644 index 673bc48a424..00000000000 --- a/tests/ui/higher-ranked/trait-bounds/future.current.stderr +++ /dev/null @@ -1,6 +0,0 @@ -error: the compiler unexpectedly panicked. this is a bug. - -query stack during panic: -#0 [evaluate_obligation] evaluating trait selection obligation `for<'a> {async fn body of strlen()}: core::future::future::Future` -#1 [codegen_select_candidate] computing candidate for `<strlen as Trait>` -end of query stack diff --git a/tests/ui/higher-ranked/trait-bounds/future.rs b/tests/ui/higher-ranked/trait-bounds/future.rs index 4b52f04dbe0..7105015b690 100644 --- a/tests/ui/higher-ranked/trait-bounds/future.rs +++ b/tests/ui/higher-ranked/trait-bounds/future.rs @@ -3,14 +3,7 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] check-pass -//@[current] known-bug: #112347 -//@[current] build-fail -//@[current] failure-status: 101 -//@[current] normalize-stderr-test "note: .*\n\n" -> "" -//@[current] normalize-stderr-test "thread 'rustc' panicked.*\n.*\n" -> "" -//@[current] normalize-stderr-test "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " -//@[current] rustc-env:RUST_BACKTRACE=0 +//@ check-pass #![feature(unboxed_closures)] diff --git a/tests/ui/lifetimes/issue-105675.rs b/tests/ui/lifetimes/issue-105675.rs index 2e2eaca0d33..0472537e7f3 100644 --- a/tests/ui/lifetimes/issue-105675.rs +++ b/tests/ui/lifetimes/issue-105675.rs @@ -4,7 +4,7 @@ fn main() { let f = | _ , y: &u32 , z | (); thing(f); //~^ ERROR implementation of `FnOnce` is not general enough - //~^^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough let f = | x, y: _ , z: u32 | (); thing(f); //~^ ERROR implementation of `FnOnce` is not general enough diff --git a/tests/ui/lifetimes/lifetime-errors/issue_74400.rs b/tests/ui/lifetimes/lifetime-errors/issue_74400.rs index b02e38bec3b..72345fa294a 100644 --- a/tests/ui/lifetimes/lifetime-errors/issue_74400.rs +++ b/tests/ui/lifetimes/lifetime-errors/issue_74400.rs @@ -1,5 +1,5 @@ //! Regression test for #74400: Type mismatch in function arguments E0631, E0271 are falsely -//! recognized as E0308 mismatched types. +//! recognized as "implementation of `FnOnce` is not general enough". use std::convert::identity; @@ -13,6 +13,6 @@ fn g<T>(data: &[T]) { //~^ ERROR the parameter type //~| ERROR the parameter type //~| ERROR the parameter type - //~| ERROR implementation of `FnOnce` is not general + //~| ERROR implementation of `FnOnce` is not general enough //~| ERROR implementation of `Fn` is not general enough } |
