diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 7cee9779c5f39..c3583eeb430be 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -732,8 +732,6 @@ fn check_opaque_meets_bounds<'tcx>( span: Span, origin: &hir::OpaqueTyOrigin, ) { - let hidden_type = tcx.bound_type_of(def_id.to_def_id()).subst(tcx, substs); - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let defining_use_anchor = match *origin { hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did, @@ -748,14 +746,26 @@ fn check_opaque_meets_bounds<'tcx>( let ocx = ObligationCtxt::new(&infcx); let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs); + // `ReErased` regions appear in the "parent_substs" of closures/generators. + // We're ignoring them here and replacing them with fresh region variables. + // See tests in ui/type-alias-impl-trait/closure_{parent_substs,wf_outlives}.rs. + // + // FIXME: Consider wrapping the hidden type in an existential `Binder` and instantiating it + // here rather than using ReErased. + let hidden_ty = tcx.bound_type_of(def_id.to_def_id()).subst(tcx, substs); + let hidden_ty = tcx.fold_regions(hidden_ty, |re, _dbi| match re.kind() { + ty::ReErased => infcx.next_region_var(RegionVariableOrigin::MiscVariable(span)), + _ => re, + }); + let misc_cause = traits::ObligationCause::misc(span, hir_id); - match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_type) { + match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_ty) { Ok(infer_ok) => ocx.register_infer_ok_obligations(infer_ok), Err(ty_err) => { tcx.sess.delay_span_bug( span, - &format!("could not unify `{hidden_type}` with revealed type:\n{ty_err}"), + &format!("could not unify `{hidden_ty}` with revealed type:\n{ty_err}"), ); } } @@ -764,7 +774,7 @@ fn check_opaque_meets_bounds<'tcx>( // Defining use functions may have more bounds than the opaque type, which is ok, as long as the // hidden type is well formed even without those bounds. let predicate = - ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_type.into())).to_predicate(tcx); + ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_ty.into())).to_predicate(tcx); ocx.register_obligation(Obligation::new(misc_cause, param_env, predicate)); // Check that all obligations are satisfied by the implementation's diff --git a/src/test/ui/type-alias-impl-trait/closure_parent_substs.rs b/src/test/ui/type-alias-impl-trait/closure_parent_substs.rs new file mode 100644 index 0000000000000..475f4724ff28f --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/closure_parent_substs.rs @@ -0,0 +1,65 @@ +// When WF checking the hidden type in the ParamEnv of the opaque type, +// one complication arises when the hidden type is a closure/generator: +// the "parent_substs" of the type may reference lifetime parameters +// not present in the opaque type. +// These region parameters are not really useful in this check. +// So here we ignore them and replace them with fresh region variables. + +// check-pass + +#![feature(type_alias_impl_trait)] +#![allow(dead_code)] + +// Basic test +mod test1 { + // Hidden type = Closure['_#0r] + type Opaque = impl Sized; + + fn define<'a: 'a>() -> Opaque { + || {} + } +} + +// the region vars cannot both be equal to `'static` or `'empty` +mod test2 { + trait Trait {} + + // Hidden type = Closure['a, '_#0r, '_#1r] + // Constraints = [('_#0r: 'a), ('a: '_#1r)] + type Opaque<'a> + where + &'a (): Trait, + = impl Sized + 'a; + + fn define<'a, 'x, 'y>() -> Opaque<'a> + where + &'a (): Trait, + 'x: 'a, + 'a: 'y, + { + || {} + } +} + +// the region var cannot be equal to `'a` or `'b` +mod test3 { + trait Trait {} + + // Hidden type = Closure['a, 'b, '_#0r] + // Constraints = [('_#0r: 'a), ('_#0r: 'b)] + type Opaque<'a, 'b> + where + (&'a (), &'b ()): Trait, + = impl Sized + 'a + 'b; + + fn define<'a, 'b, 'x>() -> Opaque<'a, 'b> + where + (&'a (), &'b ()): Trait, + 'x: 'a, + 'x: 'b, + { + || {} + } +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/closure_wf_outlives.rs b/src/test/ui/type-alias-impl-trait/closure_wf_outlives.rs new file mode 100644 index 0000000000000..53974dbb36bdf --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/closure_wf_outlives.rs @@ -0,0 +1,65 @@ +// If the hidden type is a closure, we require the "outlives" bounds that appear on the +// defining site to also appear on the opaque type. +// +// It's not clear if this is the desired behavior but at least +// it's consistent and has no back-compat risk. + +// check-fail + +#![feature(type_alias_impl_trait)] +#![allow(dead_code)] + +// requires `'a: 'b` bound +mod test1 { + type Opaque<'a, 'b> = impl Sized + 'a + 'b; + //~^ ERROR lifetime bound not satisfied + + fn define<'a, 'b>() -> Opaque<'a, 'b> + where + 'a: 'b, + { + || {} + } +} + +// Same as the above but through indirection `'x` +mod test2 { + type Opaque<'a, 'b> = impl Sized + 'a + 'b; + //~^ ERROR cannot infer an appropriate lifetime + + fn define<'a, 'b, 'x>() -> Opaque<'a, 'b> + where + 'a: 'x, + 'x: 'b, + { + || {} + } +} + +// fixed version of the above +mod test2_fixed { + type Opaque<'a: 'b, 'b> = impl Sized + 'a + 'b; + + fn define<'a, 'b, 'x>() -> Opaque<'a, 'b> + where + 'a: 'x, + 'x: 'b, + { + || {} + } +} + +// requires `T: 'static` +mod test3 { + type Opaque = impl Sized; + //~^ ERROR the parameter type `T` may not live long enough + + fn define() -> Opaque + where + T: 'static, + { + || {} + } +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/closure_wf_outlives.stderr b/src/test/ui/type-alias-impl-trait/closure_wf_outlives.stderr new file mode 100644 index 0000000000000..ae6462bb62ce5 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/closure_wf_outlives.stderr @@ -0,0 +1,64 @@ +error[E0478]: lifetime bound not satisfied + --> $DIR/closure_wf_outlives.rs:14:27 + | +LL | type Opaque<'a, 'b> = impl Sized + 'a + 'b; + | ^^^^^^^^^^^^^^^^^^^^ + | +note: lifetime parameter instantiated with the lifetime `'a` as defined here + --> $DIR/closure_wf_outlives.rs:14:17 + | +LL | type Opaque<'a, 'b> = impl Sized + 'a + 'b; + | ^^ +note: but lifetime parameter must outlive the lifetime `'b` as defined here + --> $DIR/closure_wf_outlives.rs:14:21 + | +LL | type Opaque<'a, 'b> = impl Sized + 'a + 'b; + | ^^ + +error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements + --> $DIR/closure_wf_outlives.rs:27:27 + | +LL | type Opaque<'a, 'b> = impl Sized + 'a + 'b; + | ^^^^^^^^^^^^^^^^^^^^ + | +note: first, the lifetime cannot outlive the lifetime `'a` as defined here... + --> $DIR/closure_wf_outlives.rs:27:17 + | +LL | type Opaque<'a, 'b> = impl Sized + 'a + 'b; + | ^^ +note: ...so that the declared lifetime parameter bounds are satisfied + --> $DIR/closure_wf_outlives.rs:27:27 + | +LL | type Opaque<'a, 'b> = impl Sized + 'a + 'b; + | ^^^^^^^^^^^^^^^^^^^^ +note: but, the lifetime must be valid for the lifetime `'b` as defined here... + --> $DIR/closure_wf_outlives.rs:27:21 + | +LL | type Opaque<'a, 'b> = impl Sized + 'a + 'b; + | ^^ +note: ...so that the declared lifetime parameter bounds are satisfied + --> $DIR/closure_wf_outlives.rs:27:27 + | +LL | type Opaque<'a, 'b> = impl Sized + 'a + 'b; + | ^^^^^^^^^^^^^^^^^^^^ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/closure_wf_outlives.rs:54:22 + | +LL | type Opaque = impl Sized; + | ^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds... + | +note: ...that is required by this bound + --> $DIR/closure_wf_outlives.rs:59:12 + | +LL | T: 'static, + | ^^^^^^^ +help: consider adding an explicit lifetime bound... + | +LL | type Opaque = impl Sized; + | +++++++++ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0310, E0478, E0495. +For more information about an error, try `rustc --explain E0310`.