makepad/libs/relax/compiler/tests/destructuring_assign_places.rs

132 lines
3.6 KiB
Rust

//! Destructuring assignment of a place, and overloaded deref of an unsized place.
//! Run with `makepad test libs/relax/compiler/tests/destructuring_assign_places.rs` and with
//! rustc --test (cargo test -p relax-compiler --test destructuring_assign_places): both must agree.
use std::cell::Cell;
use std::ops::{AddAssign, Deref, DerefMut};
use std::rc::Rc;
struct Noisy(&'static str, Rc<Cell<u32>>);
impl Drop for Noisy {
fn drop(&mut self) {
self.1.set(self.1.get() + 1);
}
}
fn mk(tag: &'static str, d: &Rc<Cell<u32>>) -> Noisy {
Noisy(tag, d.clone())
}
/// `_ = place;` is a no-op on a place (rustc: `let _ = place;` neither moves nor drops it).
#[test]
fn a_wildcard_assignment_of_a_place_neither_moves_nor_drops_it() {
let d = Rc::new(Cell::new(0));
{
let a = mk("a", &d);
_ = a;
assert_eq!(d.get(), 0, "still owned after `_ = a`");
assert_eq!(a.0, "a");
let pair = (1u8, mk("b", &d));
_ = pair.1;
assert_eq!(d.get(), 0);
assert_eq!(pair.1 .0, "b");
let boxed = Box::new(mk("c", &d));
_ = *boxed;
assert_eq!(d.get(), 0);
for _ in 0..3 {
_ = a;
}
assert_eq!(d.get(), 0, "a loop of `_ = a` drops nothing");
}
assert_eq!(d.get(), 3, "each value dropped once, at the end of its scope");
}
/// Parts matched by `_` / `..` of a place stay owned by it; a leaf assignee moves its part out
/// (a partial move: the rest is dropped with the place, the moved part with its new owner).
#[test]
fn unmatched_parts_of_a_place_stay_with_it_and_moved_leaves_drop_once() {
let d = Rc::new(Cell::new(0));
let moved;
{
let pt = (mk("x", &d), mk("y", &d));
let (_, b) = (0, 0);
let _ = b;
let b2;
(b2, _) = pt;
assert_eq!(d.get(), 0, "`(b2, _) = pt` drops nothing");
assert_eq!(b2.0, "x");
assert_eq!(pt.1 .0, "y");
moved = b2;
}
assert_eq!(d.get(), 1, "pt's remaining part dropped with pt; the moved one lives on");
drop(moved);
assert_eq!(d.get(), 2);
}
/// A temporary on the right still drops its unmatched parts at the end of the statement.
#[test]
fn unmatched_parts_of_a_temporary_drop_at_the_statement() {
let d = Rc::new(Cell::new(0));
let b;
(_, b) = (mk("t0", &d), mk("t1", &d));
assert_eq!(d.get(), 1, "the `_` part of the temporary is dropped at once");
_ = mk("t2", &d);
assert_eq!(d.get(), 2);
drop(b);
assert_eq!(d.get(), 3);
}
trait Tr {
fn bump(&mut self) -> u32;
fn peek(&self) -> u32;
}
impl Tr for u32 {
fn bump(&mut self) -> u32 {
*self += 1;
*self
}
fn peek(&self) -> u32 {
*self
}
}
#[repr(transparent)]
struct W<T: ?Sized>(T);
impl<T: ?Sized> Deref for W<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T: ?Sized> DerefMut for W<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T: Tr + ?Sized> AddAssign<u32> for W<T> {
fn add_assign(&mut self, n: u32) {
for _ in 0..n {
self.0.bump();
}
}
}
/// `&W<dyn Trait>` into a `Deref` / `DerefMut` / `op=` impl keeps its vtable (a fat `&self`).
#[test]
fn an_unsized_place_reaches_its_deref_and_op_assign_impls_as_a_fat_reference() {
let mut w: W<u32> = W(41);
let r: &mut W<dyn Tr> = &mut w;
assert_eq!(r.bump(), 42, "autoderef through DerefMut");
assert_eq!(r.peek(), 42, "autoderef through Deref");
let inner: &mut dyn Tr = &mut **r;
assert_eq!(inner.bump(), 43);
*r += 2;
assert_eq!(r.peek(), 45);
let shared: &W<dyn Tr> = &w;
assert_eq!(shared.peek(), 45);
}