Skip to content

Make WhereClause optional in generics - #160680

Open
joshtriplett wants to merge 3 commits into
rust-lang:mainfrom
joshtriplett:no-where
Open

Make WhereClause optional in generics#160680
joshtriplett wants to merge 3 commits into
rust-lang:mainfrom
joshtriplett:no-where

Conversation

@joshtriplett

@joshtriplett joshtriplett commented Aug 7, 2026

Copy link
Copy Markdown
Member

Many items don't have generics, and many generics don't have where
clauses. WhereClause is 24 bytes: a bool, a ThinVec of predicates,
and a Span. Reduce that to 8 bytes by making it an Option wrapper.

The has_where_token case gets encoded in the Option: a
WhereClause(None) has no where token, while a
WhereClause(Some(...)) with a non-empty span has one (even if the
predicate list is empty, like where {}).

This reduces the size of several key AST structures:

  • Impl goes from 80 bytes to 64, making it one cache line.
  • Item goes from 144 bytes to 128, making it two cache lines and
    bringing it below the threshold where copies don't use memcpy.
  • ItemKind goes from 88 bytes to 72, helping everything it's embedded in.
  • Fn goes from 192 bytes to 176.
  • Generics goes from 40 bytes to 24.

rustc_builtin_macros now collects predicates and creates a WhereClause
at the end.

rustc_ast_lowering now needs another way to get the span for inserting a
where clause if there isn't already one. Add that as a parameter to
lower_generics, and pass it in from the various callers, determined
from other spans we already have. This requires a little care, but
allows us to avoid an allocation for common cases of items that don't
have where clauses.

As an exception, we do capture the insertion span for Impl block
where clauses, because reconstructing those hits a corner case with
spans partially coming coming from macros; in that case, the logic to
find the def_span needs the where-clause span, so we ensure we have it
in that case. We also record an insertion span for synthetic derives,
pointing to the original structure definition.

Relevant -Zinput-stats diff for compiling the huge aws-sdk-ec2 crate:

-ast-stats Item              14_353_632 ( 4.4%)        99_678           144
-ast-stats - ExternCrate              144 ( 0.0%)             1
-ast-stats - MacroDef                 144 ( 0.0%)             1
-ast-stats - Trait                    576 ( 0.0%)             4
-ast-stats - TyAlias               12_960 ( 0.0%)            90
-ast-stats - Const                114_768 ( 0.0%)           797
-ast-stats - Enum                 180_432 ( 0.1%)         1_253
-ast-stats - Static               453_312 ( 0.1%)         3_148
-ast-stats - Fn                   976_320 ( 0.3%)         6_780
-ast-stats - Mod                1_241_136 ( 0.4%)         8_619
-ast-stats - Struct             1_351_152 ( 0.4%)         9_383
-ast-stats - Use                1_488_672 ( 0.5%)        10_338
-ast-stats - Impl               8_534_016 ( 2.6%)        59_264
+ast-stats Item              12_758_784 ( 4.0%)        99_678           128
+ast-stats - ExternCrate              128 ( 0.0%)             1
+ast-stats - MacroDef                 128 ( 0.0%)             1
+ast-stats - Trait                    512 ( 0.0%)             4
+ast-stats - TyAlias               11_520 ( 0.0%)            90
+ast-stats - Const                102_016 ( 0.0%)           797
+ast-stats - Enum                 160_384 ( 0.0%)         1_253
+ast-stats - Static               402_944 ( 0.1%)         3_148
+ast-stats - Fn                   867_840 ( 0.3%)         6_780
+ast-stats - Mod                1_103_232 ( 0.3%)         8_619
+ast-stats - Struct             1_201_024 ( 0.4%)         9_383
+ast-stats - Use                1_323_264 ( 0.4%)        10_338
+ast-stats - Impl               7_585_792 ( 2.4%)        59_264
[...]
-ast-stats Total             322_653_552             7_596_184
+ast-stats Total             321_058_704             7_596_184
  • I did not use an LLM to create a change in this PR.
  • I used an LLM to create a change in this PR, and I have explained below how it was used.

r? nnethercote

@rustbot

rustbot commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

The parser was modified, potentially altering the grammar of (stable) Rust
which would be a breaking change.

cc @fmease

Changes to the size of AST and/or HIR nodes.

cc @nnethercote

clippy is developed in its own repository. If possible, consider making this change to rust-lang/rust-clippy instead.

cc @rust-lang/clippy

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-clippy Relevant to the Clippy team. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Aug 7, 2026
Many items don't have generics, and many generics don't have where
clauses. `WhereClause` is 24 bytes: a `bool`, a `ThinVec` of predicates,
and a `Span`. Reduce that to 8 bytes by making it an `Option` wrapper.

The `has_where_token` case gets encoded in the `Option`: a
`WhereClause(None)` has no `where` token, while a
`WhereClause(Some(...))` with a non-empty span has one (even if the
predicate list is empty, like `where {}`).

This reduces the size of several key AST structures:
- `Impl` goes from 80 bytes to 64, making it one cache line.
- `Item` goes from 144 bytes to 128, making it two cache lines and
  bringing it below the threshold where copies don't use `memcpy`.
- `ItemKind` goes from 88 bytes to 72, tantalizingly close to 64.
- `Fn` goes from 192 bytes to 176.
- `Generics` goes from 40 bytes to 24.

rustc_builtin_macros now collects predicates and creates a `WhereClause`
at the end.

rustc_ast_lowering now needs another way to get the span for inserting a
`where` clause if there isn't already one. Add that as a parameter to
`lower_generics`, and pass it in from the various callers, determined
from other spans we already have. This requires a little care, but
allows us to avoid an allocation for common cases of items that don't
have `where` clauses.

