diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 8c3c5fc628..18de04b16d 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -55,10 +55,7 @@ use hir_def::{ use hir_expand::{name::name, MacroCallKind}; use hir_ty::{ autoderef, - consteval::{ - eval_const, unknown_const_as_generic, ComputedExpr, ConstEvalCtx, ConstEvalError, ConstExt, - }, - could_unify, + consteval::{unknown_const_as_generic, ComputedExpr, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, method_resolution::{self, TyFingerprint}, primitive::UintTy, diff --git a/crates/hir_ty/src/consteval.rs b/crates/hir_ty/src/consteval.rs index 4829482e70..9e1a856cd0 100644 --- a/crates/hir_ty/src/consteval.rs +++ b/crates/hir_ty/src/consteval.rs @@ -87,14 +87,14 @@ impl Display for ComputedExpr { match self { ComputedExpr::Literal(l) => match l { Literal::Int(x, _) => { - if *x >= 16 { + if *x >= 10 { write!(f, "{} ({:#X})", x, x) } else { x.fmt(f) } } Literal::Uint(x, _) => { - if *x >= 16 { + if *x >= 10 { write!(f, "{} ({:#X})", x, x) } else { x.fmt(f) @@ -156,6 +156,7 @@ pub fn eval_const( ) -> Result { let expr = &ctx.exprs[expr_id]; match expr { + Expr::Missing => Err(ConstEvalError::IncompleteExpr), Expr::Literal(l) => Ok(ComputedExpr::Literal(l.clone())), &Expr::UnaryOp { expr, op } => { let ty = &ctx.expr_ty(expr); @@ -339,6 +340,9 @@ pub fn eval_const( Ok(r.clone()) } ValueNs::ConstId(id) => ctx.db.const_eval(id), + ValueNs::GenericParam(_) => { + Err(ConstEvalError::NotSupported("const generic without substitution")) + } _ => Err(ConstEvalError::NotSupported("path that are not const or local")), } } @@ -433,7 +437,7 @@ pub(crate) fn const_eval_query( ) -> Result { let def = const_id.into(); let body = db.body(def); - let mut infer = db.infer_query(def); + let infer = &db.infer(def); let result = eval_const( body.body_expr, &mut ConstEvalCtx { @@ -442,7 +446,7 @@ pub(crate) fn const_eval_query( exprs: &body.exprs, pats: &body.pats, local_data: HashMap::default(), - infer: &mut infer, + infer, }, ); result @@ -473,3 +477,6 @@ pub(crate) fn eval_to_const<'a>( }; usize_const(eval_usize(expr, ctx)) } + +#[cfg(test)] +mod tests; diff --git a/crates/hir_ty/src/consteval/tests.rs b/crates/hir_ty/src/consteval/tests.rs new file mode 100644 index 0000000000..4a052851af --- /dev/null +++ b/crates/hir_ty/src/consteval/tests.rs @@ -0,0 +1,148 @@ +use base_db::fixture::WithFixture; +use hir_def::{db::DefDatabase, expr::Literal}; + +use crate::{consteval::ComputedExpr, db::HirDatabase, test_db::TestDB}; + +use super::ConstEvalError; + +fn check_fail(ra_fixture: &str, error: ConstEvalError) { + assert_eq!(eval_goal(ra_fixture), Err(error)); +} + +fn check_number(ra_fixture: &str, answer: i128) { + let r = eval_goal(ra_fixture).unwrap(); + match r { + ComputedExpr::Literal(Literal::Int(r, _)) => assert_eq!(r, answer), + ComputedExpr::Literal(Literal::Uint(r, _)) => assert_eq!(r, answer as u128), + x => panic!("Expected number but found {:?}", x), + } +} + +fn eval_goal(ra_fixture: &str) -> Result { + let (db, file_id) = TestDB::with_single_file(ra_fixture); + let module_id = db.module_for_file(file_id); + let def_map = module_id.def_map(&db); + let scope = &def_map[module_id.local_id].scope; + let const_id = scope + .declarations() + .into_iter() + .find_map(|x| match x { + hir_def::ModuleDefId::ConstId(x) => { + if db.const_data(x).name.as_ref()?.to_string() == "GOAL" { + Some(x) + } else { + None + } + } + _ => None, + }) + .unwrap(); + db.const_eval(const_id) +} + +#[test] +fn add() { + check_number(r#"const GOAL: usize = 2 + 2;"#, 4); +} + +#[test] +fn bit_op() { + check_number(r#"const GOAL: u8 = !0 & !(!0 >> 1)"#, 128); + check_number(r#"const GOAL: i8 = !0 & !(!0 >> 1)"#, 0); + // FIXME: rustc evaluate this to -128 + check_fail( + r#"const GOAL: i8 = 1 << 7"#, + ConstEvalError::Panic("attempt to run invalid arithmetic operation".to_string()), + ); + check_fail( + r#"const GOAL: i8 = 1 << 8"#, + ConstEvalError::Panic("attempt to run invalid arithmetic operation".to_string()), + ); +} + +#[test] +fn locals() { + check_number( + r#" + const GOAL: usize = { + let a = 3 + 2; + let b = a * a; + b + }; + "#, + 25, + ); +} + +#[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, + ); +} + +#[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; + "#, + ConstEvalError::Loop, + ); +} + +#[test] +fn const_impl_assoc() { + check_number( + r#" + struct U5; + impl U5 { + const VAL: usize = 5; + } + const GOAL: usize = U5::VAL; + "#, + 5, + ); +} + +#[test] +fn const_generic_subst() { + // FIXME: this should evaluate to 5 + check_fail( + r#" + struct Adder; + impl Adder { + const VAL: usize = N + M; + } + const GOAL: usize = Adder::<2, 3>::VAL; + "#, + ConstEvalError::NotSupported("const generic without substitution"), + ); +} + +#[test] +fn const_trait_assoc() { + // FIXME: this should evaluate to 0 + check_fail( + r#" + struct U0; + trait ToConst { + const VAL: usize; + } + impl ToConst for U0 { + const VAL: usize = 0; + } + const GOAL: usize = U0::VAL; + "#, + ConstEvalError::IncompleteExpr, + ); +} diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index 31045c193c..675f9038f0 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs @@ -1749,6 +1749,18 @@ fn main() { ); } +#[test] +fn const_eval_array_repeat_expr() { + check_types( + r#" +fn main() { + const X: usize = 6 - 1; + let t = [(); X + 2]; + //^ [(); 7] +}"#, + ); +} + #[test] fn shadowing_primitive_with_inner_items() { check_types( diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 987e5d1556..67dc9884ed 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -3399,13 +3399,13 @@ impl Foo {} ); } -// FIXME: move these tests to consteval module #[test] fn hover_const_eval() { + // show hex for <10 check( r#" /// This is a doc -const FOO$0: usize = !0 & !(!0 >> 1); +const FOO$0: usize = 1 << 3; "#, expect![[r#" *FOO* @@ -3415,7 +3415,7 @@ const FOO$0: usize = !0 & !(!0 >> 1); ``` ```rust - const FOO: usize = 9223372036854775808 (0x8000000000000000) + const FOO: usize = 8 ``` --- @@ -3423,14 +3423,11 @@ const FOO$0: usize = !0 & !(!0 >> 1); This is a doc "#]], ); + // show hex for >10 check( r#" /// This is a doc -const FOO$0: usize = { - let a = 3 + 2; - let b = a * a; - b -}; +const FOO$0: usize = (1 << 3) + (1 << 2); "#, expect![[r#" *FOO* @@ -3440,53 +3437,7 @@ const FOO$0: usize = { ``` ```rust - const FOO: usize = 25 (0x19) - ``` - - --- - - This is a doc - "#]], - ); - check( - r#" -/// This is a doc -const FOO$0: usize = 1 << 10; -"#, - expect![[r#" - *FOO* - - ```rust - test - ``` - - ```rust - const FOO: usize = 1024 (0x400) - ``` - - --- - - This is a doc - "#]], - ); - check( - r#" -/// This is a doc -const FOO$0: usize = { - let b = 4; - let a = { let b = 2; let a = b; a } + { let a = 1; a + b }; - a -}; -"#, - expect![[r#" - *FOO* - - ```rust - test - ``` - - ```rust - const FOO: usize = 7 + const FOO: usize = 12 (0xC) ``` --- @@ -3494,6 +3445,7 @@ const FOO$0: usize = { This is a doc "#]], ); + // show original body when const eval fails check( r#" /// This is a doc @@ -3515,6 +3467,7 @@ const FOO$0: usize = 2 - 3; This is a doc "#]], ); + // don't show hex for negatives check( r#" /// This is a doc @@ -3539,27 +3492,6 @@ const FOO$0: i32 = 2 - 3; check( r#" /// This is a doc -const FOO$0: usize = 1 << 100; -"#, - expect![[r#" - *FOO* - - ```rust - test - ``` - - ```rust - const FOO: usize = 1 << 100 - ``` - - --- - - This is a doc - "#]], - ); - check( - r#" -/// This is a doc const FOO$0: &str = "bar"; "#, expect![[r#" @@ -3578,90 +3510,6 @@ const FOO$0: &str = "bar"; This is a doc "#]], ); - check( - r#" -const F1: i32 = 1; -const F$03: i32 = 3 * F2; -const F2: i32 = 2 * F1; -"#, - expect![[r#" - *F3* - - ```rust - test - ``` - - ```rust - const F3: i32 = 6 - ``` - "#]], - ); - check( - r#" -const F1: i32 = 1 * F3; -const F2: i32 = 2 * F1; -const F$03: i32 = 3 * F2; -"#, - expect![[r#" - *F3* - - ```rust - test - ``` - - ```rust - const F3: i32 = 3 * F2 - ``` - "#]], - ); - check( - r#" -struct U5; -impl U5 { - const VAL: usize = 5; -} -const X$0X: usize = U5::VAL; -"#, - expect![[r#" - *XX* - - ```rust - test - ``` - - ```rust - const XX: usize = 5 - ``` - "#]], - ); -} - -// FIXME: this should evaluate to zero -#[test] -fn hover_const_eval_broken() { - check( - r#" -struct U0; -trait ToConst { - const VAL: usize; -} -impl ToConst for U0 { - const VAL: usize = 0; -} -const X$0X: usize = U0::VAL; -"#, - expect![[r#" - *XX* - - ```rust - test - ``` - - ```rust - const XX: usize = U0::VAL - ``` - "#]], - ); } #[test]