diff options
| author | Stuart Cook <Zalathar@users.noreply.github.com> | 2025-08-11 18:22:31 +1000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-11 18:22:31 +1000 |
| commit | 64aea0027aca53b30dff64252d858a30995c6e14 (patch) | |
| tree | 6025bc98021076c2cd1d88f6ae0ce589ba4a80ae | |
| parent | 53af067bb0b4edf9b5394e5f9b60942974b9fbc2 (diff) | |
| parent | 788fb08bdb375eb811d682b4f24566d0aad1baca (diff) | |
| download | rust-64aea0027aca53b30dff64252d858a30995c6e14.tar.gz rust-64aea0027aca53b30dff64252d858a30995c6e14.zip | |
Rollup merge of #135331 - fmease:ban-assoc-ty-unbounds, r=lcnr
Reject relaxed bounds inside associated type bounds (ATB) **Reject** relaxed bounds — most notably `?Sized` — inside associated type bounds `TraitRef<AssocTy: …>`. This was previously accepted without warning despite being incorrect: ATBs are *not* a place where we perform *sized elaboration*, meaning `TraitRef<AssocTy: …>` does *not* elaborate to `TraitRef<AssocTy: Sized + …>` if `…` doesn't contain `?Sized`. Therefore `?Sized` is meaningless. In no other (stable) place do we (intentionally) allow relaxed bounds where we don't also perform sized elab, this is highly inconsistent and confusing! Another point of comparison: For the desugared `$SelfTy: TraitRef, $SelfTy::AssocTy: …` we don't do sized elab either (and thus also don't allow relaxed bounds). Moreover — as I've alluded to back in https://github.com/rust-lang/rust/pull/135841#pullrequestreview-2619462717 — some later validation steps only happen during sized elaboration during HIR ty lowering[^1]. Namely, rejecting duplicates (e.g., `?Trait + ?Trait`) and ensuring that `Trait` in `?Trait` is equal to `Sized`[^2]. As you can probably guess, on stable/master we don't run these checks for ATBs (so we allow even more nonsensical bounds like `Iterator<Item: ?Copy>` despite T-types's ruling established in the FCP'ed rust-lang/rust#135841). This PR rectifies all of this. I cratered this back in 2025-01-10 with (allegedly) no regressions found ([report](https://github.com/rust-lang/rust/pull/135331#issuecomment-2585330783), [its analysis](https://github.com/rust-lang/rust/pull/135331#issuecomment-2585356422)). [However a contributor manually found two occurrences](https://github.com/rust-lang/rust/issues/135229#issuecomment-2581832852) of `TraitRef<AssocTy: ?Sized>` in small hobby projects (presumably via GH code search). I immediately sent downstream PRs: https://github.com/Gui-Yom/turbo-metrics/pull/14, https://github.com/ireina7/summon/pull/1 (however, the owners have showed no reaction so far). I'm leaning towards banning these forms **without a FCW** because a FCW isn't worth the maintenance cost[^3]. Note that associated type bounds were stabilized in 1.79.0 (released 2024-06-13 which is 13 months ago), so the proliferation of ATBs shouldn't be that high yet. If you think we should do another crater run since the last one was 6 months ago, I'm fine with that. Fixes rust-lang/rust#135229. [^1]: I consider this a flaw in the implementation and [I've already added a huge FIXME](https://github.com/rust-lang/rust/blob/82a02aefe07092c737c852daccebf49ca25507e3/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs#L195-L207). [^2]: To be more precise, if the internal flag `-Zexperimental-default-bounds` is provided other "default traits" (needs internal feature `lang_items`) are permitted as well (cc closely related internal feature: `more_maybe_bounds`). [^3]: Having to track this and adding an entire lint whose remnants would remain in the code base forever (we never *fully* remove lints).
| -rw-r--r-- | compiler/rustc_ast_lowering/src/lib.rs | 12 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs | 8 | ||||
| -rw-r--r-- | tests/ui/trait-bounds/more_maybe_bounds.rs | 8 | ||||
| -rw-r--r-- | tests/ui/trait-bounds/more_maybe_bounds.stderr | 6 | ||||
| -rw-r--r-- | tests/ui/unsized/relaxed-bounds-invalid-places.rs | 4 | ||||
| -rw-r--r-- | tests/ui/unsized/relaxed-bounds-invalid-places.stderr | 18 |
6 files changed, 37 insertions, 19 deletions
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 64c1b53f633..465d9dc82bc 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, + AssocTyBounds, LateBoundVarsInScope, } @@ -1109,9 +1110,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &*self.arena.alloc(self.ty(constraint.span, hir::TyKind::Err(guar))); hir::AssocItemConstraintKind::Equality { term: err_ty.into() } } else { - // FIXME(#135229): These should be forbidden! - let bounds = - self.lower_param_bounds(bounds, RelaxedBoundPolicy::Allowed, itctx); + let bounds = self.lower_param_bounds( + bounds, + RelaxedBoundPolicy::Forbidden(RelaxedBoundForbiddenReason::AssocTyBounds), + itctx, + ); hir::AssocItemConstraintKind::Bound { bounds } } } @@ -2124,7 +2127,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { diag.emit(); return; } - RelaxedBoundForbiddenReason::LateBoundVarsInScope => {} + 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 386e1091ac4..d14aef8ace4 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -199,12 +199,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // However, this can easily get out of sync! Ideally, we would perform this step // 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 e.g., trait object tys or - // supertrait bounds! + // 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. - // FIXME(associated_type_bounds): We don't call this for them. However, AST - // lowering should reject them outright (#135229). + // AST lowering should reject them outright. let bounds = collect_relaxed_bounds(hir_bounds, self_ty_where_predicates); self.check_and_report_invalid_relaxed_bounds(bounds); } diff --git a/tests/ui/trait-bounds/more_maybe_bounds.rs b/tests/ui/trait-bounds/more_maybe_bounds.rs index 47348b0a0dd..d367dd5b299 100644 --- a/tests/ui/trait-bounds/more_maybe_bounds.rs +++ b/tests/ui/trait-bounds/more_maybe_bounds.rs @@ -1,7 +1,7 @@ // FIXME(more_maybe_bounds): Even under `more_maybe_bounds` / `-Zexperimental-default-bounds`, // trying to relax non-default bounds should still be an error in all contexts! As you can see -// there are places like supertrait bounds and trait object types where we currently don't perform -// this check. +// there are places like supertrait bounds, trait object types or associated type bounds (ATB) +// where we currently don't perform this check. #![feature(auto_traits, more_maybe_bounds, negative_impls)] trait Trait1 {} @@ -13,11 +13,15 @@ trait Trait4 where Self: Trait1 {} // FIXME: `?Trait2` should be rejected, `Trait2` isn't marked `#[lang = "default_traitN"]`. fn foo(_: Box<(dyn Trait3 + ?Trait2)>) {} + fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {} //~^ ERROR bound modifier `?` can only be applied to default traits like `Sized` //~| ERROR bound modifier `?` can only be applied to default traits like `Sized` //~| ERROR bound modifier `?` can only be applied to default traits like `Sized` +// FIXME: `?Trait1` should be rejected, `Trait1` isn't marked `#[lang = "default_traitN"]`. +fn baz<T>() where T: Iterator<Item: ?Trait1> {} + struct S; impl !Trait2 for S {} impl Trait1 for S {} diff --git a/tests/ui/trait-bounds/more_maybe_bounds.stderr b/tests/ui/trait-bounds/more_maybe_bounds.stderr index 09c9fc31165..8dd83fc7728 100644 --- a/tests/ui/trait-bounds/more_maybe_bounds.stderr +++ b/tests/ui/trait-bounds/more_maybe_bounds.stderr @@ -1,17 +1,17 @@ error: bound modifier `?` can only be applied to default traits like `Sized` - --> $DIR/more_maybe_bounds.rs:16:20 + --> $DIR/more_maybe_bounds.rs:17:20 | LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {} | ^^^^^^^ error: bound modifier `?` can only be applied to default traits like `Sized` - --> $DIR/more_maybe_bounds.rs:16:30 + --> $DIR/more_maybe_bounds.rs:17:30 | LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {} | ^^^^^^^ error: bound modifier `?` can only be applied to default traits like `Sized` - --> $DIR/more_maybe_bounds.rs:16:40 + --> $DIR/more_maybe_bounds.rs:17:40 | LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {} | ^^^^^^^ diff --git a/tests/ui/unsized/relaxed-bounds-invalid-places.rs b/tests/ui/unsized/relaxed-bounds-invalid-places.rs index b8eda1e7786..4c1f242a01c 100644 --- a/tests/ui/unsized/relaxed-bounds-invalid-places.rs +++ b/tests/ui/unsized/relaxed-bounds-invalid-places.rs @@ -22,6 +22,10 @@ impl<T> S1<T> { fn f() where T: ?Sized {} //~ ERROR this relaxed bound is not permitted here } +// Test associated type bounds (ATB). +// issue: <https://github.com/rust-lang/rust/issues/135229> +struct S6<T>(T) where T: Iterator<Item: ?Sized>; //~ ERROR this relaxed bound is not permitted here + trait Tr: ?Sized {} //~ ERROR relaxed bounds are not permitted in supertrait bounds // Test that relaxed `Sized` bounds are rejected 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 30285d62693..d3f0535e2f0 100644 --- a/tests/ui/unsized/relaxed-bounds-invalid-places.stderr +++ b/tests/ui/unsized/relaxed-bounds-invalid-places.stderr @@ -38,8 +38,16 @@ LL | fn f() where T: ?Sized {} | = note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item +error: this relaxed bound is not permitted here + --> $DIR/relaxed-bounds-invalid-places.rs:27:41 + | +LL | struct S6<T>(T) where T: Iterator<Item: ?Sized>; + | ^^^^^^ + | + = note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item + error: relaxed bounds are not permitted in supertrait bounds - --> $DIR/relaxed-bounds-invalid-places.rs:25:11 + --> $DIR/relaxed-bounds-invalid-places.rs:29:11 | LL | trait Tr: ?Sized {} | ^^^^^^ @@ -47,19 +55,19 @@ LL | trait Tr: ?Sized {} = note: traits are `?Sized` by default error: relaxed bounds are not permitted in trait object types - --> $DIR/relaxed-bounds-invalid-places.rs:29:20 + --> $DIR/relaxed-bounds-invalid-places.rs:33:20 | LL | type O1 = dyn Tr + ?Sized; | ^^^^^^ error: relaxed bounds are not permitted in trait object types - --> $DIR/relaxed-bounds-invalid-places.rs:30:15 + --> $DIR/relaxed-bounds-invalid-places.rs:34:15 | LL | type O2 = dyn ?Sized + ?Sized + Tr; | ^^^^^^ error: relaxed bounds are not permitted in trait object types - --> $DIR/relaxed-bounds-invalid-places.rs:30:24 + --> $DIR/relaxed-bounds-invalid-places.rs:34:24 | LL | type O2 = dyn ?Sized + ?Sized + Tr; | ^^^^^^ @@ -76,5 +84,5 @@ error: bound modifier `?` can only be applied to `Sized` LL | struct S5<T>(*const T) where T: ?Trait<'static> + ?Sized; | ^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors |
