diff options
author | Jade <[email protected]> | 2021-05-14 08:59:30 +0100 |
---|---|---|
committer | Jade <[email protected]> | 2021-05-14 09:39:28 +0100 |
commit | 78d6b88f211cc9faf88815ce7fb1a91546cfce15 (patch) | |
tree | 6b54a10d2eeee6fc03d63015e4b7c9838b4b02b9 /crates/hir_ty | |
parent | 32c600664e5a599bbe3f0254274211474b89914a (diff) |
Add more tests, refactor array lengths/consteval work
Fix #2922: add unknown length as a condition for a type having unknown.
Incorporate reviews:
* Extract some of the const evaluation workings into functions
* Add fixmes on the hacks
* Add tests for impls on specific array lengths (these work!!! 😁)
* Add tests for const generics (indeed we don't support it yet)
Diffstat (limited to 'crates/hir_ty')
-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 |
5 files changed, 179 insertions, 34 deletions
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 | } | ||