diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-05-16 02:53:12 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2021-05-16 02:53:12 +0100 |
commit | a57bd59f351600c408ef9d2a4df3f2c10e817d0d (patch) | |
tree | 73af1b67227643ef4e8caa77ba50f2ad738ddd4e /crates/hir_def | |
parent | 92abc56bc928fb2a11b8f5b2a37e3c9ee31102d7 (diff) | |
parent | de0ed9860d86c3b905a967b1a7b5243499d32d67 (diff) |
Merge #8813
8813: Get some more array lengths! r=lf- a=lf-
This is built on #8799 and thus contains its changes. I'll rebase it onto master when that one gets merged. It adds support for r-a understanding the length of:
* `let a: [u8; 2] = ...`
* `let a = b"aaa"`
* `let a = [0u8; 4]`
I have added support for getting the values of byte strings, which was not previously there. I am least confident in the correctness of this part and it probably needs some more tests, as we currently have only one test that exercised that part (!).
Fixes #2922.
Co-authored-by: Jade <[email protected]>
Diffstat (limited to 'crates/hir_def')
-rw-r--r-- | crates/hir_def/src/body/lower.rs | 10 | ||||
-rw-r--r-- | crates/hir_def/src/expr.rs | 4 | ||||
-rw-r--r-- | crates/hir_def/src/type_ref.rs | 73 |
3 files changed, 79 insertions, 8 deletions
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 9f278d35b..2a7e0205f 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -1006,23 +1006,27 @@ impl From<ast::BinOp> for BinaryOp { | |||
1006 | impl From<ast::LiteralKind> for Literal { | 1006 | impl From<ast::LiteralKind> for Literal { |
1007 | fn from(ast_lit_kind: ast::LiteralKind) -> Self { | 1007 | fn from(ast_lit_kind: ast::LiteralKind) -> Self { |
1008 | match ast_lit_kind { | 1008 | match ast_lit_kind { |
1009 | // FIXME: these should have actual values filled in, but unsure on perf impact | ||
1009 | LiteralKind::IntNumber(lit) => { | 1010 | LiteralKind::IntNumber(lit) => { |
1010 | if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { | 1011 | if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { |
1011 | return Literal::Float(Default::default(), builtin); | 1012 | return Literal::Float(Default::default(), builtin); |
1012 | } else if let builtin @ Some(_) = | 1013 | } else if let builtin @ Some(_) = |
1013 | lit.suffix().and_then(|it| BuiltinInt::from_suffix(&it)) | 1014 | lit.suffix().and_then(|it| BuiltinInt::from_suffix(&it)) |
1014 | { | 1015 | { |
1015 | Literal::Int(Default::default(), builtin) | 1016 | Literal::Int(lit.value().unwrap_or(0) as i128, builtin) |
1016 | } else { | 1017 | } else { |
1017 | let builtin = lit.suffix().and_then(|it| BuiltinUint::from_suffix(&it)); | 1018 | let builtin = lit.suffix().and_then(|it| BuiltinUint::from_suffix(&it)); |
1018 | Literal::Uint(Default::default(), builtin) | 1019 | Literal::Uint(lit.value().unwrap_or(0), builtin) |
1019 | } | 1020 | } |
1020 | } | 1021 | } |
1021 | LiteralKind::FloatNumber(lit) => { | 1022 | LiteralKind::FloatNumber(lit) => { |
1022 | let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(&it)); | 1023 | let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(&it)); |
1023 | Literal::Float(Default::default(), ty) | 1024 | Literal::Float(Default::default(), ty) |
1024 | } | 1025 | } |
1025 | LiteralKind::ByteString(_) => Literal::ByteString(Default::default()), | 1026 | LiteralKind::ByteString(bs) => { |
1027 | let text = bs.value().map(Vec::from).unwrap_or_else(Default::default); | ||
1028 | Literal::ByteString(text) | ||
1029 | } | ||
1026 | LiteralKind::String(_) => Literal::String(Default::default()), | 1030 | LiteralKind::String(_) => Literal::String(Default::default()), |
1027 | LiteralKind::Byte => Literal::Uint(Default::default(), Some(BuiltinUint::U8)), | 1031 | LiteralKind::Byte => Literal::Uint(Default::default(), Some(BuiltinUint::U8)), |
1028 | LiteralKind::Bool(val) => Literal::Bool(val), | 1032 | LiteralKind::Bool(val) => Literal::Bool(val), |
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs index 0c3b41080..2ba619d23 100644 --- a/crates/hir_def/src/expr.rs +++ b/crates/hir_def/src/expr.rs | |||
@@ -43,8 +43,8 @@ pub enum Literal { | |||
43 | ByteString(Vec<u8>), | 43 | ByteString(Vec<u8>), |
44 | Char(char), | 44 | Char(char), |
45 | Bool(bool), | 45 | Bool(bool), |
46 | Int(u64, Option<BuiltinInt>), | 46 | Int(i128, Option<BuiltinInt>), |
47 | Uint(u64, Option<BuiltinUint>), | 47 | Uint(u128, Option<BuiltinUint>), |
48 | Float(u64, Option<BuiltinFloat>), // FIXME: f64 is not Eq | 48 | Float(u64, Option<BuiltinFloat>), // FIXME: f64 is not Eq |
49 | } | 49 | } |
50 | 50 | ||
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs index ea29da5da..9e44547cb 100644 --- a/crates/hir_def/src/type_ref.rs +++ b/crates/hir_def/src/type_ref.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | //! be directly created from an ast::TypeRef, without further queries. | 2 | //! be directly created from an ast::TypeRef, without further queries. |
3 | 3 | ||
4 | use hir_expand::{name::Name, AstId, InFile}; | 4 | use hir_expand::{name::Name, AstId, InFile}; |
5 | use std::convert::TryInto; | ||
5 | use syntax::ast; | 6 | use syntax::ast; |
6 | 7 | ||
7 | use crate::{body::LowerCtx, path::Path}; | 8 | use crate::{body::LowerCtx, path::Path}; |
@@ -79,7 +80,9 @@ pub enum TypeRef { | |||
79 | Path(Path), | 80 | Path(Path), |
80 | RawPtr(Box<TypeRef>, Mutability), | 81 | RawPtr(Box<TypeRef>, Mutability), |
81 | Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability), | 82 | Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability), |
82 | Array(Box<TypeRef> /*, Expr*/), | 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. | ||
85 | Array(Box<TypeRef>, ConstScalar), | ||
83 | Slice(Box<TypeRef>), | 86 | Slice(Box<TypeRef>), |
84 | /// 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. |
85 | Fn(Vec<TypeRef>, bool /*varargs*/), | 88 | Fn(Vec<TypeRef>, bool /*varargs*/), |
@@ -140,7 +143,16 @@ impl TypeRef { | |||
140 | TypeRef::RawPtr(Box::new(inner_ty), mutability) | 143 | TypeRef::RawPtr(Box::new(inner_ty), mutability) |
141 | } | 144 | } |
142 | ast::Type::ArrayType(inner) => { | 145 | ast::Type::ArrayType(inner) => { |
143 | TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty()))) | 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]; | ||
150 | let len = inner | ||
151 | .expr() | ||
152 | .map(ConstScalar::usize_from_literal_expr) | ||
153 | .unwrap_or(ConstScalar::Unknown); | ||
154 | |||
155 | TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())), len) | ||
144 | } | 156 | } |
145 | ast::Type::SliceType(inner) => { | 157 | ast::Type::SliceType(inner) => { |
146 | TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty()))) | 158 | TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty()))) |
@@ -212,7 +224,7 @@ impl TypeRef { | |||
212 | } | 224 | } |
213 | TypeRef::RawPtr(type_ref, _) | 225 | TypeRef::RawPtr(type_ref, _) |
214 | | TypeRef::Reference(type_ref, ..) | 226 | | TypeRef::Reference(type_ref, ..) |
215 | | TypeRef::Array(type_ref) | 227 | | TypeRef::Array(type_ref, _) |
216 | | TypeRef::Slice(type_ref) => go(&type_ref, f), | 228 | | TypeRef::Slice(type_ref) => go(&type_ref, f), |
217 | TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => { | 229 | TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => { |
218 | for bound in bounds { | 230 | for bound in bounds { |
@@ -298,3 +310,58 @@ impl TypeBound { | |||
298 | } | 310 | } |
299 | } | 311 | } |
300 | } | 312 | } |
313 | |||
314 | /// A concrete constant value | ||
315 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
316 | pub enum ConstScalar { | ||
317 | // for now, we only support the trivial case of constant evaluating the length of an array | ||
318 | // Note that this is u64 because the target usize may be bigger than our usize | ||
319 | Usize(u64), | ||
320 | |||
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 | ||
326 | Unknown, | ||
327 | } | ||
328 | |||
329 | impl std::fmt::Display for ConstScalar { | ||
330 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { | ||
331 | match self { | ||
332 | ConstScalar::Usize(us) => write!(fmt, "{}", us), | ||
333 | ConstScalar::Unknown => write!(fmt, "_"), | ||
334 | } | ||
335 | } | ||
336 | } | ||
337 | |||
338 | impl ConstScalar { | ||
339 | /// Gets a target usize out of the ConstScalar | ||
340 | pub fn as_usize(&self) -> Option<u64> { | ||
341 | match self { | ||
342 | &ConstScalar::Usize(us) => Some(us), | ||
343 | _ => None, | ||
344 | } | ||
345 | } | ||
346 | |||
347 | // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this | ||
348 | // parse stage. | ||
349 | fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar { | ||
350 | match expr { | ||
351 | ast::Expr::Literal(lit) => { | ||
352 | let lkind = lit.kind(); | ||
353 | match lkind { | ||
354 | ast::LiteralKind::IntNumber(num) | ||
355 | if num.suffix() == None || num.suffix() == Some("usize") => | ||
356 | { | ||
357 | num.value().and_then(|v| v.try_into().ok()) | ||
358 | } | ||
359 | _ => None, | ||
360 | } | ||
361 | } | ||
362 | _ => None, | ||
363 | } | ||
364 | .map(ConstScalar::Usize) | ||
365 | .unwrap_or(ConstScalar::Unknown) | ||
366 | } | ||
367 | } | ||