use facet::{Facet, Variance};
#[derive(Facet)]
struct Node {
value: i32,
#[facet(recursive_type)]
child: Box<Node>,
}
#[derive(Facet)]
struct Simple {
x: i32,
y: i32,
}
#[test]
#[cfg_attr(miri, ignore)] fn test_recursive_variance_no_stack_overflow() {
let shape = Node::SHAPE;
let variance = shape.computed_variance();
assert_eq!(
variance,
Variance::Bivariant,
"Recursive types with only bivariant fields are Bivariant"
);
}
#[test]
fn test_simple_struct_variance() {
let shape = Simple::SHAPE;
let variance = shape.computed_variance();
assert_eq!(
variance,
Variance::Bivariant,
"Simple should be Bivariant since all fields have no lifetime parameters"
);
}
#[test]
fn test_mut_ptr_stays_invariant() {
let shape = <*mut i32>::SHAPE;
let variance = shape.computed_variance();
assert_eq!(
variance,
Variance::Invariant,
"*mut T must be Invariant regardless of T's variance"
);
}
#[test]
fn test_const_ptr_propagates_variance() {
let shape = <*const i32>::SHAPE;
let variance = shape.computed_variance();
assert_eq!(
variance,
Variance::Bivariant,
"*const T propagates T's variance; *const i32 is Bivariant"
);
}
#[derive(Facet)]
struct WithMutPtr {
ptr: *mut i32,
}
#[test]
fn test_struct_with_mut_ptr_is_invariant() {
let shape = WithMutPtr::SHAPE;
let variance = shape.computed_variance();
assert_eq!(
variance,
Variance::Invariant,
"Struct containing *mut T must be Invariant"
);
}
#[derive(Facet)]
struct MultiRecursive {
#[facet(recursive_type)]
a: Box<MultiRecursive>,
#[facet(recursive_type)]
b: Box<MultiRecursive>,
#[facet(recursive_type)]
c: Box<MultiRecursive>,
#[facet(recursive_type)]
d: Box<MultiRecursive>,
}
#[test]
fn test_multi_recursive_variance_is_fast() {
let start = std::time::Instant::now();
let shape = MultiRecursive::SHAPE;
let variance = shape.computed_variance();
let elapsed = start.elapsed();
assert!(
cfg!(miri) || elapsed.as_millis() < 100,
"Variance computation took {:?}, expected < 100ms",
elapsed
);
assert_eq!(
variance,
Variance::Bivariant,
"MultiRecursive should be Bivariant"
);
}
#[derive(Facet)]
struct RecursiveInvariant {
ptr: *mut i32,
#[facet(recursive_type)]
next: Box<RecursiveInvariant>,
}
#[test]
fn test_recursive_with_invariant_field() {
let shape = RecursiveInvariant::SHAPE;
let variance = shape.computed_variance();
assert_eq!(
variance,
Variance::Invariant,
"RecursiveInvariant should be Invariant due to *mut i32"
);
}
#[derive(Facet)]
struct EarlyTermination {
ptr: *mut i32,
#[facet(recursive_type)]
a: Box<EarlyTermination>,
#[facet(recursive_type)]
b: Box<EarlyTermination>,
#[facet(recursive_type)]
c: Box<EarlyTermination>,
}
#[test]
fn test_early_termination_on_invariant() {
let start = std::time::Instant::now();
let shape = EarlyTermination::SHAPE;
let variance = shape.computed_variance();
let elapsed = start.elapsed();
assert!(
cfg!(miri) || elapsed.as_millis() < 100,
"Early termination took {:?}, expected < 100ms",
elapsed
);
assert_eq!(
variance,
Variance::Invariant,
"EarlyTermination should be Invariant due to *mut i32"
);
}
#[derive(Facet)]
struct TreeA {
value: i32,
#[facet(recursive_type)]
children: Vec<TreeB>,
}
#[derive(Facet)]
struct TreeB {
value: String,
#[facet(recursive_type)]
parent: Option<Box<TreeA>>,
}
#[test]
fn test_mutually_recursive_types() {
let shape_a = TreeA::SHAPE;
let shape_b = TreeB::SHAPE;
let start = std::time::Instant::now();
let variance_a = shape_a.computed_variance();
let variance_b = shape_b.computed_variance();
let elapsed = start.elapsed();
assert!(
cfg!(miri) || elapsed.as_millis() < 100,
"Mutually recursive variance took {:?}, expected < 100ms",
elapsed
);
assert_eq!(variance_a, Variance::Bivariant, "TreeA should be Bivariant");
assert_eq!(variance_b, Variance::Bivariant, "TreeB should be Bivariant");
}
#[derive(Facet)]
struct IssueNode(
#[facet(recursive_type)] &'static IssueNode,
#[facet(recursive_type)] &'static IssueNode,
#[facet(recursive_type)] &'static IssueNode,
#[facet(recursive_type)] &'static IssueNode,
);
#[test]
fn test_issue_1704_reproduction() {
let start = std::time::Instant::now();
let shape = IssueNode::SHAPE;
let variance = shape.computed_variance();
let elapsed = start.elapsed();
assert!(
cfg!(miri) || elapsed.as_millis() < 100,
"Issue #1704 reproduction took {:?}, expected < 100ms",
elapsed
);
assert_eq!(
variance,
Variance::Covariant,
"IssueNode should be Covariant (all fields are &'static Self which is covariant)"
);
}
#[derive(Facet)]
#[cfg(feature = "fn-ptr")]
struct RecursiveFnPtrBox {
#[facet(recursive_type)]
callback: fn(Box<RecursiveFnPtrBox>),
}
#[test]
#[cfg(feature = "fn-ptr")]
fn test_recursive_fn_ptr_box_variance() {
let start = std::time::Instant::now();
let shape = RecursiveFnPtrBox::SHAPE;
let variance = shape.computed_variance();
let elapsed = start.elapsed();
assert!(
cfg!(miri) || elapsed.as_millis() < 100,
"Recursive fn ptr (Box) took {:?}, expected < 100ms",
elapsed
);
assert_eq!(
variance,
Variance::Bivariant,
"RecursiveFnPtrBox should be Bivariant (fn(Box<Self>) with bivariant arg)"
);
}