As an exception, we do capture the insertion span for `Impl` block
`where` clauses, because reconstructing those hits a corner case with
spans partially coming coming from macros; in that case, the logic to
find the `def_span` needs the where-clause span, so we ensure we have it
in that case. We also record an insertion span for synthetic derives,
pointing to the original structure definition.

Relevant `-Zinput-stats` diff for compiling the huge `aws-sdk-ec2` crate:
```diff
-ast-stats Item              14_353_632 ( 4.4%)        99_678           144
-ast-stats - ExternCrate              144 ( 0.0%)             1
-ast-stats - MacroDef                 144 ( 0.0%)             1
-ast-stats - Trait                    576 ( 0.0%)             4
-ast-stats - TyAlias               12_960 ( 0.0%)            90
-ast-stats - Const                114_768 ( 0.0%)           797
-ast-stats - Enum                 180_432 ( 0.1%)         1_253
-ast-stats - Static               453_312 ( 0.1%)         3_148
-ast-stats - Fn                   976_320 ( 0.3%)         6_780
-ast-stats - Mod                1_241_136 ( 0.4%)         8_619
-ast-stats - Struct             1_351_152 ( 0.4%)         9_383
-ast-stats - Use                1_488_672 ( 0.5%)        10_338
-ast-stats - Impl               8_534_016 ( 2.6%)        59_264
+ast-stats Item              12_758_784 ( 4.0%)        99_678           128
+ast-stats - ExternCrate              128 ( 0.0%)             1
+ast-stats - MacroDef                 128 ( 0.0%)             1
+ast-stats - Trait                    512 ( 0.0%)             4
+ast-stats - TyAlias               11_520 ( 0.0%)            90
+ast-stats - Const                102_016 ( 0.0%)           797
+ast-stats - Enum                 160_384 ( 0.0%)         1_253
+ast-stats - Static               402_944 ( 0.1%)         3_148
+ast-stats - Fn                   867_840 ( 0.3%)         6_780
+ast-stats - Mod                1_103_232 ( 0.3%)         8_619
+ast-stats - Struct             1_201_024 ( 0.4%)         9_383
+ast-stats - Use                1_323_264 ( 0.4%)        10_338
+ast-stats - Impl               7_585_792 ( 2.4%)        59_264
[...]
-ast-stats Total             322_653_552             7_596_184
+ast-stats Total             321_058_704             7_596_184
```
@joshtriplett
joshtriplett force-pushed the no-where branch 2 times, most recently from 85f566c to 88efab5 Compare August 7, 2026 09:19
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rustbot

rustbot commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

rustfmt is developed in its own repository. If possible, consider making this change to rust-lang/rustfmt instead.

cc @rust-lang/rustfmt

@rustbot rustbot added the T-rustfmt Relevant to the rustfmt team, which will review and decide on the PR/issue. label Aug 7, 2026
@joshtriplett

Copy link
Copy Markdown
Member Author

I've now fixed up clippy and rustfmt, which needed updating for the new WhereClause semantics.

@joshtriplett

Copy link
Copy Markdown
Member Author

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Aug 7, 2026
@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Aug 7, 2026
Make `WhereClause` optional in generics
@rust-bors

rust-bors Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: 34031bf (34031bf7e3e4ecb65e0942bfbbcbc519b6891235)
Base parent: ae45457 (ae45457594a670c59cd4d5591eaa243d9a3d44d5)

@rust-timer

This comment has been minimized.

@nnethercote

Copy link
Copy Markdown
Contributor

I'll review later, one comment on the description:

ItemKind goes from 88 bytes to 72, tantalizingly close to 64.

ItemKind's size alone doesn't really matter because it's always embedded in an Item. But everything else you described is great.

@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (34031bf): comparison URL.

Overall result: ❌✅ regressions and improvements - please read:

Benchmarking means the PR may be perf-sensitive. It's automatically marked not fit for rolling up. Overriding is possible but disadvised: it risks changing compiler perf.

Next, please: If you can, justify the regressions found in this try perf run in writing along with @rustbot label: +perf-regression-triaged. If not, fix the regressions and do another perf run. Neutral or positive results will clear the label automatically.

@bors rollup=never rustc-perf
@rustbot label: -S-waiting-on-perf +perf-regression

Instruction count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
0.3% [0.3%, 0.4%] 4
Improvements ✅
(primary)
-0.2% [-0.2%, -0.2%] 3
Improvements ✅
(secondary)
-0.3% [-0.5%, -0.2%] 8
All ❌✅ (primary) -0.2% [-0.2%, -0.2%] 3

Max RSS (memory usage)

Results (primary 0.1%, secondary -0.7%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
1.1% [0.8%, 1.7%] 6
Regressions ❌
(secondary)
0.8% [0.4%, 2.1%] 8
Improvements ✅
(primary)
-3.1% [-4.0%, -2.2%] 2
Improvements ✅
(secondary)
-3.2% [-11.9%, -0.5%] 5
All ❌✅ (primary) 0.1% [-4.0%, 1.7%] 8

Cycles

Results (primary 3.0%, secondary 0.2%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
3.0% [2.9%, 3.1%] 2
Regressions ❌
(secondary)
0.9% [0.4%, 1.5%] 13
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-0.9% [-2.2%, -0.5%] 9
All ❌✅ (primary) 3.0% [2.9%, 3.1%] 2

Binary size

This perf run didn't have relevant results for this metric.

Bootstrap: 461.168s -> 459.151s (-0.44%)
Artifact size: 398.56 MiB -> 398.63 MiB (0.02%)

@rustbot rustbot added perf-regression Performance regression. and removed S-waiting-on-perf Status: Waiting on a perf run to be completed. labels Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

perf-regression Performance regression. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-clippy Relevant to the Clippy team. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-rustfmt Relevant to the rustfmt team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants