Skip to content

Roll rustc_private toolchain to nightly-2025-01-01 - #1998

Open
thedataking wants to merge 14 commits into
masterfrom
roll-toolchain
Open

Roll rustc_private toolchain to nightly-2025-01-01#1998
thedataking wants to merge 14 commits into
masterfrom
roll-toolchain

Conversation

@thedataking

@thedataking thedataking commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Review follow-up: all 15 findings in Claude's review have been evaluated. Eleven led to fixes in five focused commits; four were set aside with baseline evidence or scope reasons below. The final local release build, 275 workspace tests (same existing Miri skip), 29 shell cases, and json-c/Lua translation/refactoring/build/runtime stages pass. Main CI and nine-project integration passed before these follow-ups; fresh runs are main CI and integration. The final follow-up Fable 5.1 assessment found no functional regression in the inspected diff; its full-test and documentation conditions are satisfied. The fresh GitHub runs are still in progress.

The workspace nightly is old enough that its rustc_private tools cannot process Rust 2024. This rolls the workspace to nightly-2025-01-01, verified as rustc 1.85.0-nightly (d117b7f211835282b3b177dc64245fff0327c04c), and ports refactoring, analysis, PDG, and instrumentation together. The required rustfmt, rustc-dev, rust-src, Miri, and rust-analyzer components are available.

The workspace remains edition 2021, and published-crate MSRVs remain 1.70. Generated-code pins remain nightly-2023-04-15 for edition 2021 and nightly-2026-03-03 for edition 2024. The latter uses the newer VaList API; replacing it with the workspace pin would break variadics. Standalone tools retain their stable toolchain.

One generated-code adaptation is necessary for the real refactor workflow: edition 2021 integer/pointer conversions use equivalent exposed-provenance as casts instead of the removed expose_addr/from_exposed_addr APIs. The explicit usize intermediate, enum handling, mutability, and single evaluation are preserved. Both exact compiler implementations define the old/new APIs in terms of these casts. A native-C comparison compiles and executes the generated program on both relevant 2021 compilers and the separate 2024 pin.

The original migration has nine focused commits in the reading order below, followed by five review-fix commits. Pointer compatibility is an independent preparation. The subsequent compiler-port commits are interdependent: intermediate commits do not build the full workspace, and the completed series is the buildable, validated landing unit. Tests and snapshot changes travel with the implementation they validate. Master's CI provisioning fix is included in the base. The follow-up commits preserve the original reading order and make the review changes independently inspectable.

Commit Review focus
1. transpile: preserve generated pointer casts across compiler pins Generated pointer compatibility and its CI compiler prerequisite, with native-C comparisons on all three compiler configurations.
2. build: select the Rust 1.85-era workspace toolchain Workspace pin, CI installs, compiler-role documentation, and required rustfmt changes.
3. refactor: port the AST schema and traversal infrastructure AST schema/generated wrappers, copied walkers and attribution, traversal-preservation tests.
4. refactor: preserve macro reconstruction and source printing AST builders, macro collapse/matching, fresh-AST printing, literal and token-consumer regressions.
5. refactor: port compiler sessions, resolution, and diagnostics Compiler ownership/reload, HIR identity, diagnostics, body analysis, and driver/parser/resolution tests.
6. refactor: preserve type reflection and compiler-specific snapshots Function-pointer and binder reflection, type queries, compiler-specific snapshot routing and expectations.
7. refactor: port ownership analysis and transformation consumers Ownership MIR analysis and remaining transformation consumers, including abstract-expression fixtures.
8. instrument: port MIR events and validate PDG preservation MIR instrumentation, unwind/failure regressions, PDG snapshot explanations and fixture freshness.
9. analyze: adapt dataflow and rewrites to the new MIR lowering Analyzer MIR lowering, dataflow, pointer permissions, Pin rewrites, and associated regressions.

The follow-up commits are:

