Skip to content

Commit

Permalink
Merge #8813
Browse files Browse the repository at this point in the history
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 <software@lfcode.ca>
  • Loading branch information
bors[bot] and lf- authored May 16, 2021
2 parents 92abc56 + de0ed98 commit a57bd59
Show file tree
Hide file tree
Showing 21 changed files with 427 additions and 136 deletions.
5 changes: 4 additions & 1 deletion crates/hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ use hir_def::{
};
use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind};
use hir_ty::{
autoderef, could_unify,
autoderef,
consteval::ConstExt,
could_unify,
method_resolution::{self, def_crates, TyFingerprint},
primitive::UintTy,
subst_prefix,
Expand Down Expand Up @@ -1914,6 +1916,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)
Expand Down
10 changes: 7 additions & 3 deletions crates/hir_def/src/body/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1006,23 +1006,27 @@ impl From<ast::BinOp> for BinaryOp {
impl From<ast::LiteralKind> 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) => {
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),
Expand Down
4 changes: 2 additions & 2 deletions crates/hir_def/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ pub enum Literal {
ByteString(Vec<u8>),
Char(char),
Bool(bool),
Int(u64, Option<BuiltinInt>),
Uint(u64, Option<BuiltinUint>),
Int(i128, Option<BuiltinInt>),
Uint(u128, Option<BuiltinUint>),
Float(u64, Option<BuiltinFloat>), // FIXME: f64 is not Eq
}

Expand Down
73 changes: 70 additions & 3 deletions crates/hir_def/src/type_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -79,7 +80,9 @@ pub enum TypeRef {
Path(Path),
RawPtr(Box<TypeRef>, Mutability),
Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
Array(Box<TypeRef> /*, Expr*/),
// 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<TypeRef>, ConstScalar),
Slice(Box<TypeRef>),
/// A fn pointer. Last element of the vector is the return type.
Fn(Vec<TypeRef>, bool /*varargs*/),
Expand Down Expand Up @@ -140,7 +143,16 @@ impl TypeRef {
TypeRef::RawPtr(Box::new(inner_ty), mutability)
}
ast::Type::ArrayType(inner) => {
TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())))
// 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)
.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())))
Expand Down Expand Up @@ -212,7 +224,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 {
Expand Down Expand Up @@ -298,3 +310,58 @@ 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
// 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,
}

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 {
/// Gets a target usize out of the ConstScalar
pub fn as_usize(&self) -> Option<u64> {
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 {
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)
}
}
56 changes: 56 additions & 0 deletions crates/hir_ty/src/consteval.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//! 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 ConstExt {
/// Is a [`Const`] unknown?
fn is_unknown(&self) -> bool;
}

impl ConstExt 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
}
}
}
}

// FIXME: support more than just evaluating literals
pub fn eval_usize(expr: &Expr) -> Option<u64> {
match expr {
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<u64>) -> 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)
}
21 changes: 0 additions & 21 deletions crates/hir_ty/src/consts.rs

This file was deleted.

5 changes: 2 additions & 3 deletions crates/hir_ty/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, "[")?;
Expand Down
32 changes: 12 additions & 20 deletions crates/hir_ty/src/infer/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
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::{
expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
path::{GenericArg, GenericArgs},
Expand All @@ -15,17 +15,15 @@ use stdx::always;
use syntax::ast::RangeOp;

use crate::{
autoderef,
consts::ConstScalar,
dummy_usize_const,
autoderef, consteval,
lower::lower_to_chalk_mutability,
mapping::from_chalk,
method_resolution, op,
primitive::{self, UintTy},
static_lifetime, to_chalk_trait_id,
traits::FnTrait,
utils::{generics, Generics},
AdtId, Binders, CallableDefId, ConstValue, FnPointer, FnSig, FnSubst, InEnvironment, Interner,
AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner,
ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
};

Expand Down Expand Up @@ -724,7 +722,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(
Expand All @@ -737,32 +735,26 @@ impl<'a> InferenceContext<'a> {
TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner),
),
);
// FIXME: support length for Repeat array expressions
None

let repeat_expr = &self.body.exprs[*repeat];
consteval::eval_usize(repeat_expr)
}
};

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 as u64))
.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),
Literal::String(..) => {
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 = 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)
}
Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(&Interner),
Expand Down
3 changes: 2 additions & 1 deletion crates/hir_ty/src/interner.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
10 changes: 7 additions & 3 deletions crates/hir_ty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ mod autoderef;
mod builder;
mod chalk_db;
mod chalk_ext;
pub mod consteval;
mod infer;
mod interner;
mod consts;
mod lower;
mod mapping;
mod op;
Expand All @@ -38,9 +38,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;
Expand Down
Loading

0 comments on commit a57bd59

Please sign in to comment.