aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty
diff options
context:
space:
mode:
authorJade <[email protected]>2021-05-14 08:59:30 +0100
committerJade <[email protected]>2021-05-14 09:39:28 +0100
commit78d6b88f211cc9faf88815ce7fb1a91546cfce15 (patch)
tree6b54a10d2eeee6fc03d63015e4b7c9838b4b02b9 /crates/hir_ty
parent32c600664e5a599bbe3f0254274211474b89914a (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.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
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
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}