Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -623,7 +628,14 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
// 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
Expand Down
28 changes: 24 additions & 4 deletions compiler/rustc_trait_selection/src/regions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>)]
Expand Down Expand Up @@ -90,15 +92,30 @@ pub fn ty_known_to_outlive<'tcx>(
id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
wf_tys: &FxIndexSet<Ty<'tcx>>,
ty: Ty<'tcx>,
ty: Unnormalized<'tcx, Ty<'tcx>>,
region: ty::Region<'tcx>,
) -> bool {
test_region_obligations(tcx, id, param_env, wf_tys, |infcx| {

@adwinwhite adwinwhite Sep 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can still use test_region_obligations and just normalize the ty?

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah that's the part I overlooked 😬 I reused test_region_obligations here

// 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(())
})
}

Expand All @@ -119,6 +136,7 @@ pub fn region_known_to_outlive<'tcx>(
region_a,
ty::VisibleForLeakCheck::Unreachable,
);
Ok(())
})
}

Expand All @@ -130,14 +148,16 @@ pub fn test_region_obligations<'tcx>(
id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
wf_tys: &FxIndexSet<Ty<'tcx>>,
add_constraints: impl FnOnce(&InferCtxt<'tcx>),
add_constraints: impl FnOnce(&InferCtxt<'tcx>) -> Result<(), ()>,
) -> bool {

@lcnr lcnr Sep 17, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't have to do it in this PR, but you can also return Result here and imo should do so then you can do ?

View changes since the review

// 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");
Expand Down
36 changes: 32 additions & 4 deletions compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs
Original file line number Diff line number Diff line change
@@ -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<'tcx>>,
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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
};
Expand Down
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/rust-lang/rust/issues/87479> for more information

error: aborting due to 1 previous error

Loading