Commit 71bfe3e
committed
Initial implementation of bounded intertrait casting (RFC rust-lang/rfcs#3952)
Tracking issue: TBD
r? @ghost (draft)
## Summary
This PR implements the compiler- and library-side plumbing for the
**bounded intertrait casting** proposal in rust-lang/rfcs#3952. It adds
a mechanism for casting between `dyn Trait` objects that share an
explicitly-declared common root supertrait, resolved at runtime in
`O(1)` via a per-root metadata table — no `'static` bound, no `TypeId`,
and no global registry.
Stabilization is not proposed here; everything is gated behind
`#![feature(trait_cast)]` and the new items are `#[unstable]`. The
feature is large (~16k LoC across ~200 files) and intentionally landed
as one commit so the graph/layout/augmentation passes stay coherent;
I'd like reviewer guidance on whether to split before further review,
and where the natural seams are.
## Surface
```rust
#![feature(trait_cast)]
use core::marker::TraitMetadataTable;
trait Animal: TraitMetadataTable<dyn Animal> {} // declares `Animal` as a cast root
trait Dog: Animal { fn bark(&self); }
fn maybe_bark(a: &dyn Animal) {
if let Ok(d) = core::cast!(in dyn Animal, a => dyn Dog) {
d.bark();
}
}
```
A trait becomes a **cast root** by naming `TraitMetadataTable<dyn Self>`
as a supertrait. Every subtrait of a root inherits the
`TraitMetadataTable<dyn Root>` bound and is eligible as a cast target
within that root's graph. `core::cast!`, `core::try_cast!`, and
`core::unchecked_cast!` macros (in a new `core::trait_cast` module)
dispatch through the `TraitCast<I, U>` trait implemented for `&T`,
`&mut T`, `Box<T>`, `Rc<T>`, and `Arc<T>`.
Runtime cost per cast: two loads and a branch against the table for
the root's graph.
## Library additions (`core`/`alloc`)
- `core::marker::TraitMetadataTable<SuperTrait>` — the marker/lang-item
that declares a cast root; blanket impl for all `Sized` types (the
actual root-supertrait obligation is enforced by the supertrait
relationship itself, not the where-clauses, to break a cycle through
`Unsize`).
- `core::trait_cast` — `TraitCast`/`TraitCastError` and the `cast!` /
`try_cast!` / `unchecked_cast!` macros.
- `alloc::{boxed, rc, sync}` — owned-cast impls.
- New intrinsics in `core::intrinsics`:
- `trait_metadata_index<SuperTrait, Trait>() -> (&'static u8, usize)`
- `trait_metadata_table<SuperTrait, ConcreteType>() -> (&'static u8, NonNull<Option<NonNull<()>>>)`
- `trait_metadata_table_len<SuperTrait>() -> usize`
- `trait_cast_is_lifetime_erasure_safe<SuperTrait, TargetTrait>() -> bool`
The `&'static u8` returned alongside each index/table pointer is a
per-global-crate sentinel used to detect the `ForeignTraitGraph` case
when two independently-built artifacts are linked into one binary.
## Compiler additions
**New passes / modules** (all under `rustc_monomorphize` unless noted):
- `trait_graph.rs` — per-root `TraitGraph` built from gathered
`trait_metadata_index` / `trait_metadata_table` requests.
- `table_layout.rs` — assigns slots for `(sub_trait, outlives_class)`
pairs with condensation (`BitMatrix` row-grouping) to collapse classes
admitting identical impl sets.
- `erasure_safe.rs` — resolves `trait_cast_is_lifetime_erasure_safe` by
DFS-walking binder vars of the target dyn type and checking each is
expressible through the root's binder.
- `cast_sensitivity.rs` — SCC-based batch computation of per-`Instance`
`CastRelevantLifetimes` (direct + transitive via call-graph).
- `resolved_bodies.rs`, `trait_cast_requests.rs` — request gathering
and delayed-codegen queue.
- `partitioning.rs` — cascade-canonicalization of augmented callees so
sensitive subgraphs are emitted once per signature group.
**MIR**: `TerminatorKind::{Call, TailCall}` grows a
`call_id: &'tcx List<(DefId, u32, GenericArgsRef<'tcx>)>` recording the
full inlining chain. `TerminatorKind` size assertion goes from 80 → 88.
Before inlining each list has length 1; the inliner prepends the
caller's chain to each inlined callee's.
**Borrowck**: new `region_summary.rs` publishes a
`BorrowckRegionSummary` per fn (walk-position → `RegionVid`, call-site
region mappings keyed on the `u32` counter) consumed by the sensitivity
pass after typeck but before mono.
**Generic args**: new `GenericArgKind::Outlives(OutlivesArg)` variant
(tag `0b11`) carrying `(longer, shorter)` region-index pairs. Appended
to an `Instance`'s args when a sensitive callee must be specialized for
a given caller's outlives environment. Wired through interning,
encode/decode, folding/visiting, symbol mangling, and all the usual
suspects.
**New lang item**: `TraitMetadataTable` (`sym::trait_metadata_table`).
**HIR analysis** (`wfcheck.rs`, `dyn_trait.rs`): eagerly diagnoses at
trait-definition time when a root-connected trait introduces a lifetime
not expressible through the root (would be manufactured at downcast
time — unsound).
## Diagnostics
- `UNUSED_CAST_TARGET` lint — cast to a target no concrete type in the
final binary implements (always `Err` at runtime).
- `trait graph rooted at {root} is not downcast-safe` — erased-lifetime
manufacturability check.
- `TraitMetadataTable type argument must be a trait object` — non-`dyn T`
arg.
- `TraitMetadataTable type argument does not match a cast root` —
`dyn X` where `X` isn't `Self` or a transitive cast-root supertrait.
- `cast target not reachable in graph` / `non-dyn-compat target` /
`tmt-arg-*` — various ill-formed roots and targets.
A "not part of any global crate" diagnostic was considered but is not
feasible — the detection info is categorically unavailable at compile
time.
## Debugging / inspection flags
All `-Z`, all dump to stderr:
- `-Z dump-trait-graph[=FILTER]`,
`-Z dump-trait-cast-sensitivity[=FILTER]`,
`-Z dump-trait-cast-augmentation[=FILTER]`,
`-Z dump-trait-cast-canonicalization`,
`-Z dump-trait-cast-chain-composition[=FILTER]`,
`-Z dump-trait-cast-erasure-safety[=FILTER]`
- `-Z print-trait-cast-stats`
Each has a matching `tests/run-make/dump-*` test.
## Tests
- `tests/ui/trait-cast/` — 23 files: basic/lifetime-bounded downcasts,
erasure-safety (chain-walk, projections, structural, outlives),
cross-crate casts, invalid targets, non-dyn-compat targets, missing
root bound, TMT arg mismatch, lifetime-in-generics (565 lines),
torture-tests (306 lines), runtime cast failures.
- `tests/run-make/` — 11 rmake tests: `trait-cast-condense-*`
(baseline, param aliasing, static-in-impl,
same-class-different-impls), `trait-cast-table-layout`,
`cross-global-crate-casts`, `print-trait-cast-stats`,
`dump-trait-*`.
## Known caveats for review
- The `call_id` chain is threaded through every `TerminatorKind::Call`
construction site in the compiler and in test mocks (which use
`ty::List::empty()`). If there's a cleaner place to stash this —
e.g. a side table keyed on basic-block / statement index — I'd take
that feedback.
- `OutlivesArg` lands as a first-class `GenericArgKind` variant with
pack/unpack. Whether this belongs in `GenericArg` or should live as
a separate field on `Instance` is a legitimate design question; it's
in `GenericArgKind` today so mangling/encoding come along for free.
- `library/alloc/*` and a few other paths carry pre-existing churn
from earlier iterations; I'll rebase/squash those out before this
is reviewable outside of a draft.
- Perf was evaluated with rustc-perf and the impact on crates that do
not use trait casting was found to be minimal. The SCC +
Floyd-Warshall pass only runs over directly- and
transitively-sensitive call graphs and stops at the ground-level
caller, so crates with no cast graph pay effectively nothing. Heavy
trait-casting usage has not yet been benched; guidance on a
representative workload would be welcome.
## Not in this PR
- Stabilization / `rustc_deny_explicit_impl` on `TraitMetadataTable`
(the RFC discussion around a `pub root trait` keyword is unresolved).
- `cast!` on `Pin<P>` or user smart pointers.
- `rustdoc` surfacing of cast graphs.1 parent c28e303 commit 71bfe3e
223 files changed
Lines changed: 16056 additions & 272 deletions
File tree
- compiler
- rustc_borrowck/src
- diagnostics
- polonius/legacy
- type_check
- rustc_codegen_cranelift/src
- driver
- rustc_codegen_gcc/src
- rustc_codegen_llvm/src
- rustc_codegen_ssa/src
- back
- mir
- rustc_const_eval/src
- const_eval/type_info
- interpret
- rustc_hir_analysis/src
- check
- collect
- hir_ty_lowering
- outlives
- variance
- rustc_hir_typeck/src/method
- rustc_hir/src
- rustc_index/src
- rustc_infer/src/infer
- canonical
- outlives
- rustc_interface/src
- rustc_lint_defs/src
- rustc_lint/src
- rustc_metadata/src/rmeta
- decoder
- rustc_middle/src
- hooks
- mir
- interpret
- query
- ty
- context
- print
- rustc_mir_build/src/builder
- custom
- parse
- expr
- matches
- rustc_mir_dataflow/src
- framework
- move_paths
- rustc_mir_transform/src
- coroutine
- coverage
- shim
- rustc_monomorphize
- src
- collector
- graph_checks
- mono_checks
- rustc_next_trait_solver/src
- canonical
- solve
- eval_ctxt
- normalizes_to
- rustc_public/src
- mir
- unstable/convert/stable
- rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi
- rustc_session/src
- rustc_span/src
- rustc_symbol_mangling/src
- rustc_trait_selection/src
- error_reporting
- infer
- traits
- solve
- traits
- select
- rustc_ty_utils/src
- rustc_type_ir/src
- library
- alloc/src
- core/src
- intrinsics
- std/src
- src
- librustdoc/clean
- tools/clippy
- clippy_lints/src
- clippy_utils/src
- ty
- tests
- codegen-llvm/remap_path_prefix
- run-make
- cross-global-crate-casts
- dump-trait-cast-augmentation
- dump-trait-cast-canonicalization
- dump-trait-cast-chain-composition
- dump-trait-cast-erasure-safety
- dump-trait-cast-sensitivity
- dump-trait-graph
- print-trait-cast-stats
- trait-cast-condense-baseline
- trait-cast-condense-param-aliasing
- trait-cast-condense-static-in-impl
- trait-cast-condense-where-clause-reject
- trait-cast-table-layout
- ui
- impl-trait
- auto-trait-leakage
- in-trait
- offset-of
- pattern
- trait-cast
- auxiliary
- type-alias-impl-trait
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4372 | 4372 | | |
4373 | 4373 | | |
4374 | 4374 | | |
| 4375 | + | |
4375 | 4376 | | |
4376 | 4377 | | |
4377 | 4378 | | |
4378 | 4379 | | |
4379 | 4380 | | |
4380 | 4381 | | |
4381 | 4382 | | |
| 4383 | + | |
4382 | 4384 | | |
4383 | 4385 | | |
4384 | 4386 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
730 | 730 | | |
731 | 731 | | |
732 | 732 | | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
733 | 737 | | |
734 | 738 | | |
735 | 739 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
86 | 86 | | |
87 | 87 | | |
88 | 88 | | |
| 89 | + | |
89 | 90 | | |
90 | 91 | | |
91 | 92 | | |
| |||
104 | 105 | | |
105 | 106 | | |
106 | 107 | | |
107 | | - | |
| 108 | + | |
108 | 109 | | |
109 | 110 | | |
110 | | - | |
111 | | - | |
112 | | - | |
113 | | - | |
114 | | - | |
115 | | - | |
116 | | - | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
117 | 114 | | |
118 | 115 | | |
119 | | - | |
| 116 | + | |
120 | 117 | | |
121 | | - | |
| 118 | + | |
122 | 119 | | |
123 | 120 | | |
124 | | - | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
125 | 130 | | |
126 | 131 | | |
127 | 132 | | |
128 | 133 | | |
129 | | - | |
130 | | - | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
131 | 140 | | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | | - | |
136 | | - | |
137 | | - | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
138 | 182 | | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
139 | 190 | | |
140 | 191 | | |
141 | 192 | | |
| |||
436 | 487 | | |
437 | 488 | | |
438 | 489 | | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
439 | 494 | | |
440 | 495 | | |
441 | 496 | | |
| |||
908 | 963 | | |
909 | 964 | | |
910 | 965 | | |
| 966 | + | |
911 | 967 | | |
912 | 968 | | |
913 | 969 | | |
914 | 970 | | |
915 | 971 | | |
916 | 972 | | |
917 | 973 | | |
918 | | - | |
| 974 | + | |
919 | 975 | | |
920 | 976 | | |
921 | 977 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
125 | 125 | | |
126 | 126 | | |
127 | 127 | | |
| 128 | + | |
128 | 129 | | |
129 | 130 | | |
130 | 131 | | |
| |||
0 commit comments