Skip to content

transpile panics on GNU label-address expr (&&label) -> TagTypeUnknown at conversion.rs:1024 #1

Closed

Description

@awto-au

Summary

Running c2rust transpile against Linux kernel lib/*.c translation units (riscv64 target) crashes reliably:

thread 'main' panicked at c2rust-transpile/src/c_ast/conversion.rs:1024:22:
Type conversion not implemented for TagTypeUnknown expecting 3

Root cause (traced, not yet fixed)

Baseline run against 104 lib/*.c compilation units: 104/104 crash with the panic above. Preceding the crash, ~70/104 units emit:

warning: c2rust: Cannot translate GNU address of label expression

traced to the kernel's _THIS_IP_ macro (include/linux/instruction_pointer.h):

#define _THIS_IP_  ({ __label__ __here; __here: (unsigned long)&&__here; })

This is GCC's "label as value" / computed-goto extension (&&label, address-of-label). It's pulled in pervasively via spinlock macros (raw_spin_lock_bh, __LOCK_BH, raw_spin_unlock_bh) and separately via __sb_writers_acquired/__sb_writers_release (39/104 units) in VFS writer-lock accounting.

Hypothesis: the Clang AST exporter emits an incomplete/placeholder type (TagTypeUnknown) for the &&label expression's type node since it has no real handling for AddrLabelExpr, and the Rust-side transpiler's exhaustive type-conversion match in c_ast/conversion.rs then panics on that placeholder rather than degrading gracefully.

Impact

Every kernel source file that (transitively) uses spinlocks or VFS writer-lock accounting (i.e. nearly the entire lib/ tree) currently cannot be transpiled at all, not even with warnings-and-best-effort output. This is a hard blocker, not a quality/fidelity issue.

Requested fix shape

At minimum: don't panic. Emit a warning and skip/stub the offending declaration (matching the "Missing top-level node" dropped-declaration behavior seen elsewhere), so the rest of a translation unit still produces output.

Ideally: real AddrLabelExpr/computed-goto support, though label values have no direct Rust equivalent, so any full translation needs a real semantic rewrite, not just an AST passthrough.

Environment

  • c2rust 0.22.1 / git rev 19afbea (this fork, freshly forked from upstream master)
  • Built via cargo +nightly install --git ... (nightly-pinned per upstream's own build requirements)
  • Reproduced against lib/parser.c, lib/bcd.c, and 102 other kernel lib/*.c files

Activity

  1. awto-au commented on Jul 16, 2026

    @awto-au
    Author

    Investigated a PCH-based speedup for the outer transpile loop (precompile the 3 forced -include headers shared identically across all 104 kernel lib/*.c TUs, swap them for a single -include-pch).

    Result: doesn't work, don't pursue. Confirmed with plain clang -fsyntax-only that the PCH loads and parses correctly (~38% faster: 100ms vs 162ms for lib/parser.c) — but running the SAME PCH through c2rust transpile regresses correctness: many more "Missing child" AST-export warnings appear, and the exact TagTypeUnknown panic this issue's fix addressed comes back, even on a file that transpiles clean without the PCH. The AST exporter's tree-walk evidently doesn't handle PCH-deserialized nodes the same way it handles normally-parsed ones (probably a lazy-deserialization interaction — PCH nodes load on demand and something in the exporter's traversal assumes eager availability).

    Not investigating further for now — the underlying preprocessing-time savings this targeted (~40ms/file) were modest anyway relative to actual AST-export cost (10s-50s+/file in real runs), so this wasn't going to be the main lever. Noting it here so nobody re-tries the same approach without re-discovering this.

  2. awto-au commented on Jul 16, 2026

    @awto-au
    Author

    Follow-up: separate perf bug surfaced by the same _THIS_IP_/address-of-label pattern

    While chasing a different symptom against the same 104-file lib/*.c corpus — two files reliably hanging past the c2rust-loop harness's 120s per-file timeout even after the RVV-builtin-type crash fix (d66834387) landed — I traced it to a real, fixed bug in c2rust-transpile, now on master as 3c30d4dbc.

    Symptom: lib/is_single_threaded.c (54 lines) and lib/scatterlist.c (1438 lines) each hung well past 150s wall-clock, CPU pinned at 100%, RSS flat around 350MB (ruling out a leak — this was pure repeat work, not runaway allocation).

    Root cause: DFExpr (c2rust-transpile/src/c_ast/iterators.rs), the depth-first AST walker used by Cfg::from_stmts (goto-label map) and several translator boolean-search passes, is a plain stack-based DFS with no visited-set — unlike its sibling DFNodes, which already tracks visited: HashSet<SomeId>. immediate_children() follows CTypeId children too (casts walk into their target type, Struct(decl_id) walks into the decl and all its fields), and CTypeIds are interned/deduplicated, so one large, widely-referenced type (e.g. struct task_struct, ~200 fields, reached via current/task->mm/for_each_process/for_each_thread in is_single_threaded.c) gets its entire subtree re-walked from scratch every single time DFExpr reaches another reference to it. perf record -g on the hung process showed ~22% of cycles in DFExpr::next, ~12% in TypedAstContext::contains_node, and ~20% combined in IndexMap hashing — the fingerprint of a hash-lookup-per-node DFS re-visiting the same subtree combinatorially.

    This is downstream of the same underlying pattern this issue tracks: the "Missing child"/TagCStyleCastExpr AST-exporter degradation from _THIS_IP_'s address-of-label expression (still present after the RVV fix — that fix addressed the outright panic, not this residual malformed-cast warning) is what puts extra casts/type-references into these files' ASTs in the first place, giving DFExpr more entry points into the shared type graph to blow up on. Worth keeping in mind if further AddrLabelExpr/computed-goto work happens here — the perf pathology could resurface in different files if it introduces new cast nodes referencing big shared types.

    Fix: gave DFExpr a visited set exactly like DFNodes already has. Checked all four call sites (Cfg::from_stmts's goto map, has_decl_reference, expr_initializes_bitfield, and the static-initializer checks in translator/mod.rs) — all either already dedup their results or run idempotent "does this AST contain X" boolean searches, so visiting a node twice was always redundant, never load-bearing. Pure perf fix, no behavior change.

    Before/after:

    • lib/is_single_threaded.c: hung >150s → 1.4s
    • lib/scatterlist.c: hung >150s → 7.1s
    • Full 104-file corpus rerun (scripts/run_c2rust_baseline.py --jobs 4): 66 clean / 21 crash / 17 dropped_decls, zero timeouts (previously 2/104 timed out every run). Crash/dropped_decls counts are pre-existing, unrelated failure modes.

    Commit: 3c30d4dbc4ea6a02bfd3d5e4c9e5f0e8b8b8b8b8 (see git log on master for exact hash — c_ast: memoize DFExpr traversal to fix combinatorial blowup on shared types).

  3. awto-au commented on Jul 16, 2026

    @awto-au
    Author

    Correction: full commit hash is 3c30d4dbc8b62ae28fed0566f02e65aa17127366 (I mis-typed a placeholder in the previous comment).

  4. awto-au commented on Jul 17, 2026

    @awto-au
    Author

    Fix: panic on non-empty attributed statement (`cfg/mod.rs:1793`)

    Separate crash cause from the RVV/DFExpr bugs above, hit by `lib/crypto/blake2s.c` (1 of the then-21 remaining crashing files).

    Symptom:
    ```
    thread 'main' panicked at 'Expected empty attributed statement', c2rust-transpile/src/cfg/mod.rs:1793:26
    ```

    Root cause: `CfgBuilder::convert_stmt_help`'s `CStmtKind::Attributed` arm only ever handled the case where the wrapped substatement was `CStmtKind::Empty` (e.g. a bare `fallthrough;` statement carrying the `attribute((fallthrough))`/GCC `fallthrough` attribute) — anything else panicked. That assumption doesn't hold for kernel code: `include/linux/unroll.h`'s `unrolled`/`unrolled_full`/`unrolled_count(n)` macros expand to `_Pragma("clang loop unroll(...)")`, which Clang attaches as an `AttributedStmt` wrapping the loop statement it precedes — a real, non-trivial substatement, not `Empty`.

    Confirmed directly in `lib/crypto/blake2s.c`: `blake2s_compress_generic()` has
    ```c
    unrolled_full
    for (int r = 0; r < 10; r++) {
    ...
    }
    ```
    (lines 79-80) — the `for` loop is the `AttributedStmt`'s substatement. `AstExporter.cpp`'s `VisitAttributedStmt` exports this correctly (substatement + attr list via `encodeAttribute`); this was purely a transpiler-side gap, not an AST-export/version-skew issue like the RVV bug — the exported AST was fine, `cfg/mod.rs` just never handled the non-empty case.

    Fix (`2d556ecb3`): attributes are compiler hints with no effect on program semantics (fallthrough-warning suppression, loop-unroll hints, etc.), so dropping the attribute and translating the substatement as if unwrapped is always semantically safe. Mirrors the existing `CStmtKind::Label` handling immediately above in the same match: allocate a fresh CFG label, jump to it, recurse `convert_stmt_help` on the substatement.

    Before/after, `lib/crypto/blake2s.c`: panic -> clean transpile (unrolled `for` loop translated as a normal Rust `while` loop, unroll hint dropped as intended).

    Full-corpus baseline note: by the time I ran `scripts/run_c2rust_baseline.py`, the parallel asm-operand-panic fix was present in the working tree but not yet committed, so my baseline numbers (83 clean / 20 crash / 1 dropped_decls, up from 66/21/17 after the DFExpr fix) reflect both fixes combined, not this one in isolation. Only `c2rust-transpile/src/cfg/mod.rs` is in my commit `2d556ecb3` — I left the in-flight `assembly.rs` changes alone.

    Commit: 2d556ecb3

  5. awto-au commented on Jul 17, 2026

    @awto-au
    Author

    Third fix this session: GNU asm-goto (extended asm label operands) panic — fixed in 03b787a

    Separate bug from the two above, but same symptom class: a hard panic aborting the whole transpile run instead of a graceful per-declaration skip.

    Symptom

    20 of the (then-)21 remaining crashing files in the 104-file lib/*.c corpus hit an identical panic:

    thread 'main' panicked at 'no operand had index 2 in asm str:
    ...
    j	${2:l}
    ...
    

    All 20 traced to riscv's ALTERNATIVE() macro (arch/riscv/include/asm/alternative-macros.h), a CPU-errata-workaround patching mechanism used pervasively in locking primitives and memory barriers — hence the high fan-out (transitively pulled in by nearly everything).

    Root cause

    ${2:l} is GCC/Clang extended-asm syntax for "operand index 2, l (label) modifier" — a label operand, GCC's third operand class beyond outputs/inputs, used for GNU asm goto. Clang's GCCAsmStmt lays these out contiguously after outputs and inputs in a single Exprs array (clang/AST/Stmt.h, GCCAsmStmt::begin_labels()/getNumLabels()), so they share the same 0..N index space referenced by the asm template.

    Neither c2rust-ast-exporter's VisitGCCAsmStmt (AstExporter.cpp) nor the Rust-side CStmtKind::Asm/AsmOperand model label operands at all — only begin_inputs()/end_inputs() and begin_outputs()/end_outputs() are walked. So a template referencing a label-operand index has no corresponding entry in inputs/outputs, and c2rust-transpile's operand-index lookup (translator/assembly.rs) previously did .unwrap_or_else(|| panic!(...)) on the miss, aborting the whole process.

    Fix

    Graceful degradation, not full support: detect asm-goto templates up front (any operand reference >= outputs.len() + inputs.len()) and return a TranslationError; also hardened the operand-index lookup itself to return an error instead of panicking, as defense in depth. This routes through the existing per-declaration error-catch-and-skip machinery, so only the containing function is skipped (with a clear diagnostic), while the rest of the translation unit still transpiles.

    Chose this over full label-operand support (which would mean plumbing a labels: Vec<..> field through the exporter and CStmtKind::Asm, and emitting Rust's own asm! label-operand syntax) because ALTERNATIVE()'s self-modifying-code-style linker-section patching has no faithful Rust equivalent regardless, and this transpiler is used as a differential/reference tool here — asm output is always hand-reviewed and reimplemented, never trusted as-is.

    Before/after (104-file linux-rs lib/*.c corpus)

    clean crash dropped_decls no_output timeout
    before 83 21 0 0 0
    after 84 0 20 0 0

    The remaining 20 are the asm-goto functions themselves, now skipped with Failed to translate <fn>: Cannot translate GNU asm goto (extended asm with label operands): ... instead of aborting their whole file.

    Commit: 03b787a

  6. awto-au commented on Jul 17, 2026

    @awto-au
    Author

    Upstream PR review pass: 6 merged, ~10 evaluated-and-skipped

    Went through immunant/c2rust's open PR queue looking for safe, real improvements to cherry-pick onto our fork. Triaged ~49 open PRs by title/diff size, seriously evaluated 16, merged 6.

    Merged (all: clean cherry-pick or hand-adapted conflict, build passes, full 104-file corpus baseline unchanged — no regression)

    PR Commit(s) What
    #858 fa045c140 ast-exporter: don't assert-crash converting a constant-int expr to APInt when Clang's evaluator produced no value (respects the Optional instead of unwrapping blindly)
    #1827 7680f8909 relooper: avoid recomputing strict_reachable_from (a transitive closure) on every single-entry-no-backedge CFG node — was O(n^2)-ish on long linear control flow, same performance-bug shape as our own DFExpr memoization fix
    #1893 817c54c32 transpile: fix ctx mutation ordering bug in convert_cast — literal-cast-skip path was leaking ctx changes back to the caller when it shouldn't
    #1135 243b3c6d5 transpile: expr_stmt→semi_stmt fix for volatile post/pre-increment assignment codegen (was emitting code that doesn't compile in some volatile-increment shapes) — required hand-resolving a conflict in tests/unit/ints/ (test file had moved/restyled locally since the PR was authored)
    #1822 b43792ebc, c8c5b3167 Use raw identifiers (r#type etc.) for C identifiers that collide with Rust reserved words, instead of failing/mangling — real correctness gap, kernel C uses type/move/etc. as ordinary field and parameter names
    #1791 67df081a9 transpile: only attach macro_expansion_text to actual expression nodes — was inserting it keyed by non-CExprId node ids too, a type-confusion bug

    Evaluated and skipped

    Baseline note

    Before this pass, run_c2rust_baseline.py's DB-recorded reference (84 clean / 0 crash / 20 dropped_decls) was from 2d556ecb3. Re-running that exact commit today reproduces 13 clean / 20 crash / 71 dropped_decls instead — the linux-riscv corpus tree itself has moved on (linux-rs is actively translating lib/*.c files, TU 27→29 landed today) and now exercises more of the still-open _THIS_IP_/__LOCK_BH GNU-label-address gap this issue is tracking. Not a c2rust regression — confirmed by bisecting back to 2d556ecb3 and reproducing the same numbers there. Established a fresh same-day reference point instead (13 clean / 91 dropped_decls / 0 crash on 03b787a08+immunant#858) and diffed every subsequent merge against that; all 6 merges above hold that line exactly, no crashes introduced, no clean count regression.

    Scratch pr-eval-* branches cleaned up. All 6 commits pushed to master individually as I went.

  7. awto-au commented on Jul 17, 2026

    @awto-au
    Author

    Upstream PR review pass #2: 4 merged (+1 hotfix), 4 evaluated-and-skipped

    Reviewed the remaining 46 open, non-draft PRs in immunant/c2rust's queue that the first pass (see comment above) didn't touch, starting from 67df081a9. Of those 46, most were out of scope on a fast title/files triage (pdg/, c2rust-analyze/, dynamic_instrumentation/, c2rust-refactor/, c2rust-postprocess/, CI/build/nix/toolchain-only, or pure refactors with no behavior change) or turned out to be drafts despite showing up in the open-PR listing (isDraft isn't filtered by a plain gh pr list --json query — 15 of the "46" were actually drafts, including several that looked like strong transpile/ast-exporter candidates by title, e.g. enum/case-translation and c_ast-splitting work). Excluding drafts and out-of-scope crates left 8 real candidates in c2rust-transpile/c2rust-ast-exporter, of which 4 merged cleanly.

    Merged (all: full 510-file whole-kernel-corpus baseline unchanged vs 67df081a9 — 24 clean / 425 dropped_decls / 61 crash throughout, 0 regressed declarations end-to-end per c2rust-regress)

    PR Commit(s) What
    #290 4b720c54c transpile: map C integer literals {0, 1} used in boolean contexts straight to Rust true/false instead of 1 as c_int != 0 — pure codegen readability simplification, same family as the existing comparison-operator-unwrap simplification right above it
    n/a 3e2bfda52 Hotfix, not an upstream PR: my own conflict resolution for immunant#290's cherry-pick left a syn::ExprLit reference without adding it to the use syn::{...} list, so master did not actually compile from 4b720c54c through 8d6a2b3da. Caught by a from-scratch rebuild after earlier incremental builds gave false-positive "succeeded" results (a | tail in my own build commands was swallowing cargo's non-zero exit code). Fixed and force-verified with explicit exit-code capture from here on.
    #1879 62c070554 transpile: overflow builtins (__builtin_*_overflow) now detect truncation via result_ty::try_from(..).is_err() instead of casting the i128 intermediate back up and comparing — the old round-trip comparison could be wrong when the narrow result type is unsigned but the i128 intermediate is negative; try_from is unconditionally correct
    #1856 8d6a2b3da transpile: cast an assignment expression's result (e.g. size_t s1 = l = 1;) to the surrounding expression's expected type — previously only the assignment's own LHS/RHS types were considered, so the result of an assignment-used-as-expression could carry the wrong Rust type. Squashed from the PR's 7 incremental commits (commit-by-commit cherry-pick produced spurious conflicts against our fork's diverged history; the squashed diff applied clean)
    #1860 59dfd1a99 transpile: thread the caller's expected type through convert_builtin and cast its result once at the end, fixing several builtins whose translated Rust type didn't match the call site (__builtin_ffs/__builtin_clz/__builtin_ctz/__builtin_popcount returning u32 instead of the expected c_int, __builtin_alloca's pointer cast, __builtin_object_size's result type, va_start/va_copy/va_end early-return cleanup). These builtins are pervasive in kernel code, so this is directly relevant to corpus coverage even though today's crash/dropped_decls counts didn't move (the underlying declarations were already failing earlier in translation for unrelated reasons — this fixes correctness for cases that do get this far)

    Evaluated and skipped

    Process note

    Partway through this pass a second, concurrent session was actively developing (uncommitted) PCH-based performance work for run_c2rust_baseline.py in the same working tree, which briefly made dev.py c2rust-baseline report a mass no_output failure mode unrelated to any of the merges above (traced to a Clang PCH artifact that doesn't yet handle this corpus correctly, matching the exact caveat already documented earlier in this issue: PCH parses fine standalone but degrades AST-export correctness under c2rust transpile). Waited for that session's in-flight run to finish rather than touching its uncommitted files, then re-validated once the shared tmp/c2rust-pch/ artifact had regenerated correctly. No files belonging to that other work were modified.

    Scratch pr-eval-* branches cleaned up. All 5 commits (4 upstream cherry-picks + 1 hotfix) pushed to master individually.

  8. awto-au commented on Jul 17, 2026

    @awto-au
    Author

    Community-fork triage pass: 1 merged, 32 evaluated-and-skipped

    Different search space from the upstream-PR-review passes above: this pass looked at commits sitting on personal forks of immunant/c2rust that were never submitted as PRs at all (found via GitHub's compare API across all 353 forks; 33 forks had 1+ real unique commits ahead of immunant/c2rust:master). Reviewed all 33, ranked by commits-ahead, reading each fork's actual diff via gh api repos/immunant/c2rust/compare/master...<owner>:<branch>.

    Merged

    Fork commit Our commit What
    PsychoLlama/c2rust@99b44da49 (branch jesse.gibson/main) 056cc19c9 c2rust-bitfields: implement FieldType for usize/isize. Our transpiler emits bitfields with ty = "usize"/"isize" for C bitfields backed by size_t/ssize_t/ptrdiff_t/etc. (convert_type.rs's CTypeKind::Size/SSize/PtrDiff/IntPtr/UIntPtr handling), but c2rust-bitfields's FieldType trait was only implemented for the fixed-width integer types — so generated code for such bitfields failed to compile with "the trait FieldType is not implemented for usize". One-line fix: add usize/isize to the impl_int! macro's type list (bit read/write logic is already width-agnostic). Clean cherry-pick, full 510-file baseline unchanged (24 clean / 425 dropped_decls / 61 crash), 0 regressed declarations per c2rust-regress (14987 decls, 0 regressed / 0 fixed — expected, since this affects downstream Rust-compile success on already-translated bitfields, not transpile-time crash/dropped_decls counts).

    Flagged leads, investigated first

    Evaluated and skipped

    Unrelated crate / out of scope (touches only c2rust-analyze/, c2rust-refactor/, pdg/, dynamic_instrumentation/, ancient pre-crate-split ast-importer/, or a from-scratch rewrite into an unrelated target language):
    omarandlorraine/c2rust (17 of 18 commits are dependabot/CI; the 1 transpile-touching line is a pure cargo clippy --fix ids.len() == 0 → ids.is_empty() style change, zero behavior difference), lookibed/c2rust (wholesale fork into "c2dascript", a different C-to-DAS-language transpiler built on c2rust's skeleton — not a fix), anp/c2rust (base commit from 2018, 7726 commits behind, targets the pre-crate-split ast-importer architecture that no longer exists), fnetz/c2rust (restores/builds the legacy Lua-scripting c2rust-refactor crate on a pre-2021 codebase), AntonyBlakey/c2rust, kvinwang/c2rust (typo fix, but in c2rust-refactor/).

    Idiom/output-quality only (deferred per project stage), or already superseded by prior work in this session:
    kings-crown/c2rust (6 commits — real-looking attempt to thread an "expected type" through literal/character-literal and overflow-builtin conversion, but both ideas are already solved differently and more robustly in our current tree: the overflow-builtin type-mismatch case is already fixed by the already-merged immunant#1879/immunant#1860 try_from-based convert_overflow_arith, and the character-literal cast-target case is already handled via override_ty.unwrap_or(ty) parameter-threading through convert_expr/convert_literal, not a new ExprContext field. Applying it would mean regressing to a less-robust overflow implementation and adding a redundant, conflicting second type-threading mechanism), dgherzka/c2rust (bool→float cast fix already present verbatim in our mod.rs at the target_ty_kind.is_floating_type() && source_ty_kind.is_bool() arm; its second change, optional derive(Debug) for structs, is idiom/output-quality and gated behind a tcfg.derive_debug config flag that doesn't exist in our fork — deferred per this project's reliability-first scope), davidMcneil/c2rust (raw-identifier-for-reserved-keywords fix already present verbatim in our renamer.rs, superseded by already-merged immunant#1822), xtmono/c2rust (adds big-endian bitfield support via a new endian attribute — a feature addition with an API-breaking FieldType::set_field/get_field signature change, not a bug fix; irrelevant to linux-rs's little-endian RISC-V corpus).

    Doesn't apply cleanly / too stale / architecturally diverged:
    richardscollin/c2rust (10 commits, 2121 behind — proc-macro/library-usage + Rust-2024-edition codegen changes, output-format not correctness, includes an explicit "KLUDGE: DON'T MERGE THIS COMMIT" commit), nvxiang/other-c2rust (touches real c2rust-transpile files but the changes are Chinese-language debug comments and unconditional println!/eprintln! calls left in the per-declaration translation hot path — debug scaffolding, not a fix).

    WIP/experimental by the fork author's own commit messages:
    Lysarina/flexc2rust ("WIP", "Misc", "Modding").

    Config/CI/docs/build-only, no functional Rust-side change:
    Saldivarcher/c2rust (.travis.yml + build script), wolverian/c2rust (devcontainer/vscode/nix), WSLUser/c2rust (PNG image optimization), livstyle/c2rust (toolchain channel pin), mstone/c2rust (flake.nix), anoraktrend/c2rust (Cargo.toml features), leongross/c2rust (submodule dependency bump, unrelated xzoom example), thomasyonug/c2rust + seenry/c2rust (proc-macro2 version pins), mtlll/c2rust (Vagrantfile), Gonzih/c2rust (nixos cc-wrapper scripts), backwardn/c2rust (merge-only, no real diff), zdimension/c2rust, k2662/c2rust, jwku/iffy-iffy-c2rust, gkielian/c2rust, destenson/immunant--c2rust (CI/toolchain/README/workflow only). PsychoLlama/c2rust's remaining 5 commits beyond the merged bitfields fix are all Nix/flake build-scoping work — skipped as out of scope, only the bitfields fix was cherry-picked.

    Baseline note

    Full 510-file corpus baseline unchanged throughout this pass: 24 clean / 425 dropped_decls / 61 crash, same as the starting point at c3df84d10. The one merge (056cc19c9) doesn't move these counts because it fixes a downstream Rust-compile error for already-"clean"/dropped-decl-affected bitfield code, not a transpile-time crash or decl-drop; confirmed via c2rust-regress c3df84d10 056cc19c9: 0 regressed, 0 fixed, 14987 decls unchanged, VERDICT: OK.

    Scratch fork remotes (psychollama, kingscrown) added and removed after use; no other files touched.

  9. awto-au commented on Jul 17, 2026

    @awto-au
    Author

    Update (2026-07-17): still open, root cause reconfirmed, symptom has shifted

    Re-verified from scratch against current master (5582fcd). The bug is
    still 100% live — VisitAddrLabelExpr (AstExporter.cpp) still only
    printDiags and drops the node, never calling encode_entry. Confirmed via
    a fresh full-corpus baseline (552 files) plus direct isolated reruns.

    Symptom has changed since this issue was filed. The originally-reported
    TagTypeUnknown expecting 3 panic (conversion.rs) no longer reproduces
    anywhere in the current 552-file corpus — 0 occurrences in any transpile
    log. But this is NOT because the bug is fixed: it's because something
    between 19afbea41 (this issue's filing rev) and 5582fcd7e changed which
    panic fires first when a _THIS_IP_-affected file's dropped AddrLabelExpr
    node leaves a hole in the AST. The same 51 files that currently crash now
    hit cfg/mod.rs:744 ("IndexMap: key not found", CFG builder can't find a
    node — most common) or translator/mod.rs:4945/:1262 ("Expected decl
    name") instead — downstream consequences of the same missing-node root
    cause, just tripped over by different code before ever reaching the old
    type-conversion fallthrough.

    Corpus-wide impact, freshly measured (552-file corpus, --include-slow):

    • 518 files hit _THIS_IP_/__LOCK_BH/raw_spin_lock_bh/raw_spin_unlock_bh
      directly; 460 also hit __sb_writers_acquired/__sb_writers_release
      (same root cause, different call chain: percpu_rwsem_acquire/release).
    • Of the currently-crash-outcome files, all reproduce a panic in
      cfg/mod.rs:744, translator/mod.rs:4945, or translator/mod.rs:1262 —
      not conversion.rs's TagTypeUnknown arm, which no longer appears to be
      reachable via this path in the current tree.

    Fix in progress: give AddrLabelExpr a real CBOR tag (TagAddrLabelExpr)
    instead of dropping it, matching every other TagXxx visitor's shape (always
    encodes, unconditionally — no exporter-side gating, consistent with how
    WarnOn/FlsFamily/SwapMemSwap only gate Rust-side behavior). On the Rust
    side, a new CExprKind::AddrLabel(CQualTypeId) (distinct from BadExpr,
    which is deliberately wired to a hard translation error) converts to a
    placeholder 0usize-shaped value when a new KernelIdiomRule is enabled,
    or the same "unsupported" TranslationError as today when it's not —
    preserving the stock-upstream-byte-identical-with-no-flags invariant.
    Placeholder (not real PC capture) is correct because every _THIS_IP_ use
    in this corpus flows only into lockdep-internal debug-annotation sinks that
    are no-ops with CONFIG_LOCKDEP off (this build's actual config) — verified
    before choosing this over attempting core::panic::Location::caller()
    (wrong type: &'static Location vs unsigned long, needs #[track_caller]
    threading that doesn't exist in the wrapped call chains).

  10. awto-au commented on Jul 17, 2026

    @awto-au
    Author

    Fix landed (2026-07-17, commit bf319b9)

    TagAddrLabelExpr now always encodes the node (matches every other
    Tag* visitor's shape) instead of dropping it; Rust side gets a new
    CExprKind::AddrLabel, gated behind KernelIdiomRule::AddrLabelPlaceholder
    — substitutes a placeholder value (implicit_default_expr, the same "zero
    value of this C type" helper ImplicitValueInit uses) when enabled,
    returns the same translation-error outcome as before when it's not
    (verified byte-identical stock output with no --enable-rule flags).

    Also fixed a real, separate bug found while wiring this up:
    convert_statement_expression's GNU-statement-expression handling only
    recognized a bare Expr as a block's final statement — _THIS_IP_'s
    expansion ends in a LabelStmt wrapping the result expression (C
    requires a statement after a label), which fell through to the
    StmtExprVoid path and silently discarded the placeholder value,
    producing a ()-typed block where the caller expected unsigned long
    (confirmed via a real rustc compile error before this second fix).

    Full corpus impact (552 files, --include-slow)

    Before: 24 clean / 467 dropped_decls / 51 crash (542 files, pre---include- slow convention)
    After: 216 clean / 265 dropped_decls / 71 crash (552 files)

    Verified via dev.py c2rust-regress 5582fcd7e bf319b94b: 0 new/removed
    decls, 3883 unchanged-still-translating, but 294 declarations across 16
    files flagged as "regressed"
    (was translated, now isn't) — root-caused,
    not a flaw in this fix:

    All 16 files (drivers/char/random.c, drivers/tty/tty_ioctl.c,
    fs/file.c, fs/fs-writeback.c, fs/fserror.c, fs/kernfs/dir.c,
    fs/pipe.c, fs/super.c, kernel/async.c, kernel/printk/printk.c,
    kernel/rcu/srcutiny.c, kernel/rcu/sync.c, kernel/umh.c,
    lib/percpu-refcount.c, mm/oom_kill.c, mm/vmscan.c) previously had
    outcome=dropped_decls (partial success — some declarations individually
    blocked by the _THIS_IP_ gap, others translated fine) and now have
    outcome=crash, confirmed identical in all 4 spot-checked files to the
    pre-existing cfg/mod.rs:744 "IndexMap: key not found" panic already
    documented in this session's earlier crash-signature audit (the dominant
    cause of the original 51 crashes, ~30/51). Mechanism: these files used to
    have _THIS_IP_ block translation early, before ever reaching the point
    that triggers the unrelated cfg/mod.rs:744 bug; with that early block
    removed, translation proceeds further and now reaches it — and a full-
    file crash zeroes every declaration's recorded outcome, even ones that
    individually would have (and previously did) succeed.

    Net effect is strongly positive (+192 files net improvement in outcome
    tier) but the regression gate correctly flags this local tradeoff for 16
    files rather than silently passing it — recorded here rather than treated
    as a hidden cost. No action taken to special-case these 16 files (would
    require predicting a downstream crash before it happens, real added
    complexity for a bug that's already tracked and will fix all of them at
    once when addressed).

    Corpus-wide _THIS_IP_/lock-bh family: fully closed

    grep -c "address of label" tmp/c2rust-baseline/*/transpile.log with the
    rule enabled: 0 occurrences anywhere in the current 552-file corpus.

  11. added
    P4Known limitation / investigation, not an active fix target
    on Jul 17, 2026
  12. awto-au commented on Jul 17, 2026

    @awto-au
    Author

    Fixed: commit bf319b9 (addr-label-placeholder rule) resolves the THIS_IP/&&label AddrLabelExpr case this issue tracks. See the detailed comment thread above for full before/after corpus numbers.

  13. removed
    P4Known limitation / investigation, not an active fix target
    on Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions