aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir/src/lib.rs5
-rw-r--r--crates/hir_def/src/type_ref.rs12
-rw-r--r--crates/hir_ty/src/consteval.rs64
-rw-r--r--crates/hir_ty/src/infer/expr.rs41
-rw-r--r--crates/hir_ty/src/lib.rs1
-rw-r--r--crates/hir_ty/src/tests/simple.rs10
-rw-r--r--crates/hir_ty/src/tests/traits.rs97
-rw-r--r--crates/ide_assists/src/handlers/add_explicit_type.rs28
8 files changed, 223 insertions, 35 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index c9ef4b420..d7065ff2b 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -52,7 +52,9 @@ use hir_def::{
52}; 52};
53use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; 53use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind};
54use hir_ty::{ 54use hir_ty::{
55 autoderef, could_unify, 55 autoderef,
56 consteval::ConstExtension,
57 could_unify,
56 method_resolution::{self, def_crates, TyFingerprint}, 58 method_resolution::{self, def_crates, TyFingerprint},
57 primitive::UintTy, 59 primitive::UintTy,
58 subst_prefix, 60 subst_prefix,
@@ -1910,6 +1912,7 @@ impl Type {
1910 substs.iter(&Interner).filter_map(|a| a.ty(&Interner)).any(go) 1912 substs.iter(&Interner).filter_map(|a| a.ty(&Interner)).any(go)
1911 } 1913 }
1912 1914
1915 TyKind::Array(_ty, len) if len.is_unknown() => true,
1913 TyKind::Array(ty, _) 1916 TyKind::Array(ty, _)
1914 | TyKind::Slice(ty) 1917 | TyKind::Slice(ty)
1915 | TyKind::Raw(_, ty) 1918 | TyKind::Raw(_, ty)
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs
index 00c09a23d..6a3259b27 100644
--- a/crates/hir_def/src/type_ref.rs
+++ b/crates/hir_def/src/type_ref.rs
@@ -80,6 +80,8 @@ pub enum TypeRef {
80 Path(Path), 80 Path(Path),
81 RawPtr(Box<TypeRef>, Mutability), 81 RawPtr(Box<TypeRef>, Mutability),
82 Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability), 82 Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
83 // FIXME: for full const generics, the latter element (length) here is going to have to be an
84 // expression that is further lowered later in hir_ty.
83 Array(Box<TypeRef>, ConstScalar), 85 Array(Box<TypeRef>, ConstScalar),
84 Slice(Box<TypeRef>), 86 Slice(Box<TypeRef>),
85 /// A fn pointer. Last element of the vector is the return type. 87 /// A fn pointer. Last element of the vector is the return type.
@@ -141,6 +143,10 @@ impl TypeRef {
141 TypeRef::RawPtr(Box::new(inner_ty), mutability) 143 TypeRef::RawPtr(Box::new(inner_ty), mutability)
142 } 144 }
143 ast::Type::ArrayType(inner) => { 145 ast::Type::ArrayType(inner) => {
146 // FIXME: This is a hack. We should probably reuse the machinery of
147 // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
148 // `hir_ty` level, which would allow knowing the type of:
149 // let v: [u8; 2 + 2] = [0u8; 4];
144 let len = inner 150 let len = inner
145 .expr() 151 .expr()
146 .map(ConstScalar::usize_from_literal_expr) 152 .map(ConstScalar::usize_from_literal_expr)
@@ -313,6 +319,10 @@ pub enum ConstScalar {
313 Usize(u64), 319 Usize(u64),
314 320
315 /// Case of an unknown value that rustc might know but we don't 321 /// Case of an unknown value that rustc might know but we don't
322 // FIXME: this is a hack to get around chalk not being able to represent unevaluatable
323 // constants
324 // https://github.com/rust-analyzer/rust-analyzer/pull/8813#issuecomment-840679177
325 // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348
316 Unknown, 326 Unknown,
317} 327}
318 328
@@ -326,6 +336,8 @@ impl std::fmt::Display for ConstScalar {
326} 336}
327 337
328impl ConstScalar { 338impl ConstScalar {
339 // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
340 // parse stage.
329 fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar { 341 fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar {
330 match expr { 342 match expr {
331 ast::Expr::Literal(lit) => { 343 ast::Expr::Literal(lit) => {
diff --git a/crates/hir_ty/src/consteval.rs b/crates/hir_ty/src/consteval.rs
new file mode 100644
index 000000000..a4a430f30
--- /dev/null
+++ b/crates/hir_ty/src/consteval.rs
@@ -0,0 +1,64 @@
1//! Constant evaluation details
2
3use std::convert::TryInto;
4
5use hir_def::{
6 builtin_type::BuiltinUint,
7 expr::{Expr, Literal},
8 type_ref::ConstScalar,
9};
10
11use crate::{Const, ConstData, ConstValue, Interner, TyKind};
12
13/// Extension trait for [`Const`]
14pub trait ConstExtension {
15 /// Is a [`Const`] unknown?
16 fn is_unknown(&self) -> bool;
17}
18
19impl ConstExtension for Const {
20 fn is_unknown(&self) -> bool {
21 match self.data(&Interner).value {
22 // interned Unknown
23 chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst {
24 interned: ConstScalar::Unknown,
25 }) => true,
26
27 // interned concrete anything else
28 chalk_ir::ConstValue::Concrete(..) => false,
29
30 _ => {
31 log::error!("is_unknown was called on a non-concrete constant value! {:?}", self);
32 true
33 }
34 }
35 }
36}
37
38/// Extension trait for [`Expr`]
39pub trait ExprEval {
40 /// Attempts to evaluate the expression as a target usize.
41 fn eval_usize(&self) -> Option<u64>;
42}
43
44impl ExprEval for Expr {
45 // FIXME: support more than just evaluating literals
46 fn eval_usize(&self) -> Option<u64> {
47 match self {
48 Expr::Literal(Literal::Uint(v, None))
49 | Expr::Literal(Literal::Uint(v, Some(BuiltinUint::Usize))) => (*v).try_into().ok(),
50 _ => None,
51 }
52 }
53}
54
55/// Interns a possibly-unknown target usize
56pub fn usize_const(value: Option<u64>) -> Const {
57 ConstData {
58 ty: TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(&Interner),
59 value: ConstValue::Concrete(chalk_ir::ConcreteConst {
60 interned: value.map(|value| ConstScalar::Usize(value)).unwrap_or(ConstScalar::Unknown),
61 }),
62 }
63 .intern(&Interner)
64}
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 0b36ac861..04fc2f12b 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -1,18 +1,13 @@
1//! Type inference for expressions. 1//! Type inference for expressions.
2 2
3use std::{ 3use std::iter::{repeat, repeat_with};
4 convert::TryInto,
5 iter::{repeat, repeat_with},
6};
7use std::{mem, sync::Arc}; 4use std::{mem, sync::Arc};
8 5
9use chalk_ir::{cast::Cast, fold::Shift, ConstData, Mutability, TyVariableKind}; 6use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
10use hir_def::{ 7use hir_def::{
11 builtin_type::BuiltinUint,
12 expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, 8 expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
13 path::{GenericArg, GenericArgs}, 9 path::{GenericArg, GenericArgs},
14 resolver::resolver_for_expr, 10 resolver::resolver_for_expr,
15 type_ref::ConstScalar,
16 AssocContainerId, FieldId, Lookup, 11 AssocContainerId, FieldId, Lookup,
17}; 12};
18use hir_expand::name::{name, Name}; 13use hir_expand::name::{name, Name};
@@ -21,6 +16,7 @@ use syntax::ast::RangeOp;
21 16
22use crate::{ 17use crate::{
23 autoderef, 18 autoderef,
19 consteval::{self, ExprEval},
24 lower::lower_to_chalk_mutability, 20 lower::lower_to_chalk_mutability,
25 mapping::from_chalk, 21 mapping::from_chalk,
26 method_resolution, op, 22 method_resolution, op,
@@ -28,9 +24,8 @@ use crate::{
28 static_lifetime, to_chalk_trait_id, 24 static_lifetime, to_chalk_trait_id,
29 traits::FnTrait, 25 traits::FnTrait,
30 utils::{generics, Generics}, 26 utils::{generics, Generics},
31 AdtId, Binders, CallableDefId, ConcreteConst, ConstValue, FnPointer, FnSig, FnSubst, 27 AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner,
32 InEnvironment, Interner, ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, 28 ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
33 TyBuilder, TyExt, TyKind,
34}; 29};
35 30
36use super::{ 31use super::{
@@ -743,25 +738,11 @@ impl<'a> InferenceContext<'a> {
743 ); 738 );
744 739
745 let repeat_expr = &self.body.exprs[*repeat]; 740 let repeat_expr = &self.body.exprs[*repeat];
746 match repeat_expr { 741 repeat_expr.eval_usize()
747 Expr::Literal(Literal::Uint(v, None))
748 | Expr::Literal(Literal::Uint(v, Some(BuiltinUint::Usize))) => {
749 (*v).try_into().ok()
750 }
751 _ => None,
752 }
753 } 742 }
754 }; 743 };
755 744
756 let cd = ConstData { 745 TyKind::Array(elem_ty, consteval::usize_const(len)).intern(&Interner)
757 ty: TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner),
758 value: ConstValue::Concrete(chalk_ir::ConcreteConst {
759 interned: len
760 .map(|len| ConstScalar::Usize(len))
761 .unwrap_or(ConstScalar::Unknown),
762 }),
763 };
764 TyKind::Array(elem_ty, cd.intern(&Interner)).intern(&Interner)
765 } 746 }
766 Expr::Literal(lit) => match lit { 747 Expr::Literal(lit) => match lit {
767 Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner), 748 Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner),
@@ -772,13 +753,7 @@ impl<'a> InferenceContext<'a> {
772 Literal::ByteString(bs) => { 753 Literal::ByteString(bs) => {
773 let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner); 754 let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner);
774 755
775 let len = ConstData { 756 let len = consteval::usize_const(Some(bs.len() as u64));
776 ty: TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner),
777 value: ConstValue::Concrete(ConcreteConst {
778 interned: ConstScalar::Usize(bs.len() as u64),
779 }),
780 }
781 .intern(&Interner);
782 757
783 let array_type = TyKind::Array(byte_type, len).intern(&Interner); 758 let array_type = TyKind::Array(byte_type, len).intern(&Interner);
784 TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner) 759 TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner)
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index be3f55bdf..15b61bedc 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -10,6 +10,7 @@ mod autoderef;
10mod builder; 10mod builder;
11mod chalk_db; 11mod chalk_db;
12mod chalk_ext; 12mod chalk_ext;
13pub mod consteval;
13mod infer; 14mod infer;
14mod interner; 15mod interner;
15mod lower; 16mod lower;
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index 1032f09ac..a9cd42186 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -1271,12 +1271,14 @@ fn infer_array() {
1271 1271
1272 let b = [a, ["b"]]; 1272 let b = [a, ["b"]];
1273 let x: [u8; 0] = []; 1273 let x: [u8; 0] = [];
1274 // FIXME: requires const evaluation/taking type from rhs somehow
1275 let y: [u8; 2+2] = [1,2,3,4];
1274 } 1276 }
1275 "#, 1277 "#,
1276 expect![[r#" 1278 expect![[r#"
1277 8..9 'x': &str 1279 8..9 'x': &str
1278 17..18 'y': isize 1280 17..18 'y': isize
1279 27..292 '{ ... []; }': () 1281 27..395 '{ ...,4]; }': ()
1280 37..38 'a': [&str; 1] 1282 37..38 'a': [&str; 1]
1281 41..44 '[x]': [&str; 1] 1283 41..44 '[x]': [&str; 1]
1282 42..43 'x': &str 1284 42..43 'x': &str
@@ -1326,6 +1328,12 @@ fn infer_array() {
1326 259..262 '"b"': &str 1328 259..262 '"b"': &str
1327 274..275 'x': [u8; 0] 1329 274..275 'x': [u8; 0]
1328 287..289 '[]': [u8; 0] 1330 287..289 '[]': [u8; 0]
1331 368..369 'y': [u8; _]
1332 383..392 '[1,2,3,4]': [u8; 4]
1333 384..385 '1': u8
1334 386..387 '2': u8
1335 388..389 '3': u8
1336 390..391 '4': u8
1329 "#]], 1337 "#]],
1330 ); 1338 );
1331} 1339}
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 47a1455fd..f80cf9879 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -3474,3 +3474,100 @@ fn main(){
3474 "#]], 3474 "#]],
3475 ) 3475 )
3476} 3476}
3477
3478#[test]
3479fn array_length() {
3480 check_infer(
3481 r#"
3482trait T {
3483 type Output;
3484 fn do_thing(&self) -> Self::Output;
3485}
3486
3487impl T for [u8; 4] {
3488 type Output = usize;
3489 fn do_thing(&self) -> Self::Output {
3490 2
3491 }
3492}
3493
3494impl T for [u8; 2] {
3495 type Output = u8;
3496 fn do_thing(&self) -> Self::Output {
3497 2
3498 }
3499}
3500
3501fn main() {
3502 let v = [0u8; 2];
3503 let v2 = v.do_thing();
3504 let v3 = [0u8; 4];
3505 let v4 = v3.do_thing();
3506}
3507"#,
3508 expect![[r#"
3509 44..48 'self': &Self
3510 133..137 'self': &[u8; 4]
3511 155..172 '{ ... }': usize
3512 165..166 '2': usize
3513 236..240 'self': &[u8; 2]
3514 258..275 '{ ... }': u8
3515 268..269 '2': u8
3516 289..392 '{ ...g(); }': ()
3517 299..300 'v': [u8; 2]
3518 303..311 '[0u8; 2]': [u8; 2]
3519 304..307 '0u8': u8
3520 309..310 '2': usize
3521 321..323 'v2': u8
3522 326..327 'v': [u8; 2]
3523 326..338 'v.do_thing()': u8
3524 348..350 'v3': [u8; 4]
3525 353..361 '[0u8; 4]': [u8; 4]
3526 354..357 '0u8': u8
3527 359..360 '4': usize
3528 371..373 'v4': usize
3529 376..378 'v3': [u8; 4]
3530 376..389 'v3.do_thing()': usize
3531 "#]],
3532 )
3533}
3534
3535// FIXME: We should infer the length of the returned array :)
3536#[test]
3537fn const_generics() {
3538 check_infer(
3539 r#"
3540trait T {
3541 type Output;
3542 fn do_thing(&self) -> Self::Output;
3543}
3544
3545impl<const L: usize> T for [u8; L] {
3546 type Output = [u8; L];
3547 fn do_thing(&self) -> Self::Output {
3548 *self
3549 }
3550}
3551
3552fn main() {
3553 let v = [0u8; 2];
3554 let v2 = v.do_thing();
3555}
3556"#,
3557 expect![[r#"
3558 44..48 'self': &Self
3559 151..155 'self': &[u8; _]
3560 173..194 '{ ... }': [u8; _]
3561 183..188 '*self': [u8; _]
3562 184..188 'self': &[u8; _]
3563 208..260 '{ ...g(); }': ()
3564 218..219 'v': [u8; 2]
3565 222..230 '[0u8; 2]': [u8; 2]
3566 223..226 '0u8': u8
3567 228..229 '2': usize
3568 240..242 'v2': [u8; _]
3569 245..246 'v': [u8; 2]
3570 245..257 'v.do_thing()': [u8; _]
3571 "#]],
3572 )
3573}
diff --git a/crates/ide_assists/src/handlers/add_explicit_type.rs b/crates/ide_assists/src/handlers/add_explicit_type.rs
index 62db31952..36589203d 100644
--- a/crates/ide_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ide_assists/src/handlers/add_explicit_type.rs
@@ -198,6 +198,34 @@ fn main() {
198 ) 198 )
199 } 199 }
200 200
201 /// https://github.com/rust-analyzer/rust-analyzer/issues/2922
202 #[test]
203 fn regression_issue_2922() {
204 check_assist(
205 add_explicit_type,
206 r#"
207fn main() {
208 let $0v = [0.0; 2];
209}
210"#,
211 r#"
212fn main() {
213 let v: [f64; 2] = [0.0; 2];
214}
215"#,
216 );
217 // note: this may break later if we add more consteval. it just needs to be something that our
218 // consteval engine doesn't understand
219 check_assist_not_applicable(
220 add_explicit_type,
221 r#"
222fn main() {
223 let $0l = [0.0; 2+2];
224}
225"#,
226 );
227 }
228
201 #[test] 229 #[test]
202 fn default_generics_should_not_be_added() { 230 fn default_generics_should_not_be_added() {
203 check_assist( 231 check_assist(