Skip to content

Commit a57bd59

Browse files
bors[bot]lf-
andauthored
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]>
2 parents 92abc56 + de0ed98 commit a57bd59

File tree

21 files changed

+427
-136
lines changed

21 files changed

+427
-136
lines changed

crates/hir/src/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ use hir_def::{
5252
};
5353
use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind};
5454
use hir_ty::{
55-
autoderef, could_unify,
55+
autoderef,
56+
consteval::ConstExt,
57+
could_unify,
5658
method_resolution::{self, def_crates, TyFingerprint},
5759
primitive::UintTy,
5860
subst_prefix,
@@ -1914,6 +1916,7 @@ impl Type {
19141916
substs.iter(&Interner).filter_map(|a| a.ty(&Interner)).any(go)
19151917
}
19161918

1919+
TyKind::Array(_ty, len) if len.is_unknown() => true,
19171920
TyKind::Array(ty, _)
19181921
| TyKind::Slice(ty)
19191922
| TyKind::Raw(_, ty)

crates/hir_def/src/body/lower.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -1006,23 +1006,27 @@ impl From<ast::BinOp> for BinaryOp {
10061006
impl From<ast::LiteralKind> for Literal {
10071007
fn from(ast_lit_kind: ast::LiteralKind) -> Self {
10081008
match ast_lit_kind {
1009+
// FIXME: these should have actual values filled in, but unsure on perf impact
10091010
LiteralKind::IntNumber(lit) => {
10101011
if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
10111012
return Literal::Float(Default::default(), builtin);
10121013
} else if let builtin @ Some(_) =
10131014
lit.suffix().and_then(|it| BuiltinInt::from_suffix(&it))
10141015
{
1015-
Literal::Int(Default::default(), builtin)
1016+
Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
10161017
} else {
10171018
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)
10191020
}
10201021
}
10211022
LiteralKind::FloatNumber(lit) => {
10221023
let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(&it));
10231024
Literal::Float(Default::default(), ty)
10241025
}
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+
}
10261030
LiteralKind::String(_) => Literal::String(Default::default()),
10271031
LiteralKind::Byte => Literal::Uint(Default::default(), Some(BuiltinUint::U8)),
10281032
LiteralKind::Bool(val) => Literal::Bool(val),

crates/hir_def/src/expr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ pub enum Literal {
4343
ByteString(Vec<u8>),
4444
Char(char),
4545
Bool(bool),
46-
Int(u64, Option<BuiltinInt>),
47-
Uint(u64, Option<BuiltinUint>),
46+
Int(i128, Option<BuiltinInt>),
47+
Uint(u128, Option<BuiltinUint>),
4848
Float(u64, Option<BuiltinFloat>), // FIXME: f64 is not Eq
4949
}
5050

crates/hir_def/src/type_ref.rs

+70-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//! be directly created from an ast::TypeRef, without further queries.
33
44
use hir_expand::{name::Name, AstId, InFile};
5+
use std::convert::TryInto;
56
use syntax::ast;
67

78
use crate::{body::LowerCtx, path::Path};
@@ -79,7 +80,9 @@ pub enum TypeRef {
7980
Path(Path),
8081
RawPtr(Box<TypeRef>, Mutability),
8182
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),
8386
Slice(Box<TypeRef>),
8487
/// A fn pointer. Last element of the vector is the return type.
8588
Fn(Vec<TypeRef>, bool /*varargs*/),
@@ -140,7 +143,16 @@ impl TypeRef {
140143
TypeRef::RawPtr(Box::new(inner_ty), mutability)
141144
}
142145
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)
144156
}
145157
ast::Type::SliceType(inner) => {
146158
TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())))
@@ -212,7 +224,7 @@ impl TypeRef {
212224
}
213225
TypeRef::RawPtr(type_ref, _)
214226
| TypeRef::Reference(type_ref, ..)
215-
| TypeRef::Array(type_ref)
227+
| TypeRef::Array(type_ref, _)
216228
| TypeRef::Slice(type_ref) => go(&type_ref, f),
217229
TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
218230
for bound in bounds {
@@ -298,3 +310,58 @@ impl TypeBound {
298310
}
299311
}
300312
}
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+
}

crates/hir_ty/src/consteval.rs

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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 ConstExt {
15+
/// Is a [`Const`] unknown?
16+
fn is_unknown(&self) -> bool;
17+
}
18+
19+
impl ConstExt 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+
// FIXME: support more than just evaluating literals
39+
pub fn eval_usize(expr: &Expr) -> Option<u64> {
40+
match expr {
41+
Expr::Literal(Literal::Uint(v, None))
42+
| Expr::Literal(Literal::Uint(v, Some(BuiltinUint::Usize))) => (*v).try_into().ok(),
43+
_ => None,
44+
}
45+
}
46+
47+
/// Interns a possibly-unknown target usize
48+
pub fn usize_const(value: Option<u64>) -> Const {
49+
ConstData {
50+
ty: TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(&Interner),
51+
value: ConstValue::Concrete(chalk_ir::ConcreteConst {
52+
interned: value.map(|value| ConstScalar::Usize(value)).unwrap_or(ConstScalar::Unknown),
53+
}),
54+
}
55+
.intern(&Interner)
56+
}

