fix(reborrow): recursive implementation - #159103
Conversation
|
Some changes occurred to MIR optimizations cc @rust-lang/wg-mir-opt This PR changes MIR cc @oli-obk, @RalfJung, @JakobDegen, @vakaras Some changes occurred to the CTFE machinery Some changes occurred to the CTFE / Miri interpreter cc @rust-lang/miri Some changes occurred to constck cc @fee1-dead This PR changes rustc_public |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
cc @bjorn3
cc @rust-lang/clippy |
This comment has been minimized.
This comment has been minimized.
c74824e to
4ac1ece
Compare
This comment has been minimized.
This comment has been minimized.
aebdf5d to
b845ed2
Compare
This comment has been minimized.
This comment has been minimized.
b845ed2 to
549d21b
Compare
This comment has been minimized.
This comment has been minimized.
|
Reminder, once the PR becomes ready for a review, use |
44ad71e to
2c3a3fa
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
2c3a3fa to
d815250
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
d815250 to
c289b88
Compare
|
@bors delegate+ r=me with remaining things resolved |
|
@bors squash |
This comment has been minimized.
This comment has been minimized.
* Implement Reborrow as a recursive operation If Reborrow finds '&'a mut T' fields then it inserts a Deref and borrow of the T, and likewise if it finds a 'T: Reborrow' field then the field type is recursed into. This makes Reborrow always produce the correct borrow checking logic at the cost of most probably being inconsiderately expensive. The thinking is that performance will be a followup consideration. * PhantomDeref * Simpler deref test * Add more PhantomDeref unreachability assertions * Write out lifetime omission * Document ProjectionElem::PhantomDeref * Comment half of reborrow tests * fix PhantomDeref conflicting with AccessDepth::Shallow * Recheck CoerceShared in borrowck TypeChecker to ensure its lifetimes make sense * Fix rebase * Changes... but where to? * Typo fix * Improve comment * Use fully_perform_op to evaluate CoerceShared trait in borrowck * More CoerceShared comment * Comment rest of Reborrow tests Co-authored-by: Oli Scherer <github35764891676564198441@oli-obk.de>
|
🔨 16 commits were squashed into 5686e56. |
7b76c2d to
5686e56
Compare
|
All of the comments should now be handled, and I've also decided on the next steps:
struct Source<'a, 'b> {
a: &'a mut A,
b: &'b mut B,
}
struct Target<'a, 'b> {
a: &'a A,
b: &'b B,
}
impl<'a, 'b> CoerceShared for Source<'a, 'b> {
type Target = Target<'b, 'a>; // Note: flipped lifetimes.
}And if we do need that, do we really still need the projection? I guess we do to catch implementations like: impl<'a, 'b> CoerceShared for Source<'a, 'b> {
type Target = Target<'static, 'static>;
}
impl<T: Copy> Reborrow for T {}
impl<T: Copy> CoerceShared for T {
type Target = T;
}If the compiler can reason about reborrows of generic @bors r=oli-obk |
…ce, r=oli-obk fix(reborrow): recursive implementation If Reborrow finds '&'a mut T' fields where 'a is the Reborrowed type's first lifetime parameter (currently the only lifetime that is allowed to reborrow) then it inserts a Deref and borrow of the T, and likewise if it finds a 'T: Reborrow' field then the field type is recursed into. This makes Reborrow always produce the correct borrow checking logic at the cost of most probably being inconsiderately expensive. The thinking is that performance will be a followup consideration. r? @oli-obk
Rollup of 14 pull requests Successful merges: - #150075 (Implement clamp_to) - #159103 (fix(reborrow): recursive implementation) - #160848 (std: avoid aliasing violations when wrapping opaque C types) - #161421 (Include startup crt objects on WASI for more outputs) - #161805 (Prefer ambiguous candidates when deduplicating traits in scope, so `ambiguous_glob_imported_traits` doesn't depend on import order) - #161862 (Put data segment in specified section with link_section on wasm) - #161866 (delegation: add tests fixating behavior of delegating to default trait implementations) - #161456 (reduce perf impact of scalar size checks) - #161666 (Print vendor instructions in `x vendor`) - #161730 (Improve type mismatch annotation for lets with block-wrapped initializers) - #161828 (Never type after-stabilization cleanup) - #161860 (atomicptr.rs test: remove unused import) - #161870 (bind to [::1] instead of 127.0.0.1 in documentation examples for v6 UDP methods) - #161876 (rustdoc: Correctly handle when a macro generates multiple items in `--generate-macro-expansion`)
…ce, r=oli-obk fix(reborrow): recursive implementation If Reborrow finds '&'a mut T' fields where 'a is the Reborrowed type's first lifetime parameter (currently the only lifetime that is allowed to reborrow) then it inserts a Deref and borrow of the T, and likewise if it finds a 'T: Reborrow' field then the field type is recursed into. This makes Reborrow always produce the correct borrow checking logic at the cost of most probably being inconsiderately expensive. The thinking is that performance will be a followup consideration. r? @oli-obk
…ce, r=oli-obk fix(reborrow): recursive implementation If Reborrow finds '&'a mut T' fields where 'a is the Reborrowed type's first lifetime parameter (currently the only lifetime that is allowed to reborrow) then it inserts a Deref and borrow of the T, and likewise if it finds a 'T: Reborrow' field then the field type is recursed into. This makes Reborrow always produce the correct borrow checking logic at the cost of most probably being inconsiderately expensive. The thinking is that performance will be a followup consideration. r? @oli-obk
…uwer Rollup of 21 pull requests Successful merges: - #158609 (Update sccache to 0.16.0) - #150075 (Implement clamp_to) - #159103 (fix(reborrow): recursive implementation) - #160562 (add target feature ABI checks for SPARC) - #160848 (std: avoid aliasing violations when wrapping opaque C types) - #161421 (Include startup crt objects on WASI for more outputs) - #161805 (Prefer ambiguous candidates when deduplicating traits in scope, so `ambiguous_glob_imported_traits` doesn't depend on import order) - #161862 (Put data segment in specified section with link_section on wasm) - #161866 (delegation: add tests fixating behavior of delegating to default trait implementations) - #161456 (reduce perf impact of scalar size checks) - #161528 (Add regression test to ensure optimal compilation) - #161666 (Print vendor instructions in `x vendor`) - #161730 (Improve type mismatch annotation for lets with block-wrapped initializers) - #161828 (Never type after-stabilization cleanup) - #161859 (Do not optimize MIR for comptime ConstFns) - #161860 (atomicptr.rs test: remove unused import) - #161870 (bind to [::1] instead of 127.0.0.1 in documentation examples for v6 UDP methods) - #161876 (rustdoc: Correctly handle when a macro generates multiple items in `--generate-macro-expansion`) - #161889 (Add link to ownership section in ptr::read docs) - #161890 (rustdoc: some clarifying comments) - #161891 (Mark `extern_item_impls` feature as incomplete) Failed merges: - #161702 (Use `drop_guard` in some places in {core,alloc,std})
…uwer Rollup of 21 pull requests Successful merges: - #150075 (Implement clamp_to) - #159103 (fix(reborrow): recursive implementation) - #160562 (add target feature ABI checks for SPARC) - #160848 (std: avoid aliasing violations when wrapping opaque C types) - #161421 (Include startup crt objects on WASI for more outputs) - #161805 (Prefer ambiguous candidates when deduplicating traits in scope, so `ambiguous_glob_imported_traits` doesn't depend on import order) - #161862 (Put data segment in specified section with link_section on wasm) - #161866 (delegation: add tests fixating behavior of delegating to default trait implementations) - #157218 (Track items behind `cfg_select` in the same way we do for `cfg`) - #161456 (reduce perf impact of scalar size checks) - #161528 (Add regression test to ensure optimal compilation) - #161666 (Print vendor instructions in `x vendor`) - #161730 (Improve type mismatch annotation for lets with block-wrapped initializers) - #161828 (Never type after-stabilization cleanup) - #161859 (Do not optimize MIR for comptime ConstFns) - #161860 (atomicptr.rs test: remove unused import) - #161870 (bind to [::1] instead of 127.0.0.1 in documentation examples for v6 UDP methods) - #161876 (rustdoc: Correctly handle when a macro generates multiple items in `--generate-macro-expansion`) - #161889 (Add link to ownership section in ptr::read docs) - #161890 (rustdoc: some clarifying comments) - #161891 (Mark `extern_item_impls` feature as incomplete) Failed merges: - #161702 (Use `drop_guard` in some places in {core,alloc,std})
Rollup merge of #159103 - aapoalas:reborrow/fix-borrowed-place, r=oli-obk fix(reborrow): recursive implementation If Reborrow finds '&'a mut T' fields where 'a is the Reborrowed type's first lifetime parameter (currently the only lifetime that is allowed to reborrow) then it inserts a Deref and borrow of the T, and likewise if it finds a 'T: Reborrow' field then the field type is recursed into. This makes Reborrow always produce the correct borrow checking logic at the cost of most probably being inconsiderately expensive. The thinking is that performance will be a followup consideration. r? @oli-obk
* chore: bump pinned nightly to 2026-08-28; port mir-importer + rustc-codegen-cuda
Move the pinned toolchain from nightly-2026-04-03 to nightly-2026-08-28
(rustc 1.100.0-nightly e457a7b0d, bundled LLVM 23.1.0). The pin is updated
in all seven documented copies (both rust-toolchain.toml files, the
`cargo oxide new` scaffold const, the devcontainer, README/book install
one-liners); scripts/check-toolchain-parity.sh passes.
Mechanical port (rustc_private / rustc_public churn):
- `rustc_middle::mir::mono` -> `rustc_middle::mono` (CodegenUnit, MonoItem)
- `InstanceKind::DropGlue(..)` -> `InstanceKind::Shim(ShimKind::DropGlue(..))`;
`Instance::resolve_drop_in_place` -> `Instance::resolve_drop_glue`
- `EarlyBinder::bind` now takes `tcx`
- `TyKind::FnDef` args sit behind a `Binder`; both collector sites use
`no_bound_vars().expect(..)`, matching rustc_monomorphize's own discipline
(late-bound vars are instantiated before FnDef types reach built MIR)
- `join_codegen` gained `incr_comp_session`/`crate_info` params and returns
`WorkProductMap`; `codegen_crate` lost `crate_info`; `CompiledModule`
gained `global_asm_object`
- `fn_sig.abi`/`c_variadic` are methods; `type_of(..).instantiate_identity()`
returns `Unnormalized<Ty>`, resolved via `normalize_erasing_regions`
following `TyCtxt::static_ptr_ty`'s discipline
- rustc_public: `Statement.span` -> `.source_info.span`; `ty_with_args`
moved to the `CrateDefType` trait; `StatementKind::Retag` removed
(retags are now the `WithRetag` flag on `Rvalue::Use`, still a device
no-op); `CastKind::BoxDerefTransmute` maps to the Transmute cast kind,
exactly as rustc_codegen_ssa treats it
Behavioral ports (compiler gaps the new nightly exposed; each verified by
the full on-GPU smoketest):
- drop shims are now built from `core::ptr::drop_glue` (`&mut T` parameter,
with `drop_in_place` an ordinary forwarding fn): accept the `&mut`
parameter in emit_drop_glue and elide provably-no-op `drop_glue::<T>`
calls with the same shared predicate as `drop_in_place`
- collect `ShimKind::FnPtrAsPtr` shim bodies (they back `FnPtr::addr`,
have no HIR body, but instance_mir synthesises one)
- translate `RigidTy::Foreign` (extern types, e.g. `core::ops::Code`) as an
opaque empty struct pointee, like FnPtrTarget
- intercept `std::sys::cmath::{asinh,acosh,atanh}{,f}` (pure-Rust formulas
before this nightly) and lower to `__nv_asinh(f)`/... libdevice calls
- core atomic load/store gained a `const VOLATILE: bool` generic: parse it
(bool-typed const) separately from orderings and fail closed on
`VOLATILE = true`
- re-pack `llvm.*.with.overflow` results into the type converter's padded
`(T, bool)` tuple form; the new RangeInclusive iterator MIR forwards the
checked-op tuple through a block argument where the two forms met
- atomics example: pass the new `VOLATILE` argument at its raw-intrinsic
call sites
Also fixes the new-toolchain lint fallout so `clippy -D warnings` stays
green (chunks_exact->as_chunks, ?-operator, useless format!, and an
`allow(suspicious_runtime_symbol_definitions)` for bindgen's c_ulong
malloc/realloc declarations in cuda-bindings).
* feat(codegen): lower Rvalue::Reborrow; add reborrow regression example
nightly-2026-08-28 introduces `Rvalue::Reborrow(Ty, Mutability, Place)`
(rust-lang/rust#159103, `feature(reborrow)`): reborrowing a user ADT that
implements the `Reborrow`/`CoerceShared` marker traits. Semantically it is
a bitwise copy of the place: same type for `Mut`, the same-layout
`CoerceShared` target ADT for `Not`. rustc_codegen_ssa lowers it as
`codegen_operand(Operand::Copy(place))`; the importer mirrors that with a
place read, plus the existing same-layout Transmute cast path when the
translated dialect types diverge.
The release MIR pipeline folds these into plain copies before the importer
sees them, so the new `reborrow` example runs with `--device-debug`
(-Zmir-opt-level=0) in the smoketest, where the rvalue reaches the
translator intact (verified by stubbing the arm: the stub fails the
example under --device-debug and not under release).
* test: re-bless trybuild goldens for nightly-2026-08-28 diagnostics
Rendering-only drift: rustc 1.100 trims more type paths in E0277/E0308
notes ("cuda_device::__LaunchContractDisjointSlice" now prints through its
module path, `LaunchContext<...>` args elide as `_` instead of `...`, and
suggestion placeholders print trimmed types). Re-blessed with
TRYBUILD=overwrite and eyeballed: no test changes semantics, every case
still fails to compile for the same reason as before.
* fix(cargo-oxide): fingerprint the toolchain that built the backend
write_toolchain_fingerprint ran `rustc -vV` in the USER's cwd, but the
backend builds with `current_dir = <backend source clone>`, whose nested
rust-toolchain.toml can pin a different nightly. A bare cargo-oxide
invocation could therefore record a fingerprint that never matches the
`.so`: the StaleVsToolchain guard compares the user-cwd toolchain against
the recorded user-cwd toolchain, matches forever, and every application
build loops on "couldn't load codegen backend" with no self-heal.
Resolve the recorded fingerprint from the build directory (same cwd and
inherited env as the build command, so rustup's proxy resolves the same
rust-toolchain.toml). The mismatch CHECK still resolves from the user's
cwd, which is the toolchain that will load the `.so`.
Tests: command-construction test asserting the fingerprint command's cwd
is the build dir, and a behavioral test proving a build-dir pin (an
uninstallable channel) defeats fingerprinting even though the process cwd
resolves fine.
* chore(intrinsics): refresh recorded LLVM identity to 23.1.0 / 16696adc
The new nightly bundles LLVM 23.1.0 built from rust-lang/llvm-project
16696adcd119e6ba9cc175207d984d7021211acb. Refresh every recorded identity:
- intrinsics/upstream.lock: new LLVM revision, an llvm-tblgen built from
that revision (sha256 199fe46b...), new dump hashes. Ubuntu
llvm-tblgen-22 byte-for-byte reproduced the Intrinsics.td dump; the
NVPTX.td dump uses LLVM-23-only TableGen operators (!sort) that pre-23
binaries reject, recorded as such in the comparison-tools provenance.
- intrinsics/imported.json: re-extracted (2569 -> 2633 NVVM declarations;
the LLVM 22-era unscoped atomics are gone, fp16 sat-arith / e2m1 / e2m3
conversions arrived).
- 58 rust-llvm evidence files renamed to rust-llvm-23.1.0-16696adc* with
llvm_revision / backend_version ("LLVM version
23.1.0-rust-1.100.0-nightly") / backend_sha256 (2e5bc688..., the
rustup llvm-tools llc) headers updated; overlay profile references moved
with them.
- LLVM 23 upstream drift absorbed in the resolver's recorded facts:
IntrNoCreateUndefOrPoison joined most pure math intrinsic property sets
(not ex2/lg2), many declarations moved from NVVMPureIntrinsic to the
new target-generic PureIntrinsic class, tcgen05 ld/st data types became
overloaded per-register-count type variables (anonymous_9933..9961,
previously concrete vNi32; evidence signature records updated in both
backends' files), the tcgen05 MMA TableGen selection strings moved
`.ashift` before the collector qualifier (declaration-side spelling
split from the ptxas-validated emission spelling), tensormap.replace
dim/stride declarations gained Range<arg1,0,5>, and the fabs/ex2
overload type variable renumbered anonymous_14 -> anonymous_8.
- `generate` refreshed catalog.json (1016 intrinsics, unchanged set),
generated-reference.md, probes/*.ll, and the 9 managed generated roots
(identity banners only); the book stamp moved 90f843cc -> c8007834.
- cuda-target-spec: the LLVM-derived test gate moves 22 -> 23 (it
self-skips on any other major); all 28 recorded PTX floors and 11 ISA
spellings verified against llc-23, no table changes.
- llc/opt/llvm-link PATH fallbacks probe the -23 names first; docs and
the atomics README follow (llc-23 honors atomicrmw orderings + scopes,
PR llvm/llvm-project#176015, verified: `atomicrmw ... syncscope("block")
acq_rel` -> `atom.acq_rel.cta.global.add.u32`; fence-splitting stays
while LLVM 21/22 remain supported).
Gates: `cuda-intrinsics-gen check` clean, `probe --all --skip-terminal
--per-target` passes 2365 target configurations across all 1016 generated
routes against the pinned llc-23, `check-abi-history` appends nothing
(the ABI ledger records no llc identity).
* fix: address nightly-bump review findings (reborrow CoerceShared coverage, fingerprint heal guard, evidence re-attestation)
Four verified review fixes on top of the nightly-2026-08-28 bump:
- reborrow example: cover the Rvalue::Reborrow transmute half. The arm's
Mutability::Not (CoerceShared) path had zero coverage; the example now
coerces its MutView into a shared View at two read() call sites and
writes the sum back (0 -> 2.0 -> 4.0). Proven by stubbing the transmute
branch: the stub fails the example in BOTH release and --device-debug
device builds. The write-back happens outside v's scope because
CoerceShared's coherence rules force the same lifetime argument on
source and target, so a write through v after read(v) is E0506.
- comment truthfulness (smoketest + example header): GVN folds only the
Mutability::Mut reborrow variant at mir-opt-level>0; the CoerceShared
variant survives release MIR and reaches the importer there too. The
smoketest now runs reborrow in both its release and --device-debug
invocations.
- cargo-oxide: one-attempt toolchain heal guard. When the user's project
pin and the backend source's nested pin genuinely differ, the
StaleVsToolchain arm re-cloned and cold-rebuilt on every invocation
forever. A heal attempt now records the (active, recorded) fingerprint
pair in toolchain-heal-attempt.txt; a repeated identical pair prints
both toolchain identities plus alignment guidance and exits instead of
rebuilding. A passing fingerprint check deletes the marker. First-time
mismatches still self-heal via re-clone.
- intrinsics evidence: four families (tcgen05-mma, cluster-sreg,
tcgen05-control, tcgen05-ldst-offset) carried llc stage records still
naming LLVM 22.1.2 / ff1dcdc1 while their headers claim llc-23.
Re-attested all 73 catalog intrinsics of those families against the
pinned llc-23 (probe green) and updated the stage tool_version /
tool_sha256 to 23.1.0-rust-1.100.0 / 2e5bc688; regenerated the
catalog-stamp banners. Also fixed the asinh/acosh/atanh comments:
only asinh/acosh became std::sys::cmath shims on rustc e457a7b0d;
atanh stays pure-Rust ln_1p, its entries are defensive.
* feat(host-apis): diagnose host/device TypeId naming divergence on generic kernel misses
Generic kernels are looked up by "<base>_TID_<hex32>", where the backend
computes the hash with tcx.type_id_hash and the host extracts the same
value from core::any::TypeId (cuda-host/src/type_id.rs). The values
cannot drift while TypeId's runtime layout is the raw hash, but that
layout is a std internal: if a future nightly changes it, the transmute
still compiles and every generic launch fails with an opaque
DriverError(500, "named symbol not found").
Make that failure self-explaining, at zero happy-path cost:
- cuda-host entry_registry (new): load_all_ptx_bundles_merged retains
the merged module's .entry names (parsed with ptx-parse). Retention is
Weak-keyed by module allocation identity, prunes dropped modules on
each registration, and costs a few dozen bytes per kernel. Best-effort
by design: a parse failure retains nothing and never fails a load the
driver accepted.
- On a failed _TID_ load_function, the macro-generated launch paths now
consult the diagnosis. If the module holds the same base kernel under
a different 32-hex hash, it panics with both entry names, the cause
(host core::intrinsics::type_id vs device tcx.type_id_hash disagreed,
so the TypeId-to-u128 extraction in cuda_host::type_id is no longer
value-correct on this toolchain), and the remedy (build host and
device with the same pinned nightly; re-verify the type_id.rs contract
and the backend's compute_kernel_export_name together). An ordinary
miss keeps today's error and panic text unchanged.
- Wired sites: the #[cuda_module] generic function binding routes
through diagnose_generic_kernel_load_error (returns the DriverError
untouched for plain misses), and the cuda_launch! /
cuda_launch_async! generic and closure branches route through
panic_generic_kernel_load_failed. Non-generic lookups are untouched
and stay duck-typed over the module expression.
- The load-time canary kernel (layer 2 of the decision memo) is
deliberately not implemented; the entry_registry module rustdoc
records why so future readers see it was considered.
Tests: cuda-host unit tests drive the real ptx-parse -> match -> message
path (divergence fires with both names; different-base miss, non-_TID_
name, .func-only same-base entries, and malformed hash suffixes all keep
the ordinary path; registry identity and pruning). cuda-macros expansion
tests assert the generic and closure paths call the helpers and the
non-generic paths do not.
* chore(intrinsics): re-attest scalar-math appended fixture under the pinned llc-23
The scalar_math_inline_sm80_ptx70_appended fixture in
rust-llvm-23.1.0-16696adc-scalar-math.json still carried the llc-22
identity (LLVM 22.1.2-rust-1.96.0 / ff1dcdc1) in its backend_codegen
detail, plus an llc-22-vs-21 behavioral claim that had not been checked
since. Re-attested instead of reworded:
- Re-ran the evidence probes for all 40 scalar_math catalog intrinsics
against the pinned sysroot llc-23 (LLVM 23.1.0-rust-1.100.0-nightly,
SHA-256 2e5bc688...), terminal ptxas (CUDA 13.3 V13.3.33)
revalidation included: 40/40 green.
- The three appended per-op PTX SHA-256s (ex2_approx_f32,
ex2_approx_ftz_f32, tanh_approx_f32) are byte-identical under llc-23
because inline-PTX probe bodies pass through llc verbatim; the detail
now states that and keeps the original llc-22 identity as the
historical reference.
- The typed llvm.nvvm.tanh.approx.f32 route was re-checked with current
binaries: pinned llc-23 and Ubuntu LLVM 22.1.7 select it via
NVVMIntrinsic-class matching, while Ubuntu LLVM 21.1.8 still lowers it
to an extern funcall, so the inline route remains required while
llc-21 remains a supported fallback (doctor probes llc-23/22/21).
- Added the structured llc tool identity fields to the stage, matching
the convention of the four families re-attested in 0aec1b7.
- `generate` refreshed catalog.json, generated-reference.md,
probes/*.ll, and the generated-root banners (catalog SHA-256
478f564b -> 49bcd626); `check` reports outputs current.
* fix: clippy-clean examples under nightly-2026-08-28; re-stamp book catalog page
The bumped nightly's clippy trips two examples out of 222 lint targets:
- field_array_assign: needless_range_loop on the runtime-index loop. The
indexed loop is the (Field, Index) MIR projection under test (issue
#235), so keep it and allow the lint on the loop with a reason comment.
- function_item_call: stable_features on #![feature(never_type)], which
stabilized in 1.100.0-nightly. Drop the dead gate; the never-type
usage under test is unchanged.
Also carries the cuda-oxide-book catalog-stamp refresh (c8007834 ->
49bcd626) matching the regenerated dialect-nvvm ops/generated headers.
…-obk fix(reborrow): recursive implementation If Reborrow finds '&'a mut T' fields where 'a is the Reborrowed type's first lifetime parameter (currently the only lifetime that is allowed to reborrow) then it inserts a Deref and borrow of the T, and likewise if it finds a 'T: Reborrow' field then the field type is recursed into. This makes Reborrow always produce the correct borrow checking logic at the cost of most probably being inconsiderately expensive. The thinking is that performance will be a followup consideration. r? @oli-obk
…-obk fix(reborrow): recursive implementation If Reborrow finds '&'a mut T' fields where 'a is the Reborrowed type's first lifetime parameter (currently the only lifetime that is allowed to reborrow) then it inserts a Deref and borrow of the T, and likewise if it finds a 'T: Reborrow' field then the field type is recursed into. This makes Reborrow always produce the correct borrow checking logic at the cost of most probably being inconsiderately expensive. The thinking is that performance will be a followup consideration. r? @oli-obk
The nightly toolchain automation has been stuck since nightly-2026-08-22 (model-checking#4774), so this jumps a month at once, to nightly-2026-09-22 (rustc `1303417c4`). Most of the diff is API churn, but one change deserves a decision rather than a rubber stamp, so I've put it first. ## The part worth arguing about rustc now enables the next-generation trait solver by default (rust-lang/rust#160619), and that solver does not support `generic_const_exprs` (rust-lang/rust#160895). rustc silently reverts to the old solver for any crate that enables the feature itself — which is why `library/kani` still builds. But Kani *exposes* the feature in a public signature: ```rust pub fn pointer_generator<T, const NUM_ELTS: usize>() -> PointerGenerator<{ core::mem::size_of::<T>() * NUM_ELTS }> ``` A crate being verified does not enable the feature, so it gets the new solver and cannot call that function: ``` error[E0284]: type annotations needed = note: cannot satisfy `kani::pointer_generator<T, NUM_ELTS>::{constant#0} == _` ``` I checked that this is upstream behaviour rather than something we do wrong, by reproducing it in two plain crates outside Kani: one enabling `generic_const_exprs` and exposing such a signature, one calling it. The caller fails identically, and `-Znext-solver=coherence` on the caller fixes it. So this PR passes `-Znext-solver=coherence` in `base_rustc_flags`, covering the single-file, cargo and playback flows — the same escape hatch rustc applies for itself. **It is a stopgap and I don't want it to become permanent:** verified code is now type-checked by the solver rustc is walking away from, so Kani can in principle accept or reject a program differently from `cargo build`. Filed as model-checking#4832 (high priority) to reimplement `pointer_generator` without a computed const in its signature and drop the flag. If you would rather reshape that API here instead of bumping first, say so and I'll do that. ## A codegen bug the bump uncovered `tests/kani/Coroutines/main.rs` started ICEing: ``` assertion `left == right` failed: Error: assign statement with unequal types lhs Pointer { typ: StructTag("tag-Never") } rhs StructTag("tag-Never") ``` The cause is older than this bump: on an unsupported projection, `codegen_place_ref_stable` returned a stub typed as the *place* rather than as a reference to it, so the caller assigned a value into a pointer-typed local. The new toolchain reaches that path through the derived `PartialEq` for `CoroutineState<u8, !>`, whose uninhabited variant's field cannot be projected. With the stub given the reference's type, it degrades the way it always should have: an unsupported construct in code CBMC proves unreachable, and the harness verifies. That the projection is reported as "unsupported" rather than "unreachable" is itself wrong, tracked separately in model-checking#4831. ## Mechanical adaptations | Change | Adaptation | | --- | --- | | `box` patterns removed | match the pointer, then look through it | | `rustc_public::abi::ValueAbi` → `ValueRepr`, `LayoutShape::abi` → `value_repr` | rename | | `span_bug!` moved out of `rustc_middle` | import from `rustc_span` | | `RegionExt` gone | `Region::new_early_param` is inherent now | | `CrateType` moved to the new `rustc_structures` crate | new `extern crate` | | `target_config` takes `&EarlySession`, gained `has_reliable_f16b` | `false` — CBMC has no bfloat16 | | `Body::function_coverage_info` split | use `coverage_mir_info`, which carries the mappings | | `Diag::emit` no longer returns `ErrorGuaranteed` | `emit_err` | | new `Float::F16B` | `unimplemented!` — `f16b` is a library type with no `tcx.types` entry and no CBMC equivalent | | new `ProjectionElem::PhantomDeref` | treated as a dereference in the points-to analysis (rust-lang/rust#159103) | | the `test` crate's `TestList` borrows a slice of references; `DynTestFn` holds an `Arc<dyn Fn>` | compiletest updated; the closure clones the config it used to move | | `box` patterns removed in Charon too | `scripts/charon-patch.diff` extended to drop the feature gate and rewrite its three uses (the patch already moves Charon to edition 2024, so one site can use a let chain) | ## Test expectations, and the upstream change behind each - `derive(Eq)` no longer emits `assert_fields_are_eq` for *fieldless* types. I confirmed this by expanding a fieldless and a field-carrying type on both toolchains. Autoharness therefore has one fewer candidate in the autoderive probes (enums 9 → 8, structs 17 → 16); the table column widths shift with the longest row, so those expected files are regenerated rather than hand-edited. - rustc reworked coverage instrumentation (every file under `rustc_mir_transform/src/coverage/` changed), so the regions it emits are tighter: closing braces, `} else {` and comment lines no longer carry line counts of their own, and the uncovered region inside the unreachable `if` in `coverage/assert` now spans just the assertion rather than the whole block. Kani's side is a straight rename — the new `Body::coverage_mir_info` carries the same fields, `mappings` included — so these are rustc's regions rather than lost ones, and uncovered code is still reported as uncovered in every one of these tests. - `std::intrinsics::{sinf,cosf}{32,64}` were removed upstream. The four trig tests now go through `f32::cos()` and friends, which still exercise the same CBMC model — both the range bounds and `cos(0.0) == 1.0` still hold. Kani's `Intrinsic::{SinF32, CosF64, …}` arms are now unreachable dead code; I left them alone rather than widen this PR, happy to strip them if you prefer. ## What I ran `kani-fmt.sh --check`, both clippy invocations from `format-check.yml`, the unit tests, and every applicable compiletest suite with `--force-rerun` (without which compiletest silently reports unchanged tests as ignored): **kani 610/610, expected 478/478, ui 152/152, script-based-pre 75/75**, plus cargo-kani, cargo-ui, std-checks, kani-fixme, firecracker, prusti, smack, json-handler, kani-docs — all green. `kani-llbc-regression.sh` passes too (10/10), with the extended Charon patch, and `coverage` 20/20 plus `cargo-coverage` 2/2 — `cbmc-viewer` turned out to be installed here after all. Two things bit me that are worth knowing if you hit them: clippy caches per-crate results, so a passing incremental run can hide a warning in a file you changed earlier (that is the `place.rs` unused import in the second commit) — a forced full re-lint is the only honest check. Same shape for compiletest, which reports unchanged tests as `ignored` rather than re-running them. What I could not run locally: the firecracker codegen check (submodule) and `verify-std`, and my CBMC is 6.10.0 against the pinned 6.11.0 — CI covers those. Resolves model-checking#4774 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.
View all comments
If Reborrow finds '&'a mut T' fields where 'a is the Reborrowed type's first lifetime parameter (currently the only lifetime that is allowed to reborrow) then it inserts a Deref and borrow of the T, and likewise if it finds a 'T: Reborrow' field then the field type is recursed into.
This makes Reborrow always produce the correct borrow checking logic at the cost of most probably being inconsiderately expensive. The thinking is that performance will be a followup consideration.
r? @oli-obk