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", "visit-mut"] }
syn = { version = "2", features = ["full", "visit", "visit-mut"] }
quote = "1"
proc-macro2 = "1"
vstd = { version = "=0.0.0-2026-04-12-0118", default-features = false }
Expand Down
104 changes: 104 additions & 0 deletions crates/core/tests/equivalence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,3 +491,107 @@ fn attr_dispatch() {
assert_eq!(h.val, 99);
assert_eq!(c.val, 100);
}

// ── Associated types ──────────────────────────────────────────────────────

#[cfg(feature = "macros")]
mod attr_assoc_types {
pub struct Circle;
pub struct Rect;

#[devirt::devirt(Circle)]
pub trait Drawable {
type Color;
fn name(&self) -> &str;
fn draw(&self, color: Self::Color) -> String;
}

#[devirt::devirt]
impl Drawable for Circle {
type Color = String;
fn name(&self) -> &str { "circle" }
fn draw(&self, color: String) -> String { format!("circle: {color}") }
}

#[devirt::devirt]
impl Drawable for Rect {
type Color = u32;
fn name(&self) -> &str { "rect" }
fn draw(&self, color: u32) -> String { format!("rect: #{color:06x}") }
}
}

#[cfg(feature = "macros")]
#[test]
fn attr_assoc_type_dispatch() {
use attr_assoc_types::{Circle, Drawable, Rect};

let c = Circle;
assert_eq!((&c as &dyn Drawable<Color = String>).name(), "circle");
assert_eq!(
(&c as &dyn Drawable<Color = String>).draw("red".into()),
"circle: red"
);
assert_eq!(
(&c as &(dyn Drawable<Color = String> + Send)).name(),
"circle"
);
assert_eq!(
(&c as &(dyn Drawable<Color = String> + Send + Sync)).name(),
"circle"
);

let r = Rect;
let d: &dyn Drawable<Color = u32> = &r;
assert_eq!(d.name(), "rect");
assert_eq!(d.draw(0x00FF_0000_u32), "rect: #ff0000");
}

// ── Generic trait parameters ──────────────────────────────────────────────

#[cfg(feature = "macros")]
mod attr_generic_trait {
pub struct Handler;

#[devirt::devirt(Handler)]
pub trait Processor<T> {
fn process(&self, input: T) -> String;
fn name(&self) -> &str;
}

#[devirt::devirt]
impl Processor<String> for Handler {
fn process(&self, input: String) -> String { format!("str: {input}") }
fn name(&self) -> &str { "handler" }
}

#[devirt::devirt]
impl Processor<u32> for Handler {
fn process(&self, input: u32) -> String { format!("num: {input}") }
fn name(&self) -> &str { "handler" }
}
}

#[cfg(feature = "macros")]
#[test]
fn attr_generic_trait_dispatch() {
use attr_generic_trait::{Handler, Processor};

let h = Handler;
assert_eq!(
(&h as &dyn Processor<String>).process("hello".into()),
"str: hello"
);
assert_eq!(
(&h as &dyn Processor<u32>).process(42),
"num: 42"
);
assert_eq!(
(&h as &(dyn Processor<String> + Send)).name(),
"handler"
);
assert_eq!(
(&h as &(dyn Processor<u32> + Send + Sync)).name(),
"handler"
);
}
3 changes: 3 additions & 0 deletions crates/core/tests/ui_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ fn ui_attr() {
t.pass("tests/ui_attr/attr_where_clause.rs");
t.pass("tests/ui_attr/attr_generic_impl.rs");
t.pass("tests/ui_attr/attr_where_impl.rs");
t.pass("tests/ui_attr/attr_assoc_type.rs");
t.pass("tests/ui_attr/attr_assoc_default.rs");
t.pass("tests/ui_attr/attr_generic_trait.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
24 changes: 24 additions & 0 deletions crates/core/tests/ui_attr/attr_assoc_default.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
struct Circle;

#[devirt::devirt(Circle)]
pub trait Drawable {
type Color;
fn name(&self) -> &str;
fn draw(&self, color: Self::Color) -> String;
fn describe(&self) -> String {
format!("I am {}", self.name())
}
}

#[devirt::devirt]
impl Drawable for Circle {
type Color = String;
fn name(&self) -> &str { "circle" }
fn draw(&self, color: String) -> String { format!("circle: {color}") }
}

fn main() {
let c = Circle;
let d: &dyn Drawable<Color = String> = &c;
assert_eq!(d.describe(), "I am circle");
}
39 changes: 39 additions & 0 deletions crates/core/tests/ui_attr/attr_assoc_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
struct Circle;
struct Rect;

#[devirt::devirt(Circle)]
pub trait Drawable {
type Color;
fn name(&self) -> &str;
fn draw(&self, color: Self::Color) -> String;
}

#[devirt::devirt]
impl Drawable for Circle {
type Color = String;
fn name(&self) -> &str { "circle" }
fn draw(&self, color: String) -> String { format!("circle: {color}") }
}

#[devirt::devirt]
impl Drawable for Rect {
type Color = u32;
fn name(&self) -> &str { "rect" }
fn draw(&self, color: u32) -> String { format!("rect: #{color:06x}") }
}

fn check_name(d: &dyn Drawable<Color = String>) -> &str { d.name() }
fn check_draw(d: &dyn Drawable<Color = String>, c: String) -> String { d.draw(c) }
fn check_send(d: &(dyn Drawable<Color = String> + Send)) -> &str { d.name() }

fn main() {
let c = Circle;
assert_eq!(check_name(&c), "circle");
assert_eq!(check_draw(&c, "red".into()), "circle: red");
assert_eq!(check_send(&c), "circle");

let r = Rect;
let d: &dyn Drawable<Color = u32> = &r;
assert_eq!(d.name(), "rect");
assert_eq!(d.draw(0x00FF_0000), "rect: #ff0000");
}
30 changes: 30 additions & 0 deletions crates/core/tests/ui_attr/attr_generic_trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
struct Handler;

#[devirt::devirt(Handler)]
pub trait Processor<T> {
fn process(&self, input: T) -> String;
fn name(&self) -> &str;
}

#[devirt::devirt]
impl Processor<String> for Handler {
fn process(&self, input: String) -> String { format!("str: {input}") }
fn name(&self) -> &str { "handler" }
}

#[devirt::devirt]
impl Processor<u32> for Handler {
fn process(&self, input: u32) -> String { format!("num: {input}") }
fn name(&self) -> &str { "handler" }
}

fn use_str(p: &dyn Processor<String>) -> String { p.process("hello".into()) }
fn use_u32(p: &dyn Processor<u32>) -> String { p.process(42) }
fn use_send(p: &(dyn Processor<String> + Send)) -> &str { p.name() }

fn main() {
let h = Handler;
assert_eq!(use_str(&h), "str: hello");
assert_eq!(use_u32(&h), "num: 42");
assert_eq!(use_send(&h), "handler");
}
Loading