Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
61 changes: 61 additions & 0 deletions crates/core/tests/equivalence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,67 @@ 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,
}

#[devirt::devirt(DefHot)]
pub trait Defaulted {
fn get(&self) -> u64;
fn is_big(&self) -> bool {
self.get() > 100
}
}

#[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
}
}
}

#[cfg(feature = "macros")]
#[test]
fn attr_default_body_dispatch() {
use attr_defaults::{DefCold, DefHot, 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());
}
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