Rust signature printer: maddi's verdicts as an ownership inference, rustc as an oracle for the linking engine
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 30/100
Hướng nghiên cứu
Start by reading the existing Kotlin and C# printer infrastructure and the stored verdicts for Fernflower and LangChain4j core. Add a Rust signature printer, then translate a handful of small Fernflower class bodies in strict mode and compile them with rustc and clippy. Classify each rejection against the issue's table; done means reporting whether the results support an oracle ratchet, a full translator, or neither.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Idea
maddi's analyses already infer most of what Rust makes a programmer write down. Rust is built on aliasing XOR mutability; the Road to Immutability splits the same idea over two axes: modification (rule 1, @Modified/@NotModified) is the mutability half, independence (rule 3) is the aliasing half. A type that is dependent and modifiable from outside, the case that stops a type at @FinalFields, is exactly what Rust refuses without RefCell. An unmodifiable view is a &Vec<T> borrow; a Bag that owns and mutates a private list is an owned Vec<T>.
The experiment: a Rust signature printer that prints struct and method signatures from the existing verdicts, and, as a second step, a body-faithful translation of a few small classes, compiled by rustc. It either confirms that maddi's verdicts amount to an ownership inference, or shows where they don't.
The mapping
Each row has a test in maddi-cst-print-rust (TestJavaToRustMapping unless another class is named): a Java example with the verdicts the analyzer computes for it, the printed Rust, and rustc's judgement. Where there is one, the counterpart (the neighbouring or missing verdict) and the error rustc gives for it are listed as well. The same examples run through the CLI with the analyzer's own verdicts; see rust-printing.md, §"The table of #167, row by row".
| maddi | Rust | test (counterpart: rustc's error) |
|---|---|---|
@NotModified parameter |
&T |
parametersAndReceivers |
@Modified parameter |
&mut T, also when only its elements are modified |
parametersAndReceivers; elementModificationMissed: E0596 |
| non-modifying method | &self |
parametersAndReceivers; TestJavaToRustOwnership#correctVerdictsCompile |
| modifying method | &mut self |
parametersAndReceivers; TestJavaToRustOwnership#wrongModificationVerdict: E0596 |
@Container |
takes its arguments by &, never &mut |
container |
| independent constructor parameter (copied) | borrowed &T, copied by the constructor (.clone()) |
independentConstructorParameter; independentConstructorParameterCalledDependent: E0382 |
| dependent field (stores what it is given) | a field borrowing with a lifetime parameter: struct View<'a> { list: &'a Vec<T> } |
dependentField (outliving the list: E0515); TestJavaToRustOwnership#dependentFieldIsBorrowed (modified while borrowed: E0502) |
hidden content (T, §xs) |
an opaque generic T, handed out as &T |
hiddenContent |
@Immutable(hc=true) |
shared as Rc<T> without a lock; the hidden content cloned under T: Clone |
immutableHcShared; TestJavaToRustStructure#recordWithHiddenContentIsShared |
@Finalizer |
a method taking self by value: the move consumes the object (Rust enforces the sequencing rules the book leaves unenforced; iterator adapters take self for the same reason our stream operations are @Finalizer) |
finalizer (a call after the finalizer: E0382) |
@Mark / @Only(before/after) |
the typestate pattern: Config<Before> → Config<After>, the mark consuming self |
typestate (a call before the mark, a second mark: E0599) |
@IgnoreModifications on a method (log methods) |
interior mutability: &self over RefCell fields |
ignoreModifications; ignoreModificationsMissing: E0596 |
@StaticSideEffects (System.setOut) |
static state as static mut; the method an unsafe fn |
staticSideEffects; staticSideEffectsMissing: E0133 |
| nullability | Option<T> |
nullability |
The engines are related too. Rust's newer borrow checker, Polonius, is formulated as Datalog over subset constraints between origins plus loan liveness per program point, structurally close to the link closure (⊆ links, incremental fixpoint with witnesses). The difference is flow sensitivity: the link closure is path-insensitive on purpose. Nullability already has the two-layer shape a borrow analysis would need: a path-insensitive closure for declarations plus a flow-sensitive walk (NonNullFacts) for use sites.
Why: a stronger oracle for the linking engine
kotlinc judges two of our verdicts (nullability, and read-only vs mutable for collections). rustc's borrow checker would judge modification and independence on every parameter, receiver and field. Printing signatures straight from the verdicts and letting rustc check the bodies against them, rejections point at specific verdicts:
| rustc says | likely analyzer issue |
|---|---|
cannot borrow as mutable behind a & reference (E0596) |
a @NotModified/non-modifying verdict the body contradicts: an unsound modification verdict, at the exact line |
| borrowed value does not live long enough, lifetime errors (E0597, E0716, …) | a missed dependence: a field that keeps a reference it was given, while the verdict said independent |
| use of moved value (E0382) | an ownership or @Finalizer assumption that doesn't hold |
| overlapping mutable and immutable borrows (E0502, E0499) | aliasing plus modification the verdicts didn't see together |
Three caveats decide whether the signal is usable:
- A strict mode with no escape hatch. A
Rc<RefCell<…>>fallback wherever a verdict looks doubtful silences rustc; the error becomes aBorrowMutErrorpanic at run time, only if a test reaches it. Oracle runs print the plain types the verdicts imply. - rustc catches unsoundness, not imprecision. A
@Modifiedthat could be@NotModifiedcompiles, since&mutis accepted where&would do. Clippy'sneedless_pass_by_ref_mutandunused_mutgive the other direction: unsafe and noise columns, as in the Guava nullability oracle. - Not every rejection is maddi's. rustc's checker is itself conservative; the OOPSLA 2023 aliasing study found it the main precision limit when translating C to safe Rust. Triage rule: verdict "not modified" and rustc needs
&mut→ analyzer wrong; verdict "dependent" and rustc rejects the borrow → analyzer right, the printer chose the wrong representation (needsRcor a clone).
A signatures-only printer with todo!() bodies checks nothing (rustc doesn't look at the bodies), so step 2 is required for the oracle; step 1 is for reading plausibility by eye.
Plan
- Signatures for Fernflower and LangChain4j core, from the stored verdicts (reusing the printer infrastructure of the Kotlin and C# printers). Read them: does
&vs&mutlook right? Do dependent fields land where a Rust programmer would put a lifetime? How much would needRc<RefCell<…>>? - Bodies for a handful of small Fernflower classes, strict mode, compiled with rustc and clippy; classify every rejection with the table above.
- Decide: an oracle ratchet like the J2K/C# ones, a full translator, or neither.
Out of scope for now: parsing Rust into the CST. Macros, traits with associated types, pattern matching and lifetime generics, realistically through rustc's MIR, cost more than the K2 front end did, and most of what maddi would compute on Rust the borrow checker has already proven.
A full translator, later: library support
Rust equivalents of the JDK (the mapping, as for C#'s JDK→BCL): collections in std (Vec, HashMap, BTreeMap, VecDeque, HashSet), regex, chrono/jiff, num-bigint, rust_decimal/bigdecimal, serde_json for Jackson, log/tracing for slf4j, std::sync/crossbeam/rayon. No crate solves: UTF-16 String semantics (charAt, length) need a shim; no class inheritance, so superclasses become traits plus composition (the largest printer problem); exceptions become Result or panics; equals/hashCode become Eq/Hash.
Prior art. No mature Java→Rust translator that infers ownership. The closest is C→Rust: C2Rust produces unsafe Rust, and Crown (CAV 2023) adds a static ownership analysis to turn C pointers into safe Rust, our analysis in a different place.
Bridges to the Java not yet translated:
- Rust calling Java: the
jnicrate (JNI Invocation API, embedding a JVM),j4rson top of JNI (not verified). - GraalVM Native Image
--shared: a native library whose@CEntryPointfunctions Rust calls through the C ABI; non-object types only, so object graphs don't cross. - Java calling Rust: Panama FFM (final since JDK 22) against a Rust
cdylib. The realistic migration path: translate one module at a time.
The verdicts help at the boundary: only an immutable or independent value can be copied across safely; a dependent one is an object shared between two runtimes.
Related, separately: lifetimes for Java
maddi does not track when objects become unreachable, but it could track outlives over the link graph: x linked by ∈ into a collection reachable from a static field lives as long as the program; a listener registered and never removed lives as long as its subject. That is Rust's 'a: 'b, and its first application would be leak detection. The book's Lazy blanking out supplier = null is already a lifetime statement.
References
- Ownership guided C to Rust translation (Crown, CAV 2023): https://arxiv.org/abs/2303.10515v1
- Aliasing Limits on Translating C to Safe Rust (OOPSLA 2023): https://2023.splashcon.org/details/splash-2023-oopsla/20/Aliasing-Limits-on-Translating-C-to-Safe-Rust
- jni crate: https://docs.rs/jni/0.21.1
- GraalVM Native Image, interoperability with native code: https://docs.oracle.com/en/graalvm/jdk/24/docs/reference-manual/native-image/native-code-interoperability
- rust-calls-java example: https://github.com/rimerosolutions/rust-calls-java
🤖 Generated with Claude Code
- Ngôn ngữ chính
- Java
- Star
- 1
- Fork
- 1
- Merge trung bình
- 1 giờ 13 phút
- Pull request đã merge (30 ngày)
- 6
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của CodeLaser/maddi
-
Javadoc warnings are the only noise on an otherwise clean CI runCó thể đã có người làm @jayeshamundkar06-bit đã nhận 4 ngày trước. Đang mởbuild/ci good first issue
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
CodeLaser/maddi#4 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
front-end:csharp
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 68/100
Maintainer thường phản hồi trong vòng 1 ngày
-
bug
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 61/100
CodeLaser/maddi#185 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 14/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của CodeLaser/maddi
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 77/100
NationalSecurityAgency/ghidra#10149 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 90/100
yegor256/antlr2ebnf-maven-plugin#155 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
account bug exercise lecture programming user interface
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
ls1intum/Artemis#14512 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
kind:bug priority:major
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
swiftlang/swift-java#955 ·
Maintainer thường phản hồi trong vòng 1 ngày