mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-16 23:24:03 +00:00
2910 lines
55 KiB
Rust
2910 lines
55 KiB
Rust
use base_db::SourceDatabase;
|
|
use chalk_ir::Substitution;
|
|
use hir_def::db::DefDatabase;
|
|
use rustc_apfloat::{
|
|
ieee::{Half as f16, Quad as f128},
|
|
Float,
|
|
};
|
|
use span::EditionedFileId;
|
|
use test_fixture::WithFixture;
|
|
use test_utils::skip_slow_tests;
|
|
|
|
use crate::{
|
|
consteval::try_const_usize, db::HirDatabase, mir::pad16, test_db::TestDB, Const, ConstScalar,
|
|
Interner, MemoryMap,
|
|
};
|
|
|
|
use super::{
|
|
super::mir::{MirEvalError, MirLowerError},
|
|
ConstEvalError,
|
|
};
|
|
|
|
mod intrinsics;
|
|
|
|
fn simplify(e: ConstEvalError) -> ConstEvalError {
|
|
match e {
|
|
ConstEvalError::MirEvalError(MirEvalError::InFunction(e, _)) => {
|
|
simplify(ConstEvalError::MirEvalError(*e))
|
|
}
|
|
_ => e,
|
|
}
|
|
}
|
|
|
|
#[track_caller]
|
|
fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) {
|
|
let (db, file_id) = TestDB::with_single_file(ra_fixture);
|
|
match eval_goal(&db, file_id) {
|
|
Ok(_) => panic!("Expected fail, but it succeeded"),
|
|
Err(e) => {
|
|
assert!(error(simplify(e.clone())), "Actual error was: {}", pretty_print_err(e, db))
|
|
}
|
|
}
|
|
}
|
|
|
|
#[track_caller]
|
|
fn check_number(ra_fixture: &str, answer: i128) {
|
|
check_answer(ra_fixture, |b, _| {
|
|
assert_eq!(
|
|
b,
|
|
&answer.to_le_bytes()[0..b.len()],
|
|
"Bytes differ. In decimal form: actual = {}, expected = {answer}",
|
|
i128::from_le_bytes(pad16(b, true))
|
|
);
|
|
});
|
|
}
|
|
|
|
#[track_caller]
|
|
fn check_str(ra_fixture: &str, answer: &str) {
|
|
check_answer(ra_fixture, |b, mm| {
|
|
let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
|
|
let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());
|
|
let Some(bytes) = mm.get(addr, size) else {
|
|
panic!("string data missed in the memory map");
|
|
};
|
|
assert_eq!(
|
|
bytes,
|
|
answer.as_bytes(),
|
|
"Bytes differ. In string form: actual = {}, expected = {answer}",
|
|
String::from_utf8_lossy(bytes)
|
|
);
|
|
});
|
|
}
|
|
|
|
#[track_caller]
|
|
fn check_answer(ra_fixture: &str, check: impl FnOnce(&[u8], &MemoryMap)) {
|
|
let (db, file_ids) = TestDB::with_many_files(ra_fixture);
|
|
let file_id = *file_ids.last().unwrap();
|
|
let r = match eval_goal(&db, file_id) {
|
|
Ok(t) => t,
|
|
Err(e) => {
|
|
let err = pretty_print_err(e, db);
|
|
panic!("Error in evaluating goal: {err}");
|
|
}
|
|
};
|
|
match &r.data(Interner).value {
|
|
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
|
|
ConstScalar::Bytes(b, mm) => {
|
|
check(b, mm);
|
|
}
|
|
x => panic!("Expected number but found {x:?}"),
|
|
},
|
|
_ => panic!("result of const eval wasn't a concrete const"),
|
|
}
|
|
}
|
|
|
|
fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String {
|
|
let mut err = String::new();
|
|
let span_formatter = |file, range| format!("{file:?} {range:?}");
|
|
let edition = db.crate_graph()[db.test_crate()].edition;
|
|
match e {
|
|
ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter, edition),
|
|
ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter, edition),
|
|
}
|
|
.unwrap();
|
|
err
|
|
}
|
|
|
|
fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result<Const, ConstEvalError> {
|
|
let module_id = db.module_for_file(file_id.file_id());
|
|
let def_map = module_id.def_map(db);
|
|
let scope = &def_map[module_id.local_id].scope;
|
|
let const_id = scope
|
|
.declarations()
|
|
.find_map(|x| match x {
|
|
hir_def::ModuleDefId::ConstId(x) => {
|
|
if db.const_data(x).name.as_ref()?.display(db, file_id.edition()).to_string()
|
|
== "GOAL"
|
|
{
|
|
Some(x)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
_ => None,
|
|
})
|
|
.expect("No const named GOAL found in the test");
|
|
db.const_eval(const_id.into(), Substitution::empty(Interner), None)
|
|
}
|
|
|
|
#[test]
|
|
fn add() {
|
|
check_number(r#"const GOAL: usize = 2 + 2;"#, 4);
|
|
check_number(r#"const GOAL: i32 = -2 + --5;"#, 3);
|
|
check_number(r#"const GOAL: i32 = 7 - 5;"#, 2);
|
|
check_number(r#"const GOAL: i32 = 7 + (1 - 5);"#, 3);
|
|
}
|
|
|
|
#[test]
|
|
fn bit_op() {
|
|
check_number(r#"const GOAL: u8 = !0 & !(!0 >> 1)"#, 128);
|
|
check_number(r#"const GOAL: i8 = !0 & !(!0 >> 1)"#, 0);
|
|
check_number(r#"const GOAL: i8 = 1 << 7"#, (1i8 << 7) as i128);
|
|
check_number(r#"const GOAL: i8 = -1 << 2"#, (-1i8 << 2) as i128);
|
|
check_fail(r#"const GOAL: i8 = 1 << 8"#, |e| {
|
|
e == ConstEvalError::MirEvalError(MirEvalError::Panic("Overflow in Shl".to_owned()))
|
|
});
|
|
check_number(r#"const GOAL: i32 = 100000000i32 << 11"#, (100000000i32 << 11) as i128);
|
|
}
|
|
|
|
#[test]
|
|
fn floating_point() {
|
|
check_number(
|
|
r#"const GOAL: f128 = 2.0 + 3.0 * 5.5 - 8.;"#,
|
|
"10.5".parse::<f128>().unwrap().to_bits() as i128,
|
|
);
|
|
check_number(
|
|
r#"const GOAL: f128 = -90.0 + 36.0;"#,
|
|
"-54.0".parse::<f128>().unwrap().to_bits() as i128,
|
|
);
|
|
check_number(
|
|
r#"const GOAL: f64 = 2.0 + 3.0 * 5.5 - 8.;"#,
|
|
i128::from_le_bytes(pad16(&f64::to_le_bytes(10.5), true)),
|
|
);
|
|
check_number(
|
|
r#"const GOAL: f32 = 2.0 + 3.0 * 5.5 - 8.;"#,
|
|
i128::from_le_bytes(pad16(&f32::to_le_bytes(10.5), true)),
|
|
);
|
|
check_number(
|
|
r#"const GOAL: f32 = -90.0 + 36.0;"#,
|
|
i128::from_le_bytes(pad16(&f32::to_le_bytes(-54.0), true)),
|
|
);
|
|
check_number(
|
|
r#"const GOAL: f16 = 2.0 + 3.0 * 5.5 - 8.;"#,
|
|
i128::from_le_bytes(pad16(
|
|
&u16::try_from("10.5".parse::<f16>().unwrap().to_bits()).unwrap().to_le_bytes(),
|
|
true,
|
|
)),
|
|
);
|
|
check_number(
|
|
r#"const GOAL: f16 = -90.0 + 36.0;"#,
|
|
i128::from_le_bytes(pad16(
|
|
&u16::try_from("-54.0".parse::<f16>().unwrap().to_bits()).unwrap().to_le_bytes(),
|
|
true,
|
|
)),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn casts() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: sized
|
|
const GOAL: usize = 12 as *const i32 as usize
|
|
"#,
|
|
12,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
const GOAL: i32 = {
|
|
let a = [10, 20, 3, 15];
|
|
let x: &[i32] = &a;
|
|
let y: *const [i32] = x;
|
|
let z = y as *const i32;
|
|
unsafe { *z }
|
|
};
|
|
"#,
|
|
10,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
const GOAL: i16 = {
|
|
let a = &mut 5_i16;
|
|
let z = a as *mut _;
|
|
unsafe { *z }
|
|
};
|
|
"#,
|
|
5,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
const GOAL: usize = {
|
|
let a = &[10, 20, 30, 40] as &[i32];
|
|
a.len()
|
|
};
|
|
"#,
|
|
4,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
struct X {
|
|
unsize_field: [u8],
|
|
}
|
|
|
|
const GOAL: usize = {
|
|
let a = [10, 20, 3, 15];
|
|
let x: &[i32] = &a;
|
|
let x: *const [i32] = x;
|
|
let x = x as *const [u8]; // slice fat pointer cast don't touch metadata
|
|
let x = x as *const str;
|
|
let x = x as *const X;
|
|
let x = x as *const [i16];
|
|
let x = x as *const X;
|
|
let x = x as *const [u8];
|
|
let w = unsafe { &*x };
|
|
w.len()
|
|
};
|
|
"#,
|
|
4,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: sized
|
|
const GOAL: i32 = -12i8 as i32
|
|
"#,
|
|
-12,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn floating_point_casts() {
|
|
check_number(r#"const GOAL: usize = 12i32 as f32 as usize"#, 12);
|
|
check_number(r#"const GOAL: i8 = -12i32 as f64 as i8"#, -12);
|
|
check_number(r#"const GOAL: i32 = (-1ui8 as f32 + 2u64 as f32) as i32"#, 1);
|
|
check_number(r#"const GOAL: i8 = (0./0.) as i8"#, 0);
|
|
check_number(r#"const GOAL: i8 = (1./0.) as i8"#, 127);
|
|
check_number(r#"const GOAL: i8 = (-1./0.) as i8"#, -128);
|
|
check_number(r#"const GOAL: i64 = 1e18f64 as f32 as i64"#, 999999984306749440);
|
|
}
|
|
|
|
#[test]
|
|
fn raw_pointer_equality() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: copy, eq
|
|
const GOAL: bool = {
|
|
let a = 2;
|
|
let p1 = a as *const i32;
|
|
let p2 = a as *const i32;
|
|
p1 == p2
|
|
};
|
|
"#,
|
|
1,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn alignment() {
|
|
check_answer(
|
|
r#"
|
|
//- minicore: transmute
|
|
use core::mem::transmute;
|
|
const GOAL: usize = {
|
|
let x: i64 = 2;
|
|
transmute(&x)
|
|
}
|
|
"#,
|
|
|b, _| assert_eq!(b[0] % 8, 0),
|
|
);
|
|
check_answer(
|
|
r#"
|
|
//- minicore: transmute
|
|
use core::mem::transmute;
|
|
static X: i64 = 12;
|
|
const GOAL: usize = transmute(&X);
|
|
"#,
|
|
|b, _| assert_eq!(b[0] % 8, 0),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn locals() {
|
|
check_number(
|
|
r#"
|
|
const GOAL: usize = {
|
|
let a = 3 + 2;
|
|
let b = a * a;
|
|
b
|
|
};
|
|
"#,
|
|
25,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn references() {
|
|
check_number(
|
|
r#"
|
|
const GOAL: usize = {
|
|
let x = 3;
|
|
let y = &mut x;
|
|
*y = 5;
|
|
x
|
|
};
|
|
"#,
|
|
5,
|
|
);
|
|
check_number(
|
|
r#"
|
|
struct Foo(i32);
|
|
impl Foo {
|
|
fn method(&mut self, x: i32) {
|
|
self.0 = 2 * self.0 + x;
|
|
}
|
|
}
|
|
const GOAL: i32 = {
|
|
let mut x = Foo(3);
|
|
x.method(5);
|
|
x.0
|
|
};
|
|
"#,
|
|
11,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn reference_autoderef() {
|
|
check_number(
|
|
r#"
|
|
const GOAL: usize = {
|
|
let x = 3;
|
|
let y = &mut x;
|
|
let y: &mut usize = &mut y;
|
|
*y = 5;
|
|
x
|
|
};
|
|
"#,
|
|
5,
|
|
);
|
|
check_number(
|
|
r#"
|
|
const GOAL: usize = {
|
|
let x = 3;
|
|
let y = &&&&&&&x;
|
|
let z: &usize = &y;
|
|
*z
|
|
};
|
|
"#,
|
|
3,
|
|
);
|
|
check_number(
|
|
r#"
|
|
struct Foo<T> { x: T }
|
|
impl<T> Foo<T> {
|
|
fn foo(&mut self) -> T { self.x }
|
|
}
|
|
fn f(i: &mut &mut Foo<Foo<i32>>) -> i32 {
|
|
((**i).x).foo()
|
|
}
|
|
fn g(i: Foo<Foo<i32>>) -> i32 {
|
|
i.x.foo()
|
|
}
|
|
const GOAL: i32 = f(&mut &mut Foo { x: Foo { x: 3 } }) + g(Foo { x: Foo { x: 5 } });
|
|
"#,
|
|
8,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn overloaded_deref() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: deref_mut
|
|
struct Foo;
|
|
|
|
impl core::ops::Deref for Foo {
|
|
type Target = i32;
|
|
fn deref(&self) -> &i32 {
|
|
&5
|
|
}
|
|
}
|
|
|
|
const GOAL: i32 = {
|
|
let x = Foo;
|
|
let y = &*x;
|
|
*y + *x
|
|
};
|
|
"#,
|
|
10,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn overloaded_deref_autoref() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: deref_mut
|
|
struct Foo;
|
|
struct Bar;
|
|
|
|
impl core::ops::Deref for Foo {
|
|
type Target = Bar;
|
|
fn deref(&self) -> &Bar {
|
|
&Bar
|
|
}
|
|
}
|
|
|
|
impl Bar {
|
|
fn method(&self) -> i32 {
|
|
5
|
|
}
|
|
}
|
|
|
|
const GOAL: i32 = Foo.method();
|
|
"#,
|
|
5,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn overloaded_index() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: index
|
|
struct Foo;
|
|
|
|
impl core::ops::Index<usize> for Foo {
|
|
type Output = i32;
|
|
fn index(&self, index: usize) -> &i32 {
|
|
if index == 7 {
|
|
&700
|
|
} else {
|
|
&1000
|
|
}
|
|
}
|
|
}
|
|
|
|
impl core::ops::IndexMut<usize> for Foo {
|
|
fn index_mut(&mut self, index: usize) -> &mut i32 {
|
|
if index == 7 {
|
|
&mut 7
|
|
} else {
|
|
&mut 10
|
|
}
|
|
}
|
|
}
|
|
|
|
const GOAL: i32 = {
|
|
(Foo[2]) + (Foo[7]) + (*&Foo[2]) + (*&Foo[7]) + (*&mut Foo[2]) + (*&mut Foo[7])
|
|
};
|
|
"#,
|
|
3417,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn overloaded_binop() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: add
|
|
enum Color {
|
|
Red,
|
|
Green,
|
|
Yellow,
|
|
}
|
|
|
|
use Color::*;
|
|
|
|
impl core::ops::Add for Color {
|
|
type Output = Color;
|
|
fn add(self, rhs: Color) -> Self::Output {
|
|
Yellow
|
|
}
|
|
}
|
|
|
|
impl core::ops::AddAssign for Color {
|
|
fn add_assign(&mut self, rhs: Color) {
|
|
*self = Red;
|
|
}
|
|
}
|
|
|
|
const GOAL: bool = {
|
|
let x = Red + Green;
|
|
let mut y = Green;
|
|
y += x;
|
|
x == Yellow && y == Red && Red + Green == Yellow && Red + Red == Yellow && Yellow + Green == Yellow
|
|
};
|
|
"#,
|
|
1,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: add
|
|
impl core::ops::Add for usize {
|
|
type Output = usize;
|
|
fn add(self, rhs: usize) -> Self::Output {
|
|
self + rhs
|
|
}
|
|
}
|
|
|
|
impl core::ops::AddAssign for usize {
|
|
fn add_assign(&mut self, rhs: usize) {
|
|
*self += rhs;
|
|
}
|
|
}
|
|
|
|
#[lang = "shl"]
|
|
pub trait Shl<Rhs = Self> {
|
|
type Output;
|
|
|
|
fn shl(self, rhs: Rhs) -> Self::Output;
|
|
}
|
|
|
|
impl Shl<u8> for usize {
|
|
type Output = usize;
|
|
|
|
fn shl(self, rhs: u8) -> Self::Output {
|
|
self << rhs
|
|
}
|
|
}
|
|
|
|
const GOAL: usize = {
|
|
let mut x = 10;
|
|
x += 20;
|
|
2 + 2 + (x << 1u8)
|
|
};"#,
|
|
64,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn function_call() {
|
|
check_number(
|
|
r#"
|
|
const fn f(x: usize) -> usize {
|
|
2 * x + 5
|
|
}
|
|
const GOAL: usize = f(3);
|
|
"#,
|
|
11,
|
|
);
|
|
check_number(
|
|
r#"
|
|
const fn add(x: usize, y: usize) -> usize {
|
|
x + y
|
|
}
|
|
const GOAL: usize = add(add(1, 2), add(3, add(4, 5)));
|
|
"#,
|
|
15,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn trait_basic() {
|
|
check_number(
|
|
r#"
|
|
trait Foo {
|
|
fn f(&self) -> u8;
|
|
}
|
|
|
|
impl Foo for u8 {
|
|
fn f(&self) -> u8 {
|
|
*self + 33
|
|
}
|
|
}
|
|
|
|
const GOAL: u8 = {
|
|
let x = 3;
|
|
Foo::f(&x)
|
|
};
|
|
"#,
|
|
36,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn trait_method() {
|
|
check_number(
|
|
r#"
|
|
trait Foo {
|
|
fn f(&self) -> u8;
|
|
}
|
|
|
|
impl Foo for u8 {
|
|
fn f(&self) -> u8 {
|
|
*self + 33
|
|
}
|
|
}
|
|
|
|
const GOAL: u8 = {
|
|
let x = 3;
|
|
x.f()
|
|
};
|
|
"#,
|
|
36,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn trait_method_inside_block() {
|
|
check_number(
|
|
r#"
|
|
trait Twait {
|
|
fn a(&self) -> i32;
|
|
}
|
|
|
|
fn outer() -> impl Twait {
|
|
struct Stwuct;
|
|
|
|
impl Twait for Stwuct {
|
|
fn a(&self) -> i32 {
|
|
5
|
|
}
|
|
}
|
|
fn f() -> impl Twait {
|
|
let s = Stwuct;
|
|
s
|
|
}
|
|
f()
|
|
}
|
|
|
|
const GOAL: i32 = outer().a();
|
|
"#,
|
|
5,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn generic_fn() {
|
|
check_number(
|
|
r#"
|
|
trait Foo {
|
|
fn f(&self) -> u8;
|
|
}
|
|
|
|
impl Foo for () {
|
|
fn f(&self) -> u8 {
|
|
0
|
|
}
|
|
}
|
|
|
|
struct Succ<S>(S);
|
|
|
|
impl<T: Foo> Foo for Succ<T> {
|
|
fn f(&self) -> u8 {
|
|
self.0.f() + 1
|
|
}
|
|
}
|
|
|
|
const GOAL: u8 = Succ(Succ(())).f();
|
|
"#,
|
|
2,
|
|
);
|
|
check_number(
|
|
r#"
|
|
trait Foo {
|
|
fn f(&self) -> u8;
|
|
}
|
|
|
|
impl Foo for u8 {
|
|
fn f(&self) -> u8 {
|
|
*self + 33
|
|
}
|
|
}
|
|
|
|
fn foof<T: Foo>(x: T, y: T) -> u8 {
|
|
x.f() + y.f()
|
|
}
|
|
|
|
const GOAL: u8 = foof(2, 5);
|
|
"#,
|
|
73,
|
|
);
|
|
check_number(
|
|
r#"
|
|
fn bar<A, B>(a: A, b: B) -> B {
|
|
b
|
|
}
|
|
const GOAL: u8 = bar("hello", 12);
|
|
"#,
|
|
12,
|
|
);
|
|
check_number(
|
|
r#"
|
|
const fn y<T>(b: T) -> (T, ) {
|
|
let alloc = b;
|
|
(alloc, )
|
|
}
|
|
const GOAL: u8 = y(2).0;
|
|
"#,
|
|
2,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
fn bar<A, B>(a: A, b: B) -> B {
|
|
b
|
|
}
|
|
fn foo<T>(x: [T; 2]) -> T {
|
|
bar(x[0], x[1])
|
|
}
|
|
|
|
const GOAL: u8 = foo([2, 5]);
|
|
"#,
|
|
5,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn impl_trait() {
|
|
check_number(
|
|
r#"
|
|
trait Foo {
|
|
fn f(&self) -> u8;
|
|
}
|
|
|
|
impl Foo for u8 {
|
|
fn f(&self) -> u8 {
|
|
*self + 33
|
|
}
|
|
}
|
|
|
|
fn foof(x: impl Foo, y: impl Foo) -> impl Foo {
|
|
x.f() + y.f()
|
|
}
|
|
|
|
const GOAL: u8 = foof(2, 5).f();
|
|
"#,
|
|
106,
|
|
);
|
|
check_number(
|
|
r#"
|
|
struct Foo<T>(T, T, (T, T));
|
|
trait S {
|
|
fn sum(&self) -> i64;
|
|
}
|
|
impl S for i64 {
|
|
fn sum(&self) -> i64 {
|
|
*self
|
|
}
|
|
}
|
|
impl<T: S> S for Foo<T> {
|
|
fn sum(&self) -> i64 {
|
|
self.0.sum() + self.1.sum() + self.2 .0.sum() + self.2 .1.sum()
|
|
}
|
|
}
|
|
|
|
fn foo() -> Foo<impl S> {
|
|
Foo(
|
|
Foo(1i64, 2, (3, 4)),
|
|
Foo(5, 6, (7, 8)),
|
|
(
|
|
Foo(9, 10, (11, 12)),
|
|
Foo(13, 14, (15, 16)),
|
|
),
|
|
)
|
|
}
|
|
const GOAL: i64 = foo().sum();
|
|
"#,
|
|
136,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ifs() {
|
|
check_number(
|
|
r#"
|
|
const fn f(b: bool) -> u8 {
|
|
if b { 1 } else { 10 }
|
|
}
|
|
|
|
const GOAL: u8 = f(true) + f(true) + f(false);
|
|
"#,
|
|
12,
|
|
);
|
|
check_number(
|
|
r#"
|
|
const fn max(a: i32, b: i32) -> i32 {
|
|
if a < b { b } else { a }
|
|
}
|
|
|
|
const GOAL: i32 = max(max(1, max(10, 3)), 0-122);
|
|
"#,
|
|
10,
|
|
);
|
|
|
|
check_number(
|
|
r#"
|
|
const fn max(a: &i32, b: &i32) -> &i32 {
|
|
if *a < *b { b } else { a }
|
|
}
|
|
|
|
const GOAL: i32 = *max(max(&1, max(&10, &3)), &5);
|
|
"#,
|
|
10,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn loops() {
|
|
check_number(
|
|
r#"
|
|
const GOAL: u8 = {
|
|
let mut x = 0;
|
|
loop {
|
|
x = x + 1;
|
|
while true {
|
|
break;
|
|
}
|
|
x = x + 1;
|
|
if x == 2 {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
x
|
|
};
|
|
"#,
|
|
4,
|
|
);
|
|
check_number(
|
|
r#"
|
|
const GOAL: u8 = {
|
|
let mut x = 0;
|
|
loop {
|
|
x = x + 1;
|
|
if x == 5 {
|
|
break x + 2;
|
|
}
|
|
}
|
|
};
|
|
"#,
|
|
7,
|
|
);
|
|
check_number(
|
|
r#"
|
|
const GOAL: u8 = {
|
|
'a: loop {
|
|
let x = 'b: loop {
|
|
let x = 'c: loop {
|
|
let x = 'd: loop {
|
|
let x = 'e: loop {
|
|
break 'd 1;
|
|
};
|
|
break 2 + x;
|
|
};
|
|
break 3 + x;
|
|
};
|
|
break 'a 4 + x;
|
|
};
|
|
break 5 + x;
|
|
}
|
|
};
|
|
"#,
|
|
8,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: add
|
|
const GOAL: u8 = {
|
|
let mut x = 0;
|
|
'a: loop {
|
|
'b: loop {
|
|
'c: while x < 20 {
|
|
'd: while x < 5 {
|
|
'e: loop {
|
|
x += 1;
|
|
continue 'c;
|
|
};
|
|
};
|
|
x += 1;
|
|
};
|
|
break 'a;
|
|
};
|
|
}
|
|
x
|
|
};
|
|
"#,
|
|
20,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn for_loops() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: iterator
|
|
|
|
struct Range {
|
|
start: u8,
|
|
end: u8,
|
|
}
|
|
|
|
impl Iterator for Range {
|
|
type Item = u8;
|
|
fn next(&mut self) -> Option<u8> {
|
|
if self.start >= self.end {
|
|
None
|
|
} else {
|
|
let r = self.start;
|
|
self.start = self.start + 1;
|
|
Some(r)
|
|
}
|
|
}
|
|
}
|
|
|
|
const GOAL: u8 = {
|
|
let mut sum = 0;
|
|
let ar = Range { start: 1, end: 11 };
|
|
for i in ar {
|
|
sum = sum + i;
|
|
}
|
|
sum
|
|
};
|
|
"#,
|
|
55,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ranges() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: range
|
|
const GOAL: i32 = (1..2).start + (20..10).end + (100..=200).start + (2000..=1000).end
|
|
+ (10000..).start + (..100000).end + (..=1000000).end;
|
|
"#,
|
|
1111111,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn recursion() {
|
|
check_number(
|
|
r#"
|
|
const fn fact(k: i32) -> i32 {
|
|
if k > 0 { fact(k - 1) * k } else { 1 }
|
|
}
|
|
|
|
const GOAL: i32 = fact(5);
|
|
"#,
|
|
120,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn structs() {
|
|
check_number(
|
|
r#"
|
|
struct Point {
|
|
x: i32,
|
|
y: i32,
|
|
}
|
|
|
|
const GOAL: i32 = {
|
|
let p = Point { x: 5, y: 2 };
|
|
let y = 1;
|
|
let x = 3;
|
|
let q = Point { y, x };
|
|
p.x + p.y + p.x + q.y + q.y + q.x
|
|
};
|
|
"#,
|
|
17,
|
|
);
|
|
check_number(
|
|
r#"
|
|
struct Point {
|
|
x: i32,
|
|
y: i32,
|
|
}
|
|
|
|
const GOAL: i32 = {
|
|
let p = Point { x: 5, y: 2 };
|
|
let p2 = Point { x: 3, ..p };
|
|
p.x * 1000 + p.y * 100 + p2.x * 10 + p2.y
|
|
};
|
|
"#,
|
|
5232,
|
|
);
|
|
check_number(
|
|
r#"
|
|
struct Point {
|
|
x: i32,
|
|
y: i32,
|
|
}
|
|
|
|
const GOAL: i32 = {
|
|
let p = Point { x: 5, y: 2 };
|
|
let Point { x, y } = p;
|
|
let Point { x: x2, .. } = p;
|
|
let Point { y: y2, .. } = p;
|
|
x * 1000 + y * 100 + x2 * 10 + y2
|
|
};
|
|
"#,
|
|
5252,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn unions() {
|
|
check_number(
|
|
r#"
|
|
union U {
|
|
f1: i64,
|
|
f2: (i32, i32),
|
|
}
|
|
|
|
const GOAL: i32 = {
|
|
let p = U { f1: 0x0123ABCD0123DCBA };
|
|
let p = unsafe { p.f2 };
|
|
p.0 + p.1 + p.1
|
|
};
|
|
"#,
|
|
0x0123ABCD * 2 + 0x0123DCBA,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn tuples() {
|
|
check_number(
|
|
r#"
|
|
const GOAL: u8 = {
|
|
let a = (10, 20, 3, 15);
|
|
a.1
|
|
};
|
|
"#,
|
|
20,
|
|
);
|
|
check_number(
|
|
r#"
|
|
const GOAL: u8 = {
|
|
let mut a = (10, 20, 3, 15);
|
|
a.1 = 2;
|
|
a.0 + a.1 + a.2 + a.3
|
|
};
|
|
"#,
|
|
30,
|
|
);
|
|
check_number(
|
|
r#"
|
|
struct TupleLike(i32, i64, u8, u16);
|
|
const GOAL: i64 = {
|
|
let a = TupleLike(10, 20, 3, 15);
|
|
let TupleLike(b, .., c) = a;
|
|
a.1 * 100 + b as i64 + c as i64
|
|
};
|
|
"#,
|
|
2025,
|
|
);
|
|
check_number(
|
|
r#"
|
|
const GOAL: u8 = {
|
|
match (&(2 + 2), &4) {
|
|
(left_val, right_val) => {
|
|
if !(*left_val == *right_val) {
|
|
2
|
|
} else {
|
|
5
|
|
}
|
|
}
|
|
}
|
|
};
|
|
"#,
|
|
5,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn path_pattern_matching() {
|
|
check_number(
|
|
r#"
|
|
enum Season {
|
|
Spring,
|
|
Summer,
|
|
Fall,
|
|
Winter,
|
|
}
|
|
|
|
use Season::*;
|
|
|
|
const MY_SEASON: Season = Summer;
|
|
|
|
impl Season {
|
|
const FALL: Season = Fall;
|
|
}
|
|
|
|
const fn f(x: Season) -> i32 {
|
|
match x {
|
|
Spring => 1,
|
|
MY_SEASON => 2,
|
|
Season::FALL => 3,
|
|
Winter => 4,
|
|
}
|
|
}
|
|
const GOAL: i32 = f(Spring) + 10 * f(Summer) + 100 * f(Fall) + 1000 * f(Winter);
|
|
"#,
|
|
4321,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn pattern_matching_literal() {
|
|
check_number(
|
|
r#"
|
|
const fn f(x: i32) -> i32 {
|
|
match x {
|
|
-1 => 1,
|
|
1 => 10,
|
|
_ => 100,
|
|
}
|
|
}
|
|
const GOAL: i32 = f(-1) + f(1) + f(0) + f(-5);
|
|
"#,
|
|
211,
|
|
);
|
|
check_number(
|
|
r#"
|
|
const fn f(x: &str) -> i32 {
|
|
match x {
|
|
"f" => 1,
|
|
"foo" => 10,
|
|
"" => 100,
|
|
"bar" => 1000,
|
|
_ => 10000,
|
|
}
|
|
}
|
|
const GOAL: i32 = f("f") + f("foo") * 2 + f("") * 3 + f("bar") * 4;
|
|
"#,
|
|
4321,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn pattern_matching_range() {
|
|
check_number(
|
|
r#"
|
|
pub const L: i32 = 6;
|
|
mod x {
|
|
pub const R: i32 = 100;
|
|
}
|
|
const fn f(x: i32) -> i32 {
|
|
match x {
|
|
-1..=5 => x * 10,
|
|
L..=x::R => x * 100,
|
|
_ => x,
|
|
}
|
|
}
|
|
const GOAL: i32 = f(-1) + f(2) + f(100) + f(-2) + f(1000);
|
|
"#,
|
|
11008,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn pattern_matching_slice() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: slice, index, coerce_unsized, copy
|
|
const fn f(x: &[usize]) -> usize {
|
|
match x {
|
|
[a, b @ .., c, d] => *a + b.len() + *c + *d,
|
|
}
|
|
}
|
|
const GOAL: usize = f(&[10, 20, 3, 15, 1000, 60, 16]);
|
|
"#,
|
|
10 + 4 + 60 + 16,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: slice, index, coerce_unsized, copy
|
|
const fn f(x: &[usize]) -> usize {
|
|
match x {
|
|
[] => 0,
|
|
[a] => *a,
|
|
&[a, b] => a + b,
|
|
[a, b @ .., c, d] => *a + b.len() + *c + *d,
|
|
}
|
|
}
|
|
const GOAL: usize = f(&[]) + f(&[10]) + f(&[100, 100])
|
|
+ f(&[1000, 1000, 1000]) + f(&[10000, 57, 34, 46, 10000, 10000]);
|
|
"#,
|
|
33213,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: slice, index, coerce_unsized, copy
|
|
const fn f(mut slice: &[u32]) -> usize {
|
|
slice = match slice {
|
|
[0, rest @ ..] | rest => rest,
|
|
};
|
|
slice.len()
|
|
}
|
|
const GOAL: usize = f(&[]) + f(&[10]) + f(&[0, 100])
|
|
+ f(&[1000, 1000, 1000]) + f(&[0, 57, 34, 46, 10000, 10000]);
|
|
"#,
|
|
10,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn pattern_matching_ergonomics() {
|
|
check_number(
|
|
r#"
|
|
const fn f(x: &(u8, u8)) -> u8 {
|
|
match x {
|
|
(a, b) => *a + *b
|
|
}
|
|
}
|
|
const GOAL: u8 = f(&(2, 3));
|
|
"#,
|
|
5,
|
|
);
|
|
check_number(
|
|
r#"
|
|
const GOAL: u8 = {
|
|
let a = &(2, 3);
|
|
let &(x, y) = a;
|
|
x + y
|
|
};
|
|
"#,
|
|
5,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn destructing_assignment() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: add
|
|
const fn f(i: &mut u8) -> &mut u8 {
|
|
*i += 1;
|
|
i
|
|
}
|
|
const GOAL: u8 = {
|
|
let mut i = 4;
|
|
_ = f(&mut i);
|
|
i
|
|
};
|
|
"#,
|
|
5,
|
|
);
|
|
check_number(
|
|
r#"
|
|
const GOAL: u8 = {
|
|
let (mut a, mut b) = (2, 5);
|
|
(a, b) = (b, a);
|
|
a * 10 + b
|
|
};
|
|
"#,
|
|
52,
|
|
);
|
|
check_number(
|
|
r#"
|
|
struct Point { x: i32, y: i32 }
|
|
const GOAL: i32 = {
|
|
let mut p = Point { x: 5, y: 6 };
|
|
(p.x, _) = (p.y, p.x);
|
|
p.x * 10 + p.y
|
|
};
|
|
"#,
|
|
66,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn let_else() {
|
|
check_number(
|
|
r#"
|
|
const fn f(x: &(u8, u8)) -> u8 {
|
|
let (a, b) = x;
|
|
*a + *b
|
|
}
|
|
const GOAL: u8 = f(&(2, 3));
|
|
"#,
|
|
5,
|
|
);
|
|
check_number(
|
|
r#"
|
|
enum SingleVariant {
|
|
Var(u8, u8),
|
|
}
|
|
const fn f(x: &&&&&SingleVariant) -> u8 {
|
|
let SingleVariant::Var(a, b) = x;
|
|
*a + *b
|
|
}
|
|
const GOAL: u8 = f(&&&&&SingleVariant::Var(2, 3));
|
|
"#,
|
|
5,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: option
|
|
const fn f(x: Option<i32>) -> i32 {
|
|
let Some(x) = x else { return 10 };
|
|
2 * x
|
|
}
|
|
const GOAL: i32 = f(Some(1000)) + f(None);
|
|
"#,
|
|
2010,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn function_param_patterns() {
|
|
check_number(
|
|
r#"
|
|
const fn f((a, b): &(u8, u8)) -> u8 {
|
|
*a + *b
|
|
}
|
|
const GOAL: u8 = f(&(2, 3));
|
|
"#,
|
|
5,
|
|
);
|
|
check_number(
|
|
r#"
|
|
const fn f(c @ (a, b): &(u8, u8)) -> u8 {
|
|
*a + *b + c.0 + (*c).1
|
|
}
|
|
const GOAL: u8 = f(&(2, 3));
|
|
"#,
|
|
10,
|
|
);
|
|
check_number(
|
|
r#"
|
|
const fn f(ref a: u8) -> u8 {
|
|
*a
|
|
}
|
|
const GOAL: u8 = f(2);
|
|
"#,
|
|
2,
|
|
);
|
|
check_number(
|
|
r#"
|
|
struct Foo(u8);
|
|
impl Foo {
|
|
const fn f(&self, (a, b): &(u8, u8)) -> u8 {
|
|
self.0 + *a + *b
|
|
}
|
|
}
|
|
const GOAL: u8 = Foo(4).f(&(2, 3));
|
|
"#,
|
|
9,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn match_guards() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: option
|
|
fn f(x: Option<i32>) -> i32 {
|
|
match x {
|
|
y if let Some(42) = y => 42000,
|
|
Some(y) => y,
|
|
None => 10
|
|
}
|
|
}
|
|
const GOAL: i32 = f(Some(42)) + f(Some(2)) + f(None);
|
|
"#,
|
|
42012,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn result_layout_niche_optimization() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: option, result
|
|
const GOAL: i32 = match Some(2).ok_or(Some(2)) {
|
|
Ok(x) => x,
|
|
Err(_) => 1000,
|
|
};
|
|
"#,
|
|
2,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: result
|
|
pub enum AlignmentEnum64 {
|
|
_Align1Shl0 = 1 << 0,
|
|
_Align1Shl1 = 1 << 1,
|
|
_Align1Shl2 = 1 << 2,
|
|
_Align1Shl3 = 1 << 3,
|
|
_Align1Shl4 = 1 << 4,
|
|
_Align1Shl5 = 1 << 5,
|
|
}
|
|
const GOAL: Result<AlignmentEnum64, ()> = {
|
|
let align = Err(());
|
|
align
|
|
};
|
|
"#,
|
|
0, // It is 0 since result is niche encoded and 1 is valid for `AlignmentEnum64`
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: result
|
|
pub enum AlignmentEnum64 {
|
|
_Align1Shl0 = 1 << 0,
|
|
_Align1Shl1 = 1 << 1,
|
|
_Align1Shl2 = 1 << 2,
|
|
_Align1Shl3 = 1 << 3,
|
|
_Align1Shl4 = 1 << 4,
|
|
_Align1Shl5 = 1 << 5,
|
|
}
|
|
const GOAL: i32 = {
|
|
let align = Ok::<_, ()>(AlignmentEnum64::_Align1Shl0);
|
|
match align {
|
|
Ok(_) => 2,
|
|
Err(_) => 1,
|
|
}
|
|
};
|
|
"#,
|
|
2,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn options() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: option
|
|
const GOAL: u8 = {
|
|
let x = Some(2);
|
|
match x {
|
|
Some(y) => 2 * y,
|
|
_ => 10,
|
|
}
|
|
};
|
|
"#,
|
|
4,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: option
|
|
fn f(x: Option<Option<i32>>) -> i32 {
|
|
if let Some(y) = x && let Some(z) = y {
|
|
z
|
|
} else if let Some(y) = x {
|
|
1
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
const GOAL: i32 = f(Some(Some(10))) + f(Some(None)) + f(None);
|
|
"#,
|
|
11,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: option
|
|
const GOAL: u8 = {
|
|
let x = None;
|
|
match x {
|
|
Some(y) => 2 * y,
|
|
_ => 10,
|
|
}
|
|
};
|
|
"#,
|
|
10,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: option
|
|
const GOAL: Option<&u8> = None;
|
|
"#,
|
|
0,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn from_trait() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: from
|
|
struct E1(i32);
|
|
struct E2(i32);
|
|
|
|
impl From<E1> for E2 {
|
|
fn from(E1(x): E1) -> Self {
|
|
E2(1000 * x)
|
|
}
|
|
}
|
|
const GOAL: i32 = {
|
|
let x: E2 = E1(2).into();
|
|
x.0
|
|
};
|
|
"#,
|
|
2000,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn closure_clone() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: clone, fn
|
|
struct S(u8);
|
|
|
|
impl Clone for S(u8) {
|
|
fn clone(&self) -> S {
|
|
S(self.0 + 5)
|
|
}
|
|
}
|
|
|
|
const GOAL: u8 = {
|
|
let s = S(3);
|
|
let cl = move || s;
|
|
let cl = cl.clone();
|
|
cl().0
|
|
}
|
|
"#,
|
|
8,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn builtin_derive_macro() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: clone, derive, builtin_impls
|
|
#[derive(Clone)]
|
|
enum Z {
|
|
Foo(Y),
|
|
Bar,
|
|
}
|
|
#[derive(Clone)]
|
|
struct X(i32, Z, i64);
|
|
#[derive(Clone)]
|
|
struct Y {
|
|
field1: i32,
|
|
field2: ((i32, u8), i64),
|
|
}
|
|
|
|
const GOAL: u8 = {
|
|
let x = X(2, Z::Foo(Y { field1: 4, field2: ((32, 5), 12) }), 8);
|
|
let x = x.clone();
|
|
let Z::Foo(t) = x.1;
|
|
t.field2.0 .1
|
|
};
|
|
"#,
|
|
5,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: default, derive, builtin_impls
|
|
#[derive(Default)]
|
|
struct X(i32, Y, i64);
|
|
#[derive(Default)]
|
|
struct Y {
|
|
field1: i32,
|
|
field2: u8,
|
|
}
|
|
|
|
const GOAL: u8 = {
|
|
let x = X::default();
|
|
x.1.field2
|
|
};
|
|
"#,
|
|
0,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn try_operator() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: option, try
|
|
const fn f(x: Option<i32>, y: Option<i32>) -> Option<i32> {
|
|
Some(x? * y?)
|
|
}
|
|
const fn g(x: Option<i32>, y: Option<i32>) -> i32 {
|
|
match f(x, y) {
|
|
Some(k) => k,
|
|
None => 5,
|
|
}
|
|
}
|
|
const GOAL: i32 = g(Some(10), Some(20)) + g(Some(30), None) + g(None, Some(40)) + g(None, None);
|
|
"#,
|
|
215,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: result, try, from
|
|
struct E1(i32);
|
|
struct E2(i32);
|
|
|
|
impl From<E1> for E2 {
|
|
fn from(E1(x): E1) -> Self {
|
|
E2(1000 * x)
|
|
}
|
|
}
|
|
|
|
const fn f(x: Result<i32, E1>) -> Result<i32, E2> {
|
|
Ok(x? * 10)
|
|
}
|
|
const fn g(x: Result<i32, E1>) -> i32 {
|
|
match f(x) {
|
|
Ok(k) => 7 * k,
|
|
Err(E2(k)) => 5 * k,
|
|
}
|
|
}
|
|
const GOAL: i32 = g(Ok(2)) + g(Err(E1(3)));
|
|
"#,
|
|
15140,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn try_block() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: option, try
|
|
const fn g(x: Option<i32>, y: Option<i32>) -> i32 {
|
|
let r = try { x? * y? };
|
|
match r {
|
|
Some(k) => k,
|
|
None => 5,
|
|
}
|
|
}
|
|
const GOAL: i32 = g(Some(10), Some(20)) + g(Some(30), None) + g(None, Some(40)) + g(None, None);
|
|
"#,
|
|
215,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn closures() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: fn, copy
|
|
const GOAL: i32 = {
|
|
let y = 5;
|
|
let c = |x| x + y;
|
|
c(2)
|
|
};
|
|
"#,
|
|
7,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: fn, copy
|
|
const GOAL: i32 = {
|
|
let y = 5;
|
|
let c = |(a, b): &(i32, i32)| *a + *b + y;
|
|
c(&(2, 3))
|
|
};
|
|
"#,
|
|
10,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: fn, copy
|
|
const GOAL: i32 = {
|
|
let mut y = 5;
|
|
let c = |x| {
|
|
y = y + x;
|
|
};
|
|
c(2);
|
|
c(3);
|
|
y
|
|
};
|
|
"#,
|
|
10,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: fn, copy
|
|
const GOAL: i32 = {
|
|
let c: fn(i32) -> i32 = |x| 2 * x;
|
|
c(2) + c(10)
|
|
};
|
|
"#,
|
|
24,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: fn, copy
|
|
struct X(i32);
|
|
impl X {
|
|
fn mult(&mut self, n: i32) {
|
|
self.0 = self.0 * n
|
|
}
|
|
}
|
|
const GOAL: i32 = {
|
|
let x = X(1);
|
|
let c = || {
|
|
x.mult(2);
|
|
|| {
|
|
x.mult(3);
|
|
|| {
|
|
|| {
|
|
x.mult(4);
|
|
|| {
|
|
x.mult(x.0);
|
|
|| {
|
|
x.0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
let r = c()()()()()();
|
|
r + x.0
|
|
};
|
|
"#,
|
|
24 * 24 * 2,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn manual_fn_trait_impl() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: fn, copy
|
|
struct S(i32);
|
|
|
|
impl FnOnce<(i32, i32)> for S {
|
|
type Output = i32;
|
|
|
|
extern "rust-call" fn call_once(self, arg: (i32, i32)) -> i32 {
|
|
arg.0 + arg.1 + self.0
|
|
}
|
|
}
|
|
|
|
const GOAL: i32 = {
|
|
let s = S(1);
|
|
s(2, 3)
|
|
};
|
|
"#,
|
|
6,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn closure_capture_unsized_type() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: fn, copy, slice, index, coerce_unsized
|
|
fn f<T: A>(x: &<T as A>::Ty) -> &<T as A>::Ty {
|
|
let c = || &*x;
|
|
c()
|
|
}
|
|
|
|
trait A {
|
|
type Ty;
|
|
}
|
|
|
|
impl A for i32 {
|
|
type Ty = [u8];
|
|
}
|
|
|
|
const GOAL: u8 = {
|
|
let k: &[u8] = &[1, 2, 3];
|
|
let k = f::<i32>(k);
|
|
k[0] + k[1] + k[2]
|
|
}
|
|
"#,
|
|
6,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn closure_and_impl_fn() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: fn, copy
|
|
fn closure_wrapper<F: FnOnce() -> i32>(c: F) -> impl FnOnce() -> F {
|
|
|| c
|
|
}
|
|
|
|
const GOAL: i32 = {
|
|
let y = 5;
|
|
let c = closure_wrapper(|| y);
|
|
c()()
|
|
};
|
|
"#,
|
|
5,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: fn, copy
|
|
fn f<T, F: Fn() -> T>(t: F) -> impl Fn() -> T {
|
|
move || t()
|
|
}
|
|
|
|
const GOAL: i32 = f(|| 2)();
|
|
"#,
|
|
2,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn or_pattern() {
|
|
check_number(
|
|
r#"
|
|
const GOAL: u8 = {
|
|
let (a | a) = 2;
|
|
a
|
|
};
|
|
"#,
|
|
2,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: option
|
|
const fn f(x: Option<i32>) -> i32 {
|
|
let (Some(a) | Some(a)) = x else { return 2; };
|
|
a
|
|
}
|
|
const GOAL: i32 = f(Some(10)) + f(None);
|
|
"#,
|
|
12,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: option
|
|
const fn f(x: Option<i32>, y: Option<i32>) -> i32 {
|
|
match (x, y) {
|
|
(Some(x), Some(y)) => x * y,
|
|
(Some(a), _) | (_, Some(a)) => a,
|
|
_ => 10,
|
|
}
|
|
}
|
|
const GOAL: i32 = f(Some(10), Some(20)) + f(Some(30), None) + f(None, Some(40)) + f(None, None);
|
|
"#,
|
|
280,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn function_pointer_in_constants() {
|
|
check_number(
|
|
r#"
|
|
struct Foo {
|
|
f: fn(u8) -> u8,
|
|
}
|
|
const FOO: Foo = Foo { f: add2 };
|
|
fn add2(x: u8) -> u8 {
|
|
x + 2
|
|
}
|
|
const GOAL: u8 = (FOO.f)(3);
|
|
"#,
|
|
5,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn function_pointer_and_niche_optimization() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: option
|
|
const GOAL: i32 = {
|
|
let f: fn(i32) -> i32 = |x| x + 2;
|
|
let init = Some(f);
|
|
match init {
|
|
Some(t) => t(3),
|
|
None => 222,
|
|
}
|
|
};
|
|
"#,
|
|
5,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn function_pointer() {
|
|
check_number(
|
|
r#"
|
|
fn add2(x: u8) -> u8 {
|
|
x + 2
|
|
}
|
|
const GOAL: u8 = {
|
|
let plus2 = add2;
|
|
plus2(3)
|
|
};
|
|
"#,
|
|
5,
|
|
);
|
|
check_number(
|
|
r#"
|
|
fn add2(x: u8) -> u8 {
|
|
x + 2
|
|
}
|
|
const GOAL: u8 = {
|
|
let plus2: fn(u8) -> u8 = add2;
|
|
plus2(3)
|
|
};
|
|
"#,
|
|
5,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: sized
|
|
fn add2(x: u8) -> u8 {
|
|
x + 2
|
|
}
|
|
const GOAL: u8 = {
|
|
let plus2 = add2 as fn(u8) -> u8;
|
|
plus2(3)
|
|
};
|
|
"#,
|
|
5,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
fn add2(x: u8) -> u8 {
|
|
x + 2
|
|
}
|
|
fn mult3(x: u8) -> u8 {
|
|
x * 3
|
|
}
|
|
const GOAL: u8 = {
|
|
let x = [add2, mult3];
|
|
x[0](1) + x[1](5)
|
|
};
|
|
"#,
|
|
18,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn enum_variant_as_function() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: option
|
|
const GOAL: u8 = {
|
|
let f = Some;
|
|
f(3).unwrap_or(2)
|
|
};
|
|
"#,
|
|
3,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: option
|
|
const GOAL: u8 = {
|
|
let f: fn(u8) -> Option<u8> = Some;
|
|
f(3).unwrap_or(2)
|
|
};
|
|
"#,
|
|
3,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
enum Foo {
|
|
Add2(u8),
|
|
Mult3(u8),
|
|
}
|
|
use Foo::*;
|
|
const fn f(x: Foo) -> u8 {
|
|
match x {
|
|
Add2(x) => x + 2,
|
|
Mult3(x) => x * 3,
|
|
}
|
|
}
|
|
const GOAL: u8 = {
|
|
let x = [Add2, Mult3];
|
|
f(x[0](1)) + f(x[1](5))
|
|
};
|
|
"#,
|
|
18,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn function_traits() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: fn
|
|
fn add2(x: u8) -> u8 {
|
|
x + 2
|
|
}
|
|
fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 {
|
|
f(x)
|
|
}
|
|
fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 {
|
|
f(x)
|
|
}
|
|
fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
|
|
f(x)
|
|
}
|
|
const GOAL: u8 = call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3);
|
|
"#,
|
|
15,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, fn, dispatch_from_dyn
|
|
fn add2(x: u8) -> u8 {
|
|
x + 2
|
|
}
|
|
fn call(f: &dyn Fn(u8) -> u8, x: u8) -> u8 {
|
|
f(x)
|
|
}
|
|
fn call_mut(f: &mut dyn FnMut(u8) -> u8, x: u8) -> u8 {
|
|
f(x)
|
|
}
|
|
const GOAL: u8 = call(&add2, 3) + call_mut(&mut add2, 3);
|
|
"#,
|
|
10,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: fn
|
|
fn add2(x: u8) -> u8 {
|
|
x + 2
|
|
}
|
|
fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 {
|
|
f(x)
|
|
}
|
|
fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 {
|
|
f(x)
|
|
}
|
|
fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
|
|
f(x)
|
|
}
|
|
const GOAL: u8 = {
|
|
let add2: fn(u8) -> u8 = add2;
|
|
call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3)
|
|
};
|
|
"#,
|
|
15,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: fn
|
|
fn add2(x: u8) -> u8 {
|
|
x + 2
|
|
}
|
|
fn call(f: &&&&&impl Fn(u8) -> u8, x: u8) -> u8 {
|
|
f(x)
|
|
}
|
|
const GOAL: u8 = call(&&&&&add2, 3);
|
|
"#,
|
|
5,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn dyn_trait() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice, dispatch_from_dyn
|
|
trait Foo {
|
|
fn foo(&self) -> u8 { 10 }
|
|
}
|
|
struct S1;
|
|
struct S2;
|
|
struct S3;
|
|
impl Foo for S1 {
|
|
fn foo(&self) -> u8 { 1 }
|
|
}
|
|
impl Foo for S2 {
|
|
fn foo(&self) -> u8 { 2 }
|
|
}
|
|
impl Foo for S3 {}
|
|
const GOAL: u8 = {
|
|
let x: &[&dyn Foo] = &[&S1, &S2, &S3];
|
|
x[0].foo() + x[1].foo() + x[2].foo()
|
|
};
|
|
"#,
|
|
13,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice, dispatch_from_dyn
|
|
trait Foo {
|
|
fn foo(&self) -> i32 { 10 }
|
|
}
|
|
trait Bar {
|
|
fn bar(&self) -> i32 { 20 }
|
|
}
|
|
|
|
struct S;
|
|
impl Foo for S {
|
|
fn foo(&self) -> i32 { 200 }
|
|
}
|
|
impl Bar for dyn Foo {
|
|
fn bar(&self) -> i32 { 700 }
|
|
}
|
|
const GOAL: i32 = {
|
|
let x: &dyn Foo = &S;
|
|
x.bar() + x.foo()
|
|
};
|
|
"#,
|
|
900,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice, dispatch_from_dyn
|
|
trait A {
|
|
fn x(&self) -> i32;
|
|
}
|
|
|
|
trait B: A {}
|
|
|
|
impl A for i32 {
|
|
fn x(&self) -> i32 {
|
|
5
|
|
}
|
|
}
|
|
|
|
impl B for i32 {
|
|
|
|
}
|
|
|
|
const fn f(x: &dyn B) -> i32 {
|
|
x.x()
|
|
}
|
|
|
|
const GOAL: i32 = f(&2i32);
|
|
"#,
|
|
5,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_unsized() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, deref_mut, slice, index, transmute, non_null
|
|
use core::ops::{Deref, DerefMut, CoerceUnsized};
|
|
use core::{marker::Unsize, mem::transmute, ptr::NonNull};
|
|
|
|
struct ArcInner<T: ?Sized> {
|
|
strong: usize,
|
|
weak: usize,
|
|
data: T,
|
|
}
|
|
|
|
pub struct Arc<T: ?Sized> {
|
|
inner: NonNull<ArcInner<T>>,
|
|
}
|
|
|
|
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Arc<U>> for Arc<T> {}
|
|
|
|
const GOAL: usize = {
|
|
let x = transmute::<usize, Arc<[i32; 3]>>(12);
|
|
let y: Arc<[i32]> = x;
|
|
let z = transmute::<Arc<[i32]>, (usize, usize)>(y);
|
|
z.1
|
|
};
|
|
|
|
"#,
|
|
3,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn boxes() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, deref_mut, slice
|
|
use core::ops::{Deref, DerefMut};
|
|
use core::{marker::Unsize, ops::CoerceUnsized};
|
|
|
|
#[lang = "owned_box"]
|
|
pub struct Box<T: ?Sized> {
|
|
inner: *mut T,
|
|
}
|
|
impl<T> Box<T> {
|
|
fn new(t: T) -> Self {
|
|
#[rustc_box]
|
|
Box::new(t)
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> Deref for Box<T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &T {
|
|
&**self
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> DerefMut for Box<T> {
|
|
fn deref_mut(&mut self) -> &mut T {
|
|
&mut **self
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}
|
|
|
|
const GOAL: usize = {
|
|
let x = Box::new(5);
|
|
let y: Box<[i32]> = Box::new([1, 2, 3]);
|
|
*x + y.len()
|
|
};
|
|
"#,
|
|
8,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn array_and_index() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
const GOAL: u8 = {
|
|
let a = [10, 20, 3, 15];
|
|
let x: &[u8] = &a;
|
|
x[1]
|
|
};
|
|
"#,
|
|
20,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
const GOAL: usize = [1, 2, 3][2];"#,
|
|
3,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
const GOAL: usize = { let a = [1, 2, 3]; let x: &[i32] = &a; x.len() };"#,
|
|
3,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
const GOAL: usize = {
|
|
let a = [1, 2, 3];
|
|
let x: &[i32] = &a;
|
|
let y = &*x;
|
|
y.len()
|
|
};"#,
|
|
3,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
const GOAL: usize = [1, 2, 3, 4, 5].len();"#,
|
|
5,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
const GOAL: [u16; 5] = [1, 2, 3, 4, 5];"#,
|
|
1 + (2 << 16) + (3 << 32) + (4 << 48) + (5 << 64),
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
const GOAL: [u16; 5] = [12; 5];"#,
|
|
12 + (12 << 16) + (12 << 32) + (12 << 48) + (12 << 64),
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
const LEN: usize = 4;
|
|
const GOAL: u16 = {
|
|
let x = [7; LEN];
|
|
x[2]
|
|
}"#,
|
|
7,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn string() {
|
|
check_str(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
const GOAL: &str = "hello";
|
|
"#,
|
|
"hello",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn byte_string() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
const GOAL: u8 = {
|
|
let a = b"hello";
|
|
let x: &[u8] = a;
|
|
x[0]
|
|
};
|
|
"#,
|
|
104,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn c_string() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: index, slice
|
|
#[lang = "CStr"]
|
|
pub struct CStr {
|
|
inner: [u8]
|
|
}
|
|
const GOAL: u8 = {
|
|
let a = c"hello";
|
|
a.inner[0]
|
|
};
|
|
"#,
|
|
104,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: index, slice
|
|
#[lang = "CStr"]
|
|
pub struct CStr {
|
|
inner: [u8]
|
|
}
|
|
const GOAL: u8 = {
|
|
let a = c"hello";
|
|
a.inner[6]
|
|
};
|
|
"#,
|
|
0,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn consts() {
|
|
check_number(
|
|
r#"
|
|
const F1: i32 = 1;
|
|
const F3: i32 = 3 * F2;
|
|
const F2: i32 = 2 * F1;
|
|
const GOAL: i32 = F3;
|
|
"#,
|
|
6,
|
|
);
|
|
|
|
check_number(
|
|
r#"
|
|
const F1: i32 = 2147483647;
|
|
const F2: i32 = F1 - 25;
|
|
const GOAL: i32 = F2;
|
|
"#,
|
|
2147483622,
|
|
);
|
|
|
|
check_number(
|
|
r#"
|
|
const F1: i32 = -2147483648;
|
|
const F2: i32 = F1 + 18;
|
|
const GOAL: i32 = F2;
|
|
"#,
|
|
-2147483630,
|
|
);
|
|
|
|
check_number(
|
|
r#"
|
|
const F1: i32 = 10;
|
|
const F2: i32 = F1 - 20;
|
|
const GOAL: i32 = F2;
|
|
"#,
|
|
-10,
|
|
);
|
|
|
|
check_number(
|
|
r#"
|
|
const F1: i32 = 25;
|
|
const F2: i32 = F1 - 25;
|
|
const GOAL: i32 = F2;
|
|
"#,
|
|
0,
|
|
);
|
|
|
|
check_number(
|
|
r#"
|
|
const A: i32 = -2147483648;
|
|
const GOAL: bool = A > 0;
|
|
"#,
|
|
0,
|
|
);
|
|
|
|
check_number(
|
|
r#"
|
|
const GOAL: i64 = (-2147483648_i32) as i64;
|
|
"#,
|
|
-2147483648,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn statics() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: cell
|
|
use core::cell::Cell;
|
|
fn f() -> i32 {
|
|
static S: Cell<i32> = Cell::new(10);
|
|
S.set(S.get() + 1);
|
|
S.get()
|
|
}
|
|
const GOAL: i32 = f() + f() + f();
|
|
"#,
|
|
36,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn extern_weak_statics() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: sized
|
|
extern "C" {
|
|
#[linkage = "extern_weak"]
|
|
static __dso_handle: *mut u8;
|
|
}
|
|
const GOAL: usize = __dso_handle as usize;
|
|
"#,
|
|
0,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn from_ne_bytes() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: int_impl
|
|
const GOAL: u32 = u32::from_ne_bytes([44, 1, 0, 0]);
|
|
"#,
|
|
300,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn enums() {
|
|
check_number(
|
|
r#"
|
|
enum E {
|
|
F1 = 1,
|
|
F2 = 2 * E::F1 as isize, // Rustc expects an isize here
|
|
F3 = 3 * E::F2 as isize,
|
|
}
|
|
const GOAL: u8 = E::F3 as u8;
|
|
"#,
|
|
6,
|
|
);
|
|
check_number(
|
|
r#"
|
|
enum E { F1 = 1, F2, }
|
|
const GOAL: u8 = E::F2 as u8;
|
|
"#,
|
|
2,
|
|
);
|
|
check_number(
|
|
r#"
|
|
enum E { F1, }
|
|
const GOAL: u8 = E::F1 as u8;
|
|
"#,
|
|
0,
|
|
);
|
|
let (db, file_id) = TestDB::with_single_file(
|
|
r#"
|
|
enum E { A = 1, B }
|
|
const GOAL: E = E::A;
|
|
"#,
|
|
);
|
|
let r = eval_goal(&db, file_id).unwrap();
|
|
assert_eq!(try_const_usize(&db, &r), Some(1));
|
|
}
|
|
|
|
#[test]
|
|
fn const_loop() {
|
|
check_fail(
|
|
r#"
|
|
const F1: i32 = 1 * F3;
|
|
const F3: i32 = 3 * F2;
|
|
const F2: i32 = 2 * F1;
|
|
const GOAL: i32 = F3;
|
|
"#,
|
|
|e| e == ConstEvalError::MirLowerError(MirLowerError::Loop),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn const_transfer_memory() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: slice, index, coerce_unsized, option
|
|
const A1: &i32 = &1;
|
|
const A2: &i32 = &10;
|
|
const A3: [&i32; 3] = [&1, &2, &100];
|
|
const A4: (i32, &i32, Option<&i32>) = (1, &1000, Some(&10000));
|
|
const GOAL: i32 = *A1 + *A2 + *A3[2] + *A4.1 + *A4.2.unwrap_or(&5);
|
|
"#,
|
|
11111,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn anonymous_const_block() {
|
|
check_number(
|
|
r#"
|
|
extern "rust-intrinsic" {
|
|
pub fn size_of<T>() -> usize;
|
|
}
|
|
|
|
const fn f<T>() -> usize {
|
|
let r = const { size_of::<T>() };
|
|
r
|
|
}
|
|
|
|
const GOAL: usize = {
|
|
let x = const { 2 + const { 3 } };
|
|
let y = f::<i32>();
|
|
x + y
|
|
};
|
|
"#,
|
|
9,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn const_impl_assoc() {
|
|
check_number(
|
|
r#"
|
|
struct U5;
|
|
impl U5 {
|
|
const VAL: usize = 5;
|
|
}
|
|
const GOAL: usize = U5::VAL + <U5>::VAL;
|
|
"#,
|
|
10,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn const_generic_subst_fn() {
|
|
check_number(
|
|
r#"
|
|
const fn f<const A: usize>(x: usize) -> usize {
|
|
A * x + 5
|
|
}
|
|
const GOAL: usize = f::<2>(3);
|
|
"#,
|
|
11,
|
|
);
|
|
check_number(
|
|
r#"
|
|
fn f<const N: usize>(x: [i32; N]) -> usize {
|
|
N
|
|
}
|
|
|
|
trait ArrayExt {
|
|
fn f(self) -> usize;
|
|
}
|
|
|
|
impl<T, const N: usize> ArrayExt for [T; N] {
|
|
fn g(self) -> usize {
|
|
f(self)
|
|
}
|
|
}
|
|
|
|
const GOAL: usize = f([1, 2, 5]);
|
|
"#,
|
|
3,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn layout_of_type_with_associated_type_field_defined_inside_body() {
|
|
check_number(
|
|
r#"
|
|
trait Tr {
|
|
type Ty;
|
|
}
|
|
|
|
struct St<T: Tr>(T::Ty);
|
|
|
|
const GOAL: i64 = {
|
|
// if we move `St2` out of body, the test will fail, as we don't see the impl anymore. That
|
|
// case will probably be rejected by rustc in some later edition, but we should support this
|
|
// case.
|
|
struct St2;
|
|
|
|
impl Tr for St2 {
|
|
type Ty = i64;
|
|
}
|
|
|
|
struct Goal(St<St2>);
|
|
|
|
let x = Goal(St(5));
|
|
x.0.0
|
|
};
|
|
"#,
|
|
5,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn const_generic_subst_assoc_const_impl() {
|
|
check_number(
|
|
r#"
|
|
struct Adder<const N: usize, const M: usize>;
|
|
impl<const N: usize, const M: usize> Adder<N, M> {
|
|
const VAL: usize = N + M;
|
|
}
|
|
const GOAL: usize = Adder::<2, 3>::VAL;
|
|
"#,
|
|
5,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn associated_types() {
|
|
check_number(
|
|
r#"
|
|
trait Tr {
|
|
type Item;
|
|
fn get_item(&self) -> Self::Item;
|
|
}
|
|
|
|
struct X(i32);
|
|
struct Y(i32);
|
|
|
|
impl Tr for X {
|
|
type Item = Y;
|
|
fn get_item(&self) -> Self::Item {
|
|
Y(self.0 + 2)
|
|
}
|
|
}
|
|
|
|
fn my_get_item<T: Tr>(x: T) -> <T as Tr>::Item {
|
|
x.get_item()
|
|
}
|
|
|
|
const GOAL: i32 = my_get_item(X(3)).0;
|
|
"#,
|
|
5,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn const_trait_assoc() {
|
|
check_number(
|
|
r#"
|
|
struct U0;
|
|
trait ToConst {
|
|
const VAL: usize;
|
|
}
|
|
impl ToConst for U0 {
|
|
const VAL: usize = 0;
|
|
}
|
|
impl ToConst for i32 {
|
|
const VAL: usize = 32;
|
|
}
|
|
const GOAL: usize = U0::VAL + i32::VAL;
|
|
"#,
|
|
32,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- /a/lib.rs crate:a
|
|
pub trait ToConst {
|
|
const VAL: usize;
|
|
}
|
|
pub const fn to_const<T: ToConst>() -> usize {
|
|
T::VAL
|
|
}
|
|
//- /main.rs crate:main deps:a
|
|
use a::{ToConst, to_const};
|
|
struct U0;
|
|
impl ToConst for U0 {
|
|
const VAL: usize = 5;
|
|
}
|
|
const GOAL: usize = to_const::<U0>();
|
|
"#,
|
|
5,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: size_of, fn
|
|
//- /a/lib.rs crate:a
|
|
use core::mem::size_of;
|
|
pub struct S<T>(T);
|
|
impl<T> S<T> {
|
|
pub const X: usize = {
|
|
let k: T;
|
|
let f = || core::mem::size_of::<T>();
|
|
f()
|
|
};
|
|
}
|
|
//- /main.rs crate:main deps:a
|
|
use a::{S};
|
|
trait Tr {
|
|
type Ty;
|
|
}
|
|
impl Tr for i32 {
|
|
type Ty = u64;
|
|
}
|
|
struct K<T: Tr>(<T as Tr>::Ty);
|
|
const GOAL: usize = S::<K<i32>>::X;
|
|
"#,
|
|
8,
|
|
);
|
|
check_number(
|
|
r#"
|
|
//- minicore: sized
|
|
struct S<T>(*mut T);
|
|
|
|
trait MySized: Sized {
|
|
const SIZE: S<Self> = S(1 as *mut Self);
|
|
}
|
|
|
|
impl MySized for i32 {
|
|
const SIZE: S<i32> = S(10 as *mut i32);
|
|
}
|
|
|
|
impl MySized for i64 {
|
|
}
|
|
|
|
const fn f<T: MySized>() -> usize {
|
|
T::SIZE.0 as usize
|
|
}
|
|
|
|
const GOAL: usize = f::<i32>() + f::<i64>() * 2;
|
|
"#,
|
|
12,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn exec_limits() {
|
|
if skip_slow_tests() {
|
|
return;
|
|
}
|
|
|
|
check_fail(
|
|
r#"
|
|
const GOAL: usize = loop {};
|
|
"#,
|
|
|e| e == ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
|
|
);
|
|
check_fail(
|
|
r#"
|
|
const fn f(x: i32) -> i32 {
|
|
f(x + 1)
|
|
}
|
|
const GOAL: i32 = f(0);
|
|
"#,
|
|
|e| e == ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
|
|
);
|
|
// Reasonable code should still work
|
|
check_number(
|
|
r#"
|
|
const fn nth_odd(n: i32) -> i32 {
|
|
2 * n - 1
|
|
}
|
|
const fn f(n: i32) -> i32 {
|
|
let sum = 0;
|
|
let i = 0;
|
|
while i < n {
|
|
i = i + 1;
|
|
sum = sum + nth_odd(i);
|
|
}
|
|
sum
|
|
}
|
|
const GOAL: i32 = f(1000);
|
|
"#,
|
|
1000 * 1000,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn memory_limit() {
|
|
check_fail(
|
|
r#"
|
|
extern "Rust" {
|
|
#[rustc_allocator]
|
|
fn __rust_alloc(size: usize, align: usize) -> *mut u8;
|
|
}
|
|
|
|
const GOAL: u8 = unsafe {
|
|
__rust_alloc(30_000_000_000, 1); // 30GB
|
|
2
|
|
};
|
|
"#,
|
|
|e| {
|
|
e == ConstEvalError::MirEvalError(MirEvalError::Panic(
|
|
"Memory allocation of 30000000000 bytes failed".to_owned(),
|
|
))
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn type_error() {
|
|
check_fail(
|
|
r#"
|
|
const GOAL: u8 = {
|
|
let x: u16 = 2;
|
|
let y: (u8, u8) = x;
|
|
y.0
|
|
};
|
|
"#,
|
|
|e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::HasErrors)),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn unsized_field() {
|
|
check_number(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice, transmute
|
|
use core::mem::transmute;
|
|
|
|
struct Slice([usize]);
|
|
struct Slice2(Slice);
|
|
|
|
impl Slice2 {
|
|
fn as_inner(&self) -> &Slice {
|
|
&self.0
|
|
}
|
|
|
|
fn as_bytes(&self) -> &[usize] {
|
|
&self.as_inner().0
|
|
}
|
|
}
|
|
|
|
const GOAL: usize = unsafe {
|
|
let x: &[usize] = &[1, 2, 3];
|
|
let x: &Slice2 = transmute(x);
|
|
let x = x.as_bytes();
|
|
x[0] + x[1] + x[2] + x.len() * 100
|
|
};
|
|
"#,
|
|
306,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn unsized_local() {
|
|
check_fail(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
const fn x() -> SomeUnknownTypeThatDereferenceToSlice {
|
|
SomeUnknownTypeThatDereferenceToSlice
|
|
}
|
|
|
|
const GOAL: u16 = {
|
|
let y = x();
|
|
let z: &[u16] = &y;
|
|
z[1]
|
|
};
|
|
"#,
|
|
|e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::UnsizedTemporary(_))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn recursive_adt() {
|
|
check_fail(
|
|
r#"
|
|
//- minicore: coerce_unsized, index, slice
|
|
pub enum TagTree {
|
|
Leaf,
|
|
Choice(&'static [TagTree]),
|
|
}
|
|
const GOAL: TagTree = {
|
|
const TAG_TREE: TagTree = TagTree::Choice(&[
|
|
{
|
|
const VARIANT_TAG_TREE: TagTree = TagTree::Choice(
|
|
&[
|
|
TagTree::Leaf,
|
|
],
|
|
);
|
|
VARIANT_TAG_TREE
|
|
},
|
|
]);
|
|
TAG_TREE
|
|
};
|
|
"#,
|
|
|e| matches!(e, ConstEvalError::MirEvalError(MirEvalError::StackOverflow)),
|
|
);
|
|
}
|