Skip to content

Commit 64aea00

Browse files
authored
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 #135841 (review) — 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 #135841). This PR rectifies all of this. I cratered this back in 2025-01-10 with (allegedly) no regressions found ([report](#135331 (comment)), [its analysis](#135331 (comment))). [However a contributor manually found two occurrences](#135229 (comment)) of `TraitRef<AssocTy: ?Sized>` in small hobby projects (presumably via GH code search). I immediately sent downstream PRs: Gui-Yom/turbo-metrics#14, ireina7/summon#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 #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).
2 parents 53af067 + 788fb08 commit 64aea00

File tree

6 files changed

+37
-19
lines changed

6 files changed

+37
-19
lines changed

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ enum RelaxedBoundPolicy<'a> {
296296
enum RelaxedBoundForbiddenReason {
297297
TraitObjectTy,
298298
SuperTrait,
299+
AssocTyBounds,
299300
LateBoundVarsInScope,
300301
}
301302

@@ -1109,9 +1110,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
11091110
&*self.arena.alloc(self.ty(constraint.span, hir::TyKind::Err(guar)));
11101111
hir::AssocItemConstraintKind::Equality { term: err_ty.into() }
11111112
} else {
1112-
// FIXME(#135229): These should be forbidden!
1113-
let bounds =
1114-
self.lower_param_bounds(bounds, RelaxedBoundPolicy::Allowed, itctx);
1113+
let bounds = self.lower_param_bounds(
1114+
bounds,
1115+
RelaxedBoundPolicy::Forbidden(RelaxedBoundForbiddenReason::AssocTyBounds),
1116+
itctx,
1117+
);
11151118
hir::AssocItemConstraintKind::Bound { bounds }
11161119
}
11171120
}
@@ -2124,7 +2127,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
21242127
diag.emit();
21252128
return;
21262129
}
2127-
RelaxedBoundForbiddenReason::LateBoundVarsInScope => {}
2130+
RelaxedBoundForbiddenReason::AssocTyBounds
2131+
| RelaxedBoundForbiddenReason::LateBoundVarsInScope => {}
21282132
};
21292133
}
21302134
}

compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,12 +199,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
199199
// However, this can easily get out of sync! Ideally, we would perform this step
200200
// where we are guaranteed to catch *all* bounds like in
201201
// `Self::lower_poly_trait_ref`. List of concrete issues:
202-
// FIXME(more_maybe_bounds): We don't call this for e.g., trait object tys or
203-
// supertrait bounds!
202+
// FIXME(more_maybe_bounds): We don't call this for trait object tys, supertrait
203+
// bounds or associated type bounds (ATB)!
204204
// FIXME(trait_alias, #143122): We don't call it for the RHS. Arguably however,
205-
// AST lowering should reject them outright.
206-
// FIXME(associated_type_bounds): We don't call this for them. However, AST
207-
// lowering should reject them outright (#135229).
205+
// AST lowering should reject them outright.
208206
let bounds = collect_relaxed_bounds(hir_bounds, self_ty_where_predicates);
209207
self.check_and_report_invalid_relaxed_bounds(bounds);
210208
}

tests/ui/trait-bounds/more_maybe_bounds.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// FIXME(more_maybe_bounds): Even under `more_maybe_bounds` / `-Zexperimental-default-bounds`,
22
// trying to relax non-default bounds should still be an error in all contexts! As you can see
3-
// there are places like supertrait bounds and trait object types where we currently don't perform
4-
// this check.
3+
// there are places like supertrait bounds, trait object types or associated type bounds (ATB)
4+
// where we currently don't perform this check.
55
#![feature(auto_traits, more_maybe_bounds, negative_impls)]
66

77
trait Trait1 {}
@@ -13,11 +13,15 @@ trait Trait4 where Self: Trait1 {}
1313

1414
// FIXME: `?Trait2` should be rejected, `Trait2` isn't marked `#[lang = "default_traitN"]`.
1515
fn foo(_: Box<(dyn Trait3 + ?Trait2)>) {}
16+
1617
fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {}
1718
//~^ ERROR bound modifier `?` can only be applied to default traits like `Sized`
1819
//~| ERROR bound modifier `?` can only be applied to default traits like `Sized`
1920
//~| ERROR bound modifier `?` can only be applied to default traits like `Sized`
2021

