From 3d69d23a25d6375ff8083bb94cc5b5fbc1f25415 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 18 May 2022 14:57:22 +0000 Subject: [PATCH 01/18] Split match out into a helper function --- .../src/infer/outlives/obligations.rs | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 5bd1774f6b1ec..e87510954288d 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -71,7 +71,7 @@ use rustc_data_structures::undo_log::UndoLogs; use rustc_hir::def_id::LocalDefId; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{self, Region, SubstsRef, Ty, TyCtxt, TypeVisitable}; use smallvec::smallvec; impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { @@ -352,7 +352,7 @@ where // may not apply. let mut approx_env_bounds = self.verify_bound.projection_approx_declared_bounds_from_env(projection_ty); - debug!("projection_must_outlive: approx_env_bounds={:?}", approx_env_bounds); + debug!(?approx_env_bounds); // Remove outlives bounds that we get from the environment but // which are also deducible from the trait. This arises (cc @@ -392,27 +392,9 @@ where // edges, which winds up enforcing the same condition. let needs_infer = projection_ty.needs_infer(); if approx_env_bounds.is_empty() && trait_bounds.is_empty() && needs_infer { - debug!("projection_must_outlive: no declared bounds"); - - let constraint = origin.to_constraint_category(); - for k in projection_ty.substs { - match k.unpack() { - GenericArgKind::Lifetime(lt) => { - self.delegate.push_sub_region_constraint( - origin.clone(), - region, - lt, - constraint, - ); - } - GenericArgKind::Type(ty) => { - self.type_must_outlive(origin.clone(), ty, region, constraint); - } - GenericArgKind::Const(_) => { - // Const parameters don't impose constraints. - } - } - } + debug!("no declared bounds"); + + self.substs_must_outlive(projection_ty.substs, origin, region); return; } @@ -442,8 +424,8 @@ where .all(|b| b == Some(trait_bounds[0])) { let unique_bound = trait_bounds[0]; - debug!("projection_must_outlive: unique trait bound = {:?}", unique_bound); - debug!("projection_must_outlive: unique declared bound appears in trait ref"); + debug!(?unique_bound); + debug!("unique declared bound appears in trait ref"); let category = origin.to_constraint_category(); self.delegate.push_sub_region_constraint(origin, region, unique_bound, category); return; @@ -459,6 +441,33 @@ where debug!("projection_must_outlive: pushing {:?}", verify_bound); self.delegate.push_verify(origin, generic, region, verify_bound); } + + fn substs_must_outlive( + &mut self, + substs: SubstsRef<'tcx>, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + ) { + let constraint = origin.to_constraint_category(); + for k in substs { + match k.unpack() { + GenericArgKind::Lifetime(lt) => { + self.delegate.push_sub_region_constraint( + origin.clone(), + region, + lt, + constraint, + ); + } + GenericArgKind::Type(ty) => { + self.type_must_outlive(origin.clone(), ty, region, constraint); + } + GenericArgKind::Const(_) => { + // Const parameters don't impose constraints. + } + } + } + } } impl<'cx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'cx, 'tcx> { From 759c04a00b46d4a86d71ab69b072b956dfacabf9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 18 May 2022 14:58:47 +0000 Subject: [PATCH 02/18] Some tracing instrumentation cleanups --- compiler/rustc_infer/src/infer/outlives/verify.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index e344e192a768e..bd7b8a3004f53 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -124,13 +124,12 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { self.declared_projection_bounds_from_trait(projection_ty) } - pub fn projection_bound( + #[instrument(level = "debug", skip(self, visited))] + fn projection_bound( &self, projection_ty: ty::ProjectionTy<'tcx>, visited: &mut SsoHashSet>, ) -> VerifyBound<'tcx> { - debug!("projection_bound(projection_ty={:?})", projection_ty); - let projection_ty_as_ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); From 96b819a456672e50444dcccbfed2435f6b212225 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 18 May 2022 15:09:14 +0000 Subject: [PATCH 03/18] Inline a trivial function --- .../src/infer/outlives/obligations.rs | 4 +-- .../rustc_infer/src/infer/outlives/verify.rs | 34 ++++++------------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index e87510954288d..3f98168e2573a 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -343,7 +343,7 @@ where // These are guaranteed to apply, no matter the inference // results. let trait_bounds: Vec<_> = - self.verify_bound.projection_declared_bounds_from_trait(projection_ty).collect(); + self.verify_bound.bounds(projection_ty.item_def_id, projection_ty.substs).collect(); debug!(?trait_bounds); @@ -369,7 +369,7 @@ where match *bound.0.kind() { ty::Projection(projection_ty) => self .verify_bound - .projection_declared_bounds_from_trait(projection_ty) + .bounds(projection_ty.item_def_id, projection_ty.substs) .all(|r| r != bound.1), _ => panic!("expected only projection types from env, not {:?}", bound.0), diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index bd7b8a3004f53..30ee1229faec1 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -6,7 +6,7 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::sso::SsoHashSet; use rustc_hir::def_id::DefId; use rustc_middle::ty::GenericArg; -use rustc_middle::ty::{self, EarlyBinder, OutlivesPredicate, Ty, TyCtxt}; +use rustc_middle::ty::{self, EarlyBinder, OutlivesPredicate, SubstsRef, Ty, TyCtxt}; use smallvec::smallvec; @@ -114,16 +114,6 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { self.declared_generic_bounds_from_env_for_erased_ty(erased_projection_ty) } - /// Searches the where-clauses in scope for regions that - /// `projection_ty` is known to outlive. Currently requires an - /// exact match. - pub fn projection_declared_bounds_from_trait( - &self, - projection_ty: ty::ProjectionTy<'tcx>, - ) -> impl Iterator> + 'cx + Captures<'tcx> { - self.declared_projection_bounds_from_trait(projection_ty) - } - #[instrument(level = "debug", skip(self, visited))] fn projection_bound( &self, @@ -151,7 +141,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // Extend with bounds that we can find from the trait. let trait_bounds = self - .projection_declared_bounds_from_trait(projection_ty) + .bounds(projection_ty.item_def_id, projection_ty.substs) .map(|r| VerifyBound::OutlivedBy(r)); // see the extensive comment in projection_must_outlive @@ -294,15 +284,15 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// /// then this function would return `'x`. This is subject to the /// limitations around higher-ranked bounds described in - /// `region_bounds_declared_on_associated_item`. - fn declared_projection_bounds_from_trait( + /// `declared_region_bounds`. + #[instrument(level = "debug", skip(self))] + pub fn bounds( &self, - projection_ty: ty::ProjectionTy<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, ) -> impl Iterator> + 'cx + Captures<'tcx> { - debug!("projection_bounds(projection_ty={:?})", projection_ty); let tcx = self.tcx; - self.region_bounds_declared_on_associated_item(projection_ty.item_def_id) - .map(move |r| EarlyBinder(r).subst(tcx, projection_ty.substs)) + self.declared_region_bounds(def_id).map(move |r| EarlyBinder(r).subst(tcx, substs)) } /// Given the `DefId` of an associated item, returns any region @@ -335,12 +325,10 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// /// This is for simplicity, and because we are not really smart /// enough to cope with such bounds anywhere. - fn region_bounds_declared_on_associated_item( - &self, - assoc_item_def_id: DefId, - ) -> impl Iterator> { + fn declared_region_bounds(&self, def_id: DefId) -> impl Iterator> { let tcx = self.tcx; - let bounds = tcx.item_bounds(assoc_item_def_id); + let bounds = tcx.item_bounds(def_id); + trace!("{:#?}", bounds); bounds .into_iter() .filter_map(|p| p.to_opt_type_outlives()) From 4d4cc4fe53aec2a7f36535c3458aced2fcd2988d Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 18 May 2022 15:17:58 +0000 Subject: [PATCH 04/18] Generalize a helper to be useful for types other than projections --- compiler/rustc_infer/src/infer/outlives/obligations.rs | 6 +++--- compiler/rustc_infer/src/infer/outlives/verify.rs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 3f98168e2573a..ec26a10c5362c 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -347,11 +347,12 @@ where debug!(?trait_bounds); + let generic = GenericKind::Projection(projection_ty); + // Compute the bounds we can derive from the environment. This // is an "approximate" match -- in some cases, these bounds // may not apply. - let mut approx_env_bounds = - self.verify_bound.projection_approx_declared_bounds_from_env(projection_ty); + let mut approx_env_bounds = self.verify_bound.approx_declared_bounds_from_env(generic); debug!(?approx_env_bounds); // Remove outlives bounds that we get from the environment but @@ -436,7 +437,6 @@ where // projection outlive; in some cases, this may add insufficient // edges into the inference graph, leading to inference failures // even though a satisfactory solution exists. - let generic = GenericKind::Projection(projection_ty); let verify_bound = self.verify_bound.generic_bound(generic); debug!("projection_must_outlive: pushing {:?}", verify_bound); self.delegate.push_verify(origin, generic, region, verify_bound); diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 30ee1229faec1..5f1671b4807c9 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -105,11 +105,11 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// the clause from the environment only applies if `'0 = 'a`, /// which we don't know yet. But we would still include `'b` in /// this list. - pub fn projection_approx_declared_bounds_from_env( + pub fn approx_declared_bounds_from_env( &self, - projection_ty: ty::ProjectionTy<'tcx>, + generic: GenericKind<'tcx>, ) -> Vec, ty::Region<'tcx>>>> { - let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx); + let projection_ty = generic.to_ty(self.tcx); let erased_projection_ty = self.tcx.erase_regions(projection_ty); self.declared_generic_bounds_from_env_for_erased_ty(erased_projection_ty) } @@ -125,7 +125,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // Search the env for where clauses like `P: 'a`. let env_bounds = self - .projection_approx_declared_bounds_from_env(projection_ty) + .approx_declared_bounds_from_env(GenericKind::Projection(projection_ty)) .into_iter() .map(|binder| { if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() && ty == projection_ty_as_ty { From c8ecf09a25720bb87f1da7b1741238eb70aa2762 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 18 May 2022 15:40:16 +0000 Subject: [PATCH 05/18] Generalize projection_must_outlive --- .../src/infer/outlives/obligations.rs | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index ec26a10c5362c..55bb75c8b6da2 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -68,6 +68,7 @@ use crate::infer::{ }; use crate::traits::{ObligationCause, ObligationCauseCode}; use rustc_data_structures::undo_log::UndoLogs; +use rustc_hir::def_id::DefId; use rustc_hir::def_id::LocalDefId; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::subst::GenericArgKind; @@ -324,6 +325,29 @@ where origin: infer::SubregionOrigin<'tcx>, region: ty::Region<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, + ) { + self.generic_must_outlive( + origin, + region, + GenericKind::Projection(projection_ty), + projection_ty.item_def_id, + projection_ty.substs, + |ty| match ty.kind() { + ty::Projection(projection_ty) => (projection_ty.item_def_id, projection_ty.substs), + _ => bug!("expected only projection types from env, not {:?}", ty), + }, + ); + } + + #[instrument(level = "debug", skip(self, filter))] + fn generic_must_outlive( + &mut self, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + generic: GenericKind<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + filter: impl Fn(Ty<'tcx>) -> (DefId, SubstsRef<'tcx>), ) { // This case is thorny for inference. The fundamental problem is // that there are many cases where we have choice, and inference @@ -342,13 +366,10 @@ where // Compute the bounds we can derive from the trait definition. // These are guaranteed to apply, no matter the inference // results. - let trait_bounds: Vec<_> = - self.verify_bound.bounds(projection_ty.item_def_id, projection_ty.substs).collect(); + let trait_bounds: Vec<_> = self.verify_bound.bounds(def_id, substs).collect(); debug!(?trait_bounds); - let generic = GenericKind::Projection(projection_ty); - // Compute the bounds we can derive from the environment. This // is an "approximate" match -- in some cases, these bounds // may not apply. @@ -367,14 +388,8 @@ where // If the declaration is `trait Trait<'b> { type Item: 'b; }`, then `projection_declared_bounds_from_trait` // will be invoked with `['b => ^1]` and so we will get `^1` returned. let bound = bound_outlives.skip_binder(); - match *bound.0.kind() { - ty::Projection(projection_ty) => self - .verify_bound - .bounds(projection_ty.item_def_id, projection_ty.substs) - .all(|r| r != bound.1), - - _ => panic!("expected only projection types from env, not {:?}", bound.0), - } + let (def_id, substs) = filter(bound.0); + self.verify_bound.bounds(def_id, substs).all(|r| r != bound.1) }); // If declared bounds list is empty, the only applicable rule is @@ -391,11 +406,11 @@ where // the problem is to add `T: 'r`, which isn't true. So, if there are no // inference variables, we use a verify constraint instead of adding // edges, which winds up enforcing the same condition. - let needs_infer = projection_ty.needs_infer(); + let needs_infer = substs.needs_infer(); if approx_env_bounds.is_empty() && trait_bounds.is_empty() && needs_infer { debug!("no declared bounds"); - self.substs_must_outlive(projection_ty.substs, origin, region); + self.substs_must_outlive(substs, origin, region); return; } From 37928f59865b5ff53b95fe2f3c02491890a4129d Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 18 May 2022 16:01:10 +0000 Subject: [PATCH 06/18] Neither require nor imply lifetime bounds on opaque type for well formedness --- .../src/type_check/free_region_relations.rs | 5 ++ .../src/infer/error_reporting/mod.rs | 3 ++ .../src/infer/outlives/components.rs | 17 ++++++- .../rustc_infer/src/infer/outlives/env.rs | 4 ++ .../src/infer/outlives/obligations.rs | 24 +++++++++ .../rustc_infer/src/infer/outlives/verify.rs | 16 ++++++ .../src/infer/region_constraints/mod.rs | 10 ++++ compiler/rustc_infer/src/traits/util.rs | 12 ++++- compiler/rustc_middle/src/traits/query.rs | 4 +- .../src/implied_outlives_bounds.rs | 3 ++ .../rustc_typeck/src/mem_categorization.rs | 12 ++--- compiler/rustc_typeck/src/outlives/utils.rs | 35 ++++++++++++- .../bugs/issue-86218.stderr | 23 --------- .../generic-associated-types/issue-86218-2.rs | 24 +++++++++ .../{bugs => }/issue-86218.rs | 8 ++- .../type-alias-impl-trait/implied_bounds.rs | 51 +++++++++++++++++++ .../implied_bounds.stderr | 16 ++++++ .../type-alias-impl-trait/implied_bounds2.rs | 9 ++++ .../implied_bounds2.stderr | 12 +++++ .../implied_bounds_closure.rs | 31 +++++++++++ .../implied_bounds_closure.stderr | 11 ++++ .../implied_bounds_from_types.rs | 51 +++++++++++++++++++ .../implied_bounds_from_types.stderr | 16 ++++++ .../implied_lifetime_wf_check.rs | 18 +++++++ .../implied_lifetime_wf_check.stderr | 9 ++++ .../implied_lifetime_wf_check2.rs | 13 +++++ .../implied_lifetime_wf_check3.rs | 42 +++++++++++++++ .../implied_lifetime_wf_check3.stderr | 38 ++++++++++++++ .../issue-58662-generator-with-lifetime.rs | 2 +- .../issue-58662-simplified.rs | 20 ++++++++ 30 files changed, 497 insertions(+), 42 deletions(-) delete mode 100644 src/test/ui/generic-associated-types/bugs/issue-86218.stderr create mode 100644 src/test/ui/generic-associated-types/issue-86218-2.rs rename src/test/ui/generic-associated-types/{bugs => }/issue-86218.rs (70%) create mode 100644 src/test/ui/type-alias-impl-trait/implied_bounds.rs create mode 100644 src/test/ui/type-alias-impl-trait/implied_bounds.stderr create mode 100644 src/test/ui/type-alias-impl-trait/implied_bounds2.rs create mode 100644 src/test/ui/type-alias-impl-trait/implied_bounds2.stderr create mode 100644 src/test/ui/type-alias-impl-trait/implied_bounds_closure.rs create mode 100644 src/test/ui/type-alias-impl-trait/implied_bounds_closure.stderr create mode 100644 src/test/ui/type-alias-impl-trait/implied_bounds_from_types.rs create mode 100644 src/test/ui/type-alias-impl-trait/implied_bounds_from_types.stderr create mode 100644 src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check.rs create mode 100644 src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check.stderr create mode 100644 src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check2.rs create mode 100644 src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check3.rs create mode 100644 src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check3.stderr create mode 100644 src/test/ui/type-alias-impl-trait/issue-58662-simplified.rs diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index f1b1c33a10543..e0140e281ee73 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -362,6 +362,11 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { self.region_bound_pairs .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a)); } + + OutlivesBound::RegionSubOpaque(r_a, def_id, substs) => { + self.region_bound_pairs + .insert(ty::OutlivesPredicate(GenericKind::Opaque(def_id, substs), r_a)); + } } } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 546ab82bc238a..e33035381e0a9 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2374,6 +2374,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let labeled_user_string = match bound_kind { GenericKind::Param(ref p) => format!("the parameter type `{}`", p), GenericKind::Projection(ref p) => format!("the associated type `{}`", p), + GenericKind::Opaque(def_id, substs) => { + format!("the opaque type `{}`", self.tcx.def_path_str_with_substs(def_id, substs)) + } }; if let Some(SubregionOrigin::CompareImplItemObligation { diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs index b2d7f4a663ae4..14ee9f0519010 100644 --- a/compiler/rustc_infer/src/infer/outlives/components.rs +++ b/compiler/rustc_infer/src/infer/outlives/components.rs @@ -3,8 +3,9 @@ // RFC for reference. use rustc_data_structures::sso::SsoHashSet; +use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{self, SubstsRef, Ty, TyCtxt, TypeVisitable}; use smallvec::{smallvec, SmallVec}; #[derive(Debug)] @@ -45,6 +46,8 @@ pub enum Component<'tcx> { // them. This gives us room to improve the regionck reasoning in // the future without breaking backwards compat. EscapingProjection(Vec>), + + Opaque(DefId, SubstsRef<'tcx>), } /// Push onto `out` all the things that must outlive `'a` for the condition @@ -120,6 +123,17 @@ fn compute_components<'tcx>( out.push(Component::Param(p)); } + // Ignore lifetimes found in opaque types. Opaque types can + // have lifetimes in their substs which their hidden type doesn't + // actually use. If we inferred that an opaque type is outlived by + // its parameter lifetimes, then we could prove that any lifetime + // outlives any other lifetime, which is unsound. + // See https://github.com/rust-lang/rust/issues/84305 for + // more details. + ty::Opaque(def_id, substs) => { + out.push(Component::Opaque(def_id, substs)); + }, + // For projections, we prefer to generate an obligation like // `>::Foo: 'a`, because this gives the // regionck more ways to prove that it holds. However, @@ -168,7 +182,6 @@ fn compute_components<'tcx>( ty::Float(..) | // OutlivesScalar ty::Never | // ... ty::Adt(..) | // OutlivesNominalType - ty::Opaque(..) | // OutlivesNominalType (ish) ty::Foreign(..) | // OutlivesNominalType ty::Str | // OutlivesScalar (ish) ty::Slice(..) | // ... diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index 872886da36261..281c65bda77a7 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -140,6 +140,10 @@ impl<'a, 'tcx> OutlivesEnvironmentBuilder<'tcx> { self.region_bound_pairs .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a)); } + OutlivesBound::RegionSubOpaque(r_a, def_id, substs) => { + self.region_bound_pairs + .insert(ty::OutlivesPredicate(GenericKind::Opaque(def_id, substs), r_a)); + } OutlivesBound::RegionSubRegion(r_a, r_b) => { if let (ReEarlyBound(_) | ReFree(_), ReVar(vid_b)) = (r_a.kind(), r_b.kind()) { infcx diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 55bb75c8b6da2..ad5fbb9a15823 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -284,6 +284,9 @@ where Component::Param(param_ty) => { self.param_ty_must_outlive(origin, region, *param_ty); } + Component::Opaque(def_id, substs) => { + self.opaque_must_outlive(*def_id, substs, origin, region) + } Component::Projection(projection_ty) => { self.projection_must_outlive(origin, region, *projection_ty); } @@ -319,6 +322,27 @@ where self.delegate.push_verify(origin, generic, region, verify_bound); } + #[instrument(level = "debug", skip(self))] + fn opaque_must_outlive( + &mut self, + def_id: DefId, + substs: SubstsRef<'tcx>, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + ) { + self.generic_must_outlive( + origin, + region, + GenericKind::Opaque(def_id, substs), + def_id, + substs, + |ty| match *ty.kind() { + ty::Opaque(def_id, substs) => (def_id, substs), + _ => bug!("expected only projection types from env, not {:?}", ty), + }, + ); + } + #[instrument(level = "debug", skip(self))] fn projection_must_outlive( &mut self, diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 5f1671b4807c9..040843d837070 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -47,6 +47,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { GenericKind::Projection(projection_ty) => { self.projection_bound(projection_ty, &mut visited) } + GenericKind::Opaque(def_id, substs) => self.opaque_bound(def_id, substs), } } @@ -155,6 +156,20 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound) } + fn opaque_bound(&self, def_id: DefId, substs: SubstsRef<'tcx>) -> VerifyBound<'tcx> { + let bounds: Vec<_> = + self.bounds(def_id, substs).map(|r| VerifyBound::OutlivedBy(r)).collect(); + trace!("{:#?}", bounds); + if bounds.is_empty() { + // No bounds means the value must not have any lifetimes. + // FIXME: should we implicitly add 'static to `tcx.item_bounds` for opaque types, just + // like we add `Sized`? + VerifyBound::OutlivedBy(self.tcx.lifetimes.re_static) + } else { + VerifyBound::AnyBound(bounds) + } + } + fn bound_from_components( &self, components: &[Component<'tcx>], @@ -184,6 +199,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { match *component { Component::Region(lt) => VerifyBound::OutlivedBy(lt), Component::Param(param_ty) => self.param_bound(param_ty), + Component::Opaque(did, substs) => self.opaque_bound(did, substs), Component::Projection(projection_ty) => self.projection_bound(projection_ty, visited), Component::EscapingProjection(ref components) => { self.bound_from_components(components, visited) diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index e43d28ee56e3e..67b3da687200e 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -12,8 +12,10 @@ use rustc_data_structures::intern::Interned; use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::UndoLogs; use rustc_data_structures::unify as ut; +use rustc_hir::def_id::DefId; use rustc_index::vec::IndexVec; use rustc_middle::infer::unify_key::{RegionVidKey, UnifiedRegion}; +use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::ReStatic; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ReLateBound, ReVar}; @@ -168,6 +170,7 @@ pub struct Verify<'tcx> { pub enum GenericKind<'tcx> { Param(ty::ParamTy), Projection(ty::ProjectionTy<'tcx>), + Opaque(DefId, SubstsRef<'tcx>), } /// Describes the things that some `GenericKind` value `G` is known to @@ -747,6 +750,9 @@ impl<'tcx> fmt::Debug for GenericKind<'tcx> { match *self { GenericKind::Param(ref p) => write!(f, "{:?}", p), GenericKind::Projection(ref p) => write!(f, "{:?}", p), + GenericKind::Opaque(def_id, substs) => ty::tls::with(|tcx| { + write!(f, "{}", tcx.def_path_str_with_substs(def_id, tcx.lift(substs).unwrap())) + }), } } } @@ -756,6 +762,9 @@ impl<'tcx> fmt::Display for GenericKind<'tcx> { match *self { GenericKind::Param(ref p) => write!(f, "{}", p), GenericKind::Projection(ref p) => write!(f, "{}", p), + GenericKind::Opaque(def_id, substs) => ty::tls::with(|tcx| { + write!(f, "{}", tcx.def_path_str_with_substs(def_id, tcx.lift(substs).unwrap())) + }), } } } @@ -765,6 +774,7 @@ impl<'tcx> GenericKind<'tcx> { match *self { GenericKind::Param(ref p) => p.to_ty(tcx), GenericKind::Projection(ref p) => tcx.mk_projection(p.item_def_id, p.substs), + GenericKind::Opaque(def_id, substs) => tcx.mk_opaque(def_id, substs), } } } diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index f5a1edf6d813f..e12c069dcc1d9 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -246,6 +246,13 @@ impl<'tcx> Elaborator<'tcx> { Component::UnresolvedInferenceVariable(_) => None, + Component::Opaque(def_id, substs) => { + let ty = tcx.mk_opaque(def_id, substs); + Some(ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate( + ty, r_min, + ))) + } + Component::Projection(projection) => { // We might end up here if we have `Foo<::Assoc>: 'a`. // With this, we can deduce that `::Assoc: 'a`. @@ -262,8 +269,9 @@ impl<'tcx> Elaborator<'tcx> { None } }) - .map(ty::Binder::dummy) - .map(|predicate_kind| predicate_kind.to_predicate(tcx)) + .map(|predicate_kind| { + bound_predicate.rebind(predicate_kind).to_predicate(tcx) + }) .filter(|&predicate| visited.insert(predicate)) .map(|predicate| { predicate_obligation( diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 0e6cacb9fd0f8..fb152b63f6344 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -8,8 +8,9 @@ use crate::error::DropCheckOverflow; use crate::infer::canonical::{Canonical, QueryResponse}; use crate::ty::error::TypeError; -use crate::ty::subst::GenericArg; +use crate::ty::subst::{GenericArg, SubstsRef}; use crate::ty::{self, Ty, TyCtxt}; +use rustc_hir::def_id::DefId; use rustc_span::source_map::Span; use std::iter::FromIterator; @@ -219,4 +220,5 @@ pub enum OutlivesBound<'tcx> { RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), RegionSubParam(ty::Region<'tcx>, ty::ParamTy), RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), + RegionSubOpaque(ty::Region<'tcx>, DefId, SubstsRef<'tcx>), } diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index e3e78f70b15ef..d77ed97c6c8da 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -153,6 +153,9 @@ fn implied_bounds_from_components<'tcx>( Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)), Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)), Component::Projection(p) => Some(OutlivesBound::RegionSubProjection(sub_region, p)), + Component::Opaque(def_id, substs) => { + Some(OutlivesBound::RegionSubOpaque(sub_region, def_id, substs)) + } Component::EscapingProjection(_) => // If the projection has escaping regions, don't // try to infer any implied bounds even for its diff --git a/compiler/rustc_typeck/src/mem_categorization.rs b/compiler/rustc_typeck/src/mem_categorization.rs index ced919f66db41..39610e3ae3805 100644 --- a/compiler/rustc_typeck/src/mem_categorization.rs +++ b/compiler/rustc_typeck/src/mem_categorization.rs @@ -265,6 +265,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment) } + #[instrument(level = "debug", skip(self, previous))] fn cat_expr_adjusted_with( &self, expr: &hir::Expr<'_>, @@ -274,7 +275,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { where F: FnOnce() -> McResult>, { - debug!("cat_expr_adjusted_with({:?}): {:?}", adjustment, expr); let target = self.resolve_vars_if_possible(adjustment.target); match adjustment.kind { adjustment::Adjust::Deref(overloaded) => { @@ -299,6 +299,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self))] pub(crate) fn cat_expr_unadjusted( &self, expr: &hir::Expr<'_>, @@ -387,6 +388,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self, span))] pub(crate) fn cat_res( &self, hir_id: hir::HirId, @@ -394,8 +396,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { expr_ty: Ty<'tcx>, res: Res, ) -> McResult> { - debug!("cat_res: id={:?} expr={:?} def={:?}", hir_id, expr_ty, res); - match res { Res::Def( DefKind::Ctor(..) @@ -475,13 +475,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { ret } + #[instrument(level = "debug", skip(self))] fn cat_overloaded_place( &self, expr: &hir::Expr<'_>, base: &hir::Expr<'_>, ) -> McResult> { - debug!("cat_overloaded_place(expr={:?}, base={:?})", expr, base); - // Reconstruct the output assuming it's a reference with the // same region and mutability as the receiver. This holds for // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. @@ -497,13 +496,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.cat_deref(expr, base) } + #[instrument(level = "debug", skip(self, node))] fn cat_deref( &self, node: &impl HirNode, base_place: PlaceWithHirId<'tcx>, ) -> McResult> { - debug!("cat_deref: base_place={:?}", base_place); - let base_curr_ty = base_place.place.ty(); let deref_ty = match base_curr_ty.builtin_deref(true) { Some(mt) => mt.ty, diff --git a/compiler/rustc_typeck/src/outlives/utils.rs b/compiler/rustc_typeck/src/outlives/utils.rs index 3e8d023fb551c..4c32a1e81ba54 100644 --- a/compiler/rustc_typeck/src/outlives/utils.rs +++ b/compiler/rustc_typeck/src/outlives/utils.rs @@ -1,6 +1,6 @@ use rustc_infer::infer::outlives::components::{push_outlives_components, Component}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, Region, Ty, TyCtxt}; +use rustc_middle::ty::{self, EarlyBinder, Region, Ty, TyCtxt}; use rustc_span::Span; use smallvec::smallvec; use std::collections::BTreeMap; @@ -96,6 +96,39 @@ pub(crate) fn insert_outlives_predicate<'tcx>( .or_insert(span); } + Component::Opaque(def_id, substs) => { + for predicate in tcx.item_bounds(def_id) { + let predicate = EarlyBinder(predicate).subst(tcx, substs); + // FIXME(oli-obk): fishy skip-binder + match predicate.kind().skip_binder() { + ty::PredicateKind::Trait(tp) => { + for subst in tp.trait_ref.substs { + insert_outlives_predicate( + tcx, + subst, + outlived_region, + span, + required_predicates, + ) + } + } + ty::PredicateKind::RegionOutlives(_) + | ty::PredicateKind::TypeOutlives(_) + | ty::PredicateKind::Projection(_) + | ty::PredicateKind::WellFormed(_) + | ty::PredicateKind::ObjectSafe(_) + | ty::PredicateKind::ClosureKind(_, _, _) + | ty::PredicateKind::Subtype(_) + | ty::PredicateKind::Coerce(_) + | ty::PredicateKind::ConstEvaluatable(_) + | ty::PredicateKind::ConstEquate(_, _) + | ty::PredicateKind::TypeWellFormedFromEnv(_) => { + todo!("{:#?}", predicate) + } + } + } + } + Component::EscapingProjection(_) => { // As above, but the projection involves // late-bound regions. Therefore, the WF diff --git a/src/test/ui/generic-associated-types/bugs/issue-86218.stderr b/src/test/ui/generic-associated-types/bugs/issue-86218.stderr deleted file mode 100644 index de1b464a41dbe..0000000000000 --- a/src/test/ui/generic-associated-types/bugs/issue-86218.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0477]: the type `<() as Yay<&'a ()>>::InnerStream<'s>` does not fulfill the required lifetime - --> $DIR/issue-86218.rs:22:28 - | -LL | type InnerStream<'s> = impl Stream + 's; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: type must outlive the lifetime `'s` as defined here as required by this binding - --> $DIR/issue-86218.rs:22:22 - | -LL | type InnerStream<'s> = impl Stream + 's; - | ^^ - -error: unconstrained opaque type - --> $DIR/issue-86218.rs:22:28 - | -LL | type InnerStream<'s> = impl Stream + 's; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `InnerStream` must be used in combination with a concrete type within the same module - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0477`. diff --git a/src/test/ui/generic-associated-types/issue-86218-2.rs b/src/test/ui/generic-associated-types/issue-86218-2.rs new file mode 100644 index 0000000000000..897836af6b0b8 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-86218-2.rs @@ -0,0 +1,24 @@ +// check-pass + +#![feature(type_alias_impl_trait)] + +pub trait Stream { + type Item; +} + +impl Stream for () { + type Item = i32; +} + +trait Yay { + type InnerStream<'s>: Stream + 's; + fn foo<'s>() -> Self::InnerStream<'s>; +} + +impl Yay for () { + type InnerStream<'s> = impl Stream + 's; + //^ ERROR does not fulfill the required lifetime + fn foo<'s>() -> Self::InnerStream<'s> { () } +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/bugs/issue-86218.rs b/src/test/ui/generic-associated-types/issue-86218.rs similarity index 70% rename from src/test/ui/generic-associated-types/bugs/issue-86218.rs rename to src/test/ui/generic-associated-types/issue-86218.rs index 3a2d758e7d6fe..b2c3071f06b10 100644 --- a/src/test/ui/generic-associated-types/bugs/issue-86218.rs +++ b/src/test/ui/generic-associated-types/issue-86218.rs @@ -1,7 +1,4 @@ -// check-fail -// known-bug: #86218 - -// This should pass, but seems to run into a TAIT issue. +// check-pass #![feature(type_alias_impl_trait)] @@ -20,7 +17,8 @@ trait Yay { impl<'a> Yay<&'a ()> for () { type InnerStream<'s> = impl Stream + 's; - fn foo<'s>() -> Self::InnerStream<'s> { todo!() } + //^ ERROR does not fulfill the required lifetime + fn foo<'s>() -> Self::InnerStream<'s> { () } } fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/implied_bounds.rs b/src/test/ui/type-alias-impl-trait/implied_bounds.rs new file mode 100644 index 0000000000000..53cbf8d229006 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/implied_bounds.rs @@ -0,0 +1,51 @@ +#![feature(type_alias_impl_trait)] + +type WithLifetime<'a> = impl Equals; +fn _defining_use<'a>() -> WithLifetime<'a> {} + +trait Convert<'a> { + type Witness; + fn convert<'b, T: ?Sized>(_proof: &'b Self::Witness, x: &'a T) -> &'b T; +} + +impl<'a> Convert<'a> for () { + type Witness = WithLifetime<'a>; + + fn convert<'b, T: ?Sized>(_proof: &'b WithLifetime<'a>, x: &'a T) -> &'b T { + // compiler used to think it gets to assume 'a: 'b here because + // of the `&'b WithLifetime<'a>` argument + x + //~^ ERROR lifetime may not live long enough + } +} + +fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T { + WithLifetime::<'a>::convert_helper::<(), T>(&(), x) +} + +trait Equals { + type SelfType; + fn convert_helper<'a, 'b, W: Convert<'a, Witness = Self>, T: ?Sized>( + proof: &'b Self::SelfType, + x: &'a T, + ) -> &'b T; +} + +impl Equals for S { + type SelfType = Self; + fn convert_helper<'a, 'b, W: Convert<'a, Witness = Self>, T: ?Sized>( + proof: &'b Self, + x: &'a T, + ) -> &'b T { + W::convert(proof, x) + } +} + +fn main() { + let r; + { + let x = String::from("Hello World?"); + r = extend_lifetime(&x); + } + println!("{}", r); +} diff --git a/src/test/ui/type-alias-impl-trait/implied_bounds.stderr b/src/test/ui/type-alias-impl-trait/implied_bounds.stderr new file mode 100644 index 0000000000000..6f11b66634b29 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/implied_bounds.stderr @@ -0,0 +1,16 @@ +error: lifetime may not live long enough + --> $DIR/implied_bounds.rs:17:9 + | +LL | impl<'a> Convert<'a> for () { + | -- lifetime `'a` defined here +... +LL | fn convert<'b, T: ?Sized>(_proof: &'b WithLifetime<'a>, x: &'a T) -> &'b T { + | -- lifetime `'b` defined here +... +LL | x + | ^ associated function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/implied_bounds2.rs b/src/test/ui/type-alias-impl-trait/implied_bounds2.rs new file mode 100644 index 0000000000000..d2ad062e799b0 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/implied_bounds2.rs @@ -0,0 +1,9 @@ +#![feature(type_alias_impl_trait)] + +type Ty<'a, A> = impl Sized + 'a; +fn defining<'a, A>() -> Ty<'a, A> {} +fn assert_static() {} +fn test<'a, A>() where Ty<'a, A>: 'static, { assert_static::>() } +//~^ ERROR: may not live long enough + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/implied_bounds2.stderr b/src/test/ui/type-alias-impl-trait/implied_bounds2.stderr new file mode 100644 index 0000000000000..b1c42c2c37fe2 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/implied_bounds2.stderr @@ -0,0 +1,12 @@ +error[E0310]: the opaque type `Ty<'_, A>::{opaque#0}` may not live long enough + --> $DIR/implied_bounds2.rs:6:46 + | +LL | fn test<'a, A>() where Ty<'a, A>: 'static, { assert_static::>() } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `Ty<'_, A>::{opaque#0}: 'static`... + = note: ...so that the type `Ty<'_, A>` will meet its required lifetime bounds + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0310`. diff --git a/src/test/ui/type-alias-impl-trait/implied_bounds_closure.rs b/src/test/ui/type-alias-impl-trait/implied_bounds_closure.rs new file mode 100644 index 0000000000000..4cf35f951904a --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/implied_bounds_closure.rs @@ -0,0 +1,31 @@ +trait StaticDefaultRef: 'static { + fn default_ref() -> &'static Self; +} + +impl StaticDefaultRef for str { + fn default_ref() -> &'static str { + "" + } +} + +fn into_impl(x: &str) -> &(impl ?Sized + AsRef + StaticDefaultRef + '_) { + x +} + +fn extend_lifetime<'a>(x: &'a str) -> &'static str { + let t = into_impl(x); + helper(|_| t) //~ ERROR lifetime may not live long enough +} + +fn helper + StaticDefaultRef>(f: impl FnOnce(&T) -> &T) -> &'static str { + f(T::default_ref()).as_ref() +} + +fn main() { + let r; + { + let x = String::from("Hello World?"); + r = extend_lifetime(&x); + } + println!("{}", r); +} diff --git a/src/test/ui/type-alias-impl-trait/implied_bounds_closure.stderr b/src/test/ui/type-alias-impl-trait/implied_bounds_closure.stderr new file mode 100644 index 0000000000000..151564c3b45a1 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/implied_bounds_closure.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/implied_bounds_closure.rs:17:16 + | +LL | fn extend_lifetime<'a>(x: &'a str) -> &'static str { + | -- lifetime `'a` defined here +LL | let t = into_impl(x); +LL | helper(|_| t) + | ^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/implied_bounds_from_types.rs b/src/test/ui/type-alias-impl-trait/implied_bounds_from_types.rs new file mode 100644 index 0000000000000..8023cd24f0bf6 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/implied_bounds_from_types.rs @@ -0,0 +1,51 @@ +#![feature(type_alias_impl_trait)] + +type WithLifetime = impl Equals; +fn _defining_use() -> WithLifetime {} + +trait Convert<'a> { + type Witness; + fn convert<'b, T: ?Sized>(_proof: &'b Self::Witness, x: &'a T) -> &'b T; +} + +impl<'a> Convert<'a> for () { + type Witness = WithLifetime<&'a ()>; + + fn convert<'b, T: ?Sized>(_proof: &'b WithLifetime<&'a ()>, x: &'a T) -> &'b T { + // compiler used to think it gets to assume 'a: 'b here because + // of the `&'b WithLifetime<&'a ()>` argument + x + //~^ ERROR lifetime may not live long enough + } +} + +fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T { + WithLifetime::<&'a ()>::convert_helper::<(), T>(&(), x) +} + +trait Equals { + type SelfType; + fn convert_helper<'a, 'b, W: Convert<'a, Witness = Self>, T: ?Sized>( + proof: &'b Self::SelfType, + x: &'a T, + ) -> &'b T; +} + +impl Equals for S { + type SelfType = Self; + fn convert_helper<'a, 'b, W: Convert<'a, Witness = Self>, T: ?Sized>( + proof: &'b Self, + x: &'a T, + ) -> &'b T { + W::convert(proof, x) + } +} + +fn main() { + let r; + { + let x = String::from("Hello World?"); + r = extend_lifetime(&x); + } + println!("{}", r); +} diff --git a/src/test/ui/type-alias-impl-trait/implied_bounds_from_types.stderr b/src/test/ui/type-alias-impl-trait/implied_bounds_from_types.stderr new file mode 100644 index 0000000000000..cbc5e60731815 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/implied_bounds_from_types.stderr @@ -0,0 +1,16 @@ +error: lifetime may not live long enough + --> $DIR/implied_bounds_from_types.rs:17:9 + | +LL | impl<'a> Convert<'a> for () { + | -- lifetime `'a` defined here +... +LL | fn convert<'b, T: ?Sized>(_proof: &'b WithLifetime<&'a ()>, x: &'a T) -> &'b T { + | -- lifetime `'b` defined here +... +LL | x + | ^ associated function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check.rs b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check.rs new file mode 100644 index 0000000000000..a2063cf5e3ee5 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check.rs @@ -0,0 +1,18 @@ +trait Mirror<'a> { + type Item; +} + +impl<'a, T> Mirror<'a> for T { + type Item = T; +} + +trait AnotherTrait { + type Blah; +} + +impl<'a> AnotherTrait for >::Item { + //~^ ERROR: the lifetime parameter `'a` is not constrained + type Blah = &'a u32; +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check.stderr b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check.stderr new file mode 100644 index 0000000000000..cadf2ce4a9d5d --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check.stderr @@ -0,0 +1,9 @@ +error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates + --> $DIR/implied_lifetime_wf_check.rs:13:6 + | +LL | impl<'a> AnotherTrait for >::Item { + | ^^ unconstrained lifetime parameter + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check2.rs b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check2.rs new file mode 100644 index 0000000000000..4f52f7a34af8e --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check2.rs @@ -0,0 +1,13 @@ +#![feature(type_alias_impl_trait)] + +// check-pass + +trait Tr { type Assoc; } +impl<'a> Tr for &'a str { type Assoc = &'a str; } + +type OpaqueTy<'a> = impl Tr; +fn defining(s: &str) -> OpaqueTy<'_> { s } + +// now we must be able to conclude `'a: 'static` from `Opaque<'a>: 'static` + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check3.rs b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check3.rs new file mode 100644 index 0000000000000..4cbb6b63670ac --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check3.rs @@ -0,0 +1,42 @@ +#![feature(type_alias_impl_trait)] + +mod test_lifetime_param { + type Ty<'a> = impl Sized; + fn defining(a: &str) -> Ty<'_> { a } + fn assert_static<'a: 'static>() {} + //~^ WARN: unnecessary lifetime parameter `'a` + fn test<'a>() where Ty<'a>: 'static { assert_static::<'a>() } +} + +mod test_higher_kinded_lifetime_param { + type Ty<'a> = impl Sized; + fn defining(a: &str) -> Ty<'_> { a } + fn assert_static<'a: 'static>() {} + //~^ WARN: unnecessary lifetime parameter `'a` + fn test<'a>() where for<'b> Ty<'b>: 'a { assert_static::<'a>() } +} + +mod test_higher_kinded_lifetime_param2 { + fn assert_static<'a: 'static>() {} + //~^ WARN: unnecessary lifetime parameter `'a` + fn test<'a>() { assert_static::<'a>() } + // no error because all the other errors happen first and then we abort before + // emitting an error here. +} + +mod test_type_param { + type Ty = impl Sized; + fn defining(s: A) -> Ty { s } + fn assert_static() {} + fn test() where Ty: 'static { assert_static::() } +} + +mod test_type_param_static { + type Ty = impl Sized + 'static; + //~^ ERROR: the parameter type `A` may not live long enough + fn defining(s: A) -> Ty { s } + fn assert_static() {} + fn test() where Ty: 'static { assert_static::() } +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check3.stderr b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check3.stderr new file mode 100644 index 0000000000000..16d529698f4d6 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check3.stderr @@ -0,0 +1,38 @@ +warning: unnecessary lifetime parameter `'a` + --> $DIR/implied_lifetime_wf_check3.rs:6:22 + | +LL | fn assert_static<'a: 'static>() {} + | ^^ + | + = help: you can use the `'static` lifetime directly, in place of `'a` + +warning: unnecessary lifetime parameter `'a` + --> $DIR/implied_lifetime_wf_check3.rs:14:22 + | +LL | fn assert_static<'a: 'static>() {} + | ^^ + | + = help: you can use the `'static` lifetime directly, in place of `'a` + +warning: unnecessary lifetime parameter `'a` + --> $DIR/implied_lifetime_wf_check3.rs:20:22 + | +LL | fn assert_static<'a: 'static>() {} + | ^^ + | + = help: you can use the `'static` lifetime directly, in place of `'a` + +error[E0310]: the parameter type `A` may not live long enough + --> $DIR/implied_lifetime_wf_check3.rs:35:18 + | +LL | type Ty = impl Sized + 'static; + | ^^^^^^^^^^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | type Ty = impl Sized + 'static; + | +++++++++ + +error: aborting due to previous error; 3 warnings emitted + +For more information about this error, try `rustc --explain E0310`. diff --git a/src/test/ui/type-alias-impl-trait/issue-58662-generator-with-lifetime.rs b/src/test/ui/type-alias-impl-trait/issue-58662-generator-with-lifetime.rs index f20ddf020718e..477b61390ed46 100644 --- a/src/test/ui/type-alias-impl-trait/issue-58662-generator-with-lifetime.rs +++ b/src/test/ui/type-alias-impl-trait/issue-58662-generator-with-lifetime.rs @@ -16,7 +16,7 @@ fn rand_generator<'a>(rng: &'a ()) -> RandGenerator<'a> { } } -pub type RandGeneratorWithIndirection<'a> = impl Generator + 'a; +pub type RandGeneratorWithIndirection<'c> = impl Generator + 'c; pub fn rand_generator_with_indirection<'a>(rng: &'a ()) -> RandGeneratorWithIndirection<'a> { fn helper<'b>(rng: &'b ()) -> impl 'b + Generator { move || { diff --git a/src/test/ui/type-alias-impl-trait/issue-58662-simplified.rs b/src/test/ui/type-alias-impl-trait/issue-58662-simplified.rs new file mode 100644 index 0000000000000..27ca7d0fdc9fa --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-58662-simplified.rs @@ -0,0 +1,20 @@ +// check-pass + +#![feature(generators, generator_trait)] +#![feature(type_alias_impl_trait)] + +trait Trait {} + +impl Trait for T {} + +type Foo<'c> = impl Trait + 'c; +fn foo<'a>(rng: &'a ()) -> Foo<'a> { + fn helper<'b>(rng: &'b ()) -> impl 'b + Trait { + rng + } + + helper(rng) +} + +fn main() { +} From 90b6744af7a5c3944ef7d534b315467386e38673 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 19 May 2022 12:18:16 +0000 Subject: [PATCH 07/18] Also collect bounds from the ParamEnv for opaque types --- .../src/infer/outlives/obligations.rs | 9 ++- .../rustc_infer/src/infer/outlives/verify.rs | 69 +++++++------------ .../type-alias-impl-trait/implied_bounds2.rs | 3 +- .../implied_bounds2.stderr | 12 ---- 4 files changed, 34 insertions(+), 59 deletions(-) delete mode 100644 src/test/ui/type-alias-impl-trait/implied_bounds2.stderr diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index ad5fbb9a15823..ceea02e7817a9 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -318,7 +318,7 @@ where ); let generic = GenericKind::Param(param_ty); - let verify_bound = self.verify_bound.generic_bound(generic); + let verify_bound = self.verify_bound.param_bound(param_ty); self.delegate.push_verify(origin, generic, region, verify_bound); } @@ -476,7 +476,12 @@ where // projection outlive; in some cases, this may add insufficient // edges into the inference graph, leading to inference failures // even though a satisfactory solution exists. - let verify_bound = self.verify_bound.generic_bound(generic); + let verify_bound = self.verify_bound.projection_opaque_bounds( + generic, + def_id, + substs, + &mut Default::default(), + ); debug!("projection_must_outlive: pushing {:?}", verify_bound); self.delegate.push_verify(origin, generic, region, verify_bound); } diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 040843d837070..f5caa8d684e00 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -38,21 +38,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { Self { tcx, region_bound_pairs, implicit_region_bound, param_env } } - /// Returns a "verify bound" that encodes what we know about - /// `generic` and the regions it outlives. - pub fn generic_bound(&self, generic: GenericKind<'tcx>) -> VerifyBound<'tcx> { - let mut visited = SsoHashSet::new(); - match generic { - GenericKind::Param(param_ty) => self.param_bound(param_ty), - GenericKind::Projection(projection_ty) => { - self.projection_bound(projection_ty, &mut visited) - } - GenericKind::Opaque(def_id, substs) => self.opaque_bound(def_id, substs), - } - } - #[instrument(level = "debug", skip(self))] - fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { + pub fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { // Start with anything like `T: 'a` we can scrape from the // environment. If the environment contains something like // `for<'a> T: 'a`, then we know that `T` outlives everything. @@ -116,20 +103,21 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { } #[instrument(level = "debug", skip(self, visited))] - fn projection_bound( + pub fn projection_opaque_bounds( &self, - projection_ty: ty::ProjectionTy<'tcx>, + generic: GenericKind<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, visited: &mut SsoHashSet>, ) -> VerifyBound<'tcx> { - let projection_ty_as_ty = - self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); + let generic_ty = generic.to_ty(self.tcx); // Search the env for where clauses like `P: 'a`. - let env_bounds = self - .approx_declared_bounds_from_env(GenericKind::Projection(projection_ty)) + let projection_opaque_bounds = self + .approx_declared_bounds_from_env(generic) .into_iter() .map(|binder| { - if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() && ty == projection_ty_as_ty { + if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() && ty == generic_ty { // Micro-optimize if this is an exact match (this // occurs often when there are no region variables // involved). @@ -139,35 +127,18 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { VerifyBound::IfEq(verify_if_eq_b) } }); - // Extend with bounds that we can find from the trait. - let trait_bounds = self - .bounds(projection_ty.item_def_id, projection_ty.substs) - .map(|r| VerifyBound::OutlivedBy(r)); + let trait_bounds = self.bounds(def_id, substs).map(|r| VerifyBound::OutlivedBy(r)); // see the extensive comment in projection_must_outlive let recursive_bound = { let mut components = smallvec![]; - let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); - compute_components_recursive(self.tcx, ty.into(), &mut components, visited); + compute_components_recursive(self.tcx, generic_ty.into(), &mut components, visited); self.bound_from_components(&components, visited) }; - VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound) - } - - fn opaque_bound(&self, def_id: DefId, substs: SubstsRef<'tcx>) -> VerifyBound<'tcx> { - let bounds: Vec<_> = - self.bounds(def_id, substs).map(|r| VerifyBound::OutlivedBy(r)).collect(); - trace!("{:#?}", bounds); - if bounds.is_empty() { - // No bounds means the value must not have any lifetimes. - // FIXME: should we implicitly add 'static to `tcx.item_bounds` for opaque types, just - // like we add `Sized`? - VerifyBound::OutlivedBy(self.tcx.lifetimes.re_static) - } else { - VerifyBound::AnyBound(bounds) - } + VerifyBound::AnyBound(projection_opaque_bounds.chain(trait_bounds).collect()) + .or(recursive_bound) } fn bound_from_components( @@ -199,8 +170,18 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { match *component { Component::Region(lt) => VerifyBound::OutlivedBy(lt), Component::Param(param_ty) => self.param_bound(param_ty), - Component::Opaque(did, substs) => self.opaque_bound(did, substs), - Component::Projection(projection_ty) => self.projection_bound(projection_ty, visited), + Component::Opaque(did, substs) => self.projection_opaque_bounds( + GenericKind::Opaque(did, substs), + did, + substs, + visited, + ), + Component::Projection(projection_ty) => self.projection_opaque_bounds( + GenericKind::Projection(projection_ty), + projection_ty.item_def_id, + projection_ty.substs, + visited, + ), Component::EscapingProjection(ref components) => { self.bound_from_components(components, visited) } diff --git a/src/test/ui/type-alias-impl-trait/implied_bounds2.rs b/src/test/ui/type-alias-impl-trait/implied_bounds2.rs index d2ad062e799b0..b4c4c013cd251 100644 --- a/src/test/ui/type-alias-impl-trait/implied_bounds2.rs +++ b/src/test/ui/type-alias-impl-trait/implied_bounds2.rs @@ -1,9 +1,10 @@ +// check-pass + #![feature(type_alias_impl_trait)] type Ty<'a, A> = impl Sized + 'a; fn defining<'a, A>() -> Ty<'a, A> {} fn assert_static() {} fn test<'a, A>() where Ty<'a, A>: 'static, { assert_static::>() } -//~^ ERROR: may not live long enough fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/implied_bounds2.stderr b/src/test/ui/type-alias-impl-trait/implied_bounds2.stderr deleted file mode 100644 index b1c42c2c37fe2..0000000000000 --- a/src/test/ui/type-alias-impl-trait/implied_bounds2.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0310]: the opaque type `Ty<'_, A>::{opaque#0}` may not live long enough - --> $DIR/implied_bounds2.rs:6:46 - | -LL | fn test<'a, A>() where Ty<'a, A>: 'static, { assert_static::>() } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider adding an explicit lifetime bound `Ty<'_, A>::{opaque#0}: 'static`... - = note: ...so that the type `Ty<'_, A>` will meet its required lifetime bounds - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0310`. From a8c9784336591adce8d39a49a1da8fdf700b2410 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 24 May 2022 09:54:46 +0000 Subject: [PATCH 08/18] Add regression test --- .../type-alias-impl-trait/implied_bounds3.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/test/ui/type-alias-impl-trait/implied_bounds3.rs diff --git a/src/test/ui/type-alias-impl-trait/implied_bounds3.rs b/src/test/ui/type-alias-impl-trait/implied_bounds3.rs new file mode 100644 index 0000000000000..e39c613281dc7 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/implied_bounds3.rs @@ -0,0 +1,18 @@ +// check-pass + +fn foo(_: F) +where + F: 'static, +{ +} + +fn from(f: F) -> impl Send { + f +} + +fn bar() { + foo(from(|| ())) +} + +fn main() { +} From 615c9e8647b0ce46c1b1755201c8e2799a3ca2bc Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 21 Jun 2022 15:18:11 +0000 Subject: [PATCH 09/18] Inline substitution logic into `declared_region_bounds` --- .../src/infer/outlives/obligations.rs | 5 +-- .../rustc_infer/src/infer/outlives/verify.rs | 35 +++++-------------- 2 files changed, 11 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index ceea02e7817a9..ec9a165ea308d 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -390,7 +390,8 @@ where // Compute the bounds we can derive from the trait definition. // These are guaranteed to apply, no matter the inference // results. - let trait_bounds: Vec<_> = self.verify_bound.bounds(def_id, substs).collect(); + let trait_bounds: Vec<_> = + self.verify_bound.declared_region_bounds(def_id, substs).collect(); debug!(?trait_bounds); @@ -413,7 +414,7 @@ where // will be invoked with `['b => ^1]` and so we will get `^1` returned. let bound = bound_outlives.skip_binder(); let (def_id, substs) = filter(bound.0); - self.verify_bound.bounds(def_id, substs).all(|r| r != bound.1) + self.verify_bound.declared_region_bounds(def_id, substs).all(|r| r != bound.1) }); // If declared bounds list is empty, the only applicable rule is diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index f5caa8d684e00..f470b2eb8c193 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -2,7 +2,6 @@ use crate::infer::outlives::components::{compute_components_recursive, Component use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::region_constraints::VerifyIfEq; use crate::infer::{GenericKind, VerifyBound}; -use rustc_data_structures::captures::Captures; use rustc_data_structures::sso::SsoHashSet; use rustc_hir::def_id::DefId; use rustc_middle::ty::GenericArg; @@ -128,7 +127,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { } }); // Extend with bounds that we can find from the trait. - let trait_bounds = self.bounds(def_id, substs).map(|r| VerifyBound::OutlivedBy(r)); + let trait_bounds = + self.declared_region_bounds(def_id, substs).map(|r| VerifyBound::OutlivedBy(r)); // see the extensive comment in projection_must_outlive let recursive_bound = { @@ -279,30 +279,6 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// } /// ``` /// - /// then this function would return `'x`. This is subject to the - /// limitations around higher-ranked bounds described in - /// `declared_region_bounds`. - #[instrument(level = "debug", skip(self))] - pub fn bounds( - &self, - def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> impl Iterator> + 'cx + Captures<'tcx> { - let tcx = self.tcx; - self.declared_region_bounds(def_id).map(move |r| EarlyBinder(r).subst(tcx, substs)) - } - - /// Given the `DefId` of an associated item, returns any region - /// bounds attached to that associated item from the trait definition. - /// - /// For example: - /// - /// ```rust - /// trait Foo<'a> { - /// type Bar: 'a; - /// } - /// ``` - /// /// If we were given the `DefId` of `Foo::Bar`, we would return /// `'a`. You could then apply the substitutions from the /// projection to convert this into your namespace. This also @@ -322,7 +298,11 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// /// This is for simplicity, and because we are not really smart /// enough to cope with such bounds anywhere. - fn declared_region_bounds(&self, def_id: DefId) -> impl Iterator> { + pub fn declared_region_bounds( + &self, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> impl Iterator> { let tcx = self.tcx; let bounds = tcx.item_bounds(def_id); trace!("{:#?}", bounds); @@ -331,6 +311,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { .filter_map(|p| p.to_opt_type_outlives()) .filter_map(|p| p.no_bound_vars()) .map(|b| b.1) + .map(move |r| EarlyBinder(r).subst(tcx, substs)) } /// Searches through a predicate list for a predicate `T: 'a`. From 20d962cfa5511eece711684ff83cb721eacd60dc Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 21 Jun 2022 15:42:25 +0000 Subject: [PATCH 10/18] Simplify insert_outlives_predicate opaque type logic --- compiler/rustc_typeck/src/outlives/utils.rs | 46 ++++++------------- .../unbounded_opaque_type.rs | 8 ++++ 2 files changed, 23 insertions(+), 31 deletions(-) create mode 100644 src/test/ui/type-alias-impl-trait/unbounded_opaque_type.rs diff --git a/compiler/rustc_typeck/src/outlives/utils.rs b/compiler/rustc_typeck/src/outlives/utils.rs index 4c32a1e81ba54..f582fdf903ddd 100644 --- a/compiler/rustc_typeck/src/outlives/utils.rs +++ b/compiler/rustc_typeck/src/outlives/utils.rs @@ -1,6 +1,6 @@ use rustc_infer::infer::outlives::components::{push_outlives_components, Component}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, EarlyBinder, Region, Ty, TyCtxt}; +use rustc_middle::ty::{self, Region, Ty, TyCtxt}; use rustc_span::Span; use smallvec::smallvec; use std::collections::BTreeMap; @@ -97,36 +97,20 @@ pub(crate) fn insert_outlives_predicate<'tcx>( } Component::Opaque(def_id, substs) => { - for predicate in tcx.item_bounds(def_id) { - let predicate = EarlyBinder(predicate).subst(tcx, substs); - // FIXME(oli-obk): fishy skip-binder - match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(tp) => { - for subst in tp.trait_ref.substs { - insert_outlives_predicate( - tcx, - subst, - outlived_region, - span, - required_predicates, - ) - } - } - ty::PredicateKind::RegionOutlives(_) - | ty::PredicateKind::TypeOutlives(_) - | ty::PredicateKind::Projection(_) - | ty::PredicateKind::WellFormed(_) - | ty::PredicateKind::ObjectSafe(_) - | ty::PredicateKind::ClosureKind(_, _, _) - | ty::PredicateKind::Subtype(_) - | ty::PredicateKind::Coerce(_) - | ty::PredicateKind::ConstEvaluatable(_) - | ty::PredicateKind::ConstEquate(_, _) - | ty::PredicateKind::TypeWellFormedFromEnv(_) => { - todo!("{:#?}", predicate) - } - } - } + // This would arise from something like: + // + // ```rust + // type Opaque = impl Sized; + // fn defining() -> Opaque {} + // struct Ss<'a, T>(&'a Opaque); + // ``` + // + // Here we want to require an explicit `where Opaque: 'a` + + let ty = tcx.mk_opaque(def_id, substs); + required_predicates + .entry(ty::OutlivesPredicate(ty.into(), outlived_region)) + .or_insert(span); } Component::EscapingProjection(_) => { diff --git a/src/test/ui/type-alias-impl-trait/unbounded_opaque_type.rs b/src/test/ui/type-alias-impl-trait/unbounded_opaque_type.rs new file mode 100644 index 0000000000000..a5ab3e1acae52 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/unbounded_opaque_type.rs @@ -0,0 +1,8 @@ +// check-pass + +#![feature(type_alias_impl_trait)] +type Opaque = impl Sized; +fn defining() -> Opaque {} +struct Ss<'a, T>(&'a Opaque); + +fn main() {} From 36cb01deb3d874650881b0bc22448020751ff19c Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 15 Jul 2022 15:26:46 +0000 Subject: [PATCH 11/18] Try out a perf optimization --- compiler/rustc_infer/src/infer/outlives/obligations.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index ec9a165ea308d..5af76b5d610db 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -373,6 +373,11 @@ where substs: SubstsRef<'tcx>, filter: impl Fn(Ty<'tcx>) -> (DefId, SubstsRef<'tcx>), ) { + // An optimization for a common case with opaque types. + if substs.is_empty() { + return; + } + // This case is thorny for inference. The fundamental problem is // that there are many cases where we have choice, and inference // doesn't like choice (the current region inference in From 4b323e62ba785812fbb5d6b77f2564a59dce2171 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 1 Sep 2022 15:41:13 +0000 Subject: [PATCH 12/18] Reproduce sad diagnostic --- .../ui/impl-trait/unactionable_diagnostic.rs | 23 +++++++++++++++++++ .../impl-trait/unactionable_diagnostic.stderr | 12 ++++++++++ 2 files changed, 35 insertions(+) create mode 100644 src/test/ui/impl-trait/unactionable_diagnostic.rs create mode 100644 src/test/ui/impl-trait/unactionable_diagnostic.stderr diff --git a/src/test/ui/impl-trait/unactionable_diagnostic.rs b/src/test/ui/impl-trait/unactionable_diagnostic.rs new file mode 100644 index 0000000000000..de482e0370ca6 --- /dev/null +++ b/src/test/ui/impl-trait/unactionable_diagnostic.rs @@ -0,0 +1,23 @@ +trait Trait {} + +struct Foo; + +impl Trait for Foo {} + +fn foo<'t, P>( + post: P, + x: &'t Foo, +) -> &'t impl Trait { + x +} + +fn bar<'t, T>( + post: T, + x: &'t Foo, +) -> &'t impl Trait { + foo(post, x) + //~^ ERROR: the opaque type `foo::{opaque#0}` may not live long enough + //~| HELP: consider adding an explicit lifetime bound `foo::{opaque#0}: 't` +} + +fn main() {} diff --git a/src/test/ui/impl-trait/unactionable_diagnostic.stderr b/src/test/ui/impl-trait/unactionable_diagnostic.stderr new file mode 100644 index 0000000000000..fe6fef5815d54 --- /dev/null +++ b/src/test/ui/impl-trait/unactionable_diagnostic.stderr @@ -0,0 +1,12 @@ +error[E0309]: the opaque type `foo::{opaque#0}` may not live long enough + --> $DIR/unactionable_diagnostic.rs:18:5 + | +LL | foo(post, x) + | ^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `foo::{opaque#0}: 't`... + = note: ...so that the type `impl Trait` will meet its required lifetime bounds + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. From 86f1ca812b8704beb20459bd5723d0cc3a0ace62 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 6 Sep 2022 10:16:26 +0000 Subject: [PATCH 13/18] Improve diagnostic for adding more bounds to opaque types --- compiler/rustc_infer/src/infer/error_reporting/mod.rs | 10 +++++++++- src/test/ui/impl-trait/unactionable_diagnostic.rs | 2 +- src/test/ui/impl-trait/unactionable_diagnostic.stderr | 8 +++++--- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index e33035381e0a9..8b8cff0dbbe64 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2314,7 +2314,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { &self, generic_param_scope: LocalDefId, span: Span, - origin: Option>, + mut origin: Option>, bound_kind: GenericKind<'tcx>, sub: Region<'tcx>, ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { @@ -2349,6 +2349,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { None } } + GenericKind::Opaque(def_id, _substs) => { + // Avoid emitting a `... so that the type` message at the error site. + // It would be out of order for return position impl trait + origin = None; + // Make sure the lifetime suggestion is on the RPIT instead of proposing + // to add a bound for opaque types (which isn't possible) + Some((self.tcx.def_span(def_id).shrink_to_hi(), true)) + } _ => None, }; diff --git a/src/test/ui/impl-trait/unactionable_diagnostic.rs b/src/test/ui/impl-trait/unactionable_diagnostic.rs index de482e0370ca6..9b82332f9b067 100644 --- a/src/test/ui/impl-trait/unactionable_diagnostic.rs +++ b/src/test/ui/impl-trait/unactionable_diagnostic.rs @@ -8,6 +8,7 @@ fn foo<'t, P>( post: P, x: &'t Foo, ) -> &'t impl Trait { + //~^ HELP: consider adding an explicit lifetime bound... x } @@ -17,7 +18,6 @@ fn bar<'t, T>( ) -> &'t impl Trait { foo(post, x) //~^ ERROR: the opaque type `foo::{opaque#0}` may not live long enough - //~| HELP: consider adding an explicit lifetime bound `foo::{opaque#0}: 't` } fn main() {} diff --git a/src/test/ui/impl-trait/unactionable_diagnostic.stderr b/src/test/ui/impl-trait/unactionable_diagnostic.stderr index fe6fef5815d54..a36e89f58b98e 100644 --- a/src/test/ui/impl-trait/unactionable_diagnostic.stderr +++ b/src/test/ui/impl-trait/unactionable_diagnostic.stderr @@ -1,11 +1,13 @@ error[E0309]: the opaque type `foo::{opaque#0}` may not live long enough - --> $DIR/unactionable_diagnostic.rs:18:5 + --> $DIR/unactionable_diagnostic.rs:19:5 | LL | foo(post, x) | ^^^^^^^^^^^^ | - = help: consider adding an explicit lifetime bound `foo::{opaque#0}: 't`... - = note: ...so that the type `impl Trait` will meet its required lifetime bounds +help: consider adding an explicit lifetime bound... + | +LL | ) -> &'t impl Trait + 't { + | ++++ error: aborting due to previous error From 5ace12c409ef59c654e3f683dd5534a258945a77 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 23 Sep 2022 07:10:11 +0000 Subject: [PATCH 14/18] Showcase a broken diagnostic --- src/test/ui/impl-trait/unactionable_diagnostic.rs | 6 +++--- src/test/ui/impl-trait/unactionable_diagnostic.stderr | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/ui/impl-trait/unactionable_diagnostic.rs b/src/test/ui/impl-trait/unactionable_diagnostic.rs index 9b82332f9b067..016d7c3b7ef21 100644 --- a/src/test/ui/impl-trait/unactionable_diagnostic.rs +++ b/src/test/ui/impl-trait/unactionable_diagnostic.rs @@ -4,10 +4,10 @@ struct Foo; impl Trait for Foo {} -fn foo<'t, P>( +fn foo<'x, P>( post: P, - x: &'t Foo, -) -> &'t impl Trait { + x: &'x Foo, +) -> &'x impl Trait { //~^ HELP: consider adding an explicit lifetime bound... x } diff --git a/src/test/ui/impl-trait/unactionable_diagnostic.stderr b/src/test/ui/impl-trait/unactionable_diagnostic.stderr index a36e89f58b98e..c9b3f61254484 100644 --- a/src/test/ui/impl-trait/unactionable_diagnostic.stderr +++ b/src/test/ui/impl-trait/unactionable_diagnostic.stderr @@ -6,7 +6,7 @@ LL | foo(post, x) | help: consider adding an explicit lifetime bound... | -LL | ) -> &'t impl Trait + 't { +LL | ) -> &'x impl Trait + 't { | ++++ error: aborting due to previous error From 90ec6f847fe54e751a045044dabb7e0d252981e3 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 23 Sep 2022 07:12:59 +0000 Subject: [PATCH 15/18] Show errors instead of hiding them due to an earlier error --- .../implied_lifetime_wf_check3.rs | 17 +++++----- .../implied_lifetime_wf_check3.stderr | 34 +++++++++++++++---- .../implied_lifetime_wf_check4_static.rs | 11 ++++++ .../implied_lifetime_wf_check4_static.stderr | 14 ++++++++ 4 files changed, 61 insertions(+), 15 deletions(-) create mode 100644 src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.rs create mode 100644 src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.stderr diff --git a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check3.rs b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check3.rs index 4cbb6b63670ac..6e5b8f491eab5 100644 --- a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check3.rs +++ b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check3.rs @@ -6,6 +6,7 @@ mod test_lifetime_param { fn assert_static<'a: 'static>() {} //~^ WARN: unnecessary lifetime parameter `'a` fn test<'a>() where Ty<'a>: 'static { assert_static::<'a>() } + //~^ ERROR: lifetime may not live long enough } mod test_higher_kinded_lifetime_param { @@ -14,14 +15,14 @@ mod test_higher_kinded_lifetime_param { fn assert_static<'a: 'static>() {} //~^ WARN: unnecessary lifetime parameter `'a` fn test<'a>() where for<'b> Ty<'b>: 'a { assert_static::<'a>() } + //~^ ERROR: lifetime may not live long enough } mod test_higher_kinded_lifetime_param2 { fn assert_static<'a: 'static>() {} //~^ WARN: unnecessary lifetime parameter `'a` fn test<'a>() { assert_static::<'a>() } - // no error because all the other errors happen first and then we abort before - // emitting an error here. + //~^ ERROR: lifetime may not live long enough } mod test_type_param { @@ -29,14 +30,14 @@ mod test_type_param { fn defining(s: A) -> Ty { s } fn assert_static() {} fn test() where Ty: 'static { assert_static::() } + //~^ ERROR: parameter type `A` may not live long enough } -mod test_type_param_static { - type Ty = impl Sized + 'static; - //~^ ERROR: the parameter type `A` may not live long enough - fn defining(s: A) -> Ty { s } - fn assert_static() {} - fn test() where Ty: 'static { assert_static::() } +mod test_implied_from_fn_sig { + type Opaque = impl Sized; + fn defining() -> Opaque {} + fn assert_static() {} + fn test(_: Opaque) { assert_static::(); } } fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check3.stderr b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check3.stderr index 16d529698f4d6..887620a4d50ec 100644 --- a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check3.stderr +++ b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check3.stderr @@ -7,7 +7,7 @@ LL | fn assert_static<'a: 'static>() {} = help: you can use the `'static` lifetime directly, in place of `'a` warning: unnecessary lifetime parameter `'a` - --> $DIR/implied_lifetime_wf_check3.rs:14:22 + --> $DIR/implied_lifetime_wf_check3.rs:15:22 | LL | fn assert_static<'a: 'static>() {} | ^^ @@ -15,24 +15,44 @@ LL | fn assert_static<'a: 'static>() {} = help: you can use the `'static` lifetime directly, in place of `'a` warning: unnecessary lifetime parameter `'a` - --> $DIR/implied_lifetime_wf_check3.rs:20:22 + --> $DIR/implied_lifetime_wf_check3.rs:22:22 | LL | fn assert_static<'a: 'static>() {} | ^^ | = help: you can use the `'static` lifetime directly, in place of `'a` +error: lifetime may not live long enough + --> $DIR/implied_lifetime_wf_check3.rs:8:43 + | +LL | fn test<'a>() where Ty<'a>: 'static { assert_static::<'a>() } + | -- lifetime `'a` defined here ^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/implied_lifetime_wf_check3.rs:17:46 + | +LL | fn test<'a>() where for<'b> Ty<'b>: 'a { assert_static::<'a>() } + | -- lifetime `'a` defined here ^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/implied_lifetime_wf_check3.rs:24:21 + | +LL | fn test<'a>() { assert_static::<'a>() } + | -- ^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` + | | + | lifetime `'a` defined here + error[E0310]: the parameter type `A` may not live long enough - --> $DIR/implied_lifetime_wf_check3.rs:35:18 + --> $DIR/implied_lifetime_wf_check3.rs:32:41 | -LL | type Ty = impl Sized + 'static; - | ^^^^^^^^^^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds +LL | fn test() where Ty: 'static { assert_static::() } + | ^^^^^^^^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds | help: consider adding an explicit lifetime bound... | -LL | type Ty = impl Sized + 'static; +LL | fn test() where Ty: 'static { assert_static::() } | +++++++++ -error: aborting due to previous error; 3 warnings emitted +error: aborting due to 4 previous errors; 3 warnings emitted For more information about this error, try `rustc --explain E0310`. diff --git a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.rs b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.rs new file mode 100644 index 0000000000000..ac32dbde04b1c --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.rs @@ -0,0 +1,11 @@ +#![feature(type_alias_impl_trait)] + +mod test_type_param_static { + type Ty = impl Sized + 'static; + //~^ ERROR: the parameter type `A` may not live long enough + fn defining(s: A) -> Ty { s } + fn assert_static() {} + fn test() where Ty: 'static { assert_static::() } +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.stderr b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.stderr new file mode 100644 index 0000000000000..47bc31e78c34f --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.stderr @@ -0,0 +1,14 @@ +error[E0310]: the parameter type `A` may not live long enough + --> $DIR/implied_lifetime_wf_check4_static.rs:4:18 + | +LL | type Ty = impl Sized + 'static; + | ^^^^^^^^^^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | type Ty = impl Sized + 'static; + | +++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0310`. From e9d219e97c9b87e928d131ac071fef96e906c464 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 23 Sep 2022 07:15:55 +0000 Subject: [PATCH 16/18] Make the test actually show the problematic case --- .../implied_lifetime_wf_check.rs | 29 ++++++++++++------- .../implied_lifetime_wf_check.stderr | 9 ------ 2 files changed, 19 insertions(+), 19 deletions(-) delete mode 100644 src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check.stderr diff --git a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check.rs b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check.rs index a2063cf5e3ee5..b6a7264a529fd 100644 --- a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check.rs +++ b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check.rs @@ -1,18 +1,27 @@ -trait Mirror<'a> { - type Item; -} +#![feature(type_alias_impl_trait)] + +// known-bug: #99840 +// this should not compile +// check-pass + +type Alias = impl Sized; -impl<'a, T> Mirror<'a> for T { - type Item = T; +fn constrain() -> Alias { + 1i32 } -trait AnotherTrait { - type Blah; +trait HideIt { + type Assoc; } -impl<'a> AnotherTrait for >::Item { - //~^ ERROR: the lifetime parameter `'a` is not constrained - type Blah = &'a u32; +impl HideIt for () { + type Assoc = Alias; } +pub trait Yay {} + +impl Yay for <() as HideIt>::Assoc {} +// impl Yay for i32 {} // this already errors +// impl Yay for u32 {} // this also already errors + fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check.stderr b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check.stderr deleted file mode 100644 index cadf2ce4a9d5d..0000000000000 --- a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates - --> $DIR/implied_lifetime_wf_check.rs:13:6 - | -LL | impl<'a> AnotherTrait for >::Item { - | ^^ unconstrained lifetime parameter - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0207`. From e237aaef25db05a7f18c1a32b248fdaa06ff8669 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 23 Sep 2022 07:18:33 +0000 Subject: [PATCH 17/18] Some test cleanups --- compiler/rustc_typeck/src/outlives/utils.rs | 2 +- .../ui/generic-associated-types/issue-86218-2.rs | 1 - .../implied_lifetime_wf_check2.rs | 13 ------------- .../type-alias-impl-trait/unbounded_opaque_type.rs | 6 ++++++ 4 files changed, 7 insertions(+), 15 deletions(-) delete mode 100644 src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check2.rs diff --git a/compiler/rustc_typeck/src/outlives/utils.rs b/compiler/rustc_typeck/src/outlives/utils.rs index f582fdf903ddd..0409c7081dc4f 100644 --- a/compiler/rustc_typeck/src/outlives/utils.rs +++ b/compiler/rustc_typeck/src/outlives/utils.rs @@ -105,7 +105,7 @@ pub(crate) fn insert_outlives_predicate<'tcx>( // struct Ss<'a, T>(&'a Opaque); // ``` // - // Here we want to require an explicit `where Opaque: 'a` + // Here we want to have an implied bound `Opaque: 'a` let ty = tcx.mk_opaque(def_id, substs); required_predicates diff --git a/src/test/ui/generic-associated-types/issue-86218-2.rs b/src/test/ui/generic-associated-types/issue-86218-2.rs index 897836af6b0b8..63c839ea8712d 100644 --- a/src/test/ui/generic-associated-types/issue-86218-2.rs +++ b/src/test/ui/generic-associated-types/issue-86218-2.rs @@ -17,7 +17,6 @@ trait Yay { impl Yay for () { type InnerStream<'s> = impl Stream + 's; - //^ ERROR does not fulfill the required lifetime fn foo<'s>() -> Self::InnerStream<'s> { () } } diff --git a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check2.rs b/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check2.rs deleted file mode 100644 index 4f52f7a34af8e..0000000000000 --- a/src/test/ui/type-alias-impl-trait/implied_lifetime_wf_check2.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![feature(type_alias_impl_trait)] - -// check-pass - -trait Tr { type Assoc; } -impl<'a> Tr for &'a str { type Assoc = &'a str; } - -type OpaqueTy<'a> = impl Tr; -fn defining(s: &str) -> OpaqueTy<'_> { s } - -// now we must be able to conclude `'a: 'static` from `Opaque<'a>: 'static` - -fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/unbounded_opaque_type.rs b/src/test/ui/type-alias-impl-trait/unbounded_opaque_type.rs index a5ab3e1acae52..f43ad7dce1d40 100644 --- a/src/test/ui/type-alias-impl-trait/unbounded_opaque_type.rs +++ b/src/test/ui/type-alias-impl-trait/unbounded_opaque_type.rs @@ -5,4 +5,10 @@ type Opaque = impl Sized; fn defining() -> Opaque {} struct Ss<'a, T>(&'a Opaque); + +fn test<'a, T>(_: Ss<'a, T>) { + // test that we have an implied bound `Opaque: 'a` from fn signature + None::<&'a Opaque>; +} + fn main() {} From 59e285ff34796585a61a711e11a056c2999368ea Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 23 Sep 2022 08:00:02 +0000 Subject: [PATCH 18/18] Report diagnostics at the actually actionable site --- .../src/infer/error_reporting/mod.rs | 10 +------- .../src/infer/outlives/obligations.rs | 5 +++- .../impl-trait/unactionable_diagnostic.fixed | 25 +++++++++++++++++++ .../ui/impl-trait/unactionable_diagnostic.rs | 14 ++++++----- .../impl-trait/unactionable_diagnostic.stderr | 10 ++++---- 5 files changed, 43 insertions(+), 21 deletions(-) create mode 100644 src/test/ui/impl-trait/unactionable_diagnostic.fixed diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 8b8cff0dbbe64..e33035381e0a9 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2314,7 +2314,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { &self, generic_param_scope: LocalDefId, span: Span, - mut origin: Option>, + origin: Option>, bound_kind: GenericKind<'tcx>, sub: Region<'tcx>, ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { @@ -2349,14 +2349,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { None } } - GenericKind::Opaque(def_id, _substs) => { - // Avoid emitting a `... so that the type` message at the error site. - // It would be out of order for return position impl trait - origin = None; - // Make sure the lifetime suggestion is on the RPIT instead of proposing - // to add a bound for opaque types (which isn't possible) - Some((self.tcx.def_span(def_id).shrink_to_hi(), true)) - } _ => None, }; diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 5af76b5d610db..229b69b92e68e 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -336,6 +336,7 @@ where GenericKind::Opaque(def_id, substs), def_id, substs, + true, |ty| match *ty.kind() { ty::Opaque(def_id, substs) => (def_id, substs), _ => bug!("expected only projection types from env, not {:?}", ty), @@ -356,6 +357,7 @@ where GenericKind::Projection(projection_ty), projection_ty.item_def_id, projection_ty.substs, + false, |ty| match ty.kind() { ty::Projection(projection_ty) => (projection_ty.item_def_id, projection_ty.substs), _ => bug!("expected only projection types from env, not {:?}", ty), @@ -371,6 +373,7 @@ where generic: GenericKind<'tcx>, def_id: DefId, substs: SubstsRef<'tcx>, + is_opaque: bool, filter: impl Fn(Ty<'tcx>) -> (DefId, SubstsRef<'tcx>), ) { // An optimization for a common case with opaque types. @@ -437,7 +440,7 @@ where // inference variables, we use a verify constraint instead of adding // edges, which winds up enforcing the same condition. let needs_infer = substs.needs_infer(); - if approx_env_bounds.is_empty() && trait_bounds.is_empty() && needs_infer { + if approx_env_bounds.is_empty() && trait_bounds.is_empty() && (needs_infer || is_opaque) { debug!("no declared bounds"); self.substs_must_outlive(substs, origin, region); diff --git a/src/test/ui/impl-trait/unactionable_diagnostic.fixed b/src/test/ui/impl-trait/unactionable_diagnostic.fixed new file mode 100644 index 0000000000000..6c2505177fef8 --- /dev/null +++ b/src/test/ui/impl-trait/unactionable_diagnostic.fixed @@ -0,0 +1,25 @@ +// run-rustfix + +pub trait Trait {} + +pub struct Foo; + +impl Trait for Foo {} + +fn foo<'x, P>( + _post: P, + x: &'x Foo, +) -> &'x impl Trait { + x +} + +pub fn bar<'t, T: 't>( + //~^ HELP: consider adding an explicit lifetime bound... + post: T, + x: &'t Foo, +) -> &'t impl Trait { + foo(post, x) + //~^ ERROR: the parameter type `T` may not live long enough +} + +fn main() {} diff --git a/src/test/ui/impl-trait/unactionable_diagnostic.rs b/src/test/ui/impl-trait/unactionable_diagnostic.rs index 016d7c3b7ef21..bce35cbdd0d38 100644 --- a/src/test/ui/impl-trait/unactionable_diagnostic.rs +++ b/src/test/ui/impl-trait/unactionable_diagnostic.rs @@ -1,23 +1,25 @@ -trait Trait {} +// run-rustfix -struct Foo; +pub trait Trait {} + +pub struct Foo; impl Trait for Foo {} fn foo<'x, P>( - post: P, + _post: P, x: &'x Foo, ) -> &'x impl Trait { - //~^ HELP: consider adding an explicit lifetime bound... x } -fn bar<'t, T>( +pub fn bar<'t, T>( + //~^ HELP: consider adding an explicit lifetime bound... post: T, x: &'t Foo, ) -> &'t impl Trait { foo(post, x) - //~^ ERROR: the opaque type `foo::{opaque#0}` may not live long enough + //~^ ERROR: the parameter type `T` may not live long enough } fn main() {} diff --git a/src/test/ui/impl-trait/unactionable_diagnostic.stderr b/src/test/ui/impl-trait/unactionable_diagnostic.stderr index c9b3f61254484..a32004cda1a6f 100644 --- a/src/test/ui/impl-trait/unactionable_diagnostic.stderr +++ b/src/test/ui/impl-trait/unactionable_diagnostic.stderr @@ -1,13 +1,13 @@ -error[E0309]: the opaque type `foo::{opaque#0}` may not live long enough - --> $DIR/unactionable_diagnostic.rs:19:5 +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/unactionable_diagnostic.rs:21:5 | LL | foo(post, x) - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds | help: consider adding an explicit lifetime bound... | -LL | ) -> &'x impl Trait + 't { - | ++++ +LL | pub fn bar<'t, T: 't>( + | ++++ error: aborting due to previous error