From 4e1999d3870c7be9a4addcfcf4fd5db9d29b7d1c Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Mon, 18 Mar 2024 08:33:24 +0000 Subject: [PATCH] ignore uncaptured lifetimes when checking opaques --- .../src/region_infer/opaque_types.rs | 23 +++--- compiler/rustc_middle/src/ty/mod.rs | 32 ++++++++ ...g-use-uncaptured-non-universal-region-2.rs | 74 +++++++++++++++++++ ...g-use-uncaptured-non-universal-region-3.rs | 13 ++++ ...ing-use-uncaptured-non-universal-region.rs | 16 ++++ ...erased-regions-in-hidden-ty.current.stderr | 2 +- .../erased-regions-in-hidden-ty.next.stderr | 2 +- .../impl-trait/erased-regions-in-hidden-ty.rs | 2 +- ...ariant-duplicate-lifetime-unconstrained.rs | 18 +++++ .../escaping-bound-var.rs | 4 + .../escaping-bound-var.stderr | 14 +++- 11 files changed, 183 insertions(+), 17 deletions(-) create mode 100644 tests/ui/impl-trait/defining-use-uncaptured-non-universal-region-2.rs create mode 100644 tests/ui/impl-trait/defining-use-uncaptured-non-universal-region-3.rs create mode 100644 tests/ui/impl-trait/defining-use-uncaptured-non-universal-region.rs create mode 100644 tests/ui/type-alias-impl-trait/bivariant-duplicate-lifetime-unconstrained.rs diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 49f7242cd83..4958e2c1ade 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -179,7 +179,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Next, insert universal regions from args, so we can translate regions that appear // in them but are not subject to member constraints, for instance closure args. - let universal_args = infcx.tcx.fold_regions(args, |region, _| { + let universal_key = opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |region| { if let ty::RePlaceholder(..) = region.kind() { // Higher kinded regions don't need remapping, they don't refer to anything outside of this the args. return region; @@ -187,6 +187,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let vid = self.to_region_vid(region); to_universal_region(vid, &mut arg_regions) }); + let universal_args = universal_key.args; debug!(?universal_args); debug!(?arg_regions); @@ -431,23 +432,21 @@ fn check_opaque_type_well_formed<'tcx>( } } -fn check_opaque_type_parameter_valid( - tcx: TyCtxt<'_>, - opaque_type_key: OpaqueTypeKey<'_>, +fn check_opaque_type_parameter_valid<'tcx>( + tcx: TyCtxt<'tcx>, + opaque_type_key: OpaqueTypeKey<'tcx>, span: Span, ) -> Result<(), ErrorGuaranteed> { let opaque_ty_hir = tcx.hir().expect_item(opaque_type_key.def_id); - let (parent, is_ty_alias) = match opaque_ty_hir.expect_opaque_ty().origin { + let (_parent, is_ty_alias) = match opaque_ty_hir.expect_opaque_ty().origin { OpaqueTyOrigin::TyAlias { parent, .. } => (parent, true), OpaqueTyOrigin::AsyncFn(parent) | OpaqueTyOrigin::FnReturn(parent) => (parent, false), }; - let parent_generics = tcx.generics_of(parent); + let opaque_generics = tcx.generics_of(opaque_type_key.def_id); let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default(); - // Only check the parent generics, which will ignore any of the - // duplicated lifetime args that come from reifying late-bounds. - for (i, arg) in opaque_type_key.args.iter().take(parent_generics.count()).enumerate() { + for (i, arg) in opaque_type_key.iter_captured_args(tcx) { let arg_is_param = match arg.unpack() { GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), GenericArgKind::Lifetime(lt) if is_ty_alias => { @@ -464,7 +463,7 @@ fn check_opaque_type_parameter_valid( seen_params.entry(arg).or_default().push(i); } else { // Prevent `fn foo() -> Foo` from being defining. - let opaque_param = parent_generics.param_at(i, tcx); + let opaque_param = opaque_generics.param_at(i, tcx); let kind = opaque_param.kind.descr(); return Err(tcx.dcx().emit_err(NonGenericOpaqueTypeParam { @@ -478,10 +477,10 @@ fn check_opaque_type_parameter_valid( for (_, indices) in seen_params { if indices.len() > 1 { - let descr = parent_generics.param_at(indices[0], tcx).kind.descr(); + let descr = opaque_generics.param_at(indices[0], tcx).kind.descr(); let spans: Vec<_> = indices .into_iter() - .map(|i| tcx.def_span(parent_generics.param_at(i, tcx).def_id)) + .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id)) .collect(); #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 6ce53ccc8cd..0be1a1ab39d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -832,6 +832,38 @@ pub struct OpaqueTypeKey<'tcx> { pub args: GenericArgsRef<'tcx>, } +impl<'tcx> OpaqueTypeKey<'tcx> { + pub fn iter_captured_args( + self, + tcx: TyCtxt<'tcx>, + ) -> impl Iterator)> { + std::iter::zip(self.args, tcx.variances_of(self.def_id)).enumerate().filter_map( + |(i, (arg, v))| match (arg.unpack(), v) { + (_, ty::Invariant) => Some((i, arg)), + (ty::GenericArgKind::Lifetime(_), ty::Bivariant) => None, + _ => bug!("unexpected opaque type arg variance"), + }, + ) + } + + pub fn fold_captured_lifetime_args( + self, + tcx: TyCtxt<'tcx>, + mut f: impl FnMut(Region<'tcx>) -> Region<'tcx>, + ) -> Self { + let Self { def_id, args } = self; + let args = std::iter::zip(args, tcx.variances_of(def_id)).map(|(arg, v)| { + match (arg.unpack(), v) { + (ty::GenericArgKind::Lifetime(_), ty::Bivariant) => arg, + (ty::GenericArgKind::Lifetime(lt), _) => f(lt).into(), + _ => arg, + } + }); + let args = tcx.mk_args_from_iter(args); + Self { def_id, args } + } +} + #[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)] pub struct OpaqueHiddenType<'tcx> { /// The span of this particular definition of the opaque type. So diff --git a/tests/ui/impl-trait/defining-use-uncaptured-non-universal-region-2.rs b/tests/ui/impl-trait/defining-use-uncaptured-non-universal-region-2.rs new file mode 100644 index 00000000000..56e099c28d2 --- /dev/null +++ b/tests/ui/impl-trait/defining-use-uncaptured-non-universal-region-2.rs @@ -0,0 +1,74 @@ +// issue: #110623 +//@ check-pass + +use std::{collections::BTreeMap, num::ParseIntError, str::FromStr}; + +enum FileSystem { + File(usize), + Directory(BTreeMap), +} + +impl FromStr for FileSystem { + type Err = ParseIntError; + + fn from_str(s: &str) -> Result { + if s.starts_with("dir") { + Ok(Self::new_dir()) + } else { + Ok(Self::File(s.split_whitespace().next().unwrap().parse()?)) + } + } +} + +impl FileSystem { + fn new_dir() -> FileSystem { + FileSystem::Directory(BTreeMap::new()) + } + + fn insert(&mut self, name: String, other: FileSystem) -> Option { + match self { + FileSystem::File(_) => panic!("can only insert into directory!"), + FileSystem::Directory(tree) => tree.insert(name, other), + } + } + + // Recursively build a tree from commands. This uses (abuses?) + // the fact that `cd /` only appears at the start and that + // subsequent `cd`s can only move ONE level to use the recursion + // stack as the filesystem stack + fn build<'a>( + &mut self, + mut commands: impl Iterator + 'a, + ) -> Option + 'a> { + let cmd = commands.next()?; + let mut elements = cmd.lines(); + match elements.next().map(str::trim) { + Some("cd /") | None => self.build(commands), + Some("cd ..") => { + // return to higher scope + Some(commands) + } + Some("ls") => { + for item in elements { + let name = item.split_whitespace().last().unwrap(); + let prior = self.insert(name.to_string(), item.parse().unwrap()); + debug_assert!(prior.is_none()); + } + // continue on + self.build(commands) + } + Some(other_cd) => { + let name = other_cd + .trim() + .strip_prefix("cd ") + .expect("expected a cd command"); + let mut directory = FileSystem::new_dir(); + let further_commands = directory.build(commands); + self.insert(name.to_string(), directory); + self.build(further_commands?) // THIS LINE FAILS TO COMPILE + } + } + } +} + +fn main() {} diff --git a/tests/ui/impl-trait/defining-use-uncaptured-non-universal-region-3.rs b/tests/ui/impl-trait/defining-use-uncaptured-non-universal-region-3.rs new file mode 100644 index 00000000000..a6dcad3face --- /dev/null +++ b/tests/ui/impl-trait/defining-use-uncaptured-non-universal-region-3.rs @@ -0,0 +1,13 @@ +//@ check-pass + +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +trait Bar {} +impl Bar<"asdf"> for () {} + +fn foo() -> impl Bar<"asdf"> { + () +} + +fn main() {} diff --git a/tests/ui/impl-trait/defining-use-uncaptured-non-universal-region.rs b/tests/ui/impl-trait/defining-use-uncaptured-non-universal-region.rs new file mode 100644 index 00000000000..f90ff51c651 --- /dev/null +++ b/tests/ui/impl-trait/defining-use-uncaptured-non-universal-region.rs @@ -0,0 +1,16 @@ +// issue: #111906 +//@ check-pass + +#![allow(unconditional_recursion)] + +fn foo<'a: 'a>() -> impl Sized { + let _: () = foo::<'a>(); + loop {} +} + +fn bar<'a: 'a>() -> impl Sized + 'a { + let _: *mut &'a () = bar::<'a>(); + loop {} +} + +fn main() {} diff --git a/tests/ui/impl-trait/erased-regions-in-hidden-ty.current.stderr b/tests/ui/impl-trait/erased-regions-in-hidden-ty.current.stderr index 1d648162113..78ef8ec404c 100644 --- a/tests/ui/impl-trait/erased-regions-in-hidden-ty.current.stderr +++ b/tests/ui/impl-trait/erased-regions-in-hidden-ty.current.stderr @@ -1,4 +1,4 @@ -error: {foo::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=()} +error: {foo<'{erased}>::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=()} --> $DIR/erased-regions-in-hidden-ty.rs:12:36 | LL | fn foo<'a: 'a>(x: &'a Vec) -> impl Fn() + 'static { diff --git a/tests/ui/impl-trait/erased-regions-in-hidden-ty.next.stderr b/tests/ui/impl-trait/erased-regions-in-hidden-ty.next.stderr index 1d648162113..78ef8ec404c 100644 --- a/tests/ui/impl-trait/erased-regions-in-hidden-ty.next.stderr +++ b/tests/ui/impl-trait/erased-regions-in-hidden-ty.next.stderr @@ -1,4 +1,4 @@ -error: {foo::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=()} +error: {foo<'{erased}>::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=()} --> $DIR/erased-regions-in-hidden-ty.rs:12:36 | LL | fn foo<'a: 'a>(x: &'a Vec) -> impl Fn() + 'static { diff --git a/tests/ui/impl-trait/erased-regions-in-hidden-ty.rs b/tests/ui/impl-trait/erased-regions-in-hidden-ty.rs index 9d71685f179..e60f1badcae 100644 --- a/tests/ui/impl-trait/erased-regions-in-hidden-ty.rs +++ b/tests/ui/impl-trait/erased-regions-in-hidden-ty.rs @@ -10,7 +10,7 @@ // Make sure that the compiler can handle `ReErased` in the hidden type of an opaque. fn foo<'a: 'a>(x: &'a Vec) -> impl Fn() + 'static { - //~^ ERROR 'a/#0>::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=()} + //~^ ERROR '{erased}>::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=()} // Can't write whole type because of lack of path sanitization || () } diff --git a/tests/ui/type-alias-impl-trait/bivariant-duplicate-lifetime-unconstrained.rs b/tests/ui/type-alias-impl-trait/bivariant-duplicate-lifetime-unconstrained.rs new file mode 100644 index 00000000000..3b83b2e544b --- /dev/null +++ b/tests/ui/type-alias-impl-trait/bivariant-duplicate-lifetime-unconstrained.rs @@ -0,0 +1,18 @@ +// The defining use below has an unconstrained lifetime argument. +// Opaque<'{empty}, 'a> := (); +// Make sure we accept it because the lifetime parameter in such position is +// irrelevant - it is an artifact of how we internally represent opaque +// generics. +// See issue #122307 for details. + +//@ check-pass +#![feature(type_alias_impl_trait)] +#![allow(unconditional_recursion)] + +type Opaque<'a> = impl Sized + 'a; + +fn test<'a>() -> Opaque<'a> { + let _: () = test::<'a>(); +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/escaping-bound-var.rs b/tests/ui/type-alias-impl-trait/escaping-bound-var.rs index 1ff200680be..4abd6b75ae6 100644 --- a/tests/ui/type-alias-impl-trait/escaping-bound-var.rs +++ b/tests/ui/type-alias-impl-trait/escaping-bound-var.rs @@ -17,6 +17,10 @@ impl Test<'_> for () {} fn constrain() -> Foo { () + //~^ ERROR expected generic lifetime parameter, found `'static` + // FIXME(aliemjay): Undesirable error message appears because error regions + // are converterted internally into `'?0` which corresponds to `'static` + // This should be fixed in a later commit. } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/escaping-bound-var.stderr b/tests/ui/type-alias-impl-trait/escaping-bound-var.stderr index 09f6fba79cf..b903b9f9151 100644 --- a/tests/ui/type-alias-impl-trait/escaping-bound-var.stderr +++ b/tests/ui/type-alias-impl-trait/escaping-bound-var.stderr @@ -10,6 +10,16 @@ note: lifetime declared here LL | pub type Foo = impl for<'a> Trait<'a, Assoc = impl Test<'a>>; | ^^ -error: aborting due to 1 previous error +error[E0792]: expected generic lifetime parameter, found `'static` + --> $DIR/escaping-bound-var.rs:19:5 + | +LL | pub type Foo = impl for<'a> Trait<'a, Assoc = impl Test<'a>>; + | -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type +... +LL | () + | ^^ -For more information about this error, try `rustc --explain E0657`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0657, E0792. +For more information about an error, try `rustc --explain E0657`.