Skip to content

Commit 1a1c897

Browse files
committed
Support for impl blocks, and new test confirming this.
1 parent baa89e5 commit 1a1c897

File tree

8 files changed

+360
-0
lines changed

8 files changed

+360
-0
lines changed

Readme.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ All examples live in `tests/binary` and are compiled to JVM bytecode & run/teste
3232
- **[Collatz conjecture](tests/binary/collatz/src/main.rs)** verifier
3333
- **[Large prime](tests/binary/primes/src/main.rs)** generator
3434
- Use of nested data structures: enums, structs, tuples, arrays, slices (**[enums](tests/binary/enums/src/main.rs)**, **[structs](tests/binary/structs/src/main.rs)** - both tests use arrays and tuples)
35+
* **[Implementation blocks](tests/binary/impl/src/main.rs)**
3536
- …and more!
3637

3738
---
@@ -50,6 +51,7 @@ All examples live in `tests/binary` and are compiled to JVM bytecode & run/teste
5051
- Structs, tuples, enums (both C‑like and Rust‑style)
5152
- Executable `.jar` generation for binary crates
5253
- Mutable borrowing, references, and dereferencing
54+
- Implementations for ADTs, including using and returning `self`, `&self`, `&mut self`
5355
- **Integration tests** for all features, in debug and release modes
5456

5557
🚧 **Next Milestone:** Full support for the Rust `core` crate.

src/lib.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use rustc_data_structures::fx::FxIndexMap;
3131
use rustc_metadata::EncodedMetadata;
3232
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
3333
use rustc_middle::ty::TyCtxt;
34+
use rustc_hir::{TyKind as HirTyKind, QPath};
3435
use rustc_session::{Session, config::OutputFilenames};
3536
use std::{any::Any, io::Write, path::Path};
3637

@@ -96,6 +97,55 @@ impl CodegenBackend for MyBackend {
9697
.insert(oomir_function.name.clone(), oomir_function);
9798

9899
oomir_module.merge_data_types(&oomir_result.1);
100+
} else if let rustc_hir::ItemKind::Impl(impl_a) = item.kind {
101+
let ident = match impl_a.self_ty.kind {
102+
HirTyKind::Path(qpath) => {
103+
match qpath {
104+
QPath::Resolved(_, p) => {
105+
format!("{}", p.segments[0].ident)
106+
}
107+
QPath::TypeRelative(_, ps) => {
108+
format!("{}", ps.ident)
109+
}
110+
_ => {
111+
println!("Warning: {:?} is an unknown qpath", qpath);
112+
"unknown_qpath_kind".into()
113+
}
114+
}
115+
}
116+
_ => {
117+
println!("Warning: {:?} has unknown kind", impl_a.self_ty);
118+
"unknown_type_kind".into()
119+
}
120+
};
121+
for item in impl_a.items {
122+
let i = item.ident;
123+
let def_id = item.id.owner_id.to_def_id();
124+
125+
if tcx.generics_of(def_id).count() != 0 {
126+
println!("Skipping generic impl: {i}");
127+
continue; // Skip generic functions for now
128+
}
129+
let instance = rustc_middle::ty::Instance::mono(tcx, def_id);
130+
let mut mir = tcx.optimized_mir(instance.def_id()).clone(); // Clone the MIR
131+
132+
let i = format!("{}_{}", ident, i).to_lowercase();
133+
134+
println!("MIR for function {i}: {:?}", mir);
135+
136+
println!("--- Starting MIR to OOMIR Lowering for function: {i} ---");
137+
let oomir_result = lower1::mir_to_oomir(tcx, instance, &mut mir);
138+
println!("--- Finished MIR to OOMIR Lowering for function: {i} ---");
139+
140+
let mut oomir_function = oomir_result.0;
141+
oomir_function.name = i.clone();
142+
143+
oomir_module
144+
.functions
145+
.insert(i, oomir_function);
146+
147+
oomir_module.merge_data_types(&oomir_result.1);
148+
}
99149
}
100150
}
101151

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[build]
2+
rustflags = [
3+
"-Z", "codegen-backend=/Users/mreeve27/rustc_codegen_jvm/target/debug/librustc_codegen_jvm.dylib",
4+
"-C", "linker=/Users/mreeve27/rustc_codegen_jvm/java-linker/target/debug/java-linker",
5+
"-C", "link-args=/Users/mreeve27/rustc_codegen_jvm/library/build/distributions/library-0.1.0/lib/library-0.1.0.jar /Users/mreeve27/rustc_codegen_jvm/library/build/distributions/library-0.1.0/lib/kotlin-stdlib-2.1.20.jar /Users/mreeve27/rustc_codegen_jvm/library/build/distributions/library-0.1.0/lib/annotations-13.0.jar --r8-jar /Users/mreeve27/rustc_codegen_jvm/vendor/r8.jar --proguard-config /Users/mreeve27/rustc_codegen_jvm/proguard/default.pro"
6+
]
7+
8+
# Throwing a JVM exception will unwind and give a stack trace, no need for rust to handle unwinding.
9+
[profile.debug]
10+
panic = "abort"
11+
12+
[profile.release]
13+
panic = "abort"
14+
rustflags = [
15+
"-C", "link-args=--release"
16+
]

