mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00

This PR touches a lot of parts. But the main changes are changing `hir_expand::Name` to be raw edition-dependently and only when necessary (unrelated to how the user originally wrote the identifier), and changing `is_keyword()` and `is_raw_identifier()` to be edition-aware (this was done in #17896, but the FIXMEs were fixed here). It is possible that I missed some cases, but most IDE parts should properly escape (or not escape) identifiers now. The rules of thumb are: - If we show the identifier to the user, its rawness should be determined by the edition of the edited crate. This is nice for IDE features, but really important for changes we insert to the source code. - For tests, I chose `Edition::CURRENT` (so we only have to (maybe) update tests when an edition becomes stable, to avoid churn). - For debugging tools (helper methods and logs), I used `Edition::LATEST`.
2885 lines
54 KiB
Rust
2885 lines
54 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#"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;
|
|
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#"const GOAL: i32 = -12i8 as i32"#, -12);
|
|
}
|
|
|
|
#[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#"
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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#"
|
|
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#"
|
|
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::TypeMismatch(_))),
|
|
);
|
|
}
|
|
|
|
#[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)),
|
|
);
|
|
}
|