From f03200ec1a4198b010371f2e9fc215ad50fd2e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 10 Jun 2026 11:59:20 +0200 Subject: [PATCH 1/8] Add more tests that exercise the well-formedness checking of lazy type aliases --- tests/ui/lazy-type-alias/def-site-body-wf.rs | 19 +++++++ .../lazy-type-alias/def-site-body-wf.stderr | 54 +++++++++++++++++++ .../unsatisfied-bounds-type-alias-body.rs | 8 --- .../unsatisfied-bounds-type-alias-body.stderr | 14 ----- tests/ui/lazy-type-alias/use-site-wf.rs | 16 ++++++ tests/ui/lazy-type-alias/use-site-wf.stderr | 50 +++++++++++++++++ 6 files changed, 139 insertions(+), 22 deletions(-) create mode 100644 tests/ui/lazy-type-alias/def-site-body-wf.rs create mode 100644 tests/ui/lazy-type-alias/def-site-body-wf.stderr delete mode 100644 tests/ui/lazy-type-alias/unsatisfied-bounds-type-alias-body.rs delete mode 100644 tests/ui/lazy-type-alias/unsatisfied-bounds-type-alias-body.stderr create mode 100644 tests/ui/lazy-type-alias/use-site-wf.rs create mode 100644 tests/ui/lazy-type-alias/use-site-wf.stderr diff --git a/tests/ui/lazy-type-alias/def-site-body-wf.rs b/tests/ui/lazy-type-alias/def-site-body-wf.rs new file mode 100644 index 0000000000000..9287cb461195a --- /dev/null +++ b/tests/ui/lazy-type-alias/def-site-body-wf.rs @@ -0,0 +1,19 @@ +// Test that we check the body at the definition site for well-formedness. +// Compare with `tests/ui/type-alias/lack-of-wfcheck.rs`. + +#![feature(lazy_type_alias)] + +// unsatisified trait bounds +type _A = ::Output; //~ ERROR cannot multiply `T` by `T` +type _B = Vec; //~ ERROR the size for values of type `str` cannot be known at compilation time + +// unsatisfied outlives-bounds +type _C<'a> = &'static &'a (); //~ ERROR reference has a longer lifetime than the data it references + +// diverging const exprs +type _D = [(); panic!()]; //~ ERROR evaluation panicked + +// dyn incompatibility +type _E = dyn Sized; //~ ERROR the trait `Sized` is not dyn compatible + +fn main() {} diff --git a/tests/ui/lazy-type-alias/def-site-body-wf.stderr b/tests/ui/lazy-type-alias/def-site-body-wf.stderr new file mode 100644 index 0000000000000..99ebc95616816 --- /dev/null +++ b/tests/ui/lazy-type-alias/def-site-body-wf.stderr @@ -0,0 +1,54 @@ +error[E0277]: cannot multiply `T` by `T` + --> $DIR/def-site-body-wf.rs:7:14 + | +LL | type _A = ::Output; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `T * T` + | +help: consider restricting type parameter `T` with trait `Mul` + | +LL | type _A = ::Output; + | +++++++++++++++ + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/def-site-body-wf.rs:8:11 + | +LL | type _B = Vec; + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` +note: required by an implicit `Sized` bound in `Vec` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + +error[E0491]: in type `&'static &'a ()`, reference has a longer lifetime than the data it references + --> $DIR/def-site-body-wf.rs:11:1 + | +LL | type _C<'a> = &'static &'a (); + | ^^^^^^^^^^^ + | + = note: the pointer is valid for the static lifetime +note: but the referenced data is only valid for the lifetime `'a` as defined here + --> $DIR/def-site-body-wf.rs:11:9 + | +LL | type _C<'a> = &'static &'a (); + | ^^ + +error[E0080]: evaluation panicked: explicit panic + --> $DIR/def-site-body-wf.rs:14:16 + | +LL | type _D = [(); panic!()]; + | ^^^^^^^^ evaluation of `_D::{constant#0}` failed here + +error[E0038]: the trait `Sized` is not dyn compatible + --> $DIR/def-site-body-wf.rs:17:11 + | +LL | type _E = dyn Sized; + | ^^^^^^^^^ `Sized` is not dyn compatible + | + = note: the trait is not dyn compatible because it requires `Self: Sized` + = note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0038, E0080, E0277, E0491. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/lazy-type-alias/unsatisfied-bounds-type-alias-body.rs b/tests/ui/lazy-type-alias/unsatisfied-bounds-type-alias-body.rs deleted file mode 100644 index c798e4e436824..0000000000000 --- a/tests/ui/lazy-type-alias/unsatisfied-bounds-type-alias-body.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Test that we check lazy type aliases for well-formedness. - -#![feature(lazy_type_alias)] -#![allow(incomplete_features)] - -type Alias = ::Output; //~ ERROR cannot multiply `T` by `T` - -fn main() {} diff --git a/tests/ui/lazy-type-alias/unsatisfied-bounds-type-alias-body.stderr b/tests/ui/lazy-type-alias/unsatisfied-bounds-type-alias-body.stderr deleted file mode 100644 index d0d4d04e28a38..0000000000000 --- a/tests/ui/lazy-type-alias/unsatisfied-bounds-type-alias-body.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0277]: cannot multiply `T` by `T` - --> $DIR/unsatisfied-bounds-type-alias-body.rs:6:17 - | -LL | type Alias = ::Output; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `T * T` - | -help: consider restricting type parameter `T` with trait `Mul` - | -LL | type Alias = ::Output; - | +++++++++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/lazy-type-alias/use-site-wf.rs b/tests/ui/lazy-type-alias/use-site-wf.rs new file mode 100644 index 0000000000000..a44ff3e3493d7 --- /dev/null +++ b/tests/ui/lazy-type-alias/use-site-wf.rs @@ -0,0 +1,16 @@ +// Test that we check usage sites of lazy type aliases, aka free alias types, for well-formedness. +// We check trailing where-clauses separately in `trailing-where-clause.rs`. + +#![feature(lazy_type_alias)] + +type B = T; + +fn b(_: B) {} +//~^ ERROR the trait bound `X: Copy` is not satisfied +//~| ERROR the trait bound `X: Copy` is not satisfied + +type A<'a: 'static> = &'a (); + +fn a<'r>(_: A<'r>) {} //~ ERROR lifetime bound not satisfied + +fn main() {} diff --git a/tests/ui/lazy-type-alias/use-site-wf.stderr b/tests/ui/lazy-type-alias/use-site-wf.stderr new file mode 100644 index 0000000000000..b473a2db055e4 --- /dev/null +++ b/tests/ui/lazy-type-alias/use-site-wf.stderr @@ -0,0 +1,50 @@ +error[E0277]: the trait bound `X: Copy` is not satisfied + --> $DIR/use-site-wf.rs:8:12 + | +LL | fn b(_: B) {} + | ^^^^ the trait `Copy` is not implemented for `X` + | +note: required by a bound on the type alias `B` + --> $DIR/use-site-wf.rs:6:11 + | +LL | type B = T; + | ^^^^ required by this bound +help: consider restricting type parameter `X` with trait `Copy` + | +LL | fn b(_: B) {} + | +++++++++++++++++++ + +error[E0478]: lifetime bound not satisfied + --> $DIR/use-site-wf.rs:14:13 + | +LL | fn a<'r>(_: A<'r>) {} + | ^^^^^ + | +note: lifetime parameter instantiated with the lifetime `'r` as defined here + --> $DIR/use-site-wf.rs:14:6 + | +LL | fn a<'r>(_: A<'r>) {} + | ^^ + = note: but lifetime parameter must outlive the static lifetime + +error[E0277]: the trait bound `X: Copy` is not satisfied + --> $DIR/use-site-wf.rs:8:12 + | +LL | fn b(_: B) {} + | ^^^^ the trait `Copy` is not implemented for `X` + | +note: required by a bound on the type alias `B` + --> $DIR/use-site-wf.rs:6:11 + | +LL | type B = T; + | ^^^^ required by this bound + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider restricting type parameter `X` with trait `Copy` + | +LL | fn b(_: B) {} + | +++++++++++++++++++ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0478. +For more information about an error, try `rustc --explain E0277`. From db47eb781931c1343410de45755dc45a920f83eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 15 May 2025 12:59:20 +0200 Subject: [PATCH 2/8] Expand free alias types during variance computation --- .../rustc_hir_analysis/src/check/check.rs | 9 +-- .../rustc_hir_analysis/src/check/wfcheck.rs | 23 ++----- .../src/variance/constraints.rs | 20 ++---- .../rustc_hir_analysis/src/variance/dump.rs | 8 +-- .../rustc_hir_analysis/src/variance/mod.rs | 5 -- .../rustc_hir_analysis/src/variance/terms.rs | 3 - compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_middle/src/ty/util.rs | 10 ++- .../inherent-impls-overflow.current.stderr | 4 +- .../inherent-impls-overflow.next.stderr | 30 ++------- .../inherent-impls-overflow.rs | 6 +- ...-arguments-not-wfchecked.next-fixed.stderr | 9 +++ .../unused-generic-arguments-not-wfchecked.rs | 33 ++++++++++ .../unused-generic-parameters.fail.stderr | 50 +++++++++++++++ .../unused-generic-parameters.rs | 42 ++++++++----- .../unused-generic-parameters.stderr | 28 --------- tests/ui/lazy-type-alias/variance-0.rs | 61 +++++++++++++++++++ tests/ui/lazy-type-alias/variance-1.rs | 42 +++++++++++++ tests/ui/lazy-type-alias/variance-1.stderr | 22 +++++++ tests/ui/lazy-type-alias/variance-overflow.rs | 22 +++++++ .../lazy-type-alias/variance-overflow.stderr | 29 +++++++++ tests/ui/lazy-type-alias/variance.rs | 38 ------------ 22 files changed, 324 insertions(+), 172 deletions(-) create mode 100644 tests/ui/lazy-type-alias/unused-generic-arguments-not-wfchecked.next-fixed.stderr create mode 100644 tests/ui/lazy-type-alias/unused-generic-arguments-not-wfchecked.rs create mode 100644 tests/ui/lazy-type-alias/unused-generic-parameters.fail.stderr delete mode 100644 tests/ui/lazy-type-alias/unused-generic-parameters.stderr create mode 100644 tests/ui/lazy-type-alias/variance-0.rs create mode 100644 tests/ui/lazy-type-alias/variance-1.rs create mode 100644 tests/ui/lazy-type-alias/variance-1.stderr create mode 100644 tests/ui/lazy-type-alias/variance-overflow.rs create mode 100644 tests/ui/lazy-type-alias/variance-overflow.stderr delete mode 100644 tests/ui/lazy-type-alias/variance.rs diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 00bb8fab3cc2d..6f7cd7960a242 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -974,7 +974,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), tcx.ensure_ok().generics_of(def_id); tcx.ensure_ok().type_of(def_id); tcx.ensure_ok().predicates_of(def_id); - check_type_alias_type_params_are_used(tcx, def_id); let ty = tcx.type_of(def_id).instantiate_identity(); let span = tcx.def_span(def_id); if tcx.type_alias_is_lazy(def_id) { @@ -988,8 +987,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), check_where_clauses(wfcx, def_id); Ok(()) })); - check_variances_for_type_defn(tcx, def_id); } else { + check_type_alias_type_params_are_used(tcx, def_id); res = res.and(enter_wf_checking_ctxt(tcx, def_id, |wfcx| { // HACK: We sometimes incidentally check that const arguments have the correct // type as a side effect of the anon const desugaring. To make this "consistent" @@ -2064,12 +2063,6 @@ fn detect_discriminant_duplicate<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) } fn check_type_alias_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) { - if tcx.type_alias_is_lazy(def_id) { - // Since we compute the variances for lazy type aliases and already reject bivariant - // parameters as unused, we can and should skip this check for lazy type aliases. - return; - } - let generics = tcx.generics_of(def_id); if generics.own_counts().types == 0 { return; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index f5b414d2aa4a2..e85ecb63421d1 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2049,12 +2049,6 @@ pub(super) fn check_variances_for_type_defn<'tcx>(tcx: TyCtxt<'tcx>, def_id: Loc DefKind::Enum | DefKind::Struct | DefKind::Union => { // Ok } - DefKind::TyAlias => { - assert!( - tcx.type_alias_is_lazy(def_id), - "should not be computing variance of non-free type alias" - ); - } kind => span_bug!(tcx.def_span(def_id), "cannot compute the variances of {kind:?}"), } @@ -2206,7 +2200,6 @@ fn report_bivariance<'tcx>( errors::UnusedGenericParameterHelp::AdtNoPhantomData { param_name } } } - ItemKind::TyAlias(..) => errors::UnusedGenericParameterHelp::TyAlias { param_name }, item_kind => bug!("report_bivariance: unexpected item kind: {item_kind:?}"), }; @@ -2288,9 +2281,6 @@ impl<'tcx> IsProbablyCyclical<'tcx> { .visit_with(self) }) } - DefKind::TyAlias if self.tcx.type_alias_is_lazy(def_id) => { - self.tcx.type_of(def_id).instantiate_identity().skip_norm_wip().visit_with(self) - } _ => ControlFlow::Continue(()), } } @@ -2300,17 +2290,12 @@ impl<'tcx> TypeVisitor> for IsProbablyCyclical<'tcx> { type Result = ControlFlow<(), ()>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<(), ()> { - let def_id = match ty.kind() { - ty::Adt(adt_def, _) => Some(adt_def.did()), - &ty::Alias(ty::AliasTy { kind: ty::Free { def_id }, .. }) => Some(def_id), - _ => None, - }; - if let Some(def_id) = def_id { - if def_id == self.item_def_id { + if let Some(adt_def) = ty.ty_adt_def() { + if adt_def.did() == self.item_def_id { return ControlFlow::Break(()); } - if self.seen.insert(def_id) { - self.visit_def(def_id)?; + if self.seen.insert(adt_def.did()) { + self.visit_def(adt_def.did())?; } } ty.super_visit_with(self) diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index a45856937a8e0..3e7f5b73e4569 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -79,9 +79,6 @@ pub(crate) fn add_constraints_from_crate<'a, 'tcx>( } } DefKind::Fn | DefKind::AssocFn => constraint_cx.build_constraints_for_item(def_id), - DefKind::TyAlias if tcx.type_alias_is_lazy(def_id) => { - constraint_cx.build_constraints_for_item(def_id) - } _ => {} } } @@ -107,15 +104,6 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { let current_item = &CurrentItem { inferred_start }; let ty = tcx.type_of(def_id).instantiate_identity().skip_norm_wip(); - // The type as returned by `type_of` is the underlying type and generally not a free alias. - // Therefore we need to check the `DefKind` first. - if let DefKind::TyAlias = tcx.def_kind(def_id) - && tcx.type_alias_is_lazy(def_id) - { - self.add_constraints_from_ty(current_item, ty, self.covariant); - return; - } - match ty.kind() { ty::Adt(def, _) => { // Not entirely obvious: constraints on structs/enums do not @@ -216,14 +204,13 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { /// Adds constraints appropriate for an instance of `ty` appearing /// in a context with the generics defined in `generics` and /// ambient variance `variance` + #[instrument(level = "debug", skip(self, current))] fn add_constraints_from_ty( &mut self, current: &CurrentItem, ty: Ty<'tcx>, variance: VarianceTermPtr<'a>, ) { - debug!("add_constraints_from_ty(ty={:?}, variance={:?})", ty, variance); - match *ty.kind() { ty::Bool | ty::Char @@ -281,8 +268,9 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_invariant_args(current, args, variance); } - ty::Alias(ty::AliasTy { kind: ty::Free { def_id }, args, .. }) => { - self.add_constraints_from_args(current, def_id, args, variance); + ty::Alias(ty::AliasTy { kind: ty::Free { .. }, .. }) => { + let ty = self.tcx().expand_free_alias_tys(ty); + self.add_constraints_from_ty(current, ty, variance); } ty::Dynamic(data, r) => { diff --git a/compiler/rustc_hir_analysis/src/variance/dump.rs b/compiler/rustc_hir_analysis/src/variance/dump.rs index 2e17464736959..7963f5da0e870 100644 --- a/compiler/rustc_hir_analysis/src/variance/dump.rs +++ b/compiler/rustc_hir_analysis/src/variance/dump.rs @@ -38,18 +38,16 @@ pub(crate) fn variances(tcx: TyCtxt<'_>) { } match tcx.def_kind(id) { - DefKind::AssocFn | DefKind::Fn | DefKind::Enum | DefKind::Struct | DefKind::Union => {} - DefKind::TyAlias if tcx.type_alias_is_lazy(id) => {} + DefKind::AssocFn | DefKind::Fn | DefKind::Enum | DefKind::Struct | DefKind::Union => { + tcx.dcx().span_err(tcx.def_span(id), format_variances(tcx, id.def_id)); + } kind => { let message = format!( "attr parsing didn't report an error for `#[{}]` on {kind:?}", rustc_span::sym::rustc_dump_variances, ); tcx.dcx().span_delayed_bug(tcx.def_span(id), message); - continue; } } - - tcx.dcx().span_err(tcx.def_span(id), format_variances(tcx, id.def_id)); } } diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index 9c20ef1a73dcf..c4287a8d0bb10 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -52,11 +52,6 @@ pub(super) fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Va let crate_map = tcx.crate_variances(()); return crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]); } - DefKind::TyAlias if tcx.type_alias_is_lazy(item_def_id) => { - // These are inferred. - let crate_map = tcx.crate_variances(()); - return crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]); - } DefKind::AssocTy => match tcx.opt_rpitit_info(item_def_id.to_def_id()) { Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => { return variance_of_opaque( diff --git a/compiler/rustc_hir_analysis/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs index 9c7680b921205..6faeab4217e37 100644 --- a/compiler/rustc_hir_analysis/src/variance/terms.rs +++ b/compiler/rustc_hir_analysis/src/variance/terms.rs @@ -99,9 +99,6 @@ pub(crate) fn determine_parameters_to_be_inferred<'a, 'tcx>( } } DefKind::Fn | DefKind::AssocFn => terms_cx.add_inferreds_for_item(def_id), - DefKind::TyAlias if tcx.type_alias_is_lazy(def_id) => { - terms_cx.add_inferreds_for_item(def_id) - } _ => {} } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 1483de572c341..d6492f098d07e 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1153,6 +1153,7 @@ fn should_encode_variances<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: Def | DefKind::Static { .. } | DefKind::Const { .. } | DefKind::ForeignMod + | DefKind::TyAlias | DefKind::Impl { .. } | DefKind::Trait | DefKind::TraitAlias @@ -1166,7 +1167,6 @@ fn should_encode_variances<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: Def | DefKind::Closure | DefKind::ExternCrate | DefKind::SyntheticCoroutineBody => false, - DefKind::TyAlias => tcx.type_alias_is_lazy(def_id), } } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 9f6d1170a85f1..ee6aaf9cb6e17 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -905,7 +905,7 @@ impl<'tcx> TyCtxt<'tcx> { return Ty::new_error(self, guar); } - ty = self.type_of(def_id).instantiate(self, args).skip_norm_wip(); + ty = self.type_of(def_id).instantiate(self, args).skip_normalization(); depth += 1; } @@ -967,7 +967,7 @@ impl<'tcx> OpaqueTypeExpander<'tcx> { Some(expanded_ty) => *expanded_ty, None => { let generic_ty = self.tcx.type_of(def_id); - let concrete_ty = generic_ty.instantiate(self.tcx, args).skip_norm_wip(); + let concrete_ty = generic_ty.instantiate(self.tcx, args).skip_normalization(); let expanded_ty = self.fold_ty(concrete_ty); self.expanded_cache.insert((def_id, args), expanded_ty); expanded_ty @@ -1047,7 +1047,11 @@ impl<'tcx> TypeFolder> for FreeAliasTypeExpander<'tcx> { self.depth += 1; let ty = ensure_sufficient_stack(|| { - self.tcx.type_of(def_id).instantiate(self.tcx, args).skip_norm_wip().fold_with(self) + self.tcx + .type_of(def_id) + .instantiate(self.tcx, args) + .skip_normalization() + .fold_with(self) }); self.depth -= 1; ty diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.current.stderr b/tests/ui/lazy-type-alias/inherent-impls-overflow.current.stderr index e91946066bd08..dee809ebf7e81 100644 --- a/tests/ui/lazy-type-alias/inherent-impls-overflow.current.stderr +++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.current.stderr @@ -23,7 +23,7 @@ LL | type Poly0 = Poly1<(T,)>; = note: in case this is a recursive type alias, consider using a struct, enum, or union instead error[E0275]: overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` - --> $DIR/inherent-impls-overflow.rs:21:1 + --> $DIR/inherent-impls-overflow.rs:20:1 | LL | type Poly1 = Poly0<(T,)>; | ^^^^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | type Poly1 = Poly0<(T,)>; = note: in case this is a recursive type alias, consider using a struct, enum, or union instead error[E0275]: overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` - --> $DIR/inherent-impls-overflow.rs:26:1 + --> $DIR/inherent-impls-overflow.rs:24:1 | LL | impl Poly0<()> {} | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr b/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr index 62ed6e8e513d9..a592d3537ebde 100644 --- a/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr +++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr @@ -24,38 +24,16 @@ LL | type Poly0 = Poly1<(T,)>; | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inherent_impls_overflow`) -error: type parameter `T` is only used recursively - --> $DIR/inherent-impls-overflow.rs:17:24 - | -LL | type Poly0 = Poly1<(T,)>; - | - ^ - | | - | type parameter must be used non-recursively in the definition - | - = help: consider removing `T` or referring to it in the body of the type alias - = note: all type parameters must be used in a non-recursive way in order to constrain their variance - error[E0275]: overflow evaluating the requirement `Poly0<(T,)> == _` - --> $DIR/inherent-impls-overflow.rs:21:1 + --> $DIR/inherent-impls-overflow.rs:20:1 | LL | type Poly1 = Poly0<(T,)>; | ^^^^^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inherent_impls_overflow`) -error: type parameter `T` is only used recursively - --> $DIR/inherent-impls-overflow.rs:21:24 - | -LL | type Poly1 = Poly0<(T,)>; - | - ^ - | | - | type parameter must be used non-recursively in the definition - | - = help: consider removing `T` or referring to it in the body of the type alias - = note: all type parameters must be used in a non-recursive way in order to constrain their variance - error[E0275]: overflow evaluating the requirement `Poly0<()> == _` - --> $DIR/inherent-impls-overflow.rs:26:1 + --> $DIR/inherent-impls-overflow.rs:24:1 | LL | impl Poly0<()> {} | ^^^^^^^^^^^^^^^^^ @@ -63,14 +41,14 @@ LL | impl Poly0<()> {} = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inherent_impls_overflow`) error[E0275]: overflow evaluating the requirement `Poly0<()> == _` - --> $DIR/inherent-impls-overflow.rs:26:6 + --> $DIR/inherent-impls-overflow.rs:24:6 | LL | impl Poly0<()> {} | ^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inherent_impls_overflow`) -error: aborting due to 9 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0271, E0275. For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.rs b/tests/ui/lazy-type-alias/inherent-impls-overflow.rs index b4a347cb098ca..be3cda4d53584 100644 --- a/tests/ui/lazy-type-alias/inherent-impls-overflow.rs +++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.rs @@ -16,12 +16,10 @@ impl Loop {} type Poly0 = Poly1<(T,)>; //[current]~^ ERROR overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>` -//[next]~^^ ERROR type parameter `T` is only used recursively -//[next]~| ERROR overflow evaluating the requirement +//[next]~^^ ERROR overflow evaluating the requirement type Poly1 = Poly0<(T,)>; //[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` -//[next]~^^ ERROR type parameter `T` is only used recursively -//[next]~| ERROR overflow evaluating the requirement +//[next]~^^ ERROR overflow evaluating the requirement impl Poly0<()> {} //[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` diff --git a/tests/ui/lazy-type-alias/unused-generic-arguments-not-wfchecked.next-fixed.stderr b/tests/ui/lazy-type-alias/unused-generic-arguments-not-wfchecked.next-fixed.stderr new file mode 100644 index 0000000000000..7148c5d49e926 --- /dev/null +++ b/tests/ui/lazy-type-alias/unused-generic-arguments-not-wfchecked.next-fixed.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation panicked: explicit panic + --> $DIR/unused-generic-arguments-not-wfchecked.rs:28:43 + | +LL | #[cfg(not(next_bugged))] type A2 = A<[(); panic!()]>; // FIXME: `panic!()` diverging + | ^^^^^^^^ evaluation of `A2::{constant#0}` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/lazy-type-alias/unused-generic-arguments-not-wfchecked.rs b/tests/ui/lazy-type-alias/unused-generic-arguments-not-wfchecked.rs new file mode 100644 index 0000000000000..eeabe85d82734 --- /dev/null +++ b/tests/ui/lazy-type-alias/unused-generic-arguments-not-wfchecked.rs @@ -0,0 +1,33 @@ +// We currently fail to wfcheck generic arguments that correspond to unused LTA generic parameters +// since we generally normalize types before wfchecking them, so we accidentally "expand them away" +// before we can check them. +// +// (We do still check predicates that reference unused parameters, of course.) +// +// FIXME(lazy_type_alias): I consider #100041 to be a stabilization-blocking concern for the checked +// version of LTA! The *entire premise* of checked_LTA is wfchecking; +// we can't have such obvious holes in it! +// +//@ revisions: current-bugged next-bugged next-fixed +// +//@[next-bugged] compile-flags: -Znext-solver +//@[next-fixed] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +// +//@[current-bugged] known-bug: #100041 +//@[current-bugged] check-pass +//@[next-bugged] known-bug: #100041 +//@[next-bugged] check-pass + +#![feature(lazy_type_alias)] + +type A = (); + +type A0 = A<[str]>; // FIXME: `str: Sized` unsatisfied +type A1<'r> = A<&'static &'r ()>; // FIXME: `'r: 'static` unsatisfied +#[cfg(not(next_bugged))] type A2 = A<[(); panic!()]>; // FIXME: `panic!()` diverging +//[next-fixed]~^ ERROR evaluation panicked + +#[cfg(not(next_bugged))] const _: A<[str]> = (); // FIXME: `str: Sized` unsatisfied + +fn main() {} diff --git a/tests/ui/lazy-type-alias/unused-generic-parameters.fail.stderr b/tests/ui/lazy-type-alias/unused-generic-parameters.fail.stderr new file mode 100644 index 0000000000000..05e3881cb090c --- /dev/null +++ b/tests/ui/lazy-type-alias/unused-generic-parameters.fail.stderr @@ -0,0 +1,50 @@ +error[E0478]: lifetime bound not satisfied + --> $DIR/unused-generic-parameters.rs:24:22 + | +LL | #[cfg(fail)] fn a(_: A<'_>) {} + | ^^^^^ + | +note: lifetime parameter instantiated with the anonymous lifetime defined here + --> $DIR/unused-generic-parameters.rs:24:22 + | +LL | #[cfg(fail)] fn a(_: A<'_>) {} + | ^^^^^ + = note: but lifetime parameter must outlive the static lifetime + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/unused-generic-parameters.rs:28:22 + | +LL | #[cfg(fail)] fn b(_: B) {} + | ^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` +note: required by a bound on the type alias `B` + --> $DIR/unused-generic-parameters.rs:26:8 + | +LL | type B = (); + | ^ required by this bound + +error[E0080]: evaluation panicked: explicit panic + --> $DIR/unused-generic-parameters.rs:34:26 + | +LL | #[cfg(fail)] fn c(_: C<{ panic!() }>) {} + | ^^^^^^^^ evaluation of `c::{constant#0}` failed here + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/unused-generic-parameters.rs:28:22 + | +LL | #[cfg(fail)] fn b(_: B) {} + | ^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` +note: required by a bound on the type alias `B` + --> $DIR/unused-generic-parameters.rs:26:8 + | +LL | type B = (); + | ^ required by this bound + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0080, E0277, E0478. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/lazy-type-alias/unused-generic-parameters.rs b/tests/ui/lazy-type-alias/unused-generic-parameters.rs index 9d02de7a7212b..3482512671226 100644 --- a/tests/ui/lazy-type-alias/unused-generic-parameters.rs +++ b/tests/ui/lazy-type-alias/unused-generic-parameters.rs @@ -1,22 +1,36 @@ -// Check that we reject bivariant generic parameters as unused. -// Furthermore, check that we only emit a single diagnostic for unused type parameters: -// Previously, we would emit *two* errors, namely E0392 and E0091. +// Check that we accept unused generic parameters on lazy type aliases (for context, we reject +// unused type parameters on eager type aliases). +// +// As long as we check well-formedness before normalization there shouldn't be anything wrong with +// such parameters since we know that the corresponding arguments will get wfchecked regardless. +// +// FIXME(lazy_type_alias, #100041): At the time of writing however, that's not the case. I consider +// this to be stabilization-blocking concern for the strong / +// checked version of LTA! +// See also `unused-generic-arguments-not-wfchecked.rs`. +// +// (We *do* ofc still detect unsatisfied predicates even if they +// reference unused parameters) +// +// issue: +// +//@ revisions: pass fail +//@[pass] check-pass #![feature(lazy_type_alias)] -#![allow(incomplete_features)] -type A<'a> = (); -//~^ ERROR lifetime parameter `'a` is never used -//~| HELP consider removing `'a` +type A<'a: 'static> = (); +const _: A<'static> = (); +#[cfg(fail)] fn a(_: A<'_>) {} //[fail]~ ERROR lifetime bound not satisfied type B = (); -//~^ ERROR type parameter `T` is never used -//~| HELP consider removing `T` -//~| HELP if you intended `T` to be a const parameter +const _: B = (); +#[cfg(fail)] fn b(_: B) {} +//[fail]~^ ERROR the size for values of type `str` cannot be known at compilation time +//[fail]~| ERROR the size for values of type `str` cannot be known at compilation time -// Check that we don't emit the const param help message here: -type C = (); -//~^ ERROR type parameter `T` is never used -//~| HELP consider removing `T` +type C = (); +const _: C<{ 0 * 1 }> = (); +#[cfg(fail)] fn c(_: C<{ panic!() }>) {} //[fail]~ ERROR evaluation panicked fn main() {} diff --git a/tests/ui/lazy-type-alias/unused-generic-parameters.stderr b/tests/ui/lazy-type-alias/unused-generic-parameters.stderr deleted file mode 100644 index 484e21b0a03f9..0000000000000 --- a/tests/ui/lazy-type-alias/unused-generic-parameters.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error[E0392]: lifetime parameter `'a` is never used - --> $DIR/unused-generic-parameters.rs:8:8 - | -LL | type A<'a> = (); - | ^^ unused lifetime parameter - | - = help: consider removing `'a` or referring to it in the body of the type alias - -error[E0392]: type parameter `T` is never used - --> $DIR/unused-generic-parameters.rs:12:8 - | -LL | type B = (); - | ^ unused type parameter - | - = help: consider removing `T` or referring to it in the body of the type alias - = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead - -error[E0392]: type parameter `T` is never used - --> $DIR/unused-generic-parameters.rs:18:8 - | -LL | type C = (); - | ^ unused type parameter - | - = help: consider removing `T` or referring to it in the body of the type alias - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0392`. diff --git a/tests/ui/lazy-type-alias/variance-0.rs b/tests/ui/lazy-type-alias/variance-0.rs new file mode 100644 index 0000000000000..1cecdff2977b3 --- /dev/null +++ b/tests/ui/lazy-type-alias/variance-0.rs @@ -0,0 +1,61 @@ +// Ensure that we eagerly *expand* free alias types during variance computation. +// +// Since free alias types are always normalizable it's not unreasonable to expect that variance +// information "propagates through" free aliases unlike projections for example which constrain +// all of their generic arguments to be invariant[^1]. +// +// For context, we can't *normalize* types before trying to compute variances because we need +// variances for normalization in the first place, more specifically type relating. +// +// [^1]: Parent args: Traits are invariant over their params. Own args: Projections can be rigid. +// +// issue: +// +//@ check-pass + +// FIXME(lazy_type_alias): Revisit this before stabilization (it's not blocking tho): +// We might want to compute variances for free alias types again +// with a special rule. See `variance-1.rs` for details. + +#![feature(lazy_type_alias)] + +// `Co` is covariant over `'a` since we expand `A` to `&'a ()`. +struct Co<'a>(A<'a>); + +// `A` is *not* in a variance relation with its args since it's a type alias. +type A<'a> = &'a (); + +fn co<'a>(x: Co<'static>) { + let _: Co<'a> = x; // OK +} + +// `Contra` is contravariant over `'a` since we expand `B` to `fn(&'a ())`. +struct Contra<'a>(B<'a>); + +// (not in a variance relation) +type B<'a> = fn(&'a ()); + +fn contra<'a>(x: Contra<'a>) { + let _: Contra<'static> = x; // OK +} + +// `CoContra` is covariant over `T` and contravariant over `U` since we expand `C`. +struct CoContra(C); + +// (not in a variance relation) +type C = Option<(T, fn(U))>; + +fn co_contra<'a>(x: CoContra<&'static (), &'a ()>) -> CoContra<&'a (), &'static ()> { + x // OK +} + +// Check that we deeply expand: +struct Co2<'a>(D0<'a>); +type D0<'a> = D1<'a>; +type D1<'a> = D2<'a>; +type D2<'a> = D3<'a>; +type D3<'a> = &'a (); + +fn co2<'a>(x: Co2<'static>) -> Co2<'a> { x } // OK + +fn main() {} diff --git a/tests/ui/lazy-type-alias/variance-1.rs b/tests/ui/lazy-type-alias/variance-1.rs new file mode 100644 index 0000000000000..51480d271b439 --- /dev/null +++ b/tests/ui/lazy-type-alias/variance-1.rs @@ -0,0 +1,42 @@ +// Demonstrate that free alias types don't constrain their lifetime & type arguments if the corresp. +// lifetime & type parameters are unused in the lazy type alias (before normalization) since we +// eagerly expand them during variance computation unlike other alias types which constrain args to +// be invariant. + +// FIXME(lazy_type_alias): Revisit this before stabilization (altho it's not blocking): +// Do we want to compute variances for lazy type aliases & free alias types +// again and "force bivariant parameters to be invariant" if they're not +// constrained by a projection? +// This would make `struct WrapDiscard` below compile. + +#![feature(lazy_type_alias)] + +type Discard<'a, T> = (); + +// `'a` and `T` are bivariant & unconstrained => rejection +struct WrapDiscard<'a, T>(Discard<'a, T>); +//~^ ERROR lifetime parameter `'a` is never used +//~| ERROR type parameter `T` is never used + +type DiscardConstrained<'a, T, X> = X +where + X: Iterator; + +// `'a` and `T` are bivariant & constrained => acceptance +struct WrapDiscardConstrained<'a, T, X>(DiscardConstrained<'a, T, X>) +where + X: Iterator; + +type Co<'a> = std::vec::IntoIter<(&'a (), &'a ())>; + +// NOTE: If we end up switching back to computing variances for free alias types with the special +// rule explained in the FIXME above, then this function should still compile since +// LTA `DiscardConstrained` should be bivariant over `'a` and `T`, not invariant due to them +// being constrained by a projection. +fn bi<'r>( + x: WrapDiscardConstrained<'static, &'static (), Co<'static>>, +) -> WrapDiscardConstrained<'r, &'r (), Co<'r>> { + x +} + +fn main() {} diff --git a/tests/ui/lazy-type-alias/variance-1.stderr b/tests/ui/lazy-type-alias/variance-1.stderr new file mode 100644 index 0000000000000..5eb2f5d0673aa --- /dev/null +++ b/tests/ui/lazy-type-alias/variance-1.stderr @@ -0,0 +1,22 @@ +error[E0392]: lifetime parameter `'a` is never used + --> $DIR/variance-1.rs:17:20 + | +LL | struct WrapDiscard<'a, T>(Discard<'a, T>); + | ^^ unused lifetime parameter + | + = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` + +error[E0392]: type parameter `T` is never used + --> $DIR/variance-1.rs:17:24 + | +LL | struct WrapDiscard<'a, T>(Discard<'a, T>); + | ^ - `T` is named here, but is likely unused in the containing type + | | + | unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0392`. diff --git a/tests/ui/lazy-type-alias/variance-overflow.rs b/tests/ui/lazy-type-alias/variance-overflow.rs new file mode 100644 index 0000000000000..6bd7acc3ebef7 --- /dev/null +++ b/tests/ui/lazy-type-alias/variance-overflow.rs @@ -0,0 +1,22 @@ +// Ensure that we don't enter infinite recursion and trigger a stack overflow when computing the +// variances of items that reference diverging free alias types. This once used to happen in a dev +// version of PR #141030. +// +// This test demonstrates that we cannot rely on wfck bailing out early with a normalization error +// for such free alias types before we reach variance computation. At least at the time of writing, +// we wfck item `First` first which includes variance computation -- at which point item `Second` +// hasn't been wfck'ed yet. +// This means we can't use `type_of` to recurse into free alias types, we do have to use +// `expand_free_alias_tys`. + +#![feature(lazy_type_alias)] + +// the (unused) type parameter is necessary to actually trigger variance computation for `First`. +struct First(Second); +//~^ ERROR type parameter `T` is never used +//~| ERROR overflow normalizing the type alias `Second` + +type Second = Second; // diverging free alias type +//~^ ERROR overflow normalizing the type alias `Second` + +fn main() {} diff --git a/tests/ui/lazy-type-alias/variance-overflow.stderr b/tests/ui/lazy-type-alias/variance-overflow.stderr new file mode 100644 index 0000000000000..35486ccdc169f --- /dev/null +++ b/tests/ui/lazy-type-alias/variance-overflow.stderr @@ -0,0 +1,29 @@ +error[E0392]: type parameter `T` is never used + --> $DIR/variance-overflow.rs:15:14 + | +LL | struct First(Second); + | ^ unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error[E0275]: overflow normalizing the type alias `Second` + --> $DIR/variance-overflow.rs:15:17 + | +LL | struct First(Second); + | ^^^^^^ + | + = note: in case this is a recursive type alias, consider using a struct, enum, or union instead + +error[E0275]: overflow normalizing the type alias `Second` + --> $DIR/variance-overflow.rs:19:1 + | +LL | type Second = Second; // diverging free alias type + | ^^^^^^^^^^^ + | + = note: in case this is a recursive type alias, consider using a struct, enum, or union instead + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0275, E0392. +For more information about an error, try `rustc --explain E0275`. diff --git a/tests/ui/lazy-type-alias/variance.rs b/tests/ui/lazy-type-alias/variance.rs deleted file mode 100644 index dae2069502a20..0000000000000 --- a/tests/ui/lazy-type-alias/variance.rs +++ /dev/null @@ -1,38 +0,0 @@ -// This is a regression test for issue #114221. -// Check that we compute variances for lazy type aliases. - -//@ check-pass - -#![feature(lazy_type_alias)] -#![allow(incomplete_features)] - -// [+] `A` is covariant over `'a`. -struct A<'a>(Co<'a>); - -// [+] `Co` is covariant over `'a`. -type Co<'a> = &'a (); - -fn co<'a>(x: A<'static>) { - let _: A<'a> = x; -} - -// [-] `B` is contravariant over `'a`. -struct B<'a>(Contra<'a>); - -// [-] `Contra` is contravariant over `'a`. -type Contra<'a> = fn(&'a ()); - -fn contra<'a>(x: B<'a>) { - let _: B<'static> = x; -} - -struct C(CoContra); - -// [+, -] `CoContra` is covariant over `T` and contravariant over `U`. -type CoContra = Option<(T, fn(U))>; - -fn co_contra<'a>(x: C<&'static (), &'a ()>) -> C<&'a (), &'static ()> { - x -} - -fn main() {} From a176bccc854d009b9645ae07e3acb85fb2da5950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 14 May 2025 16:15:57 +0200 Subject: [PATCH 3/8] Expand free alias types when computing implied outlives-bounds --- .../src/ty/context/impl_interner.rs | 4 ++++ compiler/rustc_middle/src/ty/util.rs | 2 +- compiler/rustc_type_ir/src/interner.rs | 1 + compiler/rustc_type_ir/src/outlives.rs | 6 ++++++ .../implied-outlives-bounds-1.print.stderr | 11 +++++++++++ .../implied-outlives-bounds-1.rs | 17 +++++++++++++++++ 6 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tests/ui/lazy-type-alias/implied-outlives-bounds-1.print.stderr create mode 100644 tests/ui/lazy-type-alias/implied-outlives-bounds-1.rs diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index e9eef9336e8ba..58f07b38691f6 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -160,6 +160,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { // See trait-system-refactor-initiative#234. } + fn expand_free_alias_tys>>(self, t: T) -> T { + self.expand_free_alias_tys(t) + } + fn expand_abstract_consts>>(self, t: T) -> T { self.expand_abstract_consts(t) } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index ee6aaf9cb6e17..95f8b7497c4e4 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -879,7 +879,7 @@ impl<'tcx> TyCtxt<'tcx> { value.fold_with(&mut FreeAliasTypeExpander { tcx: self, depth: 0 }) } - /// Peel off all [free alias types] in this type until there are none left. + /// Peel off all [free alias types][free] in this type until there are none left. /// /// This only expands free alias types in “head” / outermost positions. It can /// be used over [expand_free_alias_tys] as an optimization in situations where diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 1e5ad0b00e7ef..215459c65d439 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -217,6 +217,7 @@ pub trait Interner: /// (in theory) only happen when concurrent. fn assert_evaluation_is_concurrent(&self); + fn expand_free_alias_tys>(self, t: T) -> T; fn expand_abstract_consts>(self, t: T) -> T; type GenericsOf: GenericsOf; diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index 2a1cbc3575d85..743e58bca8c97 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -58,6 +58,7 @@ pub fn push_outlives_components( ty: I::Ty, out: &mut SmallVec<[Component; 4]>, ) { + let ty = cx.expand_free_alias_tys(ty); ty.visit_with(&mut OutlivesCollector { cx, out, visited: Default::default() }); } @@ -140,6 +141,11 @@ impl TypeVisitor for OutlivesCollector<'_, I> { self.out.push(Component::Placeholder(p)); } + // All free alias types should've been expanded beforehand. + ty::Alias(ty::AliasTy { kind: ty::Free { .. }, .. }) => { + panic!("unexpected free alias type") + } + // For projections, we prefer to generate an obligation like // `>::Foo: 'a`, because this gives the // regionck more ways to prove that it holds. However, diff --git a/tests/ui/lazy-type-alias/implied-outlives-bounds-1.print.stderr b/tests/ui/lazy-type-alias/implied-outlives-bounds-1.print.stderr new file mode 100644 index 0000000000000..2787283a3b233 --- /dev/null +++ b/tests/ui/lazy-type-alias/implied-outlives-bounds-1.print.stderr @@ -0,0 +1,11 @@ +error: rustc_dump_inferred_outlives + --> $DIR/implied-outlives-bounds-1.rs:13:1 + | +LL | struct Type<'a, K, V>(&'a mut Alias); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: K: 'a + = note: V: 'a + +error: aborting due to 1 previous error + diff --git a/tests/ui/lazy-type-alias/implied-outlives-bounds-1.rs b/tests/ui/lazy-type-alias/implied-outlives-bounds-1.rs new file mode 100644 index 0000000000000..7d98bd6a6d7d5 --- /dev/null +++ b/tests/ui/lazy-type-alias/implied-outlives-bounds-1.rs @@ -0,0 +1,17 @@ +// Check that we infer the outlives-predicates `K: 'a`, `V: 'a` for `Type` +// from the free alias `Alias`. +// FIXME(fmease): Proper explainer. + +//@ revisions: default print +//@[default] check-pass + +#![feature(lazy_type_alias)] +#![cfg_attr(print, feature(rustc_attrs))] +#![allow(incomplete_features)] + +#[cfg_attr(print, rustc_dump_inferred_outlives)] +struct Type<'a, K, V>(&'a mut Alias); //[print]~ ERROR rustc_dump_inferred_outlives + +type Alias = (K, V); + +fn main() {} From 49aeb113cc60663e578af1959a22e0b6e3c6475b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 10 Jun 2026 19:04:30 +0200 Subject: [PATCH 4/8] [CRATER-ONLY] Always lower to free alias types --- .../rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 7 +------ .../src/outlives/implicit_infer.rs | 2 +- compiler/rustc_hir_analysis/src/outlives/mod.rs | 2 +- compiler/rustc_middle/src/queries.rs | 14 +------------- 4 files changed, 4 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 943256ce07352..b02aae4e2d464 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1211,12 +1211,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); let args = self.lower_generic_args_of_path_segment(span, def_id, item_segment); - if let DefKind::TyAlias = tcx.def_kind(def_id) - && tcx.type_alias_is_lazy(def_id) - { - // Type aliases defined in crates that have the - // feature `lazy_type_alias` enabled get encoded as a type alias that normalization will - // then actually instantiate the where bounds of. + if let DefKind::TyAlias = tcx.def_kind(def_id) { let alias_ty = ty::AliasTy::new_from_args(tcx, ty::Free { def_id }, args); Ty::new_alias(tcx, alias_ty) } else { diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index 174f702dcd0c4..ce34d67591e04 100644 --- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -56,7 +56,7 @@ pub(super) fn infer_predicates( } } - DefKind::TyAlias if tcx.type_alias_is_lazy(item_did) => { + DefKind::TyAlias => { insert_required_predicates_to_be_wf( tcx, tcx.type_of(item_did).instantiate_identity().skip_norm_wip(), diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs index d155f4f98ad79..6f6ba77d88815 100644 --- a/compiler/rustc_hir_analysis/src/outlives/mod.rs +++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs @@ -17,7 +17,7 @@ pub(super) fn inferred_outlives_of( let crate_map = tcx.inferred_outlives_crate(()); crate_map.predicates.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]) } - DefKind::TyAlias if tcx.type_alias_is_lazy(item_def_id) => { + DefKind::TyAlias => { let crate_map = tcx.inferred_outlives_crate(()); crate_map.predicates.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]) } diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 1e0abdcc196fa..be6c355f68c09 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -332,19 +332,7 @@ rustc_queries! { } } - /// Returns whether the type alias given by `DefId` is lazy. - /// - /// I.e., if the type alias expands / ought to expand to a [free] [alias type] - /// instead of the underlying aliased type. - /// - /// Relevant for features `lazy_type_alias` and `type_alias_impl_trait`. - /// - /// # Panics - /// - /// This query *may* panic if the given definition is not a type alias. - /// - /// [free]: rustc_middle::ty::Free - /// [alias type]: rustc_middle::ty::AliasTy + // NOTE: Whether this is a **checked** LTA. query type_alias_is_lazy(key: DefId) -> bool { desc { "computing whether the type alias `{path}` is lazy", From f5b14035395563518c0fc6eb1cb0245c6b9ca820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 10 Jun 2026 19:05:07 +0200 Subject: [PATCH 5/8] [UNAUDITED] Expand LTAs away in the self ty of auto trait impls during orphanck --- compiler/rustc_hir_analysis/src/coherence/orphan.rs | 4 +++- tests/ui/lazy-type-alias/auto-trait-impl-self-ty.rs | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/ui/lazy-type-alias/auto-trait-impl-self-ty.rs diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index bacc1f1549973..5d98c02a63057 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -84,6 +84,8 @@ pub(crate) fn orphan_check_impl( if tcx.trait_is_auto(trait_def_id) { let self_ty = trait_ref.self_ty(); + // FIXME(lazy_type_alias): Audit. + let self_ty = tcx.expand_free_alias_tys(self_ty); // If the impl is in the same crate as the auto-trait, almost anything // goes. @@ -190,7 +192,7 @@ pub(crate) fn orphan_check_impl( ty::Projection { .. } => "associated type", // type Foo = (impl Sized, bool) // impl AutoTrait for Foo {} - ty::Free { .. } => "type alias", + ty::Free { .. } => bug!("unexpected free alias type"), // type Opaque = impl Trait; // impl AutoTrait for Opaque {} ty::Opaque { .. } => "opaque type", diff --git a/tests/ui/lazy-type-alias/auto-trait-impl-self-ty.rs b/tests/ui/lazy-type-alias/auto-trait-impl-self-ty.rs new file mode 100644 index 0000000000000..1ee453026602e --- /dev/null +++ b/tests/ui/lazy-type-alias/auto-trait-impl-self-ty.rs @@ -0,0 +1,13 @@ +// FIXME(lazy_type_alias): Improve this test. +//@ check-pass + +#![feature(auto_traits)] + +auto trait Marker {} + +struct Local; +type Alias = Local; + +impl Marker for Alias {} + +fn main() {} From 879a24d97efad88e1583db7cf4dac38cb2850a1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 10 Jun 2026 19:19:49 +0200 Subject: [PATCH 6/8] [UNAUDITED] Privacy: Look into unchecked free alias types --- compiler/rustc_privacy/src/lib.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 1cd1101db090d..1800092f512b9 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -214,15 +214,31 @@ where ); } } + ty::Alias( data @ ty::AliasTy { kind: kind @ (ty::Inherent { def_id } | ty::Free { def_id } | ty::Projection { def_id }), + args, .. }, ) => { + if let ty::Free { def_id } = kind + && !tcx.type_alias_is_lazy(def_id) + { + // FIXME: Stack overflows may surface by using `type_of`! + // However, we can't use `expand_free_alias_tys` since that would also + // "expand away" LTAs that are meant to be checked. + // We need intro a 2nd version that only expands unchecked free alias tys. + return tcx + .type_of(def_id) + .instantiate(tcx, args) + .skip_normalization() + .visit_with(self); + } + if self.def_id_visitor.skip_assoc_tys() { // Visitors searching for minimal visibility/reachability want to // conservatively approximate associated types like `Type::Alias` From a3e880f037734b83ce383d317561dfb963befe53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 10 Jun 2026 23:35:39 +0200 Subject: [PATCH 7/8] [UNAUDITED] Fix lint `missing_debug_implementations` --- compiler/rustc_lint/src/builtin.rs | 48 ++++++++++++++----- .../xyz-xyz-xyz-missing-dbg-impl.rs | 17 +++++++ 2 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 tests/ui/lazy-type-alias/xyz-xyz-xyz-missing-dbg-impl.rs diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 5e239ad6beeb3..c9999254c1252 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -699,23 +699,49 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations { return; } - let Some(debug) = cx.tcx.get_diagnostic_item(sym::Debug) else { return }; + let Some(debug_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Debug) else { return }; - let has_impl = cx + let ty = cx.tcx.type_of(item.owner_id); + + // FIXME: unaudited potential fast path + if cx .tcx .non_blanket_impls_for_ty( - debug, - cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip(), + debug_trait_def_id, + ty.instantiate_identity().skip_normalization(), ) .next() - .is_some(); - if !has_impl { - cx.emit_span_lint( - MISSING_DEBUG_IMPLEMENTATIONS, - item.span, - BuiltinMissingDebugImpl { tcx: cx.tcx, def_id: debug }, - ); + .is_some() + { + return; + } + + // FIXME: unaudited + + let (infcx, param_env) = cx.tcx.infer_ctxt().build_with_typing_env(cx.typing_env()); + let ty = ty + .instantiate(cx.tcx, infcx.fresh_args_for_item(item.span, item.owner_id.to_def_id())) + .skip_normalization(); + + let trait_ref = ty::TraitRef::new(cx.tcx, debug_trait_def_id, [ty]); + let obligation = traits::Obligation { + cause: traits::ObligationCause::dummy(), + param_env, + recursion_depth: 0, + predicate: trait_ref.upcast(cx.tcx), + }; + + // FIXME: `predicate_must_hold_modulo_regions` is too strict but ideally we would use that + // wouldn't we? Kinda bad that free alias types "throw off" PMHMR, no? + if infcx.predicate_may_hold(&obligation) { + return; } + + cx.emit_span_lint( + MISSING_DEBUG_IMPLEMENTATIONS, + item.span, + BuiltinMissingDebugImpl { tcx: cx.tcx, def_id: debug_trait_def_id }, + ); } } diff --git a/tests/ui/lazy-type-alias/xyz-xyz-xyz-missing-dbg-impl.rs b/tests/ui/lazy-type-alias/xyz-xyz-xyz-missing-dbg-impl.rs new file mode 100644 index 0000000000000..0709e49ec7bf3 --- /dev/null +++ b/tests/ui/lazy-type-alias/xyz-xyz-xyz-missing-dbg-impl.rs @@ -0,0 +1,17 @@ +// FIXME: Rewrite into proper test. +//@ check-pass + +#![deny(missing_debug_implementations)] +#![feature(lazy_type_alias)] + +pub struct Local; + +type Alias = Local; + +impl std::fmt::Debug for Alias { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Local") + } +} + +fn main() {} From 5c3dc858a1868f20f92a8c35793bf7abe305598a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 11 Jun 2026 16:16:44 +0200 Subject: [PATCH 8/8] [WIP] --- .../src/collect/predicates_of.rs | 19 ++++++++++++++++++- .../rustc_trait_selection/src/traits/wf.rs | 10 ++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index db22b57ad22a6..856089d7691f2 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -606,7 +606,24 @@ pub(super) fn explicit_predicates_of<'tcx>( predicates: { tcx.arena.alloc_from_iter(filtered_predicates) }, }; } - gather_explicit_predicates_of(tcx, def_id) + + let predicates = gather_explicit_predicates_of(tcx, def_id); + + // // FIXME: FIXME: FIXME: MAJOR HACK: SAD. + // if let DefKind::TyAlias = def_kind + // && !tcx.type_alias_is_lazy(def_id) + // { + // return GenericPredicates { + // parent: None, + // predicates: tcx.arena.alloc_from_iter( + // predicates.predicates.iter().copied().filter(|(clause, _)| { + // matches!(clause.kind().skip_binder(), ty::ClauseKind::ConstArgHasType(..)) + // }), + // ), + // }; + // } + + predicates } } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index c458a1cd15c3e..a1907402023d6 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -801,6 +801,16 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { // Simple cases that are WF if their type args are WF. } + // FIXME: HACK: this is very sad :( + // ty::Alias(ty::AliasTy { kind: ty::Free { def_id }, args, .. }) + // if !tcx.type_alias_is_lazy(def_id) => + // { + // // FIXME likely stackoverflow-prone but we can't `expand_free_alias_tys` + // // since that would "expand away" checked ones, too! + // let ty = tcx.type_of(def_id).instantiate(tcx, args).skip_normalization(); + // // FIXME: we're likely not wfcking args corresp. to unused params. + // return ty.visit_with(self); + // } ty::Alias(ty::AliasTy { kind: ty::Projection { def_id } | ty::Opaque { def_id } | ty::Free { def_id }, args,