tests/binary/impl/Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/binary/impl/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
cargo-features = ["profile-rustflags"]
2+
3+
[package]
4+
name = "impl"
5+
version = "0.1.0"
6+
edition = "2024"
7+
8+
[dependencies]

tests/binary/impl/java-output.expected

Whitespace-only changes.

tests/binary/impl/no_jvm_target.flag

Whitespace-only changes.

tests/binary/impl/src/main.rs

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
// A struct representing a simple counter with a name (using a static string slice)
2+
struct NamedCounter {
3+
name: &'static str,
4+
count: u32,
5+
limit: u32,
6+
enabled: bool,
7+
}
8+
9+
impl NamedCounter {
10+
// --- Associated Functions (Constructors) ---
11+
12+
// Primary constructor
13+
fn new(name: &'static str, limit: u32) -> Self {
14+
NamedCounter {
15+
name,
16+
count: 0,
17+
limit,
18+
enabled: true, // Start enabled by default
19+
}
20+
}
21+
22+
// Another constructor for a disabled counter
23+
fn new_disabled(name: &'static str, limit: u32) -> Self {
24+
let mut counter = Self::new(name, limit); // Call the primary constructor
25+
counter.enabled = false;
26+
counter
27+
}
28+
29+
// --- Methods taking &self (Immutable Access) ---
30+
31+
// Get the current count
32+
fn get_count(&self) -> u32 {
33+
self.count
34+
}
35+
36+
// Get the name
37+
fn get_name(&self) -> &'static str {
38+
self.name
39+
}
40+
41+
// Get the limit
42+
fn get_limit(&self) -> u32 {
43+
self.limit
44+
}
45+
46+
// Check if the counter is enabled
47+
fn is_enabled(&self) -> bool {
48+
self.enabled
49+
}
50+
51+
// Check if the counter has reached its limit
52+
fn is_at_limit(&self) -> bool {
53+
// Example of calling other &self methods
54+
self.get_count() >= self.get_limit()
55+
}
56+
57+
// --- Methods taking &mut self (Mutable Access) ---
58+
59+
// Increment the counter by 1, respecting the limit and enabled status.
60+
// Returns true if incremented, false otherwise.
61+
fn increment(&mut self) -> bool {
62+
if !self.enabled {
63+
// Not enabled, cannot increment
64+
return false;
65+
}
66+
if self.is_at_limit() { // Calls &self method `is_at_limit`
67+
// Already at limit, cannot increment
68+
return false;
69+
}
70+
71+
// Can increment
72+
self.count += 1;
73+
true // Return true indicating success
74+
}
75+
76+
// Increment the counter by a specific amount. Clamps at the limit.
77+
// Returns the actual amount the counter was incremented by.
78+
fn increment_by(&mut self, amount: u32) -> u32 {
79+
if !self.enabled {
80+
return 0; // Not enabled, incremented by 0
81+
}
82+
83+
let current_count = self.count;
84+
let potential_count = current_count + amount;
85+
86+
if potential_count >= self.limit {
87+
// Clamp to limit
88+
self.count = self.limit;
89+
// Return how much was actually added to reach the limit
90+
self.limit - current_count
91+
} else {
92+
// Increase count by the full amount
93+
self.count = potential_count;
94+
amount // Full amount was added
95+
}
96+
}
97+
98+
// Reset the counter to zero
99+
fn reset(&mut self) {
100+
self.count = 0;
101+
}
102+
103+
// Enable the counter
104+
fn enable(&mut self) {
105+
self.enabled = true;
106+
}
107+
108+
// Disable the counter
109+
fn disable(&mut self) {
110+
self.enabled = false;
111+
}
112+
113+
// Set a new limit. Clamps count if necessary.
114+
fn set_limit(&mut self, new_limit: u32) {
115+
self.limit = new_limit;
116+
// Clamp count if it now exceeds the new limit
117+
if self.count > self.limit {
118+
self.count = self.limit;
119+
}
120+
}
121+
} // end impl NamedCounter
122+
123+
124+
fn main() {
125+
// === Test Case 1: Basic Operations ===
126+
let mut counter1 = NamedCounter::new("Clicks", 5);
127+
128+
// Initial state assertions
129+
assert!(counter1.get_name() == "Clicks");
130+
assert!(counter1.get_count() == 0);
131+
assert!(counter1.get_limit() == 5);
132+
assert!(counter1.is_enabled());
133+
assert!(!counter1.is_at_limit());
134+
135+
// Test increment
136+
assert!(counter1.increment()); // Should succeed (returns true)
137+
assert!(counter1.get_count() == 1);
138+
assert!(!counter1.is_at_limit());
139+
140+
// Test increment_by
141+
let added = counter1.increment_by(2);
142+
assert!(added == 2); // Should have added 2
143+
assert!(counter1.get_count() == 3);
144+
assert!(!counter1.is_at_limit());
145+
146+
// Increment to limit
147+
assert!(counter1.increment()); // count = 4, returns true
148+
assert!(counter1.get_count() == 4);
149+
assert!(counter1.increment()); // count = 5 (at limit), returns true
150+
assert!(counter1.get_count() == 5);
151+
assert!(counter1.is_at_limit());
152+
153+
// Try incrementing past limit
154+
assert!(!counter1.increment()); // Should fail (returns false)
155+
assert!(counter1.get_count() == 5); // Count should remain 5
156+
assert!(counter1.is_at_limit());
157+
158+
// Try increment_by past limit (clamping)
159+
let added_past_limit = counter1.increment_by(3);
160+
assert!(added_past_limit == 0); // Added 0 because already at limit 5
161+
assert!(counter1.get_count() == 5);
162+
assert!(counter1.is_at_limit());
163+
164+
165+
// === Test Case 2: Disabling and Enabling ===
166+
counter1.disable();
167+
assert!(!counter1.is_enabled());
168+
assert!(counter1.get_count() == 5); // Count unchanged
169+
170+
// Try incrementing while disabled
171+
assert!(!counter1.increment()); // Should fail (returns false)
172+
assert!(counter1.get_count() == 5);
173+
174+
// Try increment_by while disabled
175+
let added_while_disabled = counter1.increment_by(2);
176+
assert!(added_while_disabled == 0); // Added 0
177+
assert!(counter1.get_count() == 5);
178+
179+
// Re-enable
180+
counter1.enable();
181+
assert!(counter1.is_enabled());
182+
assert!(!counter1.increment()); // Still at limit, should fail (returns false)
183+
assert!(counter1.get_count() == 5);
184+
185+
186+
// === Test Case 3: Changing Limit ===
187+
counter1.set_limit(10);
188+
assert!(counter1.get_limit() == 10);
189+
assert!(counter1.get_count() == 5); // Count is still 5
190+
assert!(counter1.is_enabled());
191+
assert!(!counter1.is_at_limit()); // No longer at limit
192+
193+
// Increment now that limit is higher
194+
assert!(counter1.increment()); // count = 6, returns true
195+
assert!(counter1.get_count() == 6);
196+
197+
// Increment_by with new limit
198+
let added_new_limit = counter1.increment_by(3); // 6 + 3 = 9
199+
assert!(added_new_limit == 3); // Added 3
200+
assert!(counter1.get_count() == 9);
201+
assert!(!counter1.is_at_limit());
202+
203+
// Increment_by that hits the new limit exactly
204+
let added_to_limit = counter1.increment_by(1); // 9 + 1 = 10
205+
assert!(added_to_limit == 1); // Added 1
206+
assert!(counter1.get_count() == 10);
207+
assert!(counter1.is_at_limit());
208+
209+
// Increment_by that exceeds new limit (clamping)
210+
let added_over_limit = counter1.increment_by(5); // Try 10 + 5 -> clamps to 10
211+
assert!(added_over_limit == 0); // Added 0 because already at limit 10
212+
assert!(counter1.get_count() == 10);
213+
assert!(counter1.is_at_limit());
214+
215+
// Lower the limit below the current count
216+
counter1.set_limit(7);
217+
assert!(counter1.get_limit() == 7);
218+
assert!(counter1.get_count() == 7); // Count should be clamped to new limit
219+
assert!(counter1.is_at_limit());
220+
221+
// Try incrementing after clamping
222+
assert!(!counter1.increment()); // Should fail (at new limit, returns false)
223+
assert!(counter1.get_count() == 7);
224+
225+
226+
// === Test Case 4: Resetting ===
227+
counter1.reset();
228+
assert!(counter1.get_count() == 0);
229+
assert!(counter1.get_limit() == 7); // Limit unchanged by reset
230+
assert!(counter1.is_enabled()); // Enabled status unchanged by reset
231+
assert!(!counter1.is_at_limit());
232+
233+
234+
// === Test Case 5: Disabled Constructor ===
235+
let mut counter2 = NamedCounter::new_disabled("Skips", 100);
236+
237+
// Initial state assertions for disabled counter
238+
assert!(counter2.get_name() == "Skips");
239+
assert!(counter2.get_count() == 0);
240+
assert!(counter2.get_limit() == 100);
241+
assert!(!counter2.is_enabled()); // Should be disabled
242+
assert!(!counter2.is_at_limit());
243+
244+
// Try incrementing while initially disabled
245+
assert!(!counter2.increment()); // returns false
246+
assert!(counter2.get_count() == 0);
247+
248+
// Enable and increment
249+
counter2.enable();
250+
assert!(counter2.is_enabled());
251+
assert!(counter2.increment()); // returns true
252+
assert!(counter2.get_count() == 1);
253+
254+
255+
// === Test Case 6: Edge case with large numbers / saturation ===
256+
let mut counter3 = NamedCounter::new("OverflowTest", u32::MAX);
257+
assert!(counter3.get_count() == 0);
258+
assert!(counter3.get_limit() == u32::MAX);
259+
260+
// Increment by a large amount, but less than limit
261+
let added_large = counter3.increment_by(u32::MAX - 10);
262+
assert!(added_large == u32::MAX - 10);
263+
assert!(counter3.get_count() == u32::MAX - 10);
264+
265+
// Increment to exactly the limit
266+
let added_to_max = counter3.increment_by(10);
267+
assert!(added_to_max == 10);
268+
assert!(counter3.get_count() == u32::MAX);
269+
assert!(counter3.is_at_limit());
270+
271+
// Try to increment past MAX (should add 0 due to limit check/saturation)
272+
let added_past_max = counter3.increment_by(5);
273+
assert!(added_past_max == 0);
274+
assert!(counter3.get_count() == u32::MAX);
275+
276+
// If execution reaches here without panicking, all assertions passed.
277+
}

0 commit comments

Comments
 (0)