From 8b147624ff906a11134d2e18be071c6cb8ec4beb Mon Sep 17 00:00:00 2001 From: Jade Date: Wed, 12 May 2021 04:39:48 -0700 Subject: Add lowering of array lengths in types Now e.g. ```rust fn a(b: [u8; 2]) { } ``` will know about the length of b. --- crates/hir_def/src/type_ref.rs | 53 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) (limited to 'crates/hir_def') diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs index ea29da5da..00c09a23d 100644 --- a/crates/hir_def/src/type_ref.rs +++ b/crates/hir_def/src/type_ref.rs @@ -2,6 +2,7 @@ //! be directly created from an ast::TypeRef, without further queries. use hir_expand::{name::Name, AstId, InFile}; +use std::convert::TryInto; use syntax::ast; use crate::{body::LowerCtx, path::Path}; @@ -79,7 +80,7 @@ pub enum TypeRef { Path(Path), RawPtr(Box, Mutability), Reference(Box, Option, Mutability), - Array(Box /*, Expr*/), + Array(Box, ConstScalar), Slice(Box), /// A fn pointer. Last element of the vector is the return type. Fn(Vec, bool /*varargs*/), @@ -140,7 +141,12 @@ impl TypeRef { TypeRef::RawPtr(Box::new(inner_ty), mutability) } ast::Type::ArrayType(inner) => { - TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty()))) + let len = inner + .expr() + .map(ConstScalar::usize_from_literal_expr) + .unwrap_or(ConstScalar::Unknown); + + TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())), len) } ast::Type::SliceType(inner) => { TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty()))) @@ -212,7 +218,7 @@ impl TypeRef { } TypeRef::RawPtr(type_ref, _) | TypeRef::Reference(type_ref, ..) - | TypeRef::Array(type_ref) + | TypeRef::Array(type_ref, _) | TypeRef::Slice(type_ref) => go(&type_ref, f), TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => { for bound in bounds { @@ -298,3 +304,44 @@ impl TypeBound { } } } + +/// A concrete constant value +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ConstScalar { + // for now, we only support the trivial case of constant evaluating the length of an array + // Note that this is u64 because the target usize may be bigger than our usize + Usize(u64), + + /// Case of an unknown value that rustc might know but we don't + Unknown, +} + +impl std::fmt::Display for ConstScalar { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + ConstScalar::Usize(us) => write!(fmt, "{}", us), + ConstScalar::Unknown => write!(fmt, "_"), + } + } +} + +impl ConstScalar { + fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar { + match expr { + ast::Expr::Literal(lit) => { + let lkind = lit.kind(); + match lkind { + ast::LiteralKind::IntNumber(num) + if num.suffix() == None || num.suffix() == Some("usize") => + { + num.value().and_then(|v| v.try_into().ok()) + } + _ => None, + } + } + _ => None, + } + .map(ConstScalar::Usize) + .unwrap_or(ConstScalar::Unknown) + } +} -- cgit v1.2.3 From 73023c0299d4adeada026648c3684621f129e038 Mon Sep 17 00:00:00 2001 From: Jade Date: Wed, 12 May 2021 05:44:01 -0700 Subject: Support length for ByteStrings I am not confident that my added byte string parsing is right. --- crates/hir_def/src/body/lower.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'crates/hir_def') diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 9f278d35b..b00dcbdf0 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -1022,7 +1022,10 @@ impl From for Literal { let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(&it)); Literal::Float(Default::default(), ty) } - LiteralKind::ByteString(_) => Literal::ByteString(Default::default()), + LiteralKind::ByteString(bs) => { + let text = bs.value().map(Vec::from).unwrap_or_else(Default::default); + Literal::ByteString(text) + } LiteralKind::String(_) => Literal::String(Default::default()), LiteralKind::Byte => Literal::Uint(Default::default(), Some(BuiltinUint::U8)), LiteralKind::Bool(val) => Literal::Bool(val), -- cgit v1.2.3 From e666589e63fd4de16baa5f1ebda4ab07aa5a5437 Mon Sep 17 00:00:00 2001 From: Jade Date: Wed, 12 May 2021 05:59:35 -0700 Subject: Add support for lengths in array repeats, if they are literals Now we will get the type of `[0u8; 4]`. --- crates/hir_def/src/body/lower.rs | 5 +++-- crates/hir_def/src/expr.rs | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'crates/hir_def') diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index b00dcbdf0..2a7e0205f 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -1006,16 +1006,17 @@ impl From for BinaryOp { impl From for Literal { fn from(ast_lit_kind: ast::LiteralKind) -> Self { match ast_lit_kind { + // FIXME: these should have actual values filled in, but unsure on perf impact LiteralKind::IntNumber(lit) => { if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { return Literal::Float(Default::default(), builtin); } else if let builtin @ Some(_) = lit.suffix().and_then(|it| BuiltinInt::from_suffix(&it)) { - Literal::Int(Default::default(), builtin) + Literal::Int(lit.value().unwrap_or(0) as i128, builtin) } else { let builtin = lit.suffix().and_then(|it| BuiltinUint::from_suffix(&it)); - Literal::Uint(Default::default(), builtin) + Literal::Uint(lit.value().unwrap_or(0), builtin) } } LiteralKind::FloatNumber(lit) => { 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 { ByteString(Vec), Char(char), Bool(bool), - Int(u64, Option), - Uint(u64, Option), + Int(i128, Option), + Uint(u128, Option), Float(u64, Option), // FIXME: f64 is not Eq } -- cgit v1.2.3 From 78d6b88f211cc9faf88815ce7fb1a91546cfce15 Mon Sep 17 00:00:00 2001 From: Jade Date: Fri, 14 May 2021 00:59:30 -0700 Subject: Add more tests, refactor array lengths/consteval work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- crates/hir_def/src/type_ref.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'crates/hir_def') 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 { Path(Path), RawPtr(Box, Mutability), Reference(Box, Option, Mutability), + // FIXME: for full const generics, the latter element (length) here is going to have to be an + // expression that is further lowered later in hir_ty. Array(Box, ConstScalar), Slice(Box), /// A fn pointer. Last element of the vector is the return type. @@ -141,6 +143,10 @@ impl TypeRef { TypeRef::RawPtr(Box::new(inner_ty), mutability) } ast::Type::ArrayType(inner) => { + // FIXME: This is a hack. We should probably reuse the machinery of + // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the + // `hir_ty` level, which would allow knowing the type of: + // let v: [u8; 2 + 2] = [0u8; 4]; let len = inner .expr() .map(ConstScalar::usize_from_literal_expr) @@ -313,6 +319,10 @@ pub enum ConstScalar { Usize(u64), /// Case of an unknown value that rustc might know but we don't + // FIXME: this is a hack to get around chalk not being able to represent unevaluatable + // constants + // https://github.com/rust-analyzer/rust-analyzer/pull/8813#issuecomment-840679177 + // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348 Unknown, } @@ -326,6 +336,8 @@ impl std::fmt::Display for ConstScalar { } impl ConstScalar { + // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this + // parse stage. fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar { match expr { ast::Expr::Literal(lit) => { -- cgit v1.2.3 From de0ed9860d86c3b905a967b1a7b5243499d32d67 Mon Sep 17 00:00:00 2001 From: Jade Date: Sat, 15 May 2021 18:51:18 -0700 Subject: Address final feedback * rename ConstExtension->ConstExt * refactor a manual construction of a Const --- crates/hir_def/src/type_ref.rs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'crates/hir_def') diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs index 6a3259b27..9e44547cb 100644 --- a/crates/hir_def/src/type_ref.rs +++ b/crates/hir_def/src/type_ref.rs @@ -336,6 +336,14 @@ impl std::fmt::Display for ConstScalar { } impl ConstScalar { + /// Gets a target usize out of the ConstScalar + pub fn as_usize(&self) -> Option { + match self { + &ConstScalar::Usize(us) => Some(us), + _ => None, + } + } + // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this // parse stage. fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar { -- cgit v1.2.3