Commit Review changes
10. Target-width constants Fix usize/isize literal truncation and host-width cast evaluation; test 16/32/64-bit values and real rewrites in both editions.
11. Edition default and unsafe lookup Restore omitted-edition 2021 default, use a span set, and add actual Cargo lint-flag and accepted-error coverage.
12. Printer allocation and visitor duplication Share one lazy ParseSess per print operation, cache attribute token materialization, and consolidate equivalent associated-item paths with baseline-checked regressions.
13. Optional MIR validation Honor explicit validation settings; exercise normal/unwind paths with default, yes and no flags.
14. Explicit analyzer structures Model typed-let bindings explicitly; narrow dead-code expectations, reuse sorting, and clarify fallback diagnostics.
Feedback set aside and qualifications
  • Testing #1, Cargo unused-unsafe regression: the old compiler explicitly treated allowed warnings as used blocks. Exact old/new Cargo executions both retain redundant blocks under tool-added -Awarnings, and both remove only redundant blocks under --force-warn unused_unsafe. Added coverage; preserved suppression.
  • Provide binaries #4, diagnostic reset: accepted preexisting errors are an established refactoring contract. Old/new executable sequences have matching exits and byte-identical output; malformed syntax and new autoretype errors still fail. The actual compiler flushes delayed bugs while the previous real-error count remains, matching old handler-drop behavior.
  • Signed integer uses wrapping_neg for -1 literal #5, repeated loop-analysis pass: the structural concern is plausible, but no working hot path was established. The command's existing regression explicitly expects its broken HIR lookup to panic; other old/new probes no-op before the pass. A speculative batching rewrite was discarded.
  • Broken (or private) links in known-limitations.md #11, replacing type reflection with _: Pat and UnsafeBinder have nameable Rust forms, unlike closures/coroutines. The refactor API handles Rust beyond direct C output; erasing these constructors would break the existing nested-binder, pattern and definition-mapping tests.
  • "Known limitations" README link is broken! #2 severity qualification: the 16-bit evaluator bug is real and fixed, but the asserted unsafe rewrite was not demonstrated. Replacement bounds reject 65536usize as u16 before equality comparison. The preexisting signed-minimum case remains conservatively unsimplified; its negative runtime value is tested.

No dependency roll or workspace edition conversion is included.

Validation used Linux x86_64, an LLVM 18 AST exporter, an isolated baseline archive at e1e5bf257863107c54e9f42b345c2aeccd925458, exact old/target compiler source checkouts, and separate build directories. Workspace Cargo build/nextest commands used --locked and denied warnings.

Workflow Baseline Candidate
Release workspace build Pass Pass
Release workspace nextest 234 passed, 1 existing skip 275 passed, same skip; all 234 baseline and all 270 pre-review passing test identities retained
Python translator runner, tests/unit 148 successes, 2 expected failures Same; no unexpected failures or successes
Original refactor shell runner 29 passed 29 passed
json-c and Lua integration All 16 selected stages passed Same stages passed; 12 translation/refactor/compile/runtime stages rerun, 4 C build stages cached after earlier candidate execution
Nine-project GitHub integration, Clang 15/18 76 successful configured stages per job Same 76 stage identities and scripts per job, all successful; no missing or extra stages
Published transpiler crates, exact Rust 1.70 Check passed Check passed; review follow-ups do not change these crates
Runtime crates / bitfields no_std, Rust 1.70 2 tests and checks passed Sources unchanged
Postprocess lint/type checks and pytest 58 passed, 0 skipped 58 passed, 0 skipped, including transpiler-dependent fixtures
Four standalone tools on stable Format/build/nextest/doc passed; each has 0 tests Sources/toolchain unchanged

The rebased main CI passed every configured job, including Ubuntu Clang 15/18, macOS Clang 17/22, all four standalone tools, and postprocess. The rebased nine-project integration workflow also passed both Clang jobs. The latest five corrective commits have completed local validation and are rerunning both workflows.

Each broader integration job retained nine transpiles, eight configured refactors, and the associated build/runtime and cache-only postprocess stages. Postprocess CI exercised cached responses and uncached-work handling; each Clang job applied 50 cached json-c comment updates, while Lua/Python2 skipped uncached work. Downstream builds and runtime checks passed. No live-LLM execution is claimed.

