Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ libfuzzer-sys = "0.4"
arbitrary = { version = "1", features = ["derive"] }
devirt = { path = "crates/core" }
devirt-macros = { path = "crates/macros", version = "0.2.0" }
syn = { version = "2", features = ["full"] }
syn = { version = "2", features = ["full", "visit-mut"] }
quote = "1"
proc-macro2 = "1"
vstd = { version = "=0.0.0-2026-04-12-0118", default-features = false }
Expand Down
109 changes: 109 additions & 0 deletions crates/core/tests/equivalence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,115 @@ fn attr_auto_trait_dispatch() {
assert_eq!(boxed.get(), 7);
}

// ── Default method bodies ──────────────────────────────────────────────────

#[cfg(feature = "macros")]
mod attr_defaults {
pub struct DefHot {
pub val: u64,
}

pub struct DefCold {
pub val: u64,
}

/// Overrides the default `is_big`.
pub struct DefOver {
pub val: u64,
}

/// Relies on the `describe` default that uses `format!`.
pub struct DefFmt {
pub val: u64,
}

#[devirt::devirt(DefHot)]
pub trait Defaulted {
fn get(&self) -> u64;
fn is_big(&self) -> bool {
self.get() > 100
}
fn describe(&self) -> String {
format!("val={}", self.get())
}
}

#[devirt::devirt]
impl Defaulted for DefHot {
fn get(&self) -> u64 {
self.val
}
}

#[devirt::devirt]
impl Defaulted for DefCold {
fn get(&self) -> u64 {
self.val + 1
}
}

#[devirt::devirt]
impl Defaulted for DefOver {
fn get(&self) -> u64 {
self.val
}
fn is_big(&self) -> bool {
// Exercises sibling-call rewriting inside impl bodies:
// self.get() must be rewritten to self.__spec_get().
self.get() > 1000
}
}

#[devirt::devirt]
impl Defaulted for DefFmt {
fn get(&self) -> u64 {
self.val
}
}
}

#[cfg(feature = "macros")]
#[test]
fn attr_default_body_dispatch() {
use attr_defaults::{DefCold, DefFmt, DefHot, DefOver, Defaulted};

// Hot type via &dyn Trait
let h = DefHot { val: 200 };
assert!((&h as &dyn Defaulted).is_big());
let h2 = DefHot { val: 50 };
assert!(!(&h2 as &dyn Defaulted).is_big());

// Cold type via &dyn Trait
let c = DefCold { val: 200 };
assert!((&c as &dyn Defaulted).is_big());

// Via &(dyn Trait + Send)
let h3 = DefHot { val: 200 };
assert!((&h3 as &(dyn Defaulted + Send)).is_big());
let c2 = DefCold { val: 50 };
assert!(!(&c2 as &(dyn Defaulted + Send)).is_big());

// Via &(dyn Trait + Send + Sync)
let h4 = DefHot { val: 200 };
assert!((&h4 as &(dyn Defaulted + Send + Sync)).is_big());

// Overridden default: DefOver uses `self.get() > 1000` (tests
// sibling-call rewriting in impl bodies).
let o = DefOver { val: 200 };
assert!(!(&o as &dyn Defaulted).is_big());
assert!(!(&o as &(dyn Defaulted + Send)).is_big());
let o2 = DefOver { val: 2000 };
assert!((&o2 as &dyn Defaulted).is_big());

// Default body with write! macro (exercises token-level rewriting)
let f = DefFmt { val: 42 };
assert_eq!((&f as &dyn Defaulted).describe(), "val=42");
assert_eq!((&f as &(dyn Defaulted + Send)).describe(), "val=42");
// Hot type's describe (also via default body)
let h5 = DefHot { val: 7 };
assert_eq!((&h5 as &dyn Defaulted).describe(), "val=7");
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// ── Extended proc-macro tests: supertraits, method lifetimes, #[must_use] ──

#[cfg(feature = "macros")]
Expand Down
3 changes: 3 additions & 0 deletions crates/core/tests/ui_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ fn ui_attr() {
t.pass("tests/ui_attr/attr_supertraits.rs");
t.pass("tests/ui_attr/attr_must_use.rs");
t.pass("tests/ui_attr/attr_dyn_send.rs");
t.pass("tests/ui_attr/attr_default_body.rs");
t.pass("tests/ui_attr/attr_default_override.rs");
t.pass("tests/ui_attr/attr_default_send.rs");
t.compile_fail("tests/ui_attr/attr_must_use_unused.rs");
t.compile_fail("tests/ui_attr/attr_missing_args.rs");
t.compile_fail("tests/ui_attr/attr_unsafe_missing_on_impl.rs");
Expand Down
60 changes: 60 additions & 0 deletions crates/core/tests/ui_attr/attr_default_body.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use std::fmt::Write;

struct Hot {
val: f64,
}

struct Cold {
val: f64,
}

#[devirt::devirt(Hot)]
pub trait Shape {
fn area(&self) -> f64;
fn is_large(&self) -> bool {
self.area() > 100.0
}
fn describe(&self) -> String {
let mut s = String::new();
if self.is_large() {
write!(s, "large (area={})", self.area()).ok();
} else {
write!(s, "small (area={})", self.area()).ok();
}
s
}
}

#[devirt::devirt]
impl Shape for Hot {
fn area(&self) -> f64 {
self.val
}
}

#[devirt::devirt]
impl Shape for Cold {
fn area(&self) -> f64 {
self.val + 1.0
}
}

fn main() {
// Hot type, small
let h = Hot { val: 50.0 };
let d: &dyn Shape = &h;
assert!(!d.is_large());
assert!(d.describe().contains("small"));

// Hot type, large
let big = Hot { val: 200.0 };
let d2: &dyn Shape = &big;
assert!(d2.is_large());
assert!(d2.describe().contains("large"));

// Cold type
let c = Cold { val: 150.0 };
let d3: &dyn Shape = &c;
assert!(d3.is_large());
assert!(d3.describe().contains("large"));
}
27 changes: 27 additions & 0 deletions crates/core/tests/ui_attr/attr_default_override.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
struct Hot {
val: f64,
}

#[devirt::devirt(Hot)]
pub trait Shape {
fn area(&self) -> f64;
fn is_large(&self) -> bool {
self.area() > 100.0
}
}

#[devirt::devirt]
impl Shape for Hot {
fn area(&self) -> f64 {
self.val
}
fn is_large(&self) -> bool {
false // override default
}
}

fn main() {
let h = Hot { val: 200.0 };
let d: &dyn Shape = &h;
assert!(!d.is_large()); // overridden to always false
}
30 changes: 30 additions & 0 deletions crates/core/tests/ui_attr/attr_default_send.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
struct Hot {
val: f64,
}

#[devirt::devirt(Hot)]
pub trait Shape {
fn area(&self) -> f64;
fn is_large(&self) -> bool {
self.area() > 100.0
}
}

#[devirt::devirt]
impl Shape for Hot {
fn area(&self) -> f64 {
self.val
}
}

fn check(s: &(dyn Shape + Send)) -> bool {
s.is_large()
}

fn main() {
let h = Hot { val: 200.0 };
assert!(check(&h));

let small = Hot { val: 50.0 };
assert!(!check(&small));
}
Loading