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 ++++++++++++++++++++++++++++++++++--- crates/hir_ty/src/consts.rs | 21 --------------- crates/hir_ty/src/display.rs | 5 ++-- crates/hir_ty/src/infer/expr.rs | 8 +++--- crates/hir_ty/src/interner.rs | 3 ++- crates/hir_ty/src/lib.rs | 9 ++++--- crates/hir_ty/src/lower.rs | 26 +++++++++++------- crates/hir_ty/src/tests/coercion.rs | 44 +++++++++++++++--------------- crates/hir_ty/src/tests/patterns.rs | 8 +++--- crates/hir_ty/src/tests/simple.rs | 16 +++++------ 10 files changed, 114 insertions(+), 79 deletions(-) delete mode 100644 crates/hir_ty/src/consts.rs 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) + } +} diff --git a/crates/hir_ty/src/consts.rs b/crates/hir_ty/src/consts.rs deleted file mode 100644 index 0044b1cff..000000000 --- a/crates/hir_ty/src/consts.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! Handling of concrete const values - -/// 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, "_"), - } - } -} diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index 8a4296697..7bbd1a1f7 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs @@ -962,11 +962,10 @@ impl HirDisplay for TypeRef { write!(f, "{}", mutability)?; inner.hir_fmt(f)?; } - TypeRef::Array(inner) => { + TypeRef::Array(inner, len) => { write!(f, "[")?; inner.hir_fmt(f)?; - // FIXME: Array length? - write!(f, "; _]")?; + write!(f, "; {}]", len)?; } TypeRef::Slice(inner) => { write!(f, "[")?; diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 2178ffd07..5e9420752 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -8,6 +8,7 @@ use hir_def::{ expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, path::{GenericArg, GenericArgs}, resolver::resolver_for_expr, + type_ref::ConstScalar, AssocContainerId, FieldId, Lookup, }; use hir_expand::name::{name, Name}; @@ -15,9 +16,7 @@ use stdx::always; use syntax::ast::RangeOp; use crate::{ - autoderef, - consts::ConstScalar, - dummy_usize_const, + autoderef, dummy_usize_const, lower::lower_to_chalk_mutability, mapping::from_chalk, method_resolution, op, @@ -737,7 +736,8 @@ impl<'a> InferenceContext<'a> { TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner), ), ); - // FIXME: support length for Repeat array expressions + // FIXME: we don't know the length here because hir Exprs don't actually + // get the value out of the AST, even though it is there. None } }; diff --git a/crates/hir_ty/src/interner.rs b/crates/hir_ty/src/interner.rs index 4cbc9cd4f..7b4119747 100644 --- a/crates/hir_ty/src/interner.rs +++ b/crates/hir_ty/src/interner.rs @@ -1,11 +1,12 @@ //! Implementation of the Chalk `Interner` trait, which allows customizing the //! representation of the various objects Chalk deals with (types, goals etc.). -use crate::{chalk_db, consts::ConstScalar, tls, GenericArg}; +use crate::{chalk_db, tls, GenericArg}; use base_db::salsa::InternId; use chalk_ir::{Goal, GoalData}; use hir_def::{ intern::{impl_internable, InternStorage, Internable, Interned}, + type_ref::ConstScalar, TypeAliasId, }; use smallvec::SmallVec; diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index d23eff513..be3f55bdf 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs @@ -12,7 +12,6 @@ mod chalk_db; mod chalk_ext; mod infer; mod interner; -mod consts; mod lower; mod mapping; mod op; @@ -38,9 +37,13 @@ use chalk_ir::{ interner::HasInterner, UintTy, }; -use hir_def::{expr::ExprId, type_ref::Rawness, TypeParamId}; +use hir_def::{ + expr::ExprId, + type_ref::{ConstScalar, Rawness}, + TypeParamId, +}; -use crate::{consts::ConstScalar, db::HirDatabase, display::HirDisplay, utils::generics}; +use crate::{db::HirDatabase, display::HirDisplay, utils::generics}; pub use autoderef::autoderef; pub use builder::TyBuilder; diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index 9751b45e4..f7015e5ff 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs @@ -9,7 +9,9 @@ use std::cell::{Cell, RefCell}; use std::{iter, sync::Arc}; use base_db::CrateId; -use chalk_ir::{cast::Cast, fold::Shift, interner::HasInterner, Mutability, Safety}; +use chalk_ir::{ + cast::Cast, fold::Shift, interner::HasInterner, Mutability, Safety, Scalar, UintTy, +}; use hir_def::{ adt::StructKind, body::{Expander, LowerCtx}, @@ -30,16 +32,15 @@ use syntax::ast; use crate::{ db::HirDatabase, - dummy_usize_const, mapping::ToChalk, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, utils::{ all_super_trait_refs, associated_type_by_name_including_super_traits, generics, Generics, }, - AliasEq, AliasTy, Binders, BoundVar, CallableSig, DebruijnIndex, DynTy, FnPointer, FnSig, - FnSubst, ImplTraitId, Interner, OpaqueTy, PolyFnSig, ProjectionTy, QuantifiedWhereClause, - QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits, Substitution, - TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, + AliasEq, AliasTy, Binders, BoundVar, CallableSig, ConstData, ConstValue, DebruijnIndex, DynTy, + FnPointer, FnSig, FnSubst, ImplTraitId, Interner, OpaqueTy, PolyFnSig, ProjectionTy, + QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits, + Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, }; #[derive(Debug)] @@ -172,11 +173,16 @@ impl<'a> TyLoweringContext<'a> { let inner_ty = self.lower_ty(inner); TyKind::Raw(lower_to_chalk_mutability(*mutability), inner_ty).intern(&Interner) } - TypeRef::Array(inner) => { + TypeRef::Array(inner, len) => { let inner_ty = self.lower_ty(inner); - // FIXME: we don't have length info here because we don't store an expression for - // the length - TyKind::Array(inner_ty, dummy_usize_const()).intern(&Interner) + + let const_len = ConstData { + ty: TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner), + value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: *len }), + } + .intern(&Interner); + + TyKind::Array(inner_ty, const_len).intern(&Interner) } TypeRef::Slice(inner) => { let inner_ty = self.lower_ty(inner); diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index aad3d610e..c2afaf6ec 100644 --- a/crates/hir_ty/src/tests/coercion.rs +++ b/crates/hir_ty/src/tests/coercion.rs @@ -64,42 +64,42 @@ fn coerce_places() { 81..92 '{ loop {} }': T 83..90 'loop {}': ! 88..90 '{}': () - 121..132 '{ loop {} }': *mut [T; _] + 121..132 '{ loop {} }': *mut [T; 2] 123..130 'loop {}': ! 128..130 '{}': () 159..172 '{ gen() }': *mut [U] - 165..168 'gen': fn gen() -> *mut [U; _] - 165..170 'gen()': *mut [U; _] + 165..168 'gen': fn gen() -> *mut [U; 2] + 165..170 'gen()': *mut [U; 2] 185..419 '{ ...rr); }': () - 195..198 'arr': &[u8; _] + 195..198 'arr': &[u8; 1] 211..215 '&[1]': &[u8; 1] 212..215 '[1]': [u8; 1] 213..214 '1': u8 226..227 'a': &[u8] - 236..239 'arr': &[u8; _] + 236..239 'arr': &[u8; 1] 249..250 'b': u8 253..254 'f': fn f(&[u8]) -> u8 253..259 'f(arr)': u8 - 255..258 'arr': &[u8; _] + 255..258 'arr': &[u8; 1] 269..270 'c': &[u8] 279..286 '{ arr }': &[u8] - 281..284 'arr': &[u8; _] + 281..284 'arr': &[u8; 1] 296..297 'd': u8 300..301 'g': fn g(S<&[u8]>) -> u8 300..315 'g(S { a: arr })': u8 302..314 'S { a: arr }': S<&[u8]> - 309..312 'arr': &[u8; _] - 325..326 'e': [&[u8]; _] + 309..312 'arr': &[u8; 1] + 325..326 'e': [&[u8]; 1] 340..345 '[arr]': [&[u8]; 1] - 341..344 'arr': &[u8; _] - 355..356 'f': [&[u8]; _] + 341..344 'arr': &[u8; 1] + 355..356 'f': [&[u8]; 2] 370..378 '[arr; 2]': [&[u8]; _] - 371..374 'arr': &[u8; _] + 371..374 'arr': &[u8; 1] 376..377 '2': usize 388..389 'g': (&[u8], &[u8]) 406..416 '(arr, arr)': (&[u8], &[u8]) - 407..410 'arr': &[u8; _] - 412..415 'arr': &[u8; _] + 407..410 'arr': &[u8; 1] + 412..415 'arr': &[u8; 1] "#]], ); } @@ -159,7 +159,7 @@ fn infer_custom_coerce_unsized() { impl<'a, 'b: 'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} impl, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} "#, - expect![[r" + expect![[r#" 257..258 'x': A<[T]> 278..283 '{ x }': A<[T]> 280..281 'x': A<[T]> @@ -169,23 +169,23 @@ fn infer_custom_coerce_unsized() { 333..334 'x': C<[T]> 354..359 '{ x }': C<[T]> 356..357 'x': C<[T]> - 369..370 'a': A<[u8; _]> - 384..385 'b': B<[u8; _]> - 399..400 'c': C<[u8; _]> + 369..370 'a': A<[u8; 2]> + 384..385 'b': B<[u8; 2]> + 399..400 'c': C<[u8; 2]> 414..480 '{ ...(c); }': () 424..425 'd': A<[{unknown}]> 428..432 'foo1': fn foo1<{unknown}>(A<[{unknown}]>) -> A<[{unknown}]> 428..435 'foo1(a)': A<[{unknown}]> - 433..434 'a': A<[u8; _]> + 433..434 'a': A<[u8; 2]> 445..446 'e': B<[u8]> 449..453 'foo2': fn foo2(B<[u8]>) -> B<[u8]> 449..456 'foo2(b)': B<[u8]> - 454..455 'b': B<[u8; _]> + 454..455 'b': B<[u8; 2]> 466..467 'f': C<[u8]> 470..474 'foo3': fn foo3(C<[u8]>) -> C<[u8]> 470..477 'foo3(c)': C<[u8]> - 475..476 'c': C<[u8; _]> - "]], + 475..476 'c': C<[u8; 2]> + "#]], ); } diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs index 33305f208..b36e77e91 100644 --- a/crates/hir_ty/src/tests/patterns.rs +++ b/crates/hir_ty/src/tests/patterns.rs @@ -345,19 +345,19 @@ fn infer_pattern_match_arr() { "#, expect![[r#" 10..179 '{ ... } }': () - 20..23 'arr': [f64; _] + 20..23 'arr': [f64; 2] 36..46 '[0.0, 1.0]': [f64; 2] 37..40 '0.0': f64 42..45 '1.0': f64 52..177 'match ... }': () - 58..61 'arr': [f64; _] - 72..80 '[1.0, a]': [f64; _] + 58..61 'arr': [f64; 2] + 72..80 '[1.0, a]': [f64; 2] 73..76 '1.0': f64 73..76 '1.0': f64 78..79 'a': f64 84..110 '{ ... }': () 98..99 'a': f64 - 120..126 '[b, c]': [f64; _] + 120..126 '[b, c]': [f64; 2] 121..122 'b': f64 124..125 'c': f64 130..171 '{ ... }': () diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index 8b09f2e4a..19775a4ec 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs @@ -1313,7 +1313,7 @@ fn infer_array() { 255..256 'a': [&str; 1] 258..263 '["b"]': [&str; 1] 259..262 '"b"': &str - 274..275 'x': [u8; _] + 274..275 'x': [u8; 0] 287..289 '[]': [u8; 0] "#]], ); @@ -2409,38 +2409,38 @@ fn infer_operator_overload() { 320..422 '{ ... }': V2 334..335 'x': f32 338..342 'self': V2 - 338..344 'self.0': [f32; _] + 338..344 'self.0': [f32; 2] 338..347 'self.0[0]': {unknown} 338..358 'self.0...s.0[0]': f32 345..346 '0': i32 350..353 'rhs': V2 - 350..355 'rhs.0': [f32; _] + 350..355 'rhs.0': [f32; 2] 350..358 'rhs.0[0]': {unknown} 356..357 '0': i32 372..373 'y': f32 376..380 'self': V2 - 376..382 'self.0': [f32; _] + 376..382 'self.0': [f32; 2] 376..385 'self.0[1]': {unknown} 376..396 'self.0...s.0[1]': f32 383..384 '1': i32 388..391 'rhs': V2 - 388..393 'rhs.0': [f32; _] + 388..393 'rhs.0': [f32; 2] 388..396 'rhs.0[1]': {unknown} 394..395 '1': i32 - 406..408 'V2': V2([f32; _]) -> V2 + 406..408 'V2': V2([f32; 2]) -> V2 406..416 'V2([x, y])': V2 409..415 '[x, y]': [f32; 2] 410..411 'x': f32 413..414 'y': f32 436..519 '{ ... vb; }': () 446..448 'va': V2 - 451..453 'V2': V2([f32; _]) -> V2 + 451..453 'V2': V2([f32; 2]) -> V2 451..465 'V2([0.0, 1.0])': V2 454..464 '[0.0, 1.0]': [f32; 2] 455..458 '0.0': f32 460..463 '1.0': f32 475..477 'vb': V2 - 480..482 'V2': V2([f32; _]) -> V2 + 480..482 'V2': V2([f32; 2]) -> V2 480..494 'V2([0.0, 1.0])': V2 483..493 '[0.0, 1.0]': [f32; 2] 484..487 '0.0': f32 -- 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 +- crates/hir_ty/src/infer/expr.rs | 20 ++++-- crates/hir_ty/src/tests/simple.rs | 4 +- crates/ide/src/join_lines.rs | 2 +- crates/ide/src/syntax_highlighting/inject.rs | 2 +- crates/ide_assists/src/handlers/raw_string.rs | 2 +- .../src/handlers/replace_string_with_char.rs | 2 +- crates/syntax/src/ast/token_ext.rs | 81 ++++++++++++++++------ 8 files changed, 85 insertions(+), 33 deletions(-) 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), diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 5e9420752..46e4777a4 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -16,7 +16,7 @@ use stdx::always; use syntax::ast::RangeOp; use crate::{ - autoderef, dummy_usize_const, + autoderef, lower::lower_to_chalk_mutability, mapping::from_chalk, method_resolution, op, @@ -24,8 +24,9 @@ use crate::{ static_lifetime, to_chalk_trait_id, traits::FnTrait, utils::{generics, Generics}, - AdtId, Binders, CallableDefId, ConstValue, FnPointer, FnSig, FnSubst, InEnvironment, Interner, - ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, + AdtId, Binders, CallableDefId, ConcreteConst, ConstValue, FnPointer, FnSig, FnSubst, + InEnvironment, Interner, ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, + TyBuilder, TyExt, TyKind, }; use super::{ @@ -758,11 +759,18 @@ impl<'a> InferenceContext<'a> { TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(&Interner)) .intern(&Interner) } - Literal::ByteString(..) => { + Literal::ByteString(bs) => { let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner); - let array_type = - TyKind::Array(byte_type, dummy_usize_const()).intern(&Interner); + let len = ConstData { + ty: TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner), + value: ConstValue::Concrete(ConcreteConst { + interned: ConstScalar::Usize(bs.len() as u64), + }), + } + .intern(&Interner); + + let array_type = TyKind::Array(byte_type, len).intern(&Interner); TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner) } Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(&Interner), diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index 19775a4ec..79445a12d 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs @@ -496,7 +496,7 @@ fn infer_literals() { 26..30 '5f32': f32 36..40 '5f64': f64 46..53 '"hello"': &str - 59..67 'b"bytes"': &[u8; _] + 59..67 'b"bytes"': &[u8; 5] 73..76 ''c'': char 82..86 'b'b'': u8 92..96 '3.14': f64 @@ -504,7 +504,7 @@ fn infer_literals() { 112..117 'false': bool 123..127 'true': bool 133..197 'r#" ... "#': &str - 203..213 'br#"yolo"#': &[u8; _] + 203..213 'br#"yolo"#': &[u8; 4] "##]], ); } diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs index 61dcbb399..c67ccd1a9 100644 --- a/crates/ide/src/join_lines.rs +++ b/crates/ide/src/join_lines.rs @@ -4,7 +4,7 @@ use ide_assists::utils::extract_trivial_expression; use itertools::Itertools; use syntax::{ algo::non_trivia_sibling, - ast::{self, AstNode, AstToken}, + ast::{self, AstNode, AstToken, IsString}, Direction, NodeOrToken, SourceFile, SyntaxKind::{self, USE_TREE, WHITESPACE}, SyntaxNode, SyntaxToken, TextRange, TextSize, T, diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index bc221d599..4269d339e 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -6,7 +6,7 @@ use either::Either; use hir::{InFile, Semantics}; use ide_db::{call_info::ActiveParameter, helpers::rust_doc::is_rust_fence, SymbolKind}; use syntax::{ - ast::{self, AstNode}, + ast::{self, AstNode, IsString}, AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, }; diff --git a/crates/ide_assists/src/handlers/raw_string.rs b/crates/ide_assists/src/handlers/raw_string.rs index d0f1613f3..d98a55ae4 100644 --- a/crates/ide_assists/src/handlers/raw_string.rs +++ b/crates/ide_assists/src/handlers/raw_string.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use syntax::{ast, AstToken, TextRange, TextSize}; +use syntax::{ast, ast::IsString, AstToken, TextRange, TextSize}; use crate::{AssistContext, AssistId, AssistKind, Assists}; diff --git a/crates/ide_assists/src/handlers/replace_string_with_char.rs b/crates/ide_assists/src/handlers/replace_string_with_char.rs index 634b9c0b7..0800d291e 100644 --- a/crates/ide_assists/src/handlers/replace_string_with_char.rs +++ b/crates/ide_assists/src/handlers/replace_string_with_char.rs @@ -1,4 +1,4 @@ -use syntax::{ast, AstToken, SyntaxKind::STRING}; +use syntax::{ast, ast::IsString, AstToken, SyntaxKind::STRING}; use crate::{AssistContext, AssistId, AssistKind, Assists}; diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 29d25a58a..4b1e1ccee 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs @@ -143,6 +143,30 @@ impl QuoteOffsets { } } +pub trait IsString: AstToken { + fn quote_offsets(&self) -> Option { + let text = self.text(); + let offsets = QuoteOffsets::new(text)?; + let o = self.syntax().text_range().start(); + let offsets = QuoteOffsets { + quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o), + contents: offsets.contents + o, + }; + Some(offsets) + } + fn text_range_between_quotes(&self) -> Option { + self.quote_offsets().map(|it| it.contents) + } + fn open_quote_text_range(&self) -> Option { + self.quote_offsets().map(|it| it.quotes.0) + } + fn close_quote_text_range(&self) -> Option { + self.quote_offsets().map(|it| it.quotes.1) + } +} + +impl IsString for ast::String {} + impl ast::String { pub fn is_raw(&self) -> bool { self.text().starts_with('r') @@ -187,32 +211,49 @@ impl ast::String { (false, false) => Some(Cow::Owned(buf)), } } - - pub fn quote_offsets(&self) -> Option { - let text = self.text(); - let offsets = QuoteOffsets::new(text)?; - let o = self.syntax().text_range().start(); - let offsets = QuoteOffsets { - quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o), - contents: offsets.contents + o, - }; - Some(offsets) - } - pub fn text_range_between_quotes(&self) -> Option { - self.quote_offsets().map(|it| it.contents) - } - pub fn open_quote_text_range(&self) -> Option { - self.quote_offsets().map(|it| it.quotes.0) - } - pub fn close_quote_text_range(&self) -> Option { - self.quote_offsets().map(|it| it.quotes.1) - } } +impl IsString for ast::ByteString {} + impl ast::ByteString { pub fn is_raw(&self) -> bool { self.text().starts_with("br") } + + pub fn value(&self) -> Option> { + if self.is_raw() { + let text = self.text(); + let text = + &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; + return Some(Cow::Borrowed(text.as_bytes())); + } + + let text = self.text(); + let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; + + let mut buf: Vec = Vec::new(); + let mut text_iter = text.chars(); + let mut has_error = false; + unescape_literal(text, Mode::ByteStr, &mut |char_range, unescaped_char| match ( + unescaped_char, + buf.capacity() == 0, + ) { + (Ok(c), false) => buf.push(c as u8), + (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (), + (Ok(c), true) => { + buf.reserve_exact(text.len()); + buf.extend_from_slice(&text[..char_range.start].as_bytes()); + buf.push(c as u8); + } + (Err(_), _) => has_error = true, + }); + + match (has_error, buf.capacity() == 0) { + (true, _) => None, + (false, true) => Some(Cow::Borrowed(text.as_bytes())), + (false, false) => Some(Cow::Owned(buf)), + } + } } #[derive(Debug)] -- 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 ++-- crates/hir_ty/src/infer/expr.rs | 22 ++++++++++++++++------ crates/hir_ty/src/tests/coercion.rs | 2 +- 4 files changed, 22 insertions(+), 11 deletions(-) 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 } diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 46e4777a4..0b36ac861 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -1,10 +1,14 @@ //! Type inference for expressions. -use std::iter::{repeat, repeat_with}; +use std::{ + convert::TryInto, + iter::{repeat, repeat_with}, +}; use std::{mem, sync::Arc}; use chalk_ir::{cast::Cast, fold::Shift, ConstData, Mutability, TyVariableKind}; use hir_def::{ + builtin_type::BuiltinUint, expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, path::{GenericArg, GenericArgs}, resolver::resolver_for_expr, @@ -724,7 +728,7 @@ impl<'a> InferenceContext<'a> { for expr in items.iter() { self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone())); } - Some(items.len()) + Some(items.len() as u64) } Array::Repeat { initializer, repeat } => { self.infer_expr_coerce( @@ -737,9 +741,15 @@ impl<'a> InferenceContext<'a> { TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner), ), ); - // FIXME: we don't know the length here because hir Exprs don't actually - // get the value out of the AST, even though it is there. - None + + let repeat_expr = &self.body.exprs[*repeat]; + match repeat_expr { + Expr::Literal(Literal::Uint(v, None)) + | Expr::Literal(Literal::Uint(v, Some(BuiltinUint::Usize))) => { + (*v).try_into().ok() + } + _ => None, + } } }; @@ -747,7 +757,7 @@ impl<'a> InferenceContext<'a> { ty: TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner), value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: len - .map(|len| ConstScalar::Usize(len as u64)) + .map(|len| ConstScalar::Usize(len)) .unwrap_or(ConstScalar::Unknown), }), }; diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index c2afaf6ec..190471069 100644 --- a/crates/hir_ty/src/tests/coercion.rs +++ b/crates/hir_ty/src/tests/coercion.rs @@ -93,7 +93,7 @@ fn coerce_places() { 340..345 '[arr]': [&[u8]; 1] 341..344 'arr': &[u8; 1] 355..356 'f': [&[u8]; 2] - 370..378 '[arr; 2]': [&[u8]; _] + 370..378 '[arr; 2]': [&[u8]; 2] 371..374 'arr': &[u8; 1] 376..377 '2': usize 388..389 'g': (&[u8], &[u8]) -- cgit v1.2.3 From 32c600664e5a599bbe3f0254274211474b89914a Mon Sep 17 00:00:00 2001 From: Jade Date: Wed, 12 May 2021 21:22:13 -0700 Subject: Test lowering byte strings some more --- crates/hir_ty/src/tests/simple.rs | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index 79445a12d..1032f09ac 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs @@ -488,23 +488,34 @@ fn infer_literals() { mod foo {} "#; br#"yolo"#; + let a = b"a\x20b\ + c"; + let b = br"g\ +h"; + let c = br#"x"\"yb"#; } "##, expect![[r##" - 10..216 '{ ...o"#; }': () - 16..20 '5i32': i32 - 26..30 '5f32': f32 - 36..40 '5f64': f64 - 46..53 '"hello"': &str - 59..67 'b"bytes"': &[u8; 5] - 73..76 ''c'': char - 82..86 'b'b'': u8 - 92..96 '3.14': f64 - 102..106 '5000': i32 - 112..117 'false': bool - 123..127 'true': bool - 133..197 'r#" ... "#': &str - 203..213 'br#"yolo"#': &[u8; 4] + 18..478 '{ ... }': () + 32..36 '5i32': i32 + 50..54 '5f32': f32 + 68..72 '5f64': f64 + 86..93 '"hello"': &str + 107..115 'b"bytes"': &[u8; 5] + 129..132 ''c'': char + 146..150 'b'b'': u8 + 164..168 '3.14': f64 + 182..186 '5000': i32 + 200..205 'false': bool + 219..223 'true': bool + 237..333 'r#" ... "#': &str + 347..357 'br#"yolo"#': &[u8; 4] + 375..376 'a': &[u8; 4] + 379..403 'b"a\x2... c"': &[u8; 4] + 421..422 'b': &[u8; 4] + 425..433 'br"g\ h"': &[u8; 4] + 451..452 'c': &[u8; 6] + 455..467 'br#"x"\"yb"#': &[u8; 6] "##]], ); } -- 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/src/lib.rs | 5 +- crates/hir_def/src/type_ref.rs | 12 +++ crates/hir_ty/src/consteval.rs | 64 ++++++++++++++ crates/hir_ty/src/infer/expr.rs | 41 ++------- crates/hir_ty/src/lib.rs | 1 + crates/hir_ty/src/tests/simple.rs | 10 ++- crates/hir_ty/src/tests/traits.rs | 97 ++++++++++++++++++++++ .../ide_assists/src/handlers/add_explicit_type.rs | 28 +++++++ 8 files changed, 223 insertions(+), 35 deletions(-) create mode 100644 crates/hir_ty/src/consteval.rs 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::{ }; use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; use hir_ty::{ - autoderef, could_unify, + autoderef, + consteval::ConstExtension, + could_unify, method_resolution::{self, def_crates, TyFingerprint}, primitive::UintTy, subst_prefix, @@ -1910,6 +1912,7 @@ impl Type { substs.iter(&Interner).filter_map(|a| a.ty(&Interner)).any(go) } + TyKind::Array(_ty, len) if len.is_unknown() => true, TyKind::Array(ty, _) | TyKind::Slice(ty) | 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 { 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) => { 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 @@ +//! Constant evaluation details + +use std::convert::TryInto; + +use hir_def::{ + builtin_type::BuiltinUint, + expr::{Expr, Literal}, + type_ref::ConstScalar, +}; + +use crate::{Const, ConstData, ConstValue, Interner, TyKind}; + +/// Extension trait for [`Const`] +pub trait ConstExtension { + /// Is a [`Const`] unknown? + fn is_unknown(&self) -> bool; +} + +impl ConstExtension for Const { + fn is_unknown(&self) -> bool { + match self.data(&Interner).value { + // interned Unknown + chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + interned: ConstScalar::Unknown, + }) => true, + + // interned concrete anything else + chalk_ir::ConstValue::Concrete(..) => false, + + _ => { + log::error!("is_unknown was called on a non-concrete constant value! {:?}", self); + true + } + } + } +} + +/// Extension trait for [`Expr`] +pub trait ExprEval { + /// Attempts to evaluate the expression as a target usize. + fn eval_usize(&self) -> Option; +} + +impl ExprEval for Expr { + // FIXME: support more than just evaluating literals + fn eval_usize(&self) -> Option { + match self { + Expr::Literal(Literal::Uint(v, None)) + | Expr::Literal(Literal::Uint(v, Some(BuiltinUint::Usize))) => (*v).try_into().ok(), + _ => None, + } + } +} + +/// Interns a possibly-unknown target usize +pub fn usize_const(value: Option) -> Const { + ConstData { + ty: TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(&Interner), + value: ConstValue::Concrete(chalk_ir::ConcreteConst { + interned: value.map(|value| ConstScalar::Usize(value)).unwrap_or(ConstScalar::Unknown), + }), + } + .intern(&Interner) +} 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 @@ //! Type inference for expressions. -use std::{ - convert::TryInto, - iter::{repeat, repeat_with}, -}; +use std::iter::{repeat, repeat_with}; use std::{mem, sync::Arc}; -use chalk_ir::{cast::Cast, fold::Shift, ConstData, Mutability, TyVariableKind}; +use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind}; use hir_def::{ - builtin_type::BuiltinUint, expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, path::{GenericArg, GenericArgs}, resolver::resolver_for_expr, - type_ref::ConstScalar, AssocContainerId, FieldId, Lookup, }; use hir_expand::name::{name, Name}; @@ -21,6 +16,7 @@ use syntax::ast::RangeOp; use crate::{ autoderef, + consteval::{self, ExprEval}, lower::lower_to_chalk_mutability, mapping::from_chalk, method_resolution, op, @@ -28,9 +24,8 @@ use crate::{ static_lifetime, to_chalk_trait_id, traits::FnTrait, utils::{generics, Generics}, - AdtId, Binders, CallableDefId, ConcreteConst, ConstValue, FnPointer, FnSig, FnSubst, - InEnvironment, Interner, ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, - TyBuilder, TyExt, TyKind, + AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner, + ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, }; use super::{ @@ -743,25 +738,11 @@ impl<'a> InferenceContext<'a> { ); let repeat_expr = &self.body.exprs[*repeat]; - match repeat_expr { - Expr::Literal(Literal::Uint(v, None)) - | Expr::Literal(Literal::Uint(v, Some(BuiltinUint::Usize))) => { - (*v).try_into().ok() - } - _ => None, - } + repeat_expr.eval_usize() } }; - let cd = ConstData { - ty: TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner), - value: ConstValue::Concrete(chalk_ir::ConcreteConst { - interned: len - .map(|len| ConstScalar::Usize(len)) - .unwrap_or(ConstScalar::Unknown), - }), - }; - TyKind::Array(elem_ty, cd.intern(&Interner)).intern(&Interner) + TyKind::Array(elem_ty, consteval::usize_const(len)).intern(&Interner) } Expr::Literal(lit) => match lit { Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner), @@ -772,13 +753,7 @@ impl<'a> InferenceContext<'a> { Literal::ByteString(bs) => { let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner); - let len = ConstData { - ty: TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner), - value: ConstValue::Concrete(ConcreteConst { - interned: ConstScalar::Usize(bs.len() as u64), - }), - } - .intern(&Interner); + let len = consteval::usize_const(Some(bs.len() as u64)); let array_type = TyKind::Array(byte_type, len).intern(&Interner); 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; mod builder; mod chalk_db; mod chalk_ext; +pub mod consteval; mod infer; mod interner; 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() { let b = [a, ["b"]]; let x: [u8; 0] = []; + // FIXME: requires const evaluation/taking type from rhs somehow + let y: [u8; 2+2] = [1,2,3,4]; } "#, expect![[r#" 8..9 'x': &str 17..18 'y': isize - 27..292 '{ ... []; }': () + 27..395 '{ ...,4]; }': () 37..38 'a': [&str; 1] 41..44 '[x]': [&str; 1] 42..43 'x': &str @@ -1326,6 +1328,12 @@ fn infer_array() { 259..262 '"b"': &str 274..275 'x': [u8; 0] 287..289 '[]': [u8; 0] + 368..369 'y': [u8; _] + 383..392 '[1,2,3,4]': [u8; 4] + 384..385 '1': u8 + 386..387 '2': u8 + 388..389 '3': u8 + 390..391 '4': u8 "#]], ); } 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(){ "#]], ) } + +#[test] +fn array_length() { + check_infer( + r#" +trait T { + type Output; + fn do_thing(&self) -> Self::Output; +} + +impl T for [u8; 4] { + type Output = usize; + fn do_thing(&self) -> Self::Output { + 2 + } +} + +impl T for [u8; 2] { + type Output = u8; + fn do_thing(&self) -> Self::Output { + 2 + } +} + +fn main() { + let v = [0u8; 2]; + let v2 = v.do_thing(); + let v3 = [0u8; 4]; + let v4 = v3.do_thing(); +} +"#, + expect![[r#" + 44..48 'self': &Self + 133..137 'self': &[u8; 4] + 155..172 '{ ... }': usize + 165..166 '2': usize + 236..240 'self': &[u8; 2] + 258..275 '{ ... }': u8 + 268..269 '2': u8 + 289..392 '{ ...g(); }': () + 299..300 'v': [u8; 2] + 303..311 '[0u8; 2]': [u8; 2] + 304..307 '0u8': u8 + 309..310 '2': usize + 321..323 'v2': u8 + 326..327 'v': [u8; 2] + 326..338 'v.do_thing()': u8 + 348..350 'v3': [u8; 4] + 353..361 '[0u8; 4]': [u8; 4] + 354..357 '0u8': u8 + 359..360 '4': usize + 371..373 'v4': usize + 376..378 'v3': [u8; 4] + 376..389 'v3.do_thing()': usize + "#]], + ) +} + +// FIXME: We should infer the length of the returned array :) +#[test] +fn const_generics() { + check_infer( + r#" +trait T { + type Output; + fn do_thing(&self) -> Self::Output; +} + +impl T for [u8; L] { + type Output = [u8; L]; + fn do_thing(&self) -> Self::Output { + *self + } +} + +fn main() { + let v = [0u8; 2]; + let v2 = v.do_thing(); +} +"#, + expect![[r#" + 44..48 'self': &Self + 151..155 'self': &[u8; _] + 173..194 '{ ... }': [u8; _] + 183..188 '*self': [u8; _] + 184..188 'self': &[u8; _] + 208..260 '{ ...g(); }': () + 218..219 'v': [u8; 2] + 222..230 '[0u8; 2]': [u8; 2] + 223..226 '0u8': u8 + 228..229 '2': usize + 240..242 'v2': [u8; _] + 245..246 'v': [u8; 2] + 245..257 'v.do_thing()': [u8; _] + "#]], + ) +} 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() { ) } + /// https://github.com/rust-analyzer/rust-analyzer/issues/2922 + #[test] + fn regression_issue_2922() { + check_assist( + add_explicit_type, + r#" +fn main() { + let $0v = [0.0; 2]; +} +"#, + r#" +fn main() { + let v: [f64; 2] = [0.0; 2]; +} +"#, + ); + // note: this may break later if we add more consteval. it just needs to be something that our + // consteval engine doesn't understand + check_assist_not_applicable( + add_explicit_type, + r#" +fn main() { + let $0l = [0.0; 2+2]; +} +"#, + ); + } + #[test] fn default_generics_should_not_be_added() { check_assist( -- 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/src/lib.rs | 2 +- crates/hir_def/src/type_ref.rs | 8 ++++++++ crates/hir_ty/src/consteval.rs | 24 ++++++++---------------- crates/hir_ty/src/infer/expr.rs | 5 ++--- crates/hir_ty/src/lower.rs | 19 +++++++------------ 5 files changed, 26 insertions(+), 32 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index d7065ff2b..7e8a5bca3 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -53,7 +53,7 @@ use hir_def::{ use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; use hir_ty::{ autoderef, - consteval::ConstExtension, + consteval::ConstExt, could_unify, method_resolution::{self, def_crates, TyFingerprint}, primitive::UintTy, 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 { diff --git a/crates/hir_ty/src/consteval.rs b/crates/hir_ty/src/consteval.rs index a4a430f30..e3ceb3d62 100644 --- a/crates/hir_ty/src/consteval.rs +++ b/crates/hir_ty/src/consteval.rs @@ -11,12 +11,12 @@ use hir_def::{ use crate::{Const, ConstData, ConstValue, Interner, TyKind}; /// Extension trait for [`Const`] -pub trait ConstExtension { +pub trait ConstExt { /// Is a [`Const`] unknown? fn is_unknown(&self) -> bool; } -impl ConstExtension for Const { +impl ConstExt for Const { fn is_unknown(&self) -> bool { match self.data(&Interner).value { // interned Unknown @@ -35,20 +35,12 @@ impl ConstExtension for Const { } } -/// Extension trait for [`Expr`] -pub trait ExprEval { - /// Attempts to evaluate the expression as a target usize. - fn eval_usize(&self) -> Option; -} - -impl ExprEval for Expr { - // FIXME: support more than just evaluating literals - fn eval_usize(&self) -> Option { - match self { - Expr::Literal(Literal::Uint(v, None)) - | Expr::Literal(Literal::Uint(v, Some(BuiltinUint::Usize))) => (*v).try_into().ok(), - _ => None, - } +// FIXME: support more than just evaluating literals +pub fn eval_usize(expr: &Expr) -> Option { + match expr { + Expr::Literal(Literal::Uint(v, None)) + | Expr::Literal(Literal::Uint(v, Some(BuiltinUint::Usize))) => (*v).try_into().ok(), + _ => None, } } diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 04fc2f12b..b6b5a1b75 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -15,8 +15,7 @@ use stdx::always; use syntax::ast::RangeOp; use crate::{ - autoderef, - consteval::{self, ExprEval}, + autoderef, consteval, lower::lower_to_chalk_mutability, mapping::from_chalk, method_resolution, op, @@ -738,7 +737,7 @@ impl<'a> InferenceContext<'a> { ); let repeat_expr = &self.body.exprs[*repeat]; - repeat_expr.eval_usize() + consteval::eval_usize(repeat_expr) } }; diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index f7015e5ff..bd8bb6028 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs @@ -9,9 +9,7 @@ use std::cell::{Cell, RefCell}; use std::{iter, sync::Arc}; use base_db::CrateId; -use chalk_ir::{ - cast::Cast, fold::Shift, interner::HasInterner, Mutability, Safety, Scalar, UintTy, -}; +use chalk_ir::{cast::Cast, fold::Shift, interner::HasInterner, Mutability, Safety}; use hir_def::{ adt::StructKind, body::{Expander, LowerCtx}, @@ -31,16 +29,17 @@ use stdx::impl_from; use syntax::ast; use crate::{ + consteval, db::HirDatabase, mapping::ToChalk, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, utils::{ all_super_trait_refs, associated_type_by_name_including_super_traits, generics, Generics, }, - AliasEq, AliasTy, Binders, BoundVar, CallableSig, ConstData, ConstValue, DebruijnIndex, DynTy, - FnPointer, FnSig, FnSubst, ImplTraitId, Interner, OpaqueTy, PolyFnSig, ProjectionTy, - QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits, - Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, + AliasEq, AliasTy, Binders, BoundVar, CallableSig, DebruijnIndex, DynTy, FnPointer, FnSig, + FnSubst, ImplTraitId, Interner, OpaqueTy, PolyFnSig, ProjectionTy, QuantifiedWhereClause, + QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits, Substitution, + TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, }; #[derive(Debug)] @@ -176,11 +175,7 @@ impl<'a> TyLoweringContext<'a> { TypeRef::Array(inner, len) => { let inner_ty = self.lower_ty(inner); - let const_len = ConstData { - ty: TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner), - value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: *len }), - } - .intern(&Interner); + let const_len = consteval::usize_const(len.as_usize()); TyKind::Array(inner_ty, const_len).intern(&Interner) } -- cgit v1.2.3