The 41 new tests are included in the 275 count; five were added for the GitHub review. Refactor and analyzer regressions compile and execute edition 2021/2024 outputs; instrumentation covers normal and unwind paths across editions 2015/2018/2021/2024 at minimum MIR optimization, and verifies that invalid edition-2024 input fails without overwriting metadata. Pin adjustment coverage compiles/runs the emitted rewrite. The explicit-tail-call regression checks metadata only because this compiler cannot codegen that feature.

Snapshot review: each PDG profile retains all 80 graph write-permission sets. Each profile’s node count changes from 755 to 758 because the target retains three identity pointer casts in pointers::main, test_arg, and test_arg_rec; comparison contracts only those source-justified copy nodes. Other differences are compiler printing and MIR numbering. The PDG harness now rebuilds the primary fixture package/profile so changed instrumenter contents cannot reuse stale RUSTC_WRAPPER artifacts. Refactor snapshot changes are the compiler's new type-ascription spelling and two local overflow-lint allowances in an existing arithmetic fixture. Original transformation assertions remain, and all 46 refactor snapshot tests pass.

Core reproduction commands

Use Python 3.12+ with scripts/requirements.txt, separate baseline/candidate CARGO_TARGET_DIR values, and the corresponding checkout/compiler:

export LLVM_CONFIG_PATH=/usr/bin/llvm-config-18
export CLANG_PATH=/usr/bin/clang-18
export LIBCLANG_PATH=/usr/lib/llvm-18/lib
export CC=/usr/bin/clang-18
RUSTFLAGS=-Dwarnings RUSTDOCFLAGS=-Dwarnings cargo build --release --locked
RUSTFLAGS=-Dwarnings RUSTDOCFLAGS=-Dwarnings cargo nextest run --release --workspace --locked --no-fail-fast
python scripts/test_translator.py tests/unit
# The shell runner expects target/debug/c2rust-refactor; point it at the tested binary.
(cd c2rust-refactor/tests && ./run-all-tests.sh)
# Set C2RUST_DIR and put built c2rust/standalone tools and Python dependencies on PATH.
python tests/integration/test.py json-c lua --refactor-jobs 1 --stages \
  autogen configure make transpile cargo.transpile check.transpile \
  refactor cargo.refactor check.refactor
cargo +1.70.0 check --locked -p c2rust -p c2rust-transpile
cargo fmt --all --check
git diff --check

Postprocess: run uv sync and uv run pytest -v -ra in c2rust-postprocess, with built split/merge tools on PATH and the tested c2rust binary available at target/release/c2rust (the fixture’s expected path).

Previous migration discussions and follow-up fixes were reviewed, including #1328, #1614, #1904/#1913, and the span-collision fix #1967. API adaptations were checked against the exact target compiler, with the old implementation used for behavioral comparisons.

Adversarial review: Opus 5 subsystem reviews identified issues addressed with parser diagnostic/recovery fixes, Pin reborrow preservation, structural attribute-token equality, and additional behavioral checks. Fable 5.1 completed the final semantic and reviewability assessment on the verified exact model and found no blocking defect. Its remaining evidence questions were checked against the exact printer source and old/new executable diagnostics; accepted-error warnings, exit behavior, and output matched baseline, and resolver state is copied before HIR queries consume it.

Macro printing renders the updated AST while preserving baseline bare-fragment spelling. Exact old-binary comparisons and focused regressions compile and execute five discriminating token-consumer cases in both editions, including stringify!, concat!, arbitrary token-tree forwarding, and negative postfix expressions. Preexisting text-serialization limits remain explicit. All review findings were resolved through source checks or behavioral evidence; the final full validation above was repeated after the last code correction.

Limits: live-LLM postprocessing was not exercised. The existing PDG Miri test stays skipped. The already-excluded related_decls test still fails on a missing OTHER_CONSTANT; legacy C cross-checks fail importing removed runner helpers, and Rust cross-checks cannot build their currently resolved dependencies on either compiler (old Cargo edition parsing versus target MSRV). These are baseline limitations, not passing coverage. Existing unsupported refactor cases were not disabled or relabeled. The already-broken range-loop command's module traversal remains a deferred performance concern. MIR validation uses the compiler's mandatory final Runtime(Optimized) check instead of the old immediate private body validator. This is later in the pipeline and does not promise eager final validation for check-only or unrequested bodies; optional per-pass checking honors the caller's flags. The instrumentation README documents --rustflags=-Zvalidate-mir=yes for additional eager and per-pass checks.

