diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 9aded3aeb9318..82ae74e7b4f5f 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -517,9 +517,14 @@ pub(crate) fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) b, ) } - ty::ClauseKind::TypeOutlives(ty::OutlivesClause(a, b)) => { - !ty_known_to_outlive(tcx, gat_def_id, param_env, &FxIndexSet::default(), a, b) - } + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(a, b)) => !ty_known_to_outlive( + tcx, + gat_def_id, + param_env, + &FxIndexSet::default(), + Unnormalized::new_wip(a), + b, + ), _ => bug!("Unexpected ClauseKind"), }) .map(|clause| clause.to_string()) @@ -623,7 +628,14 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable>>( // reflected in a where clause on the GAT itself. for (ty, ty_idx) in &types { // In our example, requires that `Self: 'a` - if ty_known_to_outlive(tcx, item_def_id, param_env, wf_tys, *ty, *region_a) { + if ty_known_to_outlive( + tcx, + item_def_id, + param_env, + wf_tys, + Unnormalized::new_wip(*ty), + *region_a, + ) { debug!(?ty_idx, ?region_a_idx); debug!("required clause: {ty} must outlive {region_a}"); // Translate into the generic parameters of the GAT. In diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index 83a1af895032b..f54d774e32670 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -5,9 +5,11 @@ use rustc_infer::infer::{ InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt, TypeOutlivesConstraint, }; use rustc_macros::extension; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode, elaborate}; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized, elaborate}; use rustc_span::DUMMY_SP; +use crate::traits::ScrubbedTraitError; use crate::traits::outlives_bounds::InferCtxtExt; #[extension(pub trait OutlivesEnvironmentBuildExt<'tcx>)] @@ -90,15 +92,30 @@ pub fn ty_known_to_outlive<'tcx>( id: LocalDefId, param_env: ty::ParamEnv<'tcx>, wf_tys: &FxIndexSet>, - ty: Ty<'tcx>, + ty: Unnormalized<'tcx, Ty<'tcx>>, region: ty::Region<'tcx>, ) -> bool { test_region_obligations(tcx, id, param_env, wf_tys, |infcx| { + // Types in region obligations should be normalized. + let ty = if infcx.next_trait_solver() { + let Ok(ty) = crate::solve::deeply_normalize::<_, ScrubbedTraitError<'tcx>>( + infcx.at(&ObligationCause::dummy_with_span(DUMMY_SP), param_env), + ty, + ) else { + return Err(()); + }; + ty + } else { + ty.skip_norm_wip() + }; + infcx.register_type_outlives_constraint_inner(TypeOutlivesConstraint { sub_region: region, sup_type: ty, origin: SubregionOrigin::RelateParamBound(DUMMY_SP, ty, None), }); + + Ok(()) }) } @@ -119,6 +136,7 @@ pub fn region_known_to_outlive<'tcx>( region_a, ty::VisibleForLeakCheck::Unreachable, ); + Ok(()) }) } @@ -130,14 +148,16 @@ pub fn test_region_obligations<'tcx>( id: LocalDefId, param_env: ty::ParamEnv<'tcx>, wf_tys: &FxIndexSet>, - add_constraints: impl FnOnce(&InferCtxt<'tcx>), + add_constraints: impl FnOnce(&InferCtxt<'tcx>) -> Result<(), ()>, ) -> bool { // Unfortunately, we have to use a new `InferCtxt` each call, because // region constraints get added and solved there and we need to test each // call individually. let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - add_constraints(&infcx); + if add_constraints(&infcx).is_err() { + return false; + } let errors = infcx.resolve_regions(id, param_env, wf_tys.iter().copied()); tracing::debug!(?errors, "errors"); diff --git a/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs b/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs index b3d45f3580239..0bc8b6967bee3 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs @@ -1,16 +1,44 @@ +use std::debug_assert_matches; + use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::bit_set::DenseBitSet; +use rustc_infer::infer::{SubregionOrigin, TypeOutlivesConstraint}; use rustc_middle::ty::{ self, Flags, ImplTraitInTraitData, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; -use rustc_span::bug; +use rustc_span::{DUMMY_SP, bug}; use crate::infer::outlives::test_type_match; use crate::infer::region_constraints::VerifyIfEq; -use crate::regions::{region_known_to_outlive, ty_known_to_outlive}; +use crate::regions::{region_known_to_outlive, test_region_obligations}; + +/// Given a known `param_env` and a set of well formed types, can we prove that +/// `ty` outlives `region`. +/// +/// Copied from `ty_known_to_outlive` without normalization for `ty` because we +/// don't want trait solving in liveness queries. +fn param_known_to_outlive<'tcx>( + tcx: TyCtxt<'tcx>, + id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + wf_tys: &FxIndexSet>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, +) -> bool { + debug_assert_matches!(ty.kind(), ty::Param(_)); + + test_region_obligations(tcx, id, param_env, wf_tys, |infcx| { + infcx.register_type_outlives_constraint_inner(TypeOutlivesConstraint { + sub_region: region, + sup_type: ty, + origin: SubregionOrigin::RelateParamBound(DUMMY_SP, ty, None), + }); + Ok(()) + }) +} /// For a given alias type, this returns the set of indices into the identity generic args that /// are relevant for liveness, that can be inferred from outlives bounds on the @@ -235,7 +263,7 @@ pub(crate) fn args_known_to_outlive_opaque_params<'tcx>( ty::GenericArgKind::Const(_) => continue, // Lifetimes should be captured ty::GenericArgKind::Lifetime(_) => continue, - ty::GenericArgKind::Type(t) => ty_known_to_outlive( + ty::GenericArgKind::Type(t) => param_known_to_outlive( tcx, def_id, parent_param_env, @@ -296,7 +324,7 @@ pub(crate) fn args_known_to_outlive_non_opaque_params<'tcx>( region_known_to_outlive(tcx, def_id, param_env, &wf_tys, r, outlived_region) } ty::GenericArgKind::Type(t) => { - ty_known_to_outlive(tcx, def_id, param_env, &wf_tys, t, outlived_region) + param_known_to_outlive(tcx, def_id, param_env, &wf_tys, t, outlived_region) } ty::GenericArgKind::Const(_) => false, }; diff --git a/tests/ui/assumptions_on_binders/nested-gat-outlives-issue-161067.rs b/tests/ui/assumptions_on_binders/nested-gat-outlives-issue-161067.rs new file mode 100644 index 0000000000000..937b17e5ec872 --- /dev/null +++ b/tests/ui/assumptions_on_binders/nested-gat-outlives-issue-161067.rs @@ -0,0 +1,16 @@ +//@ compile-flags: -Zassumptions-on-binders +//@ needs-rustc-debug-assertions + +// Regression test for #161067. A nested non-rigid alias must be normalized +// before it reaches lexical region solving through `ty_known_to_outlive`. + +struct D; + +trait Des { + type Out<'x, T>; + //~^ ERROR missing required bound on `Out` + + fn des<'z>() -> Self::Out<'z, Self::Out<'z, D>>; +} + +fn main() {} diff --git a/tests/ui/assumptions_on_binders/nested-gat-outlives-issue-161067.stderr b/tests/ui/assumptions_on_binders/nested-gat-outlives-issue-161067.stderr new file mode 100644 index 0000000000000..7e73e999e0c81 --- /dev/null +++ b/tests/ui/assumptions_on_binders/nested-gat-outlives-issue-161067.stderr @@ -0,0 +1,13 @@ +error: missing required bound on `Out` + --> $DIR/nested-gat-outlives-issue-161067.rs:10:5 + | +LL | type Out<'x, T>; + | ^^^^^^^^^^^^^^^- + | | + | help: add the required where clause: `where T: 'x` + | + = note: this bound is currently required to ensure that impls have maximum flexibility + = note: we are soliciting feedback, see issue #87479 for more information + +error: aborting due to 1 previous error +