Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/consteval/tests.rs')
-rw-r--r--crates/hir-ty/src/consteval/tests.rs395
1 files changed, 362 insertions, 33 deletions
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index d987f41c70..e95e175536 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -1,4 +1,4 @@
-use base_db::fixture::WithFixture;
+use base_db::{fixture::WithFixture, FileId};
use chalk_ir::Substitution;
use hir_def::db::DefDatabase;
@@ -16,7 +16,7 @@ mod intrinsics;
fn simplify(e: ConstEvalError) -> ConstEvalError {
match e {
- ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e)) => {
+ ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e, _, _)) => {
simplify(ConstEvalError::MirEvalError(*e))
}
_ => e,
@@ -24,13 +24,30 @@ fn simplify(e: ConstEvalError) -> ConstEvalError {
}
#[track_caller]
-fn check_fail(ra_fixture: &str, error: ConstEvalError) {
- assert_eq!(eval_goal(ra_fixture).map_err(simplify), Err(error));
+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).map_err(simplify) {
+ Ok(_) => panic!("Expected fail, but it succeeded"),
+ Err(e) => assert!(error(e)),
+ }
}
#[track_caller]
fn check_number(ra_fixture: &str, answer: i128) {
- let r = eval_goal(ra_fixture).unwrap();
+ let (db, file_id) = TestDB::with_single_file(ra_fixture);
+ let r = match eval_goal(&db, file_id) {
+ Ok(t) => t,
+ Err(e) => {
+ let mut err = String::new();
+ let span_formatter = |file, range| format!("{:?} {:?}", file, range);
+ match e {
+ ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter),
+ ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter),
+ }
+ .unwrap();
+ panic!("Error in evaluating goal: {}", err);
+ }
+ };
match &r.data(Interner).value {
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
ConstScalar::Bytes(b, _) => {
@@ -47,10 +64,9 @@ fn check_number(ra_fixture: &str, answer: i128) {
}
}
-fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
- let (db, file_id) = TestDB::with_single_file(ra_fixture);
+fn eval_goal(db: &TestDB, file_id: FileId) -> Result<Const, ConstEvalError> {
let module_id = db.module_for_file(file_id);
- let def_map = module_id.def_map(&db);
+ let def_map = module_id.def_map(db);
let scope = &def_map[module_id.local_id].scope;
let const_id = scope
.declarations()
@@ -65,7 +81,7 @@ fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
_ => None,
})
.unwrap();
- db.const_eval(const_id, Substitution::empty(Interner))
+ db.const_eval(const_id.into(), Substitution::empty(Interner))
}
#[test]
@@ -304,6 +320,81 @@ fn overloaded_index() {
}
#[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#"
@@ -428,6 +519,16 @@ fn generic_fn() {
);
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
@@ -590,6 +691,30 @@ fn loops() {
"#,
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]
@@ -790,10 +915,12 @@ fn path_pattern_matching() {
use Season::*;
+ const MY_SEASON: Season = Summer;
+
const fn f(x: Season) -> i32 {
match x {
Spring => 1,
- Summer => 2,
+ MY_SEASON => 2,
Fall => 3,
Winter => 4,
}
@@ -944,19 +1071,10 @@ fn function_param_patterns() {
fn match_guards() {
check_number(
r#"
- //- minicore: option, eq
- impl<T: PartialEq> PartialEq for Option<T> {
- fn eq(&self, other: &Rhs) -> bool {
- match (self, other) {
- (Some(x), Some(y)) => x == y,
- (None, None) => true,
- _ => false,
- }
- }
- }
+ //- minicore: option
fn f(x: Option<i32>) -> i32 {
match x {
- y if y == Some(42) => 42000,
+ y if let Some(42) = y => 42000,
Some(y) => y,
None => 10
}
@@ -968,6 +1086,59 @@ fn match_guards() {
}
#[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#"
@@ -1147,6 +1318,16 @@ fn closures() {
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) {
@@ -1181,6 +1362,36 @@ fn closures() {
}
#[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#"
@@ -1219,6 +1430,23 @@ fn or_pattern() {
}
#[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() {
check_number(
r#"
@@ -1433,6 +1661,51 @@ fn dyn_trait() {
}
#[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#"
@@ -1505,6 +1778,37 @@ fn consts() {
}
#[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 enums() {
check_number(
r#"
@@ -1531,13 +1835,13 @@ fn enums() {
"#,
0,
);
- let r = eval_goal(
+ let (db, file_id) = TestDB::with_single_file(
r#"
enum E { A = 1, B }
const GOAL: E = E::A;
"#,
- )
- .unwrap();
+ );
+ let r = eval_goal(&db, file_id).unwrap();
assert_eq!(try_const_usize(&r), Some(1));
}
@@ -1550,7 +1854,7 @@ fn const_loop() {
const F2: i32 = 2 * F1;
const GOAL: i32 = F3;
"#,
- ConstEvalError::MirLowerError(MirLowerError::Loop),
+ |e| e == ConstEvalError::MirLowerError(MirLowerError::Loop),
);
}
@@ -1609,8 +1913,7 @@ fn const_generic_subst_assoc_const_impl() {
#[test]
fn const_trait_assoc() {
- // FIXME: this should evaluate to 0
- check_fail(
+ check_number(
r#"
struct U0;
trait ToConst {
@@ -1619,9 +1922,35 @@ fn const_trait_assoc() {
impl ToConst for U0 {
const VAL: usize = 0;
}
- const GOAL: usize = U0::VAL;
+ impl ToConst for i32 {
+ const VAL: usize = 32;
+ }
+ const GOAL: usize = U0::VAL + i32::VAL;
"#,
- ConstEvalError::MirLowerError(MirLowerError::IncompleteExpr),
+ 32,
+ );
+ 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,
);
}
@@ -1631,7 +1960,7 @@ fn exec_limits() {
r#"
const GOAL: usize = loop {};
"#,
- ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
+ |e| e == ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
);
check_fail(
r#"
@@ -1640,7 +1969,7 @@ fn exec_limits() {
}
const GOAL: i32 = f(0);
"#,
- ConstEvalError::MirEvalError(MirEvalError::StackOverflow),
+ |e| e == ConstEvalError::MirEvalError(MirEvalError::StackOverflow),
);
// Reasonable code should still work
check_number(
@@ -1665,7 +1994,7 @@ fn exec_limits() {
#[test]
fn type_error() {
- let e = eval_goal(
+ check_fail(
r#"
const GOAL: u8 = {
let x: u16 = 2;
@@ -1673,6 +2002,6 @@ fn type_error() {
y.0
};
"#,
+ |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_))),
);
- assert!(matches!(e, Err(ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_)))));
}