The JIT that runs makepad apps from source: lexer, front end, type checker, lazy borrow checker (Gandalf; `--check-first` opts in), codegen, session and warm/cold caches keyed by compiler stamp, provided crates bound late, startup images, live patching. User-facing name is `makepad run` (binary `makepad`, MAKEPAD_RUN_* env, ~/.makepad-run); libs/relax stays the internal library name. `makepad check` reports rustc-style diagnostics with rustc error codes and checks a whole workspace (`--workspace`). Runs on macOS, Linux, Windows and Android. makepad-remote: one tool schema for apps and the WM (app/1): grabs, captures, traces, frame profiles and tweaks. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1823 lines
49 KiB
Rust
1823 lines
49 KiB
Rust
// Repros for rustc tests/ui run-pass failures (conformance lane). Each test names the rustc
|
|
// suite file it was reduced from; all of them pass under rustc (`rustc --test conform.rs`).
|
|
use std::process::{ExitCode, Termination};
|
|
use std::rc::Rc;
|
|
|
|
// rfcs/rfc-1937-termination-trait/*: `fn main() -> Result<..>` reports through Termination
|
|
#[test]
|
|
fn c01_termination() {
|
|
assert_eq!(().report(), ExitCode::SUCCESS);
|
|
let ok: Result<(), &'static str> = Ok(());
|
|
assert_eq!(ok.report(), ExitCode::SUCCESS);
|
|
let nested: Result<Result<(), u8>, u8> = Ok(Ok(()));
|
|
assert_eq!(nested.report(), ExitCode::SUCCESS);
|
|
}
|
|
|
|
// statics/static-recursive.rs: statics refer to each other's addresses (cycle)
|
|
struct Link {
|
|
next: &'static Link,
|
|
data: i32,
|
|
}
|
|
static L1: Link = Link { next: &L2, data: 1 };
|
|
static L2: Link = Link { next: &L3, data: 2 };
|
|
static L3: Link = Link { next: &L1, data: 3 };
|
|
static mut SELF_PTR: *const u8 = unsafe { &raw const SELF_PTR as *const u8 };
|
|
|
|
#[test]
|
|
fn c02_recursive_statics() {
|
|
let mut v = Vec::new();
|
|
let mut cur = &L1;
|
|
for _ in 0..4 {
|
|
v.push(cur.data);
|
|
cur = cur.next;
|
|
}
|
|
assert_eq!(v, [1, 2, 3, 1]);
|
|
unsafe {
|
|
assert_eq!(SELF_PTR, &raw const SELF_PTR as *const u8);
|
|
}
|
|
}
|
|
|
|
// cast/cast-to-box-arr.rs, consts/static-raw-pointer-interning2.rs: `[T; N]` -> `[T]` unsizing
|
|
// behind raw pointers and smart pointers, also as an `as` cast (a coercion cast)
|
|
static mut RAW: *mut i32 = &mut [42, 43] as *mut [i32] as *mut i32;
|
|
|
|
#[test]
|
|
fn c03_array_unsizing() {
|
|
let x = Box::new([1, 2, 3]);
|
|
let y = x as Box<[i32]>;
|
|
assert_eq!(y.len(), 3);
|
|
assert_eq!(y[2], 3);
|
|
let z: Box<[u8]> = Box::new([4u8, 5]);
|
|
assert_eq!(z.len(), 2);
|
|
let r: Rc<[i32]> = Rc::new([9, 8, 7, 6]);
|
|
assert_eq!(r.len(), 4);
|
|
assert_eq!(r[3], 6);
|
|
let a = [1u16, 2, 3];
|
|
let p = &a as *const [u16];
|
|
assert_eq!(unsafe { (&*p).len() }, 3);
|
|
let m = &mut [7, 8] as *mut [i32] as *mut i32;
|
|
assert_eq!(unsafe { *m.add(1) }, 8);
|
|
unsafe {
|
|
assert_eq!(*RAW.add(1), 43);
|
|
}
|
|
}
|
|
|
|
// issues/issue-3559.rs: temporaries of a skipped `||` / `&&` operand are not dropped (each
|
|
// operand is a terminating scope; this aborted with a double free)
|
|
fn check(a: &str, e: &str) -> bool {
|
|
a == e
|
|
}
|
|
|
|
#[test]
|
|
fn c04_lazy_bool_temporaries() {
|
|
let x = 5;
|
|
let b = check(&format!("{}", x), "5") || check(&format!("{}", x), "6");
|
|
assert!(b);
|
|
let b = check(&format!("{}", x), "4") && check(&format!("{}", x), "5");
|
|
assert!(!b);
|
|
let mut hits = 0;
|
|
if check(&format!("{}", x), "5") || check(&format!("{}", x), "6") {
|
|
hits += 1;
|
|
}
|
|
if check(&format!("{}", x), "4") || check(&format!("{}", x), "5") {
|
|
hits += 1;
|
|
}
|
|
assert!(check(&format!("{}", x), "4") || check(&format!("{}", x), "5"));
|
|
assert_eq!(hits, 2);
|
|
}
|
|
|
|
// threads-sendsync/tls-try-with.rs: a closure whose body diverges returns `()` (diverging
|
|
// fallback; `!` from edition 2024) -- `try_with(|_| panic!())` must instantiate R
|
|
fn call<R>(f: impl FnOnce(bool) -> R, go: bool) -> Option<R> {
|
|
if go {
|
|
Some(f(go))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn c05_diverging_fallback() {
|
|
let r = call(|_| panic!("never"), false);
|
|
assert!(r.is_none());
|
|
}
|
|
|
|
// self/arbitrary_self_types_{struct,trait}.rs: receivers `self: &Rc<Self>`, `self: Rc<Self>`,
|
|
// `self: &Box<Rc<Self>>` are matched by type at the autoderef step they occur at
|
|
struct Foo {
|
|
x: i32,
|
|
y: i32,
|
|
}
|
|
|
|
impl Foo {
|
|
fn x(self: &Rc<Self>) -> i32 {
|
|
self.x
|
|
}
|
|
fn y(self: Rc<Self>) -> i32 {
|
|
self.y
|
|
}
|
|
}
|
|
|
|
trait Tr {
|
|
fn deep<'a>(self: &'a Box<Rc<Self>>) -> &'a [i32];
|
|
}
|
|
|
|
impl Tr for Vec<i32> {
|
|
fn deep<'a>(self: &'a Box<Rc<Self>>) -> &'a [i32] {
|
|
&***self
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn c06_typed_receivers() {
|
|
let foo = Rc::new(Foo { x: 3, y: 4 });
|
|
assert_eq!(foo.x(), 3);
|
|
assert_eq!(foo.y(), 4);
|
|
assert_eq!(Box::new(Rc::new(vec![1, 2, 3])).deep(), &[1, 2, 3]);
|
|
}
|
|
|
|
// binding/match-pipe-binding.rs: or-patterns bind the same names from whichever alternative
|
|
// matched (also with moves out of a temporary)
|
|
fn swap_or(t: (i32, i32, i32)) -> i32 {
|
|
match t {
|
|
(1, a, b) | (2, b, a) => a * 10 + b,
|
|
_ => 0,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn c07_or_pattern_bindings() {
|
|
assert_eq!(swap_or((1, 2, 3)), 23);
|
|
assert_eq!(swap_or((2, 3, 4)), 43);
|
|
assert_eq!(swap_or((5, 3, 4)), 0);
|
|
let r = match ((2, "b".to_string()), (1, "a".to_string())) {
|
|
((1, a), (2, b)) | ((2, b), (1, a)) => a + &b,
|
|
_ => String::new(),
|
|
};
|
|
assert_eq!(r, "ab");
|
|
let v: Result<u8, u8> = Err(7);
|
|
let (Ok(x) | Err(x)) = v;
|
|
assert_eq!(x, 7);
|
|
}
|
|
|
|
// fn/dyn-fn-alignment.rs, packed/*, derives/deriving-with-repr-packed-3.rs: repr(align(N)) and
|
|
// repr(packed(N)) layouts; derives and drops of packed fields go through aligned copies
|
|
#[repr(align(256))]
|
|
struct Al {
|
|
v: u8,
|
|
}
|
|
|
|
#[repr(packed)]
|
|
struct Pk {
|
|
a: u8,
|
|
b: u32,
|
|
c: u16,
|
|
}
|
|
|
|
#[repr(C, packed(2))]
|
|
struct Pk2 {
|
|
a: u8,
|
|
b: u32,
|
|
}
|
|
|
|
#[derive(Copy, Clone)]
|
|
struct Aligned(usize);
|
|
|
|
impl PartialEq for Aligned {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
assert_eq!(self as *const Aligned as usize % std::mem::align_of::<Aligned>(), 0);
|
|
assert_eq!(other as *const Aligned as usize % std::mem::align_of::<Aligned>(), 0);
|
|
self.0 == other.0
|
|
}
|
|
}
|
|
|
|
#[repr(packed)]
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
struct PackedPair(Aligned, Aligned);
|
|
|
|
#[repr(C)]
|
|
#[derive(PartialEq)]
|
|
struct Dealigned<T>(u8, T);
|
|
|
|
struct OddCheck(u16);
|
|
|
|
impl Drop for OddCheck {
|
|
fn drop(&mut self) {
|
|
assert_eq!(self as *mut OddCheck as usize % 2, 0);
|
|
}
|
|
}
|
|
|
|
#[repr(packed)]
|
|
struct Misalign(u8, OddCheck);
|
|
|
|
#[test]
|
|
fn c08_repr_align_packed() {
|
|
assert_eq!(std::mem::align_of::<Al>(), 256);
|
|
assert_eq!(std::mem::size_of::<Al>(), 256);
|
|
let b = Box::new(Al { v: 2 });
|
|
assert_eq!(&*b as *const Al as usize % 256, 0);
|
|
assert_eq!(b.v, 2);
|
|
let a = Al { v: 1 };
|
|
let f: Box<dyn FnOnce() -> u8> = Box::new(move || a.v);
|
|
assert_eq!(f(), 1);
|
|
assert_eq!((std::mem::size_of::<Pk>(), std::mem::align_of::<Pk>()), (7, 1));
|
|
assert_eq!(std::mem::size_of::<Pk2>(), 6);
|
|
let p = Pk { a: 1, b: 0x01020304, c: 7 };
|
|
let (pa, pb, pc) = (p.a, p.b, p.c);
|
|
assert_eq!((pa, pb, pc), (1, 0x01020304, 7));
|
|
let d = Dealigned(0, PackedPair(Aligned(1), Aligned(2)));
|
|
assert!(d == d);
|
|
let m: ([u16; 0], Misalign) = ([], Misalign(0, OddCheck(10)));
|
|
drop(m);
|
|
}
|
|
|
|
// cast/cast-rfc0401.rs, drop/multiple-drop-safe-code-25549.rs: casts that are coercions
|
|
// (trivial casts type their literals; struct-tail unsizing)
|
|
struct Holder<T: ?Sized>(u8, T);
|
|
|
|
#[test]
|
|
fn c09_coercion_casts() {
|
|
assert_eq!(Some(4u32) as Option<u32>, Some(4u32));
|
|
assert_eq!((1u32, 2u32) as (u32, u32), (1, 2));
|
|
let h = &Holder(1, [5u16, 6, 7]) as &Holder<[u16]>;
|
|
assert_eq!(h.1.len(), 3);
|
|
assert_eq!(h.1[2], 7);
|
|
}
|
|
|
|
// String + &String: the right operand is a coercion site for the impl's Rhs (&String -> &str)
|
|
#[test]
|
|
fn c10_operator_rhs_coercion() {
|
|
let a = "x".to_string();
|
|
let b = "y".to_string();
|
|
let mut c = a + &b;
|
|
c += &b;
|
|
assert_eq!(c, "xyy");
|
|
}
|
|
|
|
// borrowck/borrowck-box-sensitivity.rs, borrowck-field-sensitivity-rpass.rs: moving fields out
|
|
// of a Box and functional record update move only those fields; the rest is dropped once
|
|
use std::cell::RefCell;
|
|
|
|
thread_local!(static LOG: RefCell<Vec<&'static str>> = RefCell::new(Vec::new()));
|
|
|
|
struct D(&'static str);
|
|
|
|
impl Drop for D {
|
|
fn drop(&mut self) {
|
|
LOG.with(|l| l.borrow_mut().push(self.0));
|
|
}
|
|
}
|
|
|
|
fn take_log() -> Vec<&'static str> {
|
|
LOG.with(|l| std::mem::take(&mut *l.borrow_mut()))
|
|
}
|
|
|
|
struct Two {
|
|
a: u8,
|
|
b: D,
|
|
c: D,
|
|
}
|
|
|
|
fn mk_two() -> Two {
|
|
Two { a: 1, b: D("tb"), c: D("tc") }
|
|
}
|
|
|
|
#[test]
|
|
fn c11_box_field_moves() {
|
|
take_log();
|
|
{
|
|
let bx = Box::new(Two { a: 1, b: D("x"), c: D("y") });
|
|
let moved = bx.b;
|
|
assert_eq!(bx.a, 1);
|
|
drop(moved);
|
|
LOG.with(|l| l.borrow_mut().push("end"));
|
|
}
|
|
assert_eq!(take_log(), ["x", "end", "y"]);
|
|
}
|
|
|
|
#[test]
|
|
fn c12_struct_update_moves() {
|
|
take_log();
|
|
{
|
|
let x = Two { a: 1, b: D("xb"), c: D("xc") };
|
|
let y = Two { b: D("yb"), ..x };
|
|
assert_eq!(y.a, 1);
|
|
drop(x.b);
|
|
let z = Two { c: D("zc"), ..mk_two() };
|
|
drop(z);
|
|
LOG.with(|l| l.borrow_mut().push("end"));
|
|
}
|
|
assert_eq!(take_log(), ["xb", "tc", "tb", "zc", "end", "yb", "xc"]);
|
|
}
|
|
|
|
// derives/eq-ord/derive-partial-ord-discriminant*.rs, enum-discrim-*.rs, consts/const-eval/
|
|
// enum_discr.rs: discriminants are constants of the repr type (wrapping, `!`, casts, other
|
|
// variants), tags are the smallest fitting integer, derived orderings compare discriminants
|
|
#[derive(PartialEq, PartialOrd, Clone, Copy)]
|
|
enum E1 {
|
|
Pos2 = 2,
|
|
Neg1 = -1,
|
|
Pos1 = 1,
|
|
}
|
|
|
|
#[derive(PartialEq, PartialOrd, Clone, Copy)]
|
|
#[repr(i64)]
|
|
enum Ei64 {
|
|
Pos2 = 2,
|
|
NegMin = 1 << 63,
|
|
PosMax = !(1 << 63),
|
|
}
|
|
|
|
#[repr(u8)]
|
|
#[derive(Clone, Copy)]
|
|
enum Eu8 {
|
|
Max = !0 as u8,
|
|
}
|
|
|
|
enum Fwd {
|
|
X = Fwd::Y as isize * 2,
|
|
Y = 9,
|
|
}
|
|
|
|
enum Small {
|
|
A = -1,
|
|
B = 0x80,
|
|
}
|
|
|
|
#[test]
|
|
fn c13_discriminants() {
|
|
assert!(E1::Pos2 > E1::Pos1 && E1::Pos1 > E1::Neg1);
|
|
assert!(Ei64::NegMin < Ei64::Pos2 && Ei64::Pos2 < Ei64::PosMax);
|
|
assert_eq!(Ei64::PosMax as i64, i64::MAX);
|
|
assert_eq!(Eu8::Max as u8, 255);
|
|
assert_eq!(Fwd::X as isize, 18);
|
|
assert_eq!(std::mem::size_of::<Small>(), 2);
|
|
assert_eq!(Small::A as i32 + Small::B as i32, 127);
|
|
}
|
|
|
|
// overloaded/overloaded-deref.rs, overloaded-deref-count.rs: `*x` of a Deref type calls deref
|
|
// (the guard of `*cell.borrow()` is a statement temporary), `(*v).push(..)` uses DerefMut
|
|
use std::cell::Cell;
|
|
use std::ops::{Deref, DerefMut};
|
|
|
|
struct Wrap {
|
|
tag: u64,
|
|
inner: u32,
|
|
muts: Cell<u32>,
|
|
}
|
|
|
|
impl Deref for Wrap {
|
|
type Target = u32;
|
|
fn deref(&self) -> &u32 {
|
|
&self.inner
|
|
}
|
|
}
|
|
|
|
impl DerefMut for Wrap {
|
|
fn deref_mut(&mut self) -> &mut u32 {
|
|
self.muts.set(self.muts.get() + 1);
|
|
&mut self.inner
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn c14_overloaded_deref_rvalue() {
|
|
let i = RefCell::new(2);
|
|
let a = *i.borrow();
|
|
*i.borrow_mut() = 5;
|
|
assert_eq!((a, *i.borrow()), (2, 5));
|
|
let mut w = Wrap { tag: 99, inner: 7, muts: Cell::new(0) };
|
|
let v = *w;
|
|
assert_eq!((v, w.tag), (7, 99));
|
|
(*w).clone_from(&3);
|
|
assert_eq!((*w, w.muts.get()), (3, 1));
|
|
}
|
|
|
|
// moves/move-3-unique.rs: a variable initialised in a branch lives to the end of its own block
|
|
// (its drop flag was registered in the branch: dropped there, read after free)
|
|
fn branch_init(c: bool, foo: Box<u32>) -> u32 {
|
|
let y: Box<u32>;
|
|
if c {
|
|
y = foo;
|
|
} else {
|
|
y = Box::new(5);
|
|
}
|
|
*y + 1
|
|
}
|
|
|
|
// drop/repeat-drop.rs: `[v; 0]` drops v at once (a const operand makes no value)
|
|
struct Dc<'a>(&'a Cell<u32>);
|
|
|
|
impl Drop for Dc<'_> {
|
|
fn drop(&mut self) {
|
|
self.0.set(self.0.get() + 1);
|
|
}
|
|
}
|
|
|
|
// autoref-autoderef/autoderef-method-twice-but-not-thrice.rs: by-value receiver under boxes
|
|
trait Double {
|
|
fn double(self: Box<Self>) -> usize;
|
|
}
|
|
|
|
impl Double for Box<usize> {
|
|
fn double(self: Box<Box<usize>>) -> usize {
|
|
**self * 2
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn c15_scopes_and_moves() {
|
|
assert_eq!(branch_init(true, Box::new(1)), 2);
|
|
assert_eq!(branch_init(false, Box::new(1)), 6);
|
|
let n = Cell::new(0);
|
|
let d = Dc(&n);
|
|
let v: [Dc; 0] = [d; 0];
|
|
assert_eq!(n.get(), 1);
|
|
drop(v);
|
|
assert_eq!(n.get(), 1);
|
|
let x: Box<Box<Box<Box<usize>>>> = Box::new(Box::new(Box::new(Box::new(3))));
|
|
assert_eq!(x.double(), 6);
|
|
}
|
|
|
|
// drop/drop-count-assertion-16151.rs, same-alloca-reassigned-match-binding-16151.rs: `while let`
|
|
// bindings and its scrutinee temporary are dropped at the end of every iteration (they leaked;
|
|
// the last one dropped at function end)
|
|
#[test]
|
|
fn c16_while_let_drops() {
|
|
take_log();
|
|
{
|
|
let mut v = vec![D("1"), D("2"), D("3")];
|
|
while let Some(x) = v.pop() {
|
|
if x.0 == "0" {
|
|
return;
|
|
}
|
|
}
|
|
LOG.with(|l| l.borrow_mut().push("mid"));
|
|
let mut it = vec![D("6"), D("7")].into_iter();
|
|
while let Some(x) = it.next() {
|
|
if x.0 == "9" {
|
|
break;
|
|
}
|
|
}
|
|
LOG.with(|l| l.borrow_mut().push("end"));
|
|
}
|
|
assert_eq!(take_log(), ["3", "2", "1", "mid", "6", "7", "end"]);
|
|
}
|
|
|
|
// consts/consts-in-patterns.rs, const_in_pattern/*: constant patterns match structurally by
|
|
// value (a reference constant compares its pointee; the scrutinee is not auto-dereferenced)
|
|
#[derive(PartialEq, Eq)]
|
|
enum Shape {
|
|
Dot,
|
|
Line(u8, u8),
|
|
}
|
|
|
|
const LINE: Shape = Shape::Line(1, 2);
|
|
const NAME: &str = "abc";
|
|
const UNIT_REF: &() = &();
|
|
const WORDS: &[&str] = &["a", "bc"];
|
|
|
|
fn classify(s: &Shape) -> u8 {
|
|
match *s {
|
|
LINE => 1,
|
|
Shape::Line(..) => 2,
|
|
Shape::Dot => 3,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn c17_const_patterns() {
|
|
assert_eq!((classify(&Shape::Line(1, 2)), classify(&Shape::Line(1, 3)), classify(&Shape::Dot)), (1, 2, 3));
|
|
let n = match "abc" {
|
|
NAME => 1,
|
|
_ => 2,
|
|
};
|
|
assert_eq!(n, 1);
|
|
let z = match &() {
|
|
UNIT_REF => 9,
|
|
};
|
|
assert_eq!(z, 9);
|
|
let w: &[&str] = &["a", "bc"];
|
|
assert!(matches!(w, WORDS));
|
|
assert!(!matches!(&["a"][..], WORDS));
|
|
}
|
|
|
|
// for-loop-while/for-loop-has-unit-body.rs: a loop body's tail has type `()`
|
|
#[test]
|
|
fn c18_loop_body_unit() {
|
|
let mut n = 0;
|
|
for _ in 0..3 {
|
|
n += 1;
|
|
unsafe { std::mem::zeroed() }
|
|
}
|
|
assert_eq!(n, 3);
|
|
}
|
|
|
|
// macros/*, offset-of: std's addr_of!/addr_of_mut!/offset_of!/try! macros; array lengths from
|
|
// char/bool casts and enum variants
|
|
#[repr(C)]
|
|
struct Inner {
|
|
x: u8,
|
|
y: u32,
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct Outer {
|
|
a: u8,
|
|
b: u64,
|
|
c: Inner,
|
|
}
|
|
|
|
enum Bunny {
|
|
Flopsy = 3,
|
|
}
|
|
|
|
#[test]
|
|
fn c19_std_macros_and_lengths() {
|
|
assert_eq!(std::mem::offset_of!(Outer, b), 8);
|
|
assert_eq!(std::mem::offset_of!(Outer, c.y), 20);
|
|
let mut o = Outer { a: 1, b: 2, c: Inner { x: 3, y: 4 } };
|
|
let p = std::ptr::addr_of!(o.b);
|
|
let q = std::ptr::addr_of_mut!(o.c.x);
|
|
unsafe {
|
|
*q = 9;
|
|
assert_eq!(*p, 2);
|
|
}
|
|
assert_eq!((o.a, o.c.x, o.c.y), (1, 9, 4));
|
|
let a = [0u8; '\n' as usize];
|
|
let b = [1u8; true as usize];
|
|
let c = [2u8; Bunny::Flopsy as usize];
|
|
assert_eq!((a.len(), b.len(), c.len()), (10, 1, 3));
|
|
}
|
|
|
|
// structs-enums/struct-aliases.rs: struct literals/patterns through aliases and with explicit
|
|
// generic args fix those args (`S4::<u8> { x: 4, .. }` makes x a u8)
|
|
struct S3<U, V> {
|
|
x: U,
|
|
y: V,
|
|
}
|
|
|
|
type S4<U> = S3<U, char>;
|
|
|
|
#[test]
|
|
fn c20_struct_path_args() {
|
|
let s = S4 { x: 4, y: 'a' };
|
|
match s {
|
|
S4::<u8> { x, y } => assert_eq!((std::mem::size_of_val(&x), y), (1, 'a')),
|
|
}
|
|
let t = S3::<u16, bool> { x: 5, y: true };
|
|
assert_eq!(std::mem::size_of_val(&t.x), 2);
|
|
assert!(t.y);
|
|
}
|
|
|
|
// for-loop-while/label_break_value.rs: a labeled block whose body diverges has its breaks' type
|
|
// (it was `!`: the value was lost, garbage/unreachable)
|
|
#[test]
|
|
fn c21_labeled_block_value() {
|
|
let v = 5;
|
|
let a = 'b: {
|
|
break 'b v == 5;
|
|
};
|
|
let m = match v {
|
|
w if 'b: {
|
|
break 'b w == 5;
|
|
} => 1,
|
|
_ => 2,
|
|
};
|
|
assert!(a);
|
|
assert_eq!(m, 1);
|
|
}
|
|
|
|
// associated-types/associated-types-doubleendediterator-object.rs: the object type's bindings
|
|
// type the coerced value (`vec![1, ..]` as `dyn Iterator<Item = isize>` was a Vec<i32>: SILENT)
|
|
#[test]
|
|
fn c22_dyn_binding_types_source() {
|
|
let mut t: Box<dyn DoubleEndedIterator<Item = isize>> = Box::new(vec![1, 2, 3, 4, 5, 6].into_iter());
|
|
let mut r = 0;
|
|
while let (Some(f), Some(b)) = (t.next(), t.next_back()) {
|
|
r += b - f;
|
|
}
|
|
assert_eq!(r, 9);
|
|
}
|
|
|
|
// destructuring-assignment/*: `(a, b) = (b, a)`, `..`, `_`, struct assignees; parts of a `let`
|
|
// pattern's temporary that are not moved drop at the statement end
|
|
struct Pt {
|
|
x: i32,
|
|
y: i32,
|
|
}
|
|
|
|
#[test]
|
|
fn c23_destructuring_assignment() {
|
|
let (mut a, mut b);
|
|
(a, b) = (0, 1);
|
|
(b, a) = (a, b);
|
|
assert_eq!((a, b), (1, 0));
|
|
(a, .., b) = (7, 8, 9);
|
|
assert_eq!((a, b), (7, 9));
|
|
(..) = (3, 4);
|
|
(a, _) = (5, 6);
|
|
assert_eq!(a, 5);
|
|
let (mut p, mut q) = (0u8, 0u8);
|
|
(p, q) = (q + 1, p + 2);
|
|
assert_eq!((p, q), (1, 2));
|
|
let (x, y);
|
|
Pt { x, y } = Pt { x: 3, y: 4 };
|
|
assert_eq!((x, y), (3, 4));
|
|
let mut arr = [0; 3];
|
|
[arr[0], .., arr[2]] = [1, 2, 3];
|
|
assert_eq!(arr, [1, 0, 3]);
|
|
take_log();
|
|
{
|
|
let (keep, _) = (D("k"), D("t"));
|
|
LOG.with(|l| l.borrow_mut().push("mid"));
|
|
drop(keep);
|
|
}
|
|
assert_eq!(take_log(), ["t", "mid", "k"]);
|
|
}
|
|
|
|
// traits/default-method/*, traits/to-str.rs, traits/inheritance/visibility.rs: an integer
|
|
// literal receiver / argument takes the one numeric type that implements the trait
|
|
trait Meow {
|
|
fn meow(&self) -> u32 {
|
|
7
|
|
}
|
|
}
|
|
|
|
impl Meow for isize {}
|
|
|
|
fn bounded<T: Meow>(x: T) -> u32 {
|
|
x.meow() + std::mem::size_of::<T>() as u32
|
|
}
|
|
|
|
#[test]
|
|
fn c24_literal_trait_selection() {
|
|
assert_eq!(3.meow(), 7);
|
|
assert_eq!(bounded(0), 7 + std::mem::size_of::<isize>() as u32);
|
|
}
|
|
|
|
// F37 (bring-up, apple_sys): a discriminant calling a const fn is evaluated like a const
|
|
const fn four_cc(s: &str) -> u32 {
|
|
let b = s.as_bytes();
|
|
((b[0] as u32) << 24) | ((b[1] as u32) << 16) | ((b[2] as u32) << 8) | (b[3] as u32)
|
|
}
|
|
|
|
#[repr(u32)]
|
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
|
enum Sel {
|
|
Devices = four_cc("dev#"),
|
|
Other = 7,
|
|
Next,
|
|
}
|
|
|
|
#[test]
|
|
fn c25_const_fn_discriminant() {
|
|
assert_eq!(Sel::Devices as u32, 0x6465_7623);
|
|
assert_eq!((Sel::Other as u32, Sel::Next as u32), (7, 8));
|
|
assert!(Sel::Devices != Sel::Next);
|
|
}
|
|
|
|
// traits/vtable/empty-supertrait-with-nonempty-supersupertrait.rs, traits/trait-upcasting/*:
|
|
// `&dyn Sub` as `&dyn Super` takes Sub's stored vtable for Super (it kept Sub's vtable: the
|
|
// wrong method ran, SILENT)
|
|
trait UpOne {
|
|
fn one(&self) -> u32 {
|
|
1
|
|
}
|
|
}
|
|
|
|
trait UpTwo {
|
|
fn two(&self) -> u32 {
|
|
2
|
|
}
|
|
}
|
|
|
|
trait UpTwoAgain: UpTwo {}
|
|
|
|
trait UpOneTwo: UpOne + UpTwoAgain {
|
|
fn both(&self) -> u32 {
|
|
self.one() * 10 + self.two()
|
|
}
|
|
}
|
|
|
|
impl UpOne for () {}
|
|
impl UpTwo for () {}
|
|
impl<T: UpTwo> UpTwoAgain for T {}
|
|
impl UpOneTwo for () {}
|
|
|
|
#[test]
|
|
fn c26_trait_upcasting() {
|
|
let x: &dyn UpOneTwo = &();
|
|
assert_eq!(x.both(), 12);
|
|
let t: &dyn UpTwo = x;
|
|
assert_eq!(t.two(), 2);
|
|
let ta: &dyn UpTwoAgain = x;
|
|
assert_eq!(ta.two(), 2);
|
|
let t2: &dyn UpTwo = ta;
|
|
assert_eq!(t2.two(), 2);
|
|
let o = x as &dyn UpOne;
|
|
assert_eq!(o.one(), 1);
|
|
let b: Box<dyn UpOneTwo> = Box::new(());
|
|
let bo: Box<dyn UpOne> = b;
|
|
assert_eq!(bo.one(), 1);
|
|
let f: Box<dyn Fn() -> u32> = Box::new(|| 5);
|
|
let fm: Box<dyn FnMut() -> u32> = f;
|
|
let mut fm = fm;
|
|
assert_eq!(fm(), 5);
|
|
}
|
|
|
|
// array-slice-vec/slice_is_sorted_by_borrow.rs: ordering of tuples / arrays of primitives is
|
|
// lexicographic (it was rejected)
|
|
#[test]
|
|
fn c27_tuple_ordering() {
|
|
assert!((1, 2) < (1, 3));
|
|
assert!(!((2, 0) < (1, 9)));
|
|
assert!((1, 2) <= (1, 2));
|
|
assert!([3u8, 1] > [3, 0]);
|
|
assert!(!((1.0, f64::NAN) < (1.0, 2.0)));
|
|
assert!(('a', true) >= ('a', false));
|
|
}
|
|
|
|
// closures/2229_closure_analysis/capture-enum-field.rs: `let E(ref mut r, ..) = captured;`
|
|
// borrows the captured place (it bound into a copy: the writes were lost, SILENT)
|
|
#[derive(Debug, PartialEq)]
|
|
enum Color {
|
|
Rgb(u8, u8, u8),
|
|
}
|
|
|
|
#[test]
|
|
fn c28_ref_pattern_on_capture() {
|
|
let mut color = Color::Rgb(0, 0, 0);
|
|
let mut red = |v| {
|
|
let Color::Rgb(ref mut r, _, _) = color;
|
|
*r = v;
|
|
};
|
|
red(1);
|
|
let mut blue = |v| {
|
|
let Color::Rgb(_, _, ref mut b) = color;
|
|
*b = v;
|
|
};
|
|
blue(3);
|
|
assert_eq!(color, Color::Rgb(1, 0, 3));
|
|
}
|
|
|
|
// binding/ref-pattern-drop-behavior-8860.rs, for-loop-lifetime-of-unbound-values.rs,
|
|
// unboxed-closures-drop.rs: argument / loop items bound by `ref` or `_` drop with their scope;
|
|
// `x;` moves x and drops it (all three leaked)
|
|
fn takes_ref(ref _d: D) {}
|
|
|
|
#[test]
|
|
fn c29_unbound_values_drop() {
|
|
take_log();
|
|
takes_ref(D("arg"));
|
|
LOG.with(|l| l.borrow_mut().push("after-call"));
|
|
for _ in std::iter::once(D("item")) {
|
|
LOG.with(|l| l.borrow_mut().push("body"));
|
|
}
|
|
let x = D("x");
|
|
x;
|
|
LOG.with(|l| l.borrow_mut().push("end"));
|
|
assert_eq!(take_log(), ["arg", "after-call", "body", "item", "x", "end"]);
|
|
}
|
|
|
|
// derives/clone-copy/misbehaving-clone-impl.rs: a Copy type's derived Clone copies bits
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
static CLONED: AtomicBool = AtomicBool::new(false);
|
|
|
|
#[derive(Copy)]
|
|
struct Liar;
|
|
|
|
impl Clone for Liar {
|
|
fn clone(&self) -> Self {
|
|
CLONED.store(true, Ordering::SeqCst);
|
|
*self
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone)]
|
|
struct Innocent(#[allow(dead_code)] Liar);
|
|
|
|
// consts/const_fn_unsize.rs: a smart-pointer unsizing at a function's tail keeps N
|
|
fn dangling_slice<T>() -> std::ptr::NonNull<[T]> {
|
|
std::ptr::NonNull::<[T; 1]>::dangling()
|
|
}
|
|
|
|
#[test]
|
|
fn c30_copy_clone_and_tail_unsize() {
|
|
let _ = Innocent(Liar).clone();
|
|
assert!(!CLONED.load(Ordering::SeqCst));
|
|
assert_eq!(dangling_slice::<i32>().as_ptr().len(), 1);
|
|
}
|
|
|
|
// std lane core_niche (a): dropping a Box<[T]> / drop_in_place on `*mut [T]` drops every element
|
|
// (none or only the first were dropped); (b) `==` on fat raw pointers compares the metadata too
|
|
#[test]
|
|
fn c31_slice_drop_and_fat_ptr_eq() {
|
|
take_log();
|
|
let b: Box<[D]> = vec![D("1"), D("2"), D("3")].into_boxed_slice();
|
|
drop(b);
|
|
let p: *mut [D] = Box::into_raw(vec![D("4"), D("5")].into_boxed_slice());
|
|
unsafe {
|
|
std::ptr::drop_in_place(p);
|
|
drop(Box::from_raw(p as *mut [std::mem::ManuallyDrop<D>]));
|
|
}
|
|
assert_eq!(take_log(), ["1", "2", "3", "4", "5"]);
|
|
let a = [1u8, 2, 3];
|
|
let p1: *const [u8] = &a[..2];
|
|
let p2: *const [u8] = &a[..3];
|
|
assert!(p1 != p2);
|
|
assert!(p1 == p1);
|
|
assert!(p1 < p2);
|
|
}
|
|
|
|
// destructuring-assignment/nested_destructure.rs, structs-enums/align-enum.rs
|
|
struct Pair<S, T>(S, T);
|
|
|
|
#[repr(align(32))]
|
|
#[allow(dead_code)]
|
|
enum Align32 {
|
|
Foo,
|
|
Bar,
|
|
}
|
|
|
|
#[test]
|
|
fn c32_tuple_struct_assignee_and_enum_align() {
|
|
let (a, b, c);
|
|
Pair((a, b), c) = Pair((0, 1), 2);
|
|
assert_eq!((a, b, c), (0, 1, 2));
|
|
assert_eq!((std::mem::align_of::<Align32>(), std::mem::size_of::<Align32>()), (32, 32));
|
|
}
|
|
|
|
// structs-enums/multiple-reprs.rs, enum-non-c-like-repr-c*.rs, align-struct.rs: repr(C) enums
|
|
// are C ints (fieldless) or a tag followed by a union of the variants (FFI layout); hex align
|
|
#[repr(C)]
|
|
#[allow(dead_code)]
|
|
enum CEnum {
|
|
A,
|
|
B,
|
|
}
|
|
|
|
#[repr(C, u8)]
|
|
#[allow(dead_code)]
|
|
enum CU8 {
|
|
A(u8, u16, u8),
|
|
B(u8, u16, u8),
|
|
}
|
|
|
|
#[repr(u8)]
|
|
#[allow(dead_code)]
|
|
enum PrimU8 {
|
|
A(u8, u16, u8),
|
|
B(u8, u16, u8),
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[allow(dead_code)]
|
|
enum CData {
|
|
A(u32),
|
|
B { x: u8, y: u64 },
|
|
}
|
|
|
|
#[repr(align(0x100))]
|
|
struct Big {
|
|
_v: u8,
|
|
}
|
|
|
|
#[test]
|
|
fn c33_repr_c_enums() {
|
|
use std::mem::{align_of, size_of};
|
|
assert_eq!(size_of::<CEnum>(), 4);
|
|
assert_eq!(size_of::<CU8>(), 8);
|
|
assert_eq!(size_of::<PrimU8>(), 6);
|
|
assert_eq!((size_of::<CData>(), align_of::<CData>()), (24, 8));
|
|
assert_eq!(align_of::<Big>(), 0x100);
|
|
let d = CData::B { x: 7, y: 9 };
|
|
match d {
|
|
CData::B { x, y } => assert_eq!((x, y), (7, 9)),
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
// issues/issue-51655.rs, consts/const_prop_slice_pat_ice.rs: `&[..]` patterns against `&[T]`
|
|
// (an internal error: the pointee place lost its length)
|
|
fn ref_slice_pat(e: &[u8]) -> u8 {
|
|
match e {
|
|
&[] => 0,
|
|
&[a] => a,
|
|
_ => 9,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn c34_ref_slice_patterns() {
|
|
assert_eq!((ref_slice_pat(&[]), ref_slice_pat(&[5]), ref_slice_pat(&[1, 2])), (0, 5, 9));
|
|
}
|
|
|
|
// drop/issue-30018-nopanic.rs: operands already evaluated drop when a later one exits early
|
|
// (`f(a, b?)`, `S { a, b: c? }`, `(a, b?)`, `[a, { break }]` leaked them)
|
|
struct TwoD {
|
|
_a: D,
|
|
_b: D,
|
|
}
|
|
|
|
fn none_d() -> Option<D> {
|
|
None
|
|
}
|
|
|
|
fn takes_two(_a: D, _b: D) {}
|
|
|
|
fn early_struct() -> Option<TwoD> {
|
|
Some(TwoD { _a: D("s"), _b: none_d()? })
|
|
}
|
|
|
|
fn early_tuple() -> Option<(D, D)> {
|
|
Some((D("t"), none_d()?))
|
|
}
|
|
|
|
fn early_call() -> Option<()> {
|
|
takes_two(D("c"), none_d()?);
|
|
Some(())
|
|
}
|
|
|
|
#[test]
|
|
fn c35_operands_drop_on_early_exit() {
|
|
take_log();
|
|
assert!(early_struct().is_none());
|
|
assert!(early_tuple().is_none());
|
|
assert!(early_call().is_none());
|
|
loop {
|
|
let _r = [D("a0"), D("a1"), {
|
|
break;
|
|
}];
|
|
}
|
|
assert_eq!(take_log(), ["s", "t", "c", "a1", "a0"]);
|
|
}
|
|
|
|
// structs/newtype-struct-drop-run.rs, drop/static-issue-17302.rs: `let Foo(i) = *self;` in a
|
|
// Drop impl reads the place (Copy parts); it made a temporary copy that was dropped again:
|
|
// infinite drop recursion
|
|
struct Setter<'a>(&'a std::cell::Cell<i32>);
|
|
|
|
impl Drop for Setter<'_> {
|
|
fn drop(&mut self) {
|
|
let Setter(c) = *self;
|
|
c.set(c.get() + 1);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn c36_place_pattern_in_drop() {
|
|
let c = std::cell::Cell::new(0);
|
|
{
|
|
let _s = Setter(&c);
|
|
}
|
|
assert_eq!(c.get(), 1);
|
|
}
|
|
|
|
// attributes/inner-attrs-impl-cfg.rs: `impl X { #![cfg(false)] .. }` removes the impl
|
|
struct CfgImpl;
|
|
|
|
impl CfgImpl {
|
|
#![cfg(any())]
|
|
|
|
fn which(&self) -> u8 {
|
|
1
|
|
}
|
|
}
|
|
|
|
impl CfgImpl {
|
|
fn which(&self) -> u8 {
|
|
2
|
|
}
|
|
}
|
|
|
|
// autoref-autoderef/autoderef-privacy.rs: a private method not visible at the call site is no
|
|
// candidate; the probe continues through Deref
|
|
pub struct Outer2 {
|
|
v: Vec<u8>,
|
|
}
|
|
|
|
mod hidden {
|
|
pub struct Wrapper(pub Vec<u8>);
|
|
|
|
impl Wrapper {
|
|
fn len(&self) -> usize {
|
|
999
|
|
}
|
|
pub fn inner_len(&self) -> usize {
|
|
self.len()
|
|
}
|
|
}
|
|
|
|
impl std::ops::Deref for Wrapper {
|
|
type Target = Vec<u8>;
|
|
fn deref(&self) -> &Vec<u8> {
|
|
&self.0
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn c37_inner_cfg_and_privacy() {
|
|
assert_eq!(CfgImpl.which(), 2);
|
|
let w = hidden::Wrapper(vec![1, 2, 3]);
|
|
assert_eq!(w.len(), 3);
|
|
assert_eq!(w.inner_len(), 999);
|
|
let _ = Outer2 { v: Vec::new() }.v;
|
|
}
|
|
|
|
// overloaded/overloaded_deref_with_ref_pattern.rs: `ref mut` bindings on a Deref place use
|
|
// DerefMut (match scrutinee, let)
|
|
struct OnlyMut<T>(T, std::cell::Cell<u32>);
|
|
|
|
impl<T> std::ops::Deref for OnlyMut<T> {
|
|
type Target = T;
|
|
fn deref(&self) -> &T {
|
|
self.1.set(self.1.get() + 1);
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl<T> std::ops::DerefMut for OnlyMut<T> {
|
|
fn deref_mut(&mut self) -> &mut T {
|
|
&mut self.0
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn c38_ref_mut_patterns_use_deref_mut() {
|
|
let mut b = OnlyMut(0, std::cell::Cell::new(0));
|
|
match *b {
|
|
ref mut n => *n += 1,
|
|
}
|
|
let ref mut z = *b;
|
|
*z += 1;
|
|
let mut w = OnlyMut((1, 2), std::cell::Cell::new(0));
|
|
let (ref mut p, ref q) = *w;
|
|
*p += *q;
|
|
assert_eq!((b.0, (w.0).0, b.1.get(), w.1.get()), (2, 3, 0, 0));
|
|
}
|
|
|
|
// let-else/let-else-drop-order.rs: a non-matching let-else value and the initializer's
|
|
// temporaries drop before the else block, exactly once
|
|
struct Holder2(&'static str, Option<u8>);
|
|
|
|
impl Drop for Holder2 {
|
|
fn drop(&mut self) {
|
|
LOG.with(|l| l.borrow_mut().push(self.0));
|
|
}
|
|
}
|
|
|
|
fn let_else_none() -> u8 {
|
|
let Some(x) = Holder2("tmp", None).1 else {
|
|
LOG.with(|l| l.borrow_mut().push("else"));
|
|
return 0;
|
|
};
|
|
x
|
|
}
|
|
|
|
#[test]
|
|
fn c39_let_else_drop_order() {
|
|
take_log();
|
|
assert_eq!(let_else_none(), 0);
|
|
assert_eq!(take_log(), ["tmp", "else"]);
|
|
}
|
|
|
|
// coercion/coerce-unify.rs, coerce-many-with-ty-var.rs, closures/closure_no_cap_coerce_many*:
|
|
// if/match arms and array elements meet like rustc's CoerceMany (fn items -> fn pointer,
|
|
// `&String` then `&str` -> `&str`)
|
|
fn one_fn() -> u8 {
|
|
1
|
|
}
|
|
|
|
fn two_fn() -> u8 {
|
|
2
|
|
}
|
|
|
|
#[test]
|
|
fn c40_coerce_many() {
|
|
let c = std::hint::black_box(true);
|
|
let f = if c { one_fn } else { two_fn };
|
|
let g = match std::hint::black_box(1) {
|
|
0 => one_fn,
|
|
_ => two_fn,
|
|
};
|
|
let arr = [one_fn, two_fn, |/**/| 3];
|
|
let s = String::from("x");
|
|
let r = if c { &s } else { "lit" };
|
|
let e: [_; 2] = [one_fn, two_fn];
|
|
assert_eq!((f(), g(), arr[2](), r, e[1]()), (1, 2, 3, "x", 2));
|
|
}
|
|
|
|
// consts/large-zst-array-77062.rs, array-slice-vec/large-zst-array-compilation-time-68010.rs,
|
|
// layout/big-type-no-err.rs: `[(); usize::MAX]` is a valid zero-sized type (its length is not a
|
|
// type-variable tag) and repeating a zero-sized value stores nothing
|
|
static HUGE_ZST: [(); usize::MAX] = [(); usize::MAX];
|
|
|
|
#[test]
|
|
fn c41_usize_max_zst_array() {
|
|
let r = &[(); usize::MAX];
|
|
assert_eq!((r.len(), HUGE_ZST.len(), std::mem::size_of::<[(); usize::MAX]>()), (usize::MAX, usize::MAX, 0));
|
|
}
|
|
|
|
// impl-trait/closure-in-impl-trait.rs: a return-position `impl Trait` whose binding has an array
|
|
// length evaluated by running it (the opaque's bounds are lowered again once it is known)
|
|
fn blocky() -> impl Iterator<Item = [u8; { let _k = |x: u32| x; 3 }]> {
|
|
std::iter::once([7; 3])
|
|
}
|
|
|
|
#[test]
|
|
fn c42_impl_trait_anon_len() {
|
|
let v: Vec<[u8; 3]> = blocky().collect();
|
|
assert_eq!(v, [[7, 7, 7]]);
|
|
}
|
|
|
|
// a method with type params of its own or `where Self: Sized` has no vtable entry (rustc
|
|
// vtable_entries); the slots after it still dispatch
|
|
trait Area {
|
|
fn area(&self) -> u32;
|
|
fn scaled<T: Into<u32>>(&self, k: T) -> u32
|
|
where
|
|
Self: Sized,
|
|
{
|
|
self.area() * k.into()
|
|
}
|
|
fn boxed(self) -> Box<Self>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
Box::new(self)
|
|
}
|
|
fn name(&self) -> &'static str;
|
|
}
|
|
|
|
struct Sq(u32);
|
|
|
|
impl Area for Sq {
|
|
fn area(&self) -> u32 {
|
|
self.0 * self.0
|
|
}
|
|
fn name(&self) -> &'static str {
|
|
"sq"
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn c43_vtable_vacant_methods() {
|
|
let d: &dyn Area = &Sq(3);
|
|
let b = Sq(2).boxed();
|
|
let it: Box<dyn Iterator<Item = u8>> = Box::new([1u8, 2].into_iter());
|
|
assert_eq!((d.area(), d.name(), b.scaled(2u8), it.count()), (9, "sq", 8, 2));
|
|
}
|
|
|
|
// F44 (backend2/promo_autoref.rs, rustybuzz kerning): a shared autoref of a promotable rvalue
|
|
// receiver borrows a promoted static (rustc promote_consts), so `[A, B].iter()` outlives the fn
|
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
|
enum Kern {
|
|
A = 0,
|
|
B = 1,
|
|
}
|
|
|
|
fn kern_iter() -> std::iter::Copied<std::slice::Iter<'static, Kern>> {
|
|
[Kern::A, Kern::B].iter().copied()
|
|
}
|
|
|
|
fn len_of_promoted() -> &'static [u8] {
|
|
[1u8, 2, 3].as_slice()
|
|
}
|
|
|
|
#[inline(never)]
|
|
fn clobber_stack(n: u64) -> u64 {
|
|
let a = std::hint::black_box([n; 64]);
|
|
a.iter().sum()
|
|
}
|
|
|
|
#[test]
|
|
fn c44_promoted_autoref_receiver() {
|
|
let f: fn() -> std::iter::Copied<std::slice::Iter<'static, Kern>> = std::hint::black_box(kern_iter);
|
|
let g: fn() -> &'static [u8] = std::hint::black_box(len_of_promoted);
|
|
let mut i = f();
|
|
let s = g();
|
|
assert!(clobber_stack(0xffff_ffff) > 0);
|
|
let v: Vec<Kern> = i.by_ref().collect();
|
|
assert_eq!((v, s), (vec![Kern::A, Kern::B], &[1u8, 2, 3][..]));
|
|
}
|
|
|
|
// dst/dst-struct.rs, dst-trait.rs, dst-raw.rs, dst-coercions.rs, issues/issue-23491.rs: unsizing
|
|
// through a struct tail behind any pointer (`&Fat<Bar1>` -> `&Fat<dyn ToBar>`, `Box<Fat<[T; N]>>`
|
|
// -> `Box<Fat<[T]>>`) and `&T` -> `*const dyn Tr`
|
|
struct FatTail<T: ?Sized> {
|
|
f1: isize,
|
|
ptr: T,
|
|
}
|
|
|
|
trait ToNum {
|
|
fn num(&self) -> isize;
|
|
}
|
|
|
|
impl ToNum for u8 {
|
|
fn num(&self) -> isize {
|
|
*self as isize * 10
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn c45_struct_tail_unsizing() {
|
|
let a = FatTail { f1: 1, ptr: 4u8 };
|
|
let r: &FatTail<dyn ToNum> = &a;
|
|
let b: Box<FatTail<[isize]>> = Box::new(FatTail { f1: 2, ptr: [7, 8, 9] });
|
|
let rc: std::rc::Rc<std::cell::RefCell<[isize]>> = std::rc::Rc::new(std::cell::RefCell::new([1, 2, 3]));
|
|
let p: *const dyn ToNum = &5u8;
|
|
let n = unsafe { (*p).num() };
|
|
assert_eq!((r.f1, r.ptr.num(), b.f1, b.ptr.len(), b.ptr[2], rc.borrow().len(), n), (1, 40, 2, 3, 9, 3, 50));
|
|
}
|
|
|
|
// consts/issue-33537.rs: equal string / byte-string literals are one allocation (rustc
|
|
// allocate_bytes_dedup), also across fns
|
|
fn lit_ptr() -> *const u8 {
|
|
b"dedup-lit" as *const _ as *const u8
|
|
}
|
|
|
|
#[test]
|
|
fn c46_literal_dedup() {
|
|
assert_eq!(lit_ptr(), b"dedup-lit" as *const _ as *const u8);
|
|
}
|
|
|
|
// F45 (rustybuzz GSUB ligature.rs:32): an integer literal passed where a projection whose Self
|
|
// is still open is expected (`SmallVec::from_elem(0, 4)`: `A::Item`) waits for the projection
|
|
// (rustc: a fresh variable under a projection obligation)
|
|
unsafe trait ArrayLike {
|
|
type Item;
|
|
}
|
|
|
|
unsafe impl<T, const N: usize> ArrayLike for [T; N] {
|
|
type Item = T;
|
|
}
|
|
|
|
struct SmallV<A: ArrayLike> {
|
|
v: Vec<A::Item>,
|
|
}
|
|
|
|
impl<A: ArrayLike> SmallV<A> {
|
|
fn from_elem(elem: A::Item, n: usize) -> Self
|
|
where
|
|
A::Item: Clone,
|
|
{
|
|
SmallV { v: vec![elem; n] }
|
|
}
|
|
}
|
|
|
|
fn fill_small(x: &mut SmallV<[usize; 4]>) -> usize {
|
|
x.v[1] = 7;
|
|
x.v.iter().sum::<usize>() + x.v.len()
|
|
}
|
|
|
|
#[test]
|
|
fn c47_literal_into_open_projection() {
|
|
let mut m = SmallV::from_elem(0, 4);
|
|
assert_eq!(fill_small(&mut m), 11);
|
|
}
|
|
|
|
// F46 (rustybuzz ot_layout_gsubgpos.rs:588): `&impl Fn(..)` coerces to `&dyn Fn(..)` through the
|
|
// opaque type's bound
|
|
type MatchF = dyn Fn(u32, u16) -> bool;
|
|
|
|
fn match_same(g: u32, v: u16) -> bool {
|
|
g == v as u32
|
|
}
|
|
|
|
fn match_in(c: &'static [u16]) -> impl Fn(u32, u16) -> bool {
|
|
move |g, v| c[g as usize] == v
|
|
}
|
|
|
|
fn apply3(fs: [&MatchF; 3]) -> (bool, bool, bool) {
|
|
(fs[0](1, 1), fs[1](1, 5), fs[2](0, 3))
|
|
}
|
|
|
|
#[test]
|
|
fn c48_opaque_fn_to_dyn() {
|
|
static CLS: [u16; 2] = [4, 5];
|
|
let one: &MatchF = &match_in(&CLS);
|
|
assert_eq!((apply3([&match_same, &match_in(&CLS), &match_in(&CLS)]), one(0, 4)), ((true, true, false), true));
|
|
}
|
|
|
|
// coercion/coerce-expect-unsized.rs: `vec![..]` is `<[_]>::into_vec(box [..])`, so the expected
|
|
// `Vec<T>` reaches the elements; `_` inside `dyn Fn(A) -> _` / `fn(_)` is a type variable
|
|
#[test]
|
|
fn c49_vec_expectation_and_infer_in_dyn() {
|
|
let v: Vec<Box<dyn Fn(isize) -> _>> = vec![Box::new(|x| x as u8), Box::new(|x| x as i16 as u8)];
|
|
let b: Box<dyn Fn(isize) -> _> = Box::new(|x| x as u16);
|
|
let f: fn(_) -> u8 = |x: u8| x + 1;
|
|
let e: Vec<u8> = vec![];
|
|
let n = vec![String::from("a"), String::from("b")];
|
|
assert_eq!((v[0](300), v[1](300), b(70000), f(1), e.len(), n[1].as_str()), (44, 44, 4464, 2, 0, "b"));
|
|
}
|
|
|
|
// std::env::args() under `makepad run` is the program's argv (it was Relax's own command line on
|
|
// macOS: `args().len() > 1` held in every program)
|
|
#[test]
|
|
fn c50_env_args_are_the_programs() {
|
|
let a: Vec<String> = std::env::args().collect();
|
|
assert!(!a.iter().any(|x| x == "--units" || x == "test"));
|
|
}
|
|
|
|
// runtime/atomic-print.rs: inside `impl Tr for X` the trait Tr is in scope for method calls
|
|
// (rustc resolve traits_in_scope: current_trait_ref), without a `use`
|
|
mod impl_trait_scope {
|
|
pub struct Wrap(pub u32);
|
|
impl core::fmt::Debug for Wrap {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
|
self.0.fmt(f)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn c51_impl_trait_in_scope() {
|
|
assert_eq!(format!("{:?}", impl_trait_scope::Wrap(7)), "7");
|
|
}
|
|
|
|
// rustc ast_lowering inline_literals: `{}` of a string / in-range integer literal becomes part of
|
|
// the pieces, so `format_args!("x {}", 1).as_str()` is Some and `panic!("t {}", "lit")` has a
|
|
// `&'static str` payload
|
|
#[test]
|
|
fn c52_format_args_inline_literals() {
|
|
let p = std::panic::catch_unwind(|| panic!("thread {}", "lit")).unwrap_err();
|
|
assert_eq!(
|
|
(
|
|
format_args!("x {}", 1).as_str(),
|
|
format_args!("a {} b {}", "lit", 0x10u8).as_str(),
|
|
format_args!("{:?}", "lit").as_str(),
|
|
format_args!("{:>3}", 7).as_str(),
|
|
format_args!("{0} {0} {x}", "q", x = 2_000_000_000).as_str(),
|
|
p.downcast_ref::<&'static str>().copied(),
|
|
format!("{:3}|{}", 2, "z"),
|
|
),
|
|
(Some("x 1"), Some("a lit b 16"), None, None, Some("q q 2000000000"), Some("thread lit"), " 2|z".to_string())
|
|
);
|
|
}
|
|
|
|
// drop/or-pattern-drop-order.rs: an argument-position `impl Trait<Assoc = X>` is an anonymous
|
|
// generic param with that bound (`1..=3` becomes u32)
|
|
fn apit_items(x: impl IntoIterator<Item = u32>) -> Vec<u32> {
|
|
x.into_iter().collect()
|
|
}
|
|
|
|
#[test]
|
|
fn c53_apit_binding_infers_literals() {
|
|
assert_eq!(apit_items(1..=3), [1u32, 2, 3]);
|
|
}
|
|
|
|
// a scope drops its locals in reverse declaration order, not reverse first-assignment order
|
|
// (`let (x, y, z); z = ..; y = ..; x = ..;` drops z, y, x)
|
|
#[test]
|
|
fn c54_deferred_init_drop_order() {
|
|
take_log();
|
|
{
|
|
let (x, y, z);
|
|
z = Holder2("z", None);
|
|
y = Holder2("y", None);
|
|
x = Holder2("x", None);
|
|
let _ = (&x, &y, &z);
|
|
}
|
|
assert_eq!(take_log(), ["z", "y", "x"]);
|
|
}
|
|
|
|
// issues/issue-39808.rs, threads-sendsync/send_str_hashmap.rs: a value against a projection whose
|
|
// Self is still open (`Cow::Owned(s)`: `<?B as ToOwned>::Owned`) waits for the projection
|
|
#[test]
|
|
fn c55_open_projection_equation() {
|
|
use std::borrow::Cow;
|
|
let c = if std::hint::black_box(false) { Cow::Owned(String::from("o")) } else { Cow::Borrowed("b") };
|
|
assert_eq!(c, "b");
|
|
}
|
|
|
|
// where-clauses on non-param types (`where [T]: Concat<Item>`) take part in inference, and a
|
|
// candidate impl whose where-clauses cannot hold is dropped (rustc evaluates nested
|
|
// obligations): `[String]::concat()` is the `Concat<str>` impl
|
|
#[test]
|
|
fn c56_concat_join_inference() {
|
|
let v = vec![String::from("a"), String::from("b")];
|
|
let n = [[1, 2], [3, 4]].concat();
|
|
let j = ["x", "y"].join("-");
|
|
assert_eq!((v.concat(), n, j, v.join("+")), ("ab".to_string(), vec![1, 2, 3, 4], "x-y".to_string(), "a+b".to_string()));
|
|
}
|
|
|
|
// issues/issue-51907.rs, self/objects-owned-object-owned-method.rs: `self: Box<Self>` /
|
|
// `self: Rc<Self>` methods called on trait objects (the receiver without its metadata, rustc
|
|
// DispatchFromDyn); `for` over `&mut dyn Iterator` (iterators/for-loop-over-mut-iterator-21655.rs)
|
|
trait Consume {
|
|
fn consume(self: Box<Self>) -> &'static str;
|
|
fn shared(self: std::rc::Rc<Self>) -> usize;
|
|
}
|
|
|
|
impl Consume for Holder2 {
|
|
fn consume(self: Box<Self>) -> &'static str {
|
|
self.0
|
|
}
|
|
fn shared(self: std::rc::Rc<Self>) -> usize {
|
|
std::rc::Rc::strong_count(&self)
|
|
}
|
|
}
|
|
|
|
fn sum_dyn(it: &mut dyn Iterator<Item = u32>) -> u32 {
|
|
let mut s = 0;
|
|
for x in it {
|
|
s += x;
|
|
}
|
|
s
|
|
}
|
|
|
|
#[test]
|
|
fn c57_dyn_box_self_and_for_over_dyn() {
|
|
take_log();
|
|
let b: Box<dyn Consume> = Box::new(Holder2("boxed", None));
|
|
let name = b.consume();
|
|
let r: std::rc::Rc<dyn Consume> = std::rc::Rc::new(Holder2("rc", None));
|
|
let r2 = r.clone();
|
|
let n = r.shared();
|
|
drop(r2);
|
|
let s = sum_dyn(&mut vec![1u32, 2, 3].into_iter());
|
|
assert_eq!((name, n, s, take_log()), ("boxed", 2, 6, vec!["boxed", "rc"]));
|
|
}
|
|
|
|
// generics/mid-path-type-params.rs: `Trait::<A>::m::<B>` puts A on the trait and B on the
|
|
// method; generics/generic-default-type-params.rs: `Foo` in a type is `Foo<Defaults..>`
|
|
trait MakeWith<T> {
|
|
fn make<U: Default>(x: T) -> (Self, U)
|
|
where
|
|
Self: Sized;
|
|
}
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
struct Made(isize);
|
|
|
|
impl MakeWith<isize> for Made {
|
|
fn make<U: Default>(x: isize) -> (Made, U) {
|
|
(Made(x), U::default())
|
|
}
|
|
}
|
|
|
|
struct Deflt<A = (isize, char)> {
|
|
a: A,
|
|
}
|
|
|
|
fn deflt_first(x: Deflt) -> isize {
|
|
x.a.0
|
|
}
|
|
|
|
#[test]
|
|
fn c58_trait_turbofish_and_type_defaults() {
|
|
let (m, u): (Made, u8) = MakeWith::<isize>::make::<u8>(4);
|
|
assert_eq!((m, u, deflt_first(Deflt { a: (7, 'c') })), (Made(4), 0, 7));
|
|
}
|
|
|
|
// array-slice-vec/subslice-patterns-const-eval.rs: a `ref` binding into a promotable let
|
|
// initializer borrows a promoted static (also in a const fn / const initializer);
|
|
// consts/zst_no_llvm_alloc.rs: a promoted zero-sized value is at its alignment
|
|
#[derive(PartialEq, Debug)]
|
|
struct Nn(u8);
|
|
|
|
const SUB_C: &[Nn; 2] = {
|
|
let [_, ref x @ .., _] = [Nn(1), Nn(2), Nn(3), Nn(4)];
|
|
x
|
|
};
|
|
|
|
const fn sub_f() -> &'static [Nn; 2] {
|
|
let [_, ref x @ .., _] = [Nn(1), Nn(2), Nn(3), Nn(4)];
|
|
x
|
|
}
|
|
|
|
#[test]
|
|
fn c59_promoted_ref_bindings_and_zst_address() {
|
|
let z: &'static () = &();
|
|
assert_eq!((SUB_C, sub_f(), z as *const () as usize), (&[Nn(2), Nn(3)], &[Nn(2), Nn(3)], 1));
|
|
}
|
|
|
|
// cast/as-cast-unifies-raw-pointer-and-fn-type.rs, cast/cast.rs: a cast that is a coercion is
|
|
// one (`&() as *const _` has pointee `()`, `generic as fn(i32)` instantiates it);
|
|
// coercion/coerce-unify-return.rs: a method call's expected result types its arguments
|
|
fn raw_of<T>(p: *const T) -> usize {
|
|
std::mem::size_of::<T>() + (p as usize & 0)
|
|
}
|
|
|
|
fn takes<T>(_t: T) -> u8 {
|
|
1
|
|
}
|
|
|
|
struct Wrapf;
|
|
|
|
impl Wrapf {
|
|
fn wrap<T>(self, x: T) -> Option<T> {
|
|
Some(x)
|
|
}
|
|
}
|
|
|
|
fn unit_fn() {}
|
|
|
|
#[test]
|
|
fn c60_cast_coercions_and_method_expectation() {
|
|
let p = &7u64 as *const _;
|
|
let f = takes as fn(i32) -> u8;
|
|
let o: Option<fn()> = Wrapf.wrap(unit_fn);
|
|
assert_eq!((raw_of(p), f(3), o.is_some()), (8, 1, true));
|
|
}
|
|
|
|
// overloaded/overloaded-index.rs: an integer literal index is typed by the selected Index impl
|
|
// (`impl Index<isize> for Foo`: `f[1]` is isize), generic ones (`Vec<T>`) take usize; an impl's
|
|
// params fixed only by a bound's binding are completed (traits/index-trait-multiple-impls-15734.rs)
|
|
struct Pair2 {
|
|
x: isize,
|
|
y: isize,
|
|
}
|
|
|
|
impl std::ops::Index<isize> for Pair2 {
|
|
type Output = isize;
|
|
fn index(&self, z: isize) -> &isize {
|
|
if z == 0 { &self.x } else { &self.y }
|
|
}
|
|
}
|
|
|
|
struct RowOf<M> {
|
|
m: M,
|
|
}
|
|
|
|
impl<T, M: std::ops::Index<usize, Output = T>> std::ops::Index<u8> for RowOf<M> {
|
|
type Output = T;
|
|
fn index(&self, i: u8) -> &T {
|
|
&self.m[i as usize]
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn c61_literal_index_typed_by_impl() {
|
|
let p = Pair2 { x: 1, y: 2 };
|
|
let v = vec![10, 20, 30];
|
|
let r = RowOf { m: vec![5, 6] };
|
|
assert_eq!((p[1], v[2], r[1]), (2, 30, 6));
|
|
}
|
|
|
|
// `&mut T` is noalias only for T: Unpin (rustc): a !Unpin value may be written through a pointer
|
|
// into itself while the `&mut` is live
|
|
struct SelfRef {
|
|
x: u32,
|
|
p: *mut u32,
|
|
_pin: std::marker::PhantomPinned,
|
|
}
|
|
|
|
#[inline(never)]
|
|
fn write_through_self(s: &mut SelfRef) -> u32 {
|
|
s.x = 1;
|
|
unsafe { *s.p = 2 };
|
|
s.x
|
|
}
|
|
|
|
#[test]
|
|
fn c62_not_unpin_mut_ref_may_alias() {
|
|
let mut s = Box::pin(SelfRef { x: 0, p: std::ptr::null_mut(), _pin: std::marker::PhantomPinned });
|
|
let r = unsafe { s.as_mut().get_unchecked_mut() };
|
|
r.p = &mut r.x;
|
|
let f: fn(&mut SelfRef) -> u32 = std::hint::black_box(write_through_self);
|
|
assert_eq!(f(r), 2);
|
|
}
|
|
|
|
// or-patterns/issue-70413-no-unreachable-pat-and-guard.rs, search-via-bindings.rs,
|
|
// drop/or-pattern-drop-order.rs: a guarded arm tries each or-alternative combination as its own
|
|
// candidate (the guard runs per candidate), its moves happen only once the guard holds (a
|
|
// failing guard left moved-out parts that nothing dropped), and array elements moved out by a
|
|
// pattern are tracked one by one (the rest drops last range first, as rustc's drop ladder)
|
|
#[test]
|
|
fn c63_guarded_or_candidates_no_leak() {
|
|
let mut runs = 0;
|
|
let hit = match (3, 42) {
|
|
(a, _) | (_, a)
|
|
if {
|
|
runs += 1;
|
|
a > 10
|
|
} =>
|
|
{
|
|
a
|
|
}
|
|
_ => 0,
|
|
};
|
|
take_log();
|
|
match [Holder2("6", None), Holder2("4", None)] {
|
|
[_, w] | [w, _] if w.0 == "nope" => unreachable!(),
|
|
[_, w] if true => {
|
|
let _ = &w;
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
let after_match = take_log();
|
|
match [[D("6"), D("4")], [D("3"), D("2")]] {
|
|
[[_, w], [x, _]] if true => {
|
|
let _ = (&w, &x);
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
assert_eq!((hit, runs, after_match, take_log()), (42, 2, vec!["4", "6"], vec!["3", "4", "2", "6"]));
|
|
}
|
|
|
|
// expr/compound-assignment/eval-order.rs: rustc decides built-in vs overloaded `a op= b` on the
|
|
// generic body, so in a generic fn where the left operand's type is a param the left operand is
|
|
// evaluated first even in the i32 instance (built-in compound assignment: right first)
|
|
struct Wrapper2<T>(T);
|
|
|
|
fn generic_compound<T: std::ops::AddAssign<T> + Default>() -> Vec<&'static str> {
|
|
let mut order = vec![];
|
|
let mut w: Wrapper2<T> = Wrapper2(Default::default());
|
|
{
|
|
order.push("lhs");
|
|
&mut w
|
|
}
|
|
.0 += {
|
|
order.push("rhs");
|
|
Default::default()
|
|
};
|
|
order
|
|
}
|
|
|
|
#[test]
|
|
fn c64_generic_compound_assign_order() {
|
|
let mut order = vec![];
|
|
let mut int = Wrapper2(0);
|
|
{
|
|
order.push("lhs");
|
|
&mut int
|
|
}
|
|
.0 += {
|
|
order.push("rhs");
|
|
0
|
|
};
|
|
assert_eq!((order, generic_compound::<i32>()), (vec!["rhs", "lhs"], vec!["lhs", "rhs"]));
|
|
}
|
|
|
|
// let-else/let-else-drop-order.rs: a field of a temporary (`make().1`) is a value the statement
|
|
// owns -- a non-matching one is dropped before the else block (it leaked: it was taken for a
|
|
// place), and so are lifetime-extended temporaries (`let 0 = &make().1 else`)
|
|
enum TwoWay {
|
|
Yes(u8),
|
|
No(u8),
|
|
}
|
|
|
|
impl Drop for TwoWay {
|
|
fn drop(&mut self) {
|
|
LOG.with(|l| l.borrow_mut().push(match self {
|
|
TwoWay::Yes(_) => "yes",
|
|
TwoWay::No(_) => "no",
|
|
}));
|
|
}
|
|
}
|
|
|
|
fn make_two() -> ((), TwoWay) {
|
|
((), TwoWay::No(1))
|
|
}
|
|
|
|
fn make_held() -> (D, u32) {
|
|
(D("held"), 1)
|
|
}
|
|
|
|
#[test]
|
|
fn c65_let_else_field_of_temporary() {
|
|
take_log();
|
|
loop {
|
|
let TwoWay::Yes(_) = make_two().1 else {
|
|
LOG.with(|l| l.borrow_mut().push("else1"));
|
|
break;
|
|
};
|
|
break;
|
|
}
|
|
loop {
|
|
let 0 = &make_held().1 else {
|
|
LOG.with(|l| l.borrow_mut().push("else2"));
|
|
break;
|
|
};
|
|
break;
|
|
}
|
|
assert_eq!(take_log(), ["no", "else1", "held", "else2"]);
|
|
}
|
|
|
|
// hygiene/hygiene.rs, binding/match-var-hygiene.rs, hygiene/hygienic-labels.rs: macro_rules
|
|
// locals and labels are hygienic (rustc SyntaxContext marks): a body's bindings do not capture
|
|
// or shadow the caller's, and a body's free variables resolve where the macro is defined
|
|
macro_rules! shadow_in_body {
|
|
($ex:expr) => {{
|
|
match 9 {
|
|
_x => $ex,
|
|
}
|
|
}};
|
|
}
|
|
|
|
macro_rules! loop_x {
|
|
($e:expr) => {
|
|
'x: loop {
|
|
$e
|
|
}
|
|
};
|
|
}
|
|
|
|
#[test]
|
|
fn c66_macro_rules_hygiene() {
|
|
let x = 0;
|
|
macro_rules! def_site {
|
|
() => {
|
|
x
|
|
};
|
|
}
|
|
let x = 1;
|
|
let seen = def_site!();
|
|
let caller = match 8 {
|
|
_x => shadow_in_body!(_x),
|
|
};
|
|
let mut broke_outer = true;
|
|
'x: for _ in 0..1 {
|
|
loop_x!(break 'x);
|
|
#[allow(unreachable_code)]
|
|
{
|
|
broke_outer = false;
|
|
}
|
|
}
|
|
assert_eq!((seen, x, caller, broke_outer), (0, 1, 8, true));
|
|
}
|
|
|
|
// traits/trait-upcasting/correct-supertrait-substitution.rs, basic.rs, diamond.rs: a trait
|
|
// object's vtable has one entry set per distinct supertrait instantiation (rustc
|
|
// vtable_entries: Bar<i32> and Bar<bool> are two), upcasting picks the instantiation, and a
|
|
// static call of a trait method with Self = the trait object (`*a == 3` through a supertrait
|
|
// PartialEq<i32>) is a virtual call (InstanceKind::Virtual)
|
|
trait BarOf<T: Default + ToString> {
|
|
fn bar(&self) -> String {
|
|
T::default().to_string()
|
|
}
|
|
}
|
|
|
|
trait FooOf<T: Default + ToString>: BarOf<i32> + BarOf<T> {}
|
|
|
|
struct TwoBars;
|
|
|
|
impl BarOf<i32> for TwoBars {}
|
|
impl BarOf<bool> for TwoBars {}
|
|
impl FooOf<bool> for TwoBars {}
|
|
|
|
trait EqFoo: PartialEq<i32> {}
|
|
|
|
struct Three(i32);
|
|
|
|
impl PartialEq<i32> for Three {
|
|
fn eq(&self, o: &i32) -> bool {
|
|
self.0 == *o
|
|
}
|
|
}
|
|
|
|
impl EqFoo for Three {}
|
|
|
|
#[test]
|
|
fn c67_generic_supertrait_vtables() {
|
|
let x: &dyn FooOf<bool> = &TwoBars;
|
|
let p = x as &dyn BarOf<i32>;
|
|
let q = x as &dyn BarOf<bool>;
|
|
let e: Box<dyn EqFoo> = Box::new(Three(3));
|
|
assert_eq!((p.bar(), q.bar(), *e == 3, *e == 4), ("0".to_string(), "false".to_string(), true, false));
|
|
}
|
|
|
|
// c68: an integer literal's type variable only unifies with integral types (rustc IntVar), so
|
|
// the impl on `W<&i64>` is no candidate for `W({integer})`; a `Mutex<dyn Fn>` reached through
|
|
// `Arc` unsizing finds its closure at the sized instance's offset (reordered prefix fields)
|
|
struct LitW<T>(T);
|
|
trait LitK {
|
|
fn k(&self) -> u32;
|
|
}
|
|
impl LitK for LitW<i64> {
|
|
fn k(&self) -> u32 {
|
|
1
|
|
}
|
|
}
|
|
impl<'a> LitK for LitW<&'a i64> {
|
|
fn k(&self) -> u32 {
|
|
2
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn c68_literal_vars_and_dyn_mutex() {
|
|
let one = 7i64;
|
|
assert_eq!((LitW(5).k(), LitW(&one).k()), (1, 2));
|
|
let hits = std::sync::Arc::new(std::sync::atomic::AtomicU64::new(0));
|
|
let h2 = hits.clone();
|
|
let tag = 0x5au8;
|
|
let f: std::sync::Arc<std::sync::Mutex<dyn Fn(u64) -> u64 + Send>> =
|
|
std::sync::Arc::new(std::sync::Mutex::new(move |x: u64| {
|
|
h2.fetch_add(x, std::sync::atomic::Ordering::Relaxed);
|
|
x + tag as u64
|
|
}));
|
|
let r = f.lock().unwrap()(3);
|
|
assert_eq!((r, hits.load(std::sync::atomic::Ordering::Relaxed)), (93, 3));
|
|
}
|
|
|
|
// c69: concat! writes literals by value (rustc concat.rs): integers in decimal without suffix,
|
|
// floats as written without suffix and underscores
|
|
#[test]
|
|
fn c69_concat_literal_values() {
|
|
assert_eq!(concat!(0x1b, "-", 0o17, "-", 0b101, "-", 1_000u32, "-", -0x10i8, "-", 1_0.5e3f64, "-", 2.5, "-", 'c', true), "27-15-5-1000--16-10.5e3-2.5-ctrue");
|
|
}
|