Generated edition 2021 code uses pointer exposure APIs removed by the
new workspace compiler. Emit the equivalent exposed-provenance `as`
casts, preserving the explicit usize intermediate, enum handling,
mutability, and single evaluation. Keep edition 2024 generation unchanged.

Add a native-C comparison that compiles and executes the generated output
on the old 2021 pin, the new workspace pin, and the separate 2024 pin.
Install the new compiler in CI for this forward-compatibility regression;
the workspace still uses its old compiler at this point. Published crates
still pass the Rust 1.70 checks. This preparation is independent of the
following rustc_private compiler transition.
Select nightly-2025-01-01, rustc 1.85.0-nightly at
d117b7f211835282b3b177dc64245fff0327c04c. Keep workspace edition 2021,
published MSRV 1.70, and the separate generated 2021/2024 compiler pins.
CI installs the new workspace compiler alongside the generated-code pins.

Document the compiler roles and exact-source requirement. Include only
the small let-else formatting changes required by the new rustfmt.

This begins a coupled compiler port. Intermediate port commits separate
review concerns; full-workspace buildability resumes at the end of the
series. Validation is against the completed tree.
Update the exhaustive AST schema, generated wrappers, and AST-only
consumers for the target compiler's node and token representations.
Adapt shared mutable token traversal and copy the now-private walker
helpers from the exact target Rust revision, with upstream attribution.

Keep traversal tests here: callback ordering, standalone item roots,
precise captures, token spacing/origin, copy-on-write, and VISIT_TOKENS.
Later commits port the handwritten syntax and compiler-context consumers.
This is part of the coupled toolchain transition.
The target nonterminal printer can reuse captured tokens after a rewrite
changes their AST. Render fresh AST fragments in a print-only copy,
including nested macros, while retaining baseline bare interpolation
spelling and the live AST's identity and hygiene data.

Port literal construction, matching, macro collapse, and rewrite printing.
Use structural attribute-token equality despite non-reflexive invisible
delimiters, and keep placeholder literals distinct from source strings.

Keep builder/printer/attribute tests and macro compile/run regressions
with this change. Baseline comparisons cover stringify, concat, token-tree
forwarding, negative postfix, and format arguments. Target compile/run
regressions cover these and C strings in editions 2021 and 2024.
Part of the coupled toolchain transition.
Replace mirrored private Compiler/Session ownership with public compiler
callbacks and fresh analysis contexts. Reload source maps at commit and
interactive boundaries; preserve Cargo/direct arguments, accepted errors,
autoretype failures, and unused-unsafe diagnostics.

Copy resolution before HIR lowering consumes it and retain namespace and
reverse-span identity checks. Use the real body parameter environment
for expression-use analysis. Consume parser diagnostics before unwinding.

Driver, parser, and resolution regressions compile and execute rewritten
2021/2024 programs, including repeated transforms and external modules.
Part of the coupled toolchain transition.
Preserve function-pointer headers, binders, pattern types, opaque type
environments, and definition remapping across the changed type APIs.
Reconstruct nested unsafe binders without losing lifetime binding or
source syntax, with compile-checked reflected aliases.

Opt refactor snapshots into the workspace compiler because their AST
syntax comes from that compiler. Generated-code tool routing retains its
existing defaults and pins. Review the type-ascription snapshot changes
with this implementation. Part of the coupled toolchain transition.
Port the remaining ownership and transformation consumers to the target
AST/HIR/MIR APIs. Preserve pointer permissions and account for scalar
alignment/offset queries newly emitted around raw pointer operations.
Retain the existing transformation algorithms and assertions.

Keep the two local overflow-lint allowances with their abstract-expression
fixtures; the new compiler rejects those inputs before the transformation
without them. Final validation retains all 29 shell cases and all 46
refactor snapshots. Part of the coupled toolchain transition.
Adapt built-MIR pointer operations, operands, callbacks, and event metadata.
Retain mandatory post-borrowck coercion cleanup and validate instrumented
normal, unwind, and compiler-failure paths in all four supported editions.

