diff options
-rw-r--r-- | crates/hir/src/lib.rs | 5 | ||||
-rw-r--r-- | crates/hir_def/src/type_ref.rs | 12 | ||||
-rw-r--r-- | crates/hir_ty/src/consteval.rs | 64 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/expr.rs | 41 | ||||
-rw-r--r-- | crates/hir_ty/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/hir_ty/src/tests/simple.rs | 10 | ||||
-rw-r--r-- | crates/hir_ty/src/tests/traits.rs | 97 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/add_explicit_type.rs | 28 |
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 | }; |
53 | use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; | 53 | use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; |
54 | use hir_ty::{ | 54 | use 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 | ||
328 | impl ConstScalar { | 338 | impl 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 | |||
3 | use std::convert::TryInto; | ||
4 | |||
5 | use hir_def::{ | ||
6 | builtin_type::BuiltinUint, | ||
7 | expr::{Expr, Literal}, | ||
8 | type_ref::ConstScalar, | ||
9 | }; | ||
10 | |||
11 | use crate::{Const, ConstData, ConstValue, Interner, TyKind}; | ||
12 | |||
13 | /// Extension trait for [`Const`] | ||
14 | pub trait ConstExtension { | ||
15 | /// Is a [`Const`] unknown? | ||
16 | fn is_unknown(&self) -> bool; | ||
17 | } | ||
18 | |||
19 | impl 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`] | ||
39 | pub trait ExprEval { | ||
40 | /// Attempts to evaluate the expression as a target usize. | ||
41 | fn eval_usize(&self) -> Option<u64>; | ||
42 | } | ||
43 | |||
44 | impl 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 | ||
56 | pub 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 | ||
3 | use std::{ | 3 | use std::iter::{repeat, repeat_with}; |
4 | convert::TryInto, | ||
5 | iter::{repeat, repeat_with}, | ||
6 | }; | ||
7 | use std::{mem, sync::Arc}; | 4 | use std::{mem, sync::Arc}; |
8 | 5 | ||
9 | use chalk_ir::{cast::Cast, fold::Shift, ConstData, Mutability, TyVariableKind}; | 6 | use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind}; |
10 | use hir_def::{ | 7 | use 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 | }; |
18 | use hir_expand::name::{name, Name}; | 13 | use hir_expand::name::{name, Name}; |
@@ -21,6 +16,7 @@ use syntax::ast::RangeOp; | |||
21 | 16 | ||
22 | use crate::{ | 17 | use 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 | ||
36 | use super::{ | 31 | use 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; | |||
10 | mod builder; | 10 | mod builder; |
11 | mod chalk_db; | 11 | mod chalk_db; |
12 | mod chalk_ext; | 12 | mod chalk_ext; |
13 | pub mod consteval; | ||
13 | mod infer; | 14 | mod infer; |
14 | mod interner; | 15 | mod interner; |
15 | mod lower; | 16 | mod 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] | ||
3479 | fn array_length() { | ||
3480 | check_infer( | ||
3481 | r#" | ||
3482 | trait T { | ||
3483 | type Output; | ||
3484 | fn do_thing(&self) -> Self::Output; | ||
3485 | } | ||
3486 | |||
3487 | impl T for [u8; 4] { | ||
3488 | type Output = usize; | ||
3489 | fn do_thing(&self) -> Self::Output { | ||
3490 | 2 | ||
3491 | } | ||
3492 | } | ||
3493 | |||
3494 | impl T for [u8; 2] { | ||
3495 | type Output = u8; | ||
3496 | fn do_thing(&self) -> Self::Output { | ||
3497 | 2 | ||
3498 | } | ||
3499 | } | ||
3500 | |||
3501 | fn 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] | ||
3537 | fn const_generics() { | ||
3538 | check_infer( | ||
3539 | r#" | ||
3540 | trait T { | ||
3541 | type Output; | ||
3542 | fn do_thing(&self) -> Self::Output; | ||
3543 | } | ||
3544 | |||
3545 | impl<const L: usize> T for [u8; L] { | ||
3546 | type Output = [u8; L]; | ||
3547 | fn do_thing(&self) -> Self::Output { | ||
3548 | *self | ||
3549 | } | ||
3550 | } | ||
3551 | |||
3552 | fn 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#" | ||
207 | fn main() { | ||
208 | let $0v = [0.0; 2]; | ||
209 | } | ||
210 | "#, | ||
211 | r#" | ||
212 | fn 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#" | ||
222 | fn 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( |