From ce68cd3762fef8eb799b5b75ba07eb82597de44f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 16 Oct 2025 16:14:39 +0200 Subject: [PATCH] Reject relaxed bounds inside trait alias bounds --- compiler/rustc_ast_lowering/src/item.rs | 2 +- compiler/rustc_ast_lowering/src/lib.rs | 5 ++++ .../src/hir_ty_lowering/bounds.rs | 4 +-- tests/rustdoc-ui/doc-alias-assoc-const.rs | 2 -- tests/rustdoc-ui/doc-alias-assoc-const.stderr | 2 +- tests/ui/sized-hierarchy/trait-aliases.rs | 9 ------ .../effectively-empty-trait-object-type.rs | 20 +++++++++++++ ...effectively-empty-trait-object-type.stderr | 21 ++++++++++++++ tests/ui/traits/alias/empty.rs | 17 +++++++++++ tests/ui/traits/alias/maybe-bound.rs | 29 ------------------- tests/ui/traits/alias/only-maybe-bound.rs | 22 -------------- tests/ui/traits/alias/only-maybe-bound.stderr | 21 -------------- tests/ui/traits/alias/relaxed-bounds.rs | 19 ++++++++++++ tests/ui/traits/alias/relaxed-bounds.stderr | 24 +++++++++++++++ tests/ui/traits/wf-object/only-maybe-bound.rs | 7 ----- .../traits/wf-object/only-maybe-bound.stderr | 17 ----------- .../unsized/relaxed-bounds-invalid-places.rs | 3 ++ .../relaxed-bounds-invalid-places.stderr | 23 ++++++++++++--- 18 files changed, 131 insertions(+), 116 deletions(-) delete mode 100644 tests/ui/sized-hierarchy/trait-aliases.rs create mode 100644 tests/ui/traits/alias/effectively-empty-trait-object-type.rs create mode 100644 tests/ui/traits/alias/effectively-empty-trait-object-type.stderr create mode 100644 tests/ui/traits/alias/empty.rs delete mode 100644 tests/ui/traits/alias/maybe-bound.rs delete mode 100644 tests/ui/traits/alias/only-maybe-bound.rs delete mode 100644 tests/ui/traits/alias/only-maybe-bound.stderr create mode 100644 tests/ui/traits/alias/relaxed-bounds.rs create mode 100644 tests/ui/traits/alias/relaxed-bounds.stderr delete mode 100644 tests/ui/traits/wf-object/only-maybe-bound.rs delete mode 100644 tests/ui/traits/wf-object/only-maybe-bound.stderr diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 53351f91c46b..1a52ed02c404 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -426,7 +426,7 @@ impl<'hir> LoweringContext<'_, 'hir> { |this| { this.lower_param_bounds( bounds, - RelaxedBoundPolicy::Allowed, + RelaxedBoundPolicy::Forbidden(RelaxedBoundForbiddenReason::TraitAlias), ImplTraitContext::Disallowed(ImplTraitPosition::Bound), ) }, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 5968596dacce..aaca9f4547e9 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -296,6 +296,7 @@ enum RelaxedBoundPolicy<'a> { enum RelaxedBoundForbiddenReason { TraitObjectTy, SuperTrait, + TraitAlias, AssocTyBounds, LateBoundVarsInScope, } @@ -2132,6 +2133,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { gate("supertrait bounds", "traits"); return; } + RelaxedBoundForbiddenReason::TraitAlias => { + gate("trait alias bounds", "trait aliases"); + return; + } RelaxedBoundForbiddenReason::AssocTyBounds | RelaxedBoundForbiddenReason::LateBoundVarsInScope => {} }; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 7accab8df87c..b6c4ca3b9594 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -202,9 +202,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // where we are guaranteed to catch *all* bounds like in // `Self::lower_poly_trait_ref`. List of concrete issues: // FIXME(more_maybe_bounds): We don't call this for trait object tys, supertrait - // bounds or associated type bounds (ATB)! - // FIXME(trait_alias, #143122): We don't call it for the RHS. Arguably however, - // AST lowering should reject them outright. + // bounds, trait alias bounds, assoc type bounds (ATB)! let bounds = collect_relaxed_bounds(hir_bounds, self_ty_where_predicates); self.check_and_report_invalid_relaxed_bounds(bounds); } diff --git a/tests/rustdoc-ui/doc-alias-assoc-const.rs b/tests/rustdoc-ui/doc-alias-assoc-const.rs index d95324734be4..cbbc3fe4e21f 100644 --- a/tests/rustdoc-ui/doc-alias-assoc-const.rs +++ b/tests/rustdoc-ui/doc-alias-assoc-const.rs @@ -1,5 +1,3 @@ -#![feature(trait_alias)] - pub struct Foo; pub trait Bar { diff --git a/tests/rustdoc-ui/doc-alias-assoc-const.stderr b/tests/rustdoc-ui/doc-alias-assoc-const.stderr index cd3ad4ab393a..cc628c39400b 100644 --- a/tests/rustdoc-ui/doc-alias-assoc-const.stderr +++ b/tests/rustdoc-ui/doc-alias-assoc-const.stderr @@ -1,5 +1,5 @@ error: `#[doc(alias = "...")]` isn't allowed on associated constant in trait implementation block - --> $DIR/doc-alias-assoc-const.rs:10:11 + --> $DIR/doc-alias-assoc-const.rs:8:11 | LL | #[doc(alias = "CONST_BAZ")] | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/sized-hierarchy/trait-aliases.rs b/tests/ui/sized-hierarchy/trait-aliases.rs deleted file mode 100644 index ffec302adaa5..000000000000 --- a/tests/ui/sized-hierarchy/trait-aliases.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ check-pass -//@ compile-flags: --crate-type=lib -#![feature(trait_alias)] - -// Checks that `?Sized` in a trait alias doesn't trigger an ICE. - -use std::ops::{Index, IndexMut}; - -pub trait SlicePrereq = ?Sized + IndexMut>::Output>; diff --git a/tests/ui/traits/alias/effectively-empty-trait-object-type.rs b/tests/ui/traits/alias/effectively-empty-trait-object-type.rs new file mode 100644 index 000000000000..f7e8daa88e7c --- /dev/null +++ b/tests/ui/traits/alias/effectively-empty-trait-object-type.rs @@ -0,0 +1,20 @@ +// Test that we reject trait object types that effectively (i.e., after trait alias expansion) +// don't contain any bounds. + +#![feature(trait_alias)] + +trait Empty0 =; + +// Nest a couple of levels deep: +trait Empty1 = Empty0; +trait Empty2 = Empty1; + +// Straight list expansion: +type Type0 = dyn Empty2; //~ ERROR at least one trait is required for an object type [E0224] + +// Twice: +trait Empty3 = Empty2 + Empty2; + +type Type1 = dyn Empty3; //~ ERROR at least one trait is required for an object type [E0224] + +fn main() {} diff --git a/tests/ui/traits/alias/effectively-empty-trait-object-type.stderr b/tests/ui/traits/alias/effectively-empty-trait-object-type.stderr new file mode 100644 index 000000000000..dbef9c3b5b31 --- /dev/null +++ b/tests/ui/traits/alias/effectively-empty-trait-object-type.stderr @@ -0,0 +1,21 @@ +error[E0224]: at least one trait is required for an object type + --> $DIR/effectively-empty-trait-object-type.rs:13:14 + | +LL | trait Empty2 = Empty1; + | ------------ this alias does not contain a trait +... +LL | type Type0 = dyn Empty2; + | ^^^^^^^^^^ + +error[E0224]: at least one trait is required for an object type + --> $DIR/effectively-empty-trait-object-type.rs:18:14 + | +LL | trait Empty3 = Empty2 + Empty2; + | ------------ this alias does not contain a trait +LL | +LL | type Type1 = dyn Empty3; + | ^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0224`. diff --git a/tests/ui/traits/alias/empty.rs b/tests/ui/traits/alias/empty.rs new file mode 100644 index 000000000000..68d642f48539 --- /dev/null +++ b/tests/ui/traits/alias/empty.rs @@ -0,0 +1,17 @@ +// Ensure that there are straightforward ways to define "empty" / "trivial" / "unit" trait aliases +// which don't impose any constraints when used as a bound (since they expand to nothing). +//@ check-pass +#![feature(trait_alias)] + +trait Empty =; + +trait Trivial = where; + +trait Unit = where Self:; + +fn check() {} + +fn main() { + check::<()>(); // OK. "`(): Empty`" is trivially satisfied + check::(); // OK. `Empty` is truly empty and isn't implicitly bounded by `Sized`. +} diff --git a/tests/ui/traits/alias/maybe-bound.rs b/tests/ui/traits/alias/maybe-bound.rs deleted file mode 100644 index 9fdeb714d5e7..000000000000 --- a/tests/ui/traits/alias/maybe-bound.rs +++ /dev/null @@ -1,29 +0,0 @@ -//@ build-pass (FIXME(62277): could be check-pass?) - -// Test that `dyn ... + ?Sized + ...` resulting from the expansion of trait aliases is okay. - -#![feature(trait_alias)] - -trait Foo {} - -trait S = ?Sized; - -// Nest a couple of levels deep: -trait _0 = S; -trait _1 = _0; - -// Straight list expansion: -type _T0 = dyn _1 + Foo; - -// In second position: -type _T1 = dyn Foo + _1; - -// ... and with an auto trait: -type _T2 = dyn Foo + Send + _1; - -// Twice: -trait _2 = _1 + _1; - -type _T3 = dyn _2 + Foo; - -fn main() {} diff --git a/tests/ui/traits/alias/only-maybe-bound.rs b/tests/ui/traits/alias/only-maybe-bound.rs deleted file mode 100644 index e4abf314e0a9..000000000000 --- a/tests/ui/traits/alias/only-maybe-bound.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Test that `dyn ?Sized` (i.e., a trait object with only a maybe buond) is not allowed, when just -// `?Sized` results from trait alias expansion. - -#![feature(trait_alias)] - -trait S = ?Sized; - -// Nest a couple of levels deep: -trait _0 = S; -trait _1 = _0; - -// Straight list expansion: -type _T0 = dyn _1; -//~^ ERROR at least one trait is required for an object type [E0224] - -// Twice: -trait _2 = _1 + _1; - -type _T1 = dyn _2; -//~^ ERROR at least one trait is required for an object type [E0224] - -fn main() {} diff --git a/tests/ui/traits/alias/only-maybe-bound.stderr b/tests/ui/traits/alias/only-maybe-bound.stderr deleted file mode 100644 index 175ec8120ecc..000000000000 --- a/tests/ui/traits/alias/only-maybe-bound.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error[E0224]: at least one trait is required for an object type - --> $DIR/only-maybe-bound.rs:13:12 - | -LL | trait _1 = _0; - | -------- this alias does not contain a trait -... -LL | type _T0 = dyn _1; - | ^^^^^^ - -error[E0224]: at least one trait is required for an object type - --> $DIR/only-maybe-bound.rs:19:12 - | -LL | trait _2 = _1 + _1; - | -------- this alias does not contain a trait -LL | -LL | type _T1 = dyn _2; - | ^^^^^^ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0224`. diff --git a/tests/ui/traits/alias/relaxed-bounds.rs b/tests/ui/traits/alias/relaxed-bounds.rs new file mode 100644 index 000000000000..88e092c63162 --- /dev/null +++ b/tests/ui/traits/alias/relaxed-bounds.rs @@ -0,0 +1,19 @@ +#![feature(trait_alias)] + +// Ensure that relaxed bounds are not permitted in the `Self` bounds of trait aliases because trait +// aliases (like traits) aren't implicitly bounded by `Sized` so there's nothing to relax. + +trait Alias0 = ?Sized; //~ ERROR relaxed bounds are not permitted in trait alias bounds +trait Alias1 = where Self: ?Sized; //~ ERROR this relaxed bound is not permitted here + +trait Alias2 =; // OK +trait Alias3 = where T: ?Sized; // OK + +// Make sure that we don't permit "relaxing" trait aliases since we don't want to expand trait +// aliases during sized elaboration for simplicity as we'd need to handle relaxing arbitrary bounds +// (e.g., ones with modifiers, outlives-bounds, …) and where-clauses. + +trait SizedAlias = Sized; +fn take() {} //~ ERROR bound modifier `?` can only be applied to `Sized` + +fn main() {} diff --git a/tests/ui/traits/alias/relaxed-bounds.stderr b/tests/ui/traits/alias/relaxed-bounds.stderr new file mode 100644 index 000000000000..d7782b723a30 --- /dev/null +++ b/tests/ui/traits/alias/relaxed-bounds.stderr @@ -0,0 +1,24 @@ +error: relaxed bounds are not permitted in trait alias bounds + --> $DIR/relaxed-bounds.rs:6:16 + | +LL | trait Alias0 = ?Sized; + | ^^^^^^ + | + = note: trait aliases are not implicitly bounded by `Sized`, so there is nothing to relax + +error: this relaxed bound is not permitted here + --> $DIR/relaxed-bounds.rs:7:28 + | +LL | trait Alias1 = where Self: ?Sized; + | ^^^^^^ + | + = note: in this context, relaxed bounds are only allowed on type parameters defined on the closest item + +error: bound modifier `?` can only be applied to `Sized` + --> $DIR/relaxed-bounds.rs:17:12 + | +LL | fn take() {} + | ^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/traits/wf-object/only-maybe-bound.rs b/tests/ui/traits/wf-object/only-maybe-bound.rs deleted file mode 100644 index 96360e0331cd..000000000000 --- a/tests/ui/traits/wf-object/only-maybe-bound.rs +++ /dev/null @@ -1,7 +0,0 @@ -// Test that `dyn ?Sized` (i.e., a trait object with only a maybe buond) is not allowed. - -type _0 = dyn ?Sized; -//~^ ERROR at least one trait is required for an object type [E0224] -//~| ERROR relaxed bounds are not permitted in trait object types - -fn main() {} diff --git a/tests/ui/traits/wf-object/only-maybe-bound.stderr b/tests/ui/traits/wf-object/only-maybe-bound.stderr deleted file mode 100644 index 3f02d5a8a0c8..000000000000 --- a/tests/ui/traits/wf-object/only-maybe-bound.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: relaxed bounds are not permitted in trait object types - --> $DIR/only-maybe-bound.rs:3:15 - | -LL | type _0 = dyn ?Sized; - | ^^^^^^ - | - = note: trait object types are not implicitly bounded by `Sized`, so there is nothing to relax - -error[E0224]: at least one trait is required for an object type - --> $DIR/only-maybe-bound.rs:3:11 - | -LL | type _0 = dyn ?Sized; - | ^^^^^^^^^^ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0224`. diff --git a/tests/ui/unsized/relaxed-bounds-invalid-places.rs b/tests/ui/unsized/relaxed-bounds-invalid-places.rs index 4c1f242a01ce..5aa1dbb9a094 100644 --- a/tests/ui/unsized/relaxed-bounds-invalid-places.rs +++ b/tests/ui/unsized/relaxed-bounds-invalid-places.rs @@ -30,6 +30,9 @@ trait Tr: ?Sized {} //~ ERROR relaxed bounds are not permitted in supertrait bou // Test that relaxed `Sized` bounds are rejected in trait object types: +type O0 = dyn ?Sized; +//~^ ERROR relaxed bounds are not permitted in trait object types +//~| ERROR at least one trait is required for an object type [E0224] type O1 = dyn Tr + ?Sized; //~ ERROR relaxed bounds are not permitted in trait object types type O2 = dyn ?Sized + ?Sized + Tr; //~^ ERROR relaxed bounds are not permitted in trait object types diff --git a/tests/ui/unsized/relaxed-bounds-invalid-places.stderr b/tests/ui/unsized/relaxed-bounds-invalid-places.stderr index 34f5cebeeaf1..7f54d045a1ce 100644 --- a/tests/ui/unsized/relaxed-bounds-invalid-places.stderr +++ b/tests/ui/unsized/relaxed-bounds-invalid-places.stderr @@ -55,7 +55,15 @@ LL | trait Tr: ?Sized {} = note: traits are not implicitly bounded by `Sized`, so there is nothing to relax error: relaxed bounds are not permitted in trait object types - --> $DIR/relaxed-bounds-invalid-places.rs:33:20 + --> $DIR/relaxed-bounds-invalid-places.rs:33:15 + | +LL | type O0 = dyn ?Sized; + | ^^^^^^ + | + = note: trait object types are not implicitly bounded by `Sized`, so there is nothing to relax + +error: relaxed bounds are not permitted in trait object types + --> $DIR/relaxed-bounds-invalid-places.rs:36:20 | LL | type O1 = dyn Tr + ?Sized; | ^^^^^^ @@ -63,7 +71,7 @@ LL | type O1 = dyn Tr + ?Sized; = note: trait object types are not implicitly bounded by `Sized`, so there is nothing to relax error: relaxed bounds are not permitted in trait object types - --> $DIR/relaxed-bounds-invalid-places.rs:34:15 + --> $DIR/relaxed-bounds-invalid-places.rs:37:15 | LL | type O2 = dyn ?Sized + ?Sized + Tr; | ^^^^^^ @@ -71,7 +79,7 @@ LL | type O2 = dyn ?Sized + ?Sized + Tr; = note: trait object types are not implicitly bounded by `Sized`, so there is nothing to relax error: relaxed bounds are not permitted in trait object types - --> $DIR/relaxed-bounds-invalid-places.rs:34:24 + --> $DIR/relaxed-bounds-invalid-places.rs:37:24 | LL | type O2 = dyn ?Sized + ?Sized + Tr; | ^^^^^^ @@ -90,5 +98,12 @@ error: bound modifier `?` can only be applied to `Sized` LL | struct S5(*const T) where T: ?Trait<'static> + ?Sized; | ^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error[E0224]: at least one trait is required for an object type + --> $DIR/relaxed-bounds-invalid-places.rs:33:11 + | +LL | type O0 = dyn ?Sized; + | ^^^^^^^^^^ +error: aborting due to 14 previous errors + +For more information about this error, try `rustc --explain E0224`.