Review PDG snapshots with the MIR port: each profile retains all 80 graph
write-permission sets; three identity pointer casts explain 755 to 758
nodes. Other deltas are compiler display and numbering changes.

Rebuild the instrumented fixture package/profile before snapshots because
Cargo does not fingerprint RUSTC_WRAPPER contents. Keep runtime regressions
and snapshot freshness checks with this implementation. Part of the
coupled toolchain transition.
Port analyzer type/dataflow consumers and reconstruct the target's MIR
lowering for slice metadata, indexing, bounds checks, and arithmetic.
Preserve pointer permissions, implicit Pin reborrows, and rewrite origins.
Represent explicit tail-call metadata without claiming codegen support.

Add 2021/2024 rewrite/compile/run coverage and focused Pin/tail-call cases.
This completes the compiler transition. The final tree is identical to
the previously reviewed and validated integrated migration: 270 Linux
workspace tests pass with every one of the 234 baseline passes retained,
plus the same existing Miri skip. Translator, shell, real-project, MSRV,
and standalone-tool results are recorded in the PR.
@thedataking

thedataking commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Code review: toolchain roll to nightly-2025-01-01

Reviewed master...roll-toolchain (171 files, ~7.5k insertions), excluding the regenerated .snap files (spot-checked; consistent with the new MIR spelling). New rustc APIs were cross-checked against the actual nightly-2025-01-01 sources, and the copied ast_manip/fold/walk.rs was diffed function-by-function against upstream rustc_ast/src/mut_visit.rs — all 46 functions match modulo rustfmt, with visit_tts the only intentional (documented) adaptation. The precedence migration (gen/rewrite.py + binop_left_prec + strategy/print.rs) is internally consistent on the new contiguous ExprPrecedence scale.

Correctness

2. eval_const truncates isize/usize literals to 16 bitsc2rust-refactor/src/transform/casts.rs:474 and :498

LitKind::Int(i, LitIntType::Signed(IntTy::Isize)) => Some(ConstantValue::Int(i.get() as i16 as i128)),
LitKind::Int(i, LitIntType::Unsigned(UintTy::Usize)) => Some(ConstantValue::Uint(i.get() as u16 as u128)),

Pre-existing, but both lines are touched here and they gate redundant-cast removal. 70000usize folds to 4464, so a valid simplification is skipped.

3. Refactoring without --edition now parses as Rust 2015c2rust-refactor/src/driver.rs:198

run_compiler dropped config.opts.edition = Edition::Edition2021, and run_refactoring no longer wraps the session in create_session_globals_then(Edition2021, ..). For the documented CmdLine form (c2rust-refactor ... -- src/main.rs), build_session_options leaves the 2015 default, so paths/dyn/prelude resolve differently and typed (Phase 2/3) commands fail on edition-2021 output. Every new test passes --edition explicitly, so the default is untested.

Efficiency

6. A fresh ParseSess per interpolated tokenc2rust-refactor/src/ast_manip/print.rs:40

printable_tokens builds a ParseSess (own SourceMap, DiagCtxt, registries) inside the .map() closure, recursively. tokens_to_string/nonterminal_to_string are called from rewrite/strategy/print.rs for every reprinted node, so one pass creates thousands of throwaway sessions. Hoisting one session above the closure gives identical output.

7. Linear span scan per blockc2rust-refactor/src/context.rs:73

is_unused_unsafe_block does Vec::contains and is called (via funcs.rs:364) for every block/expr/stmt: O(blocks x spans). A FxHashSet<Span> built once in with_unused_unsafe_spans makes it O(blocks).

8. same_attr reallocates token trees quadraticallyc2rust-refactor/src/collapse/macros.rs:369

restore_attrs runs old.filter(|a| !new.any(|b| same_attr(a, b))) and same_attr calls token_trees() on both sides each time — 2·K² Vec<TokenTree> allocations for K attributes. Hoist a's trees out of the inner loop and precompute the new attrs' trees once.