22+
// FIXME: `?Trait1` should be rejected, `Trait1` isn't marked `#[lang = "default_traitN"]`.
23+
fn baz<T>() where T: Iterator<Item: ?Trait1> {}
24+
2125
struct S;
2226
impl !Trait2 for S {}
2327
impl Trait1 for S {}

tests/ui/trait-bounds/more_maybe_bounds.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
error: bound modifier `?` can only be applied to default traits like `Sized`
2-
--> $DIR/more_maybe_bounds.rs:16:20
2+
--> $DIR/more_maybe_bounds.rs:17:20
33
|
44
LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {}
55
| ^^^^^^^
66

77
error: bound modifier `?` can only be applied to default traits like `Sized`
8-
--> $DIR/more_maybe_bounds.rs:16:30
8+
--> $DIR/more_maybe_bounds.rs:17:30
99
|
1010
LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {}
1111
| ^^^^^^^
1212

1313
error: bound modifier `?` can only be applied to default traits like `Sized`
14-
--> $DIR/more_maybe_bounds.rs:16:40
14+
--> $DIR/more_maybe_bounds.rs:17:40
1515
|
1616
LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {}
1717
| ^^^^^^^

tests/ui/unsized/relaxed-bounds-invalid-places.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ impl<T> S1<T> {
2222
fn f() where T: ?Sized {} //~ ERROR this relaxed bound is not permitted here
2323
}
2424

25+
// Test associated type bounds (ATB).
26+
// issue: <https://github.com/rust-lang/rust/issues/135229>
27+
struct S6<T>(T) where T: Iterator<Item: ?Sized>; //~ ERROR this relaxed bound is not permitted here
28+
2529
trait Tr: ?Sized {} //~ ERROR relaxed bounds are not permitted in supertrait bounds
2630

2731
// Test that relaxed `Sized` bounds are rejected in trait object types:

tests/ui/unsized/relaxed-bounds-invalid-places.stderr

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,36 @@ LL | fn f() where T: ?Sized {}
3838
|
3939
= note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item
4040

41+
error: this relaxed bound is not permitted here
42+
--> $DIR/relaxed-bounds-invalid-places.rs:27:41
43+
|
44+
LL | struct S6<T>(T) where T: Iterator<Item: ?Sized>;
45+
| ^^^^^^
46+
|
47+
= note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item
48+
4149
error: relaxed bounds are not permitted in supertrait bounds
42-
--> $DIR/relaxed-bounds-invalid-places.rs:25:11
50+
--> $DIR/relaxed-bounds-invalid-places.rs:29:11
4351
|
4452
LL | trait Tr: ?Sized {}
4553
| ^^^^^^
4654
|
4755
= note: traits are `?Sized` by default
4856

4957
error: relaxed bounds are not permitted in trait object types
50-
--> $DIR/relaxed-bounds-invalid-places.rs:29:20
58+
--> $DIR/relaxed-bounds-invalid-places.rs:33:20
5159
|
5260
LL | type O1 = dyn Tr + ?Sized;
5361
| ^^^^^^
5462

5563
error: relaxed bounds are not permitted in trait object types
56-
--> $DIR/relaxed-bounds-invalid-places.rs:30:15
64+
--> $DIR/relaxed-bounds-invalid-places.rs:34:15
5765
|
5866
LL | type O2 = dyn ?Sized + ?Sized + Tr;
5967
| ^^^^^^
6068

6169
error: relaxed bounds are not permitted in trait object types
62-
--> $DIR/relaxed-bounds-invalid-places.rs:30:24
70+
--> $DIR/relaxed-bounds-invalid-places.rs:34:24
6371
|
6472
LL | type O2 = dyn ?Sized + ?Sized + Tr;
6573
| ^^^^^^
@@ -76,5 +84,5 @@ error: bound modifier `?` can only be applied to `Sized`
7684
LL | struct S5<T>(*const T) where T: ?Trait<'static> + ?Sized;
7785
| ^^^^^^^^^^^^^^^
7886

79-
error: aborting due to 11 previous errors
87+
error: aborting due to 12 previous errors
8088

0 commit comments

Comments
 (0)