crates/hir_ty/src/consts.rs

-21
This file was deleted.

crates/hir_ty/src/display.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -962,11 +962,10 @@ impl HirDisplay for TypeRef {
962962
write!(f, "{}", mutability)?;
963963
inner.hir_fmt(f)?;
964964
}
965-
TypeRef::Array(inner) => {
965+
TypeRef::Array(inner, len) => {
966966
write!(f, "[")?;
967967
inner.hir_fmt(f)?;
968-
// FIXME: Array length?
969-
write!(f, "; _]")?;
968+
write!(f, "; {}]", len)?;
970969
}
971970
TypeRef::Slice(inner) => {
972971
write!(f, "[")?;

crates/hir_ty/src/infer/expr.rs

+12-20
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use std::iter::{repeat, repeat_with};
44
use std::{mem, sync::Arc};
55

6-
use chalk_ir::{cast::Cast, fold::Shift, ConstData, Mutability, TyVariableKind};
6+
use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
77
use hir_def::{
88
expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
99
path::{GenericArg, GenericArgs},
@@ -15,17 +15,15 @@ use stdx::always;
1515
use syntax::ast::RangeOp;
1616

1717
use crate::{
18-
autoderef,
19-
consts::ConstScalar,
20-
dummy_usize_const,
18+
autoderef, consteval,
2119
lower::lower_to_chalk_mutability,
2220
mapping::from_chalk,
2321
method_resolution, op,
2422
primitive::{self, UintTy},
2523
static_lifetime, to_chalk_trait_id,
2624
traits::FnTrait,
2725
utils::{generics, Generics},
28-
AdtId, Binders, CallableDefId, ConstValue, FnPointer, FnSig, FnSubst, InEnvironment, Interner,
26+
AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner,
2927
ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
3028
};
3129

@@ -724,7 +722,7 @@ impl<'a> InferenceContext<'a> {
724722
for expr in items.iter() {
725723
self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone()));
726724
}
727-
Some(items.len())
725+
Some(items.len() as u64)
728726
}
729727
Array::Repeat { initializer, repeat } => {
730728
self.infer_expr_coerce(
@@ -737,32 +735,26 @@ impl<'a> InferenceContext<'a> {
737735
TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner),
738736
),
739737
);
740-
// FIXME: support length for Repeat array expressions
741-
None
738+
739+
let repeat_expr = &self.body.exprs[*repeat];
740+
consteval::eval_usize(repeat_expr)
742741
}
743742
};
744743

745-
let cd = ConstData {
746-
ty: TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner),
747-
value: ConstValue::Concrete(chalk_ir::ConcreteConst {
748-
interned: len
749-
.map(|len| ConstScalar::Usize(len as u64))
750-
.unwrap_or(ConstScalar::Unknown),
751-
}),
752-
};
753-
TyKind::Array(elem_ty, cd.intern(&Interner)).intern(&Interner)
744+
TyKind::Array(elem_ty, consteval::usize_const(len)).intern(&Interner)
754745
}
755746
Expr::Literal(lit) => match lit {
756747
Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner),
757748
Literal::String(..) => {
758749
TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(&Interner))
759750
.intern(&Interner)
760751
}
761-
Literal::ByteString(..) => {
752+
Literal::ByteString(bs) => {
762753
let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner);
763754

764-
let array_type =
765-
TyKind::Array(byte_type, dummy_usize_const()).intern(&Interner);
755+
let len = consteval::usize_const(Some(bs.len() as u64));
756+
757+
let array_type = TyKind::Array(byte_type, len).intern(&Interner);
766758
TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner)
767759
}
768760
Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(&Interner),

crates/hir_ty/src/interner.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
//! Implementation of the Chalk `Interner` trait, which allows customizing the
22
//! representation of the various objects Chalk deals with (types, goals etc.).
33
4-
use crate::{chalk_db, consts::ConstScalar, tls, GenericArg};
4+
use crate::{chalk_db, tls, GenericArg};
55
use base_db::salsa::InternId;
66
use chalk_ir::{Goal, GoalData};
77
use hir_def::{
88
intern::{impl_internable, InternStorage, Internable, Interned},
9+
type_ref::ConstScalar,
910
TypeAliasId,
1011
};
1112
use smallvec::SmallVec;

crates/hir_ty/src/lib.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ mod autoderef;
1010
mod builder;
1111
mod chalk_db;
1212
mod chalk_ext;
13+
pub mod consteval;
1314
mod infer;
1415
mod interner;
15-
mod consts;
1616
mod lower;
1717
mod mapping;
1818
mod op;
@@ -38,9 +38,13 @@ use chalk_ir::{
3838
interner::HasInterner,
3939
UintTy,
4040
};
41-
use hir_def::{expr::ExprId, type_ref::Rawness, TypeParamId};
41+
use hir_def::{
42+
expr::ExprId,
43+
type_ref::{ConstScalar, Rawness},
44+
TypeParamId,
45+
};
4246

43-
use crate::{consts::ConstScalar, db::HirDatabase, display::HirDisplay, utils::generics};
47+
use crate::{db::HirDatabase, display::HirDisplay, utils::generics};
4448

4549
pub use autoderef::autoderef;
4650
pub use builder::TyBuilder;

0 commit comments

Comments
 (0)