9. Whole-crate MIR validation forced ondynamic_instrumentation/src/callbacks.rs:23

config.opts.unstable_opts.validate_mir = true replaces a targeted Validator run over just the instrumented body with validation of every body after every MIR pass, and it silently overrides an explicit -Zvalidate-mir=no — including via the --rustflags path tests/editions.rs itself uses to control MIR options.

Simplification / altitude

10. Duplicated flat_map_assoc_item branchesc2rust-refactor/src/collapse/macros.rs:672 (and :229)

In ReplaceTokens the AssocCtxt::Impl and AssocCtxt::Trait arms are byte-for-byte identical apart from the local binding name; in CollapseMacros ~30 lines are duplicated, differing only in mac_impl_item vs mac_trait_item. fn_edit.rs already shows the right shape for this API merge (map ctxt to a small value, then one code path). The same boilerplate landed in funcs.rs, items.rs, output_exprs.rs, and cfg_attr.rs.

12. Type annotation smuggled into an identifier fieldc2rust-analyze/src/rewrite/expr/convert.rs:747

reborrow_pin passes "__c2rust_pin: ::core::pin::Pin<&mut _>" as Rewrite::Let1's name, relying on apply.rs emitting let {name} = verbatim. Every other Let1 use passes a real binding name; a future change that validates or reformats that string emits uncompilable code. A Rewrite::LetTyped(name, ty, expr) variant states the intent.

13. Blanket #[allow(dead_code)] on three central enumsc2rust-analyze/src/util.rs:20, :104, :122

Suppresses dead-code reporting for all present and future variants/fields of RvalueDesc, UnknownDefCallee, and Callee. The lint is only about fields read solely through Debug; a field-scoped #[expect(dead_code)] keeps the signal for the rest of the type.

14. DefId sort key copy-pasted seven timesc2rust-analyze/src/analyze.rs:635, :1543, :1562, :1602, :1637, plus c2rust-refactor/src/transform/externs.rs and analysis/ownership/mod.rs

sorted_def_ids already exists in analyze.rs. One fn def_id_sort_key(did: DefId) -> (u32, u32) (or reusing that helper) keeps the seven comparators from drifting.

15. Inconsistent unsupported-type renderingc2rust-analyze/src/context.rs:1626

The new Pat(..) | UnsafeBinder(..) | CoroutineClosure(..) arm prints "{:?}" while the adjacent arm prints "unknown:{:?}", so a fallback rendering is indistinguishable from a fully labeled one. The comment also claims these constructors "retain their recursive labels", but the arm never descends into lty.args.

The literal evaluator truncated usize/isize through 16-bit types, while cast destinations used host pointer widths. Use the compilation target layout for both. Verify 16/32/64-bit signed and unsigned conversions, float saturation versus integer truncation, and real suffix rewrites in both editions. Preserve narrowing casts and signed-minimum runtime values.
Restore the historical Rust 2021 default when no edition is supplied, preserving explicit and Cargo-derived editions. Store captured unused-unsafe spans in a set. Add actual Cargo warning-flag coverage and strengthen the accepted-error command sequence; exact old/new executable comparisons confirm that lint suppression and accepted preexisting errors are intentional baseline behavior.
Share one lazily created parse session throughout each print operation and materialize attribute comparison tokens once per attribute. Consolidate equivalent trait/impl visitor branches while retaining their context-specific guards. Extend attribute duplicate coverage and compile/run associated-item macro rewrites in both editions against the exact baseline output shape.
Leave per-pass MIR validation under the caller's control. The pinned compiler unconditionally validates the transition to Runtime(Optimized), including at MIR optimization level zero. Validation therefore occurs later than the old private per-body check and does not promise eager final checks for unused or check-only bodies. Exercise normal and unwind paths with default, explicit yes, and explicit no settings across four editions.
Add a typed-let rewrite node with separate annotation and initializer children, preserving Pin coercions and substitution through compile/run coverage. Limit dead-code expectations to Debug-only fields, reuse the existing DefId sorting helper, and label unsupported diagnostic rendering consistently without claiming recursive labels are printed.
@thedataking
thedataking marked this pull request as ready for review September 7, 2026 10:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant