Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/hir-def/src/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ pub struct Mark {
}

/// The body of an item (function, const etc.).
#[derive(Debug, Eq, PartialEq)]
#[derive(Debug, Default, Eq, PartialEq)]
pub struct Body {
pub exprs: Arena<Expr>,
pub pats: Arena<Pat>,
Expand Down
45 changes: 19 additions & 26 deletions crates/hir-def/src/body/lower.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
//! representation.

use std::{mem, sync::Arc};
use std::{collections::HashMap, mem, sync::Arc};

use either::Either;
use hir_expand::{
Expand All @@ -10,8 +10,6 @@ use hir_expand::{
name::{name, AsName, Name},
ExpandError, HirFileId, InFile,
};
use la_arena::Arena;
use profile::Count;
use rustc_hash::FxHashMap;
use syntax::{
ast::{
Expand All @@ -28,8 +26,8 @@ use crate::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
db::DefDatabase,
expr::{
dummy_expr_id, Array, BindingAnnotation, Expr, ExprId, FloatTypeWrapper, Label, LabelId,
Literal, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
Array, BindingAnnotation, Expr, ExprId, FloatTypeWrapper, Label, LabelId, Literal,
MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
},
intern::Interned,
item_scope::BuiltinShadowMode,
Expand Down Expand Up @@ -82,24 +80,8 @@ pub(super) fn lower(
params: Option<ast::ParamList>,
body: Option<ast::Expr>,
) -> (Body, BodySourceMap) {
ExprCollector {
db,
source_map: BodySourceMap::default(),
body: Body {
exprs: Arena::default(),
pats: Arena::default(),
labels: Arena::default(),
params: Vec::new(),
body_expr: dummy_expr_id(),
block_scopes: Vec::new(),
_c: Count::new(),
or_pats: Default::default(),
},
expander,
name_to_pat_grouping: Default::default(),
is_lowering_inside_or_pat: false,
}
.collect(params, body)
let collector = ExprCollector::new(db, expander);
collector.collect(params, body)
}

struct ExprCollector<'a> {
Expand All @@ -112,7 +94,18 @@ struct ExprCollector<'a> {
is_lowering_inside_or_pat: bool,
}

impl ExprCollector<'_> {
impl<'a> ExprCollector<'a> {
pub(crate) fn new(db: &'a dyn DefDatabase, expander: Expander) -> Self {
Self {
db,
expander,
body: Body::default(),
source_map: BodySourceMap::default(),
name_to_pat_grouping: HashMap::default(),
is_lowering_inside_or_pat: false,
}
}

fn collect(
mut self,
param_list: Option<ast::ParamList>,
Expand Down Expand Up @@ -197,7 +190,8 @@ impl ExprCollector<'_> {
}

fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
self.maybe_collect_expr(expr).unwrap_or_else(|| self.missing_expr())
let expr_id = self.maybe_collect_expr(expr).unwrap_or_else(|| self.missing_expr());
expr_id
}

/// Returns `None` if and only if the expression is `#[cfg]`d out.
Expand Down Expand Up @@ -689,7 +683,6 @@ impl ExprCollector<'_> {
};
let prev_def_map = mem::replace(&mut self.expander.def_map, def_map);
let prev_local_module = mem::replace(&mut self.expander.module, module);

let mut statements: Vec<_> =
block.statements().filter_map(|s| self.collect_stmt(s)).collect();
let tail = block.tail_expr().and_then(|e| self.maybe_collect_expr(e));
Expand Down
5 changes: 1 addition & 4 deletions crates/hir-def/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//! See also a neighboring `body` module.

use hir_expand::name::Name;
use la_arena::{Idx, RawIdx};
use la_arena::Idx;

use crate::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
Expand All @@ -26,9 +26,6 @@ use crate::{
pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};

pub type ExprId = Idx<Expr>;
pub(crate) fn dummy_expr_id() -> ExprId {
ExprId::from_raw(RawIdx::from(!0))
}

pub type PatId = Idx<Pat>;

Expand Down
10 changes: 8 additions & 2 deletions crates/hir-def/src/path/lower.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Transforms syntax into `Path` objects, ideally with accounting for hygiene

use crate::{intern::Interned, type_ref::ConstScalarOrPath};
use crate::{
intern::Interned,
type_ref::{ConstScalar, ConstScalarOrPath},
};

use either::Either;
use hir_expand::name::{name, AsName};
Expand Down Expand Up @@ -181,7 +184,10 @@ pub(super) fn lower_generic_args(
}
}
ast::GenericArg::ConstArg(arg) => {
let arg = ConstScalarOrPath::from_expr_opt(arg.expr());
let arg = arg.expr().map_or(
ConstScalarOrPath::Scalar(ConstScalar::Unknown),
ConstScalarOrPath::from_expr,
);
args.push(GenericArg::Const(arg))
}
}
Expand Down
101 changes: 64 additions & 37 deletions crates/hir-def/src/type_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ use hir_expand::{
name::{AsName, Name},
AstId, InFile,
};
use std::{convert::TryInto, fmt::Write};
use syntax::ast::{self, HasName};

use crate::{body::LowerCtx, intern::Interned, path::Path};
use crate::{
body::LowerCtx,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
expr::Literal,
intern::Interned,
path::Path,
};

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum Mutability {
Expand Down Expand Up @@ -177,7 +182,10 @@ impl TypeRef {
// `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 = ConstScalarOrPath::from_expr_opt(inner.expr());
let len = inner.expr().map_or(
ConstScalarOrPath::Scalar(ConstScalar::Unknown),
ConstScalarOrPath::from_expr,
);

TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
}
Expand Down Expand Up @@ -386,39 +394,41 @@ impl std::fmt::Display for ConstScalarOrPath {
}

impl ConstScalarOrPath {
pub(crate) fn from_expr_opt(expr: Option<ast::Expr>) -> Self {
match expr {
Some(x) => Self::from_expr(x),
None => Self::Scalar(ConstScalar::Unknown),
}
}

// FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
// parse stage.
fn from_expr(expr: ast::Expr) -> Self {
pub(crate) fn from_expr(expr: ast::Expr) -> Self {
match expr {
ast::Expr::PathExpr(p) => {
match p.path().and_then(|x| x.segment()).and_then(|x| x.name_ref()) {
Some(x) => Self::Path(x.as_name()),
None => Self::Scalar(ConstScalar::Unknown),
}
}
ast::Expr::Literal(lit) => {
let lkind = lit.kind();
match lkind {
ast::LiteralKind::IntNumber(num)
if num.suffix() == None || num.suffix() == Some("usize") =>
{
Self::Scalar(
num.value()
.and_then(|v| v.try_into().ok())
.map(ConstScalar::Usize)
.unwrap_or(ConstScalar::Unknown),
)
ast::Expr::PrefixExpr(prefix_expr) => match prefix_expr.op_kind() {
Some(ast::UnaryOp::Neg) => {
let unsigned = prefix_expr
.expr()
.map_or(Self::Scalar(ConstScalar::Unknown), Self::from_expr);
// Add sign
match unsigned {
Self::Scalar(ConstScalar::UInt(num)) => {
Self::Scalar(ConstScalar::Int(-(num as i128)))
}
other => other,
}
_ => Self::Scalar(ConstScalar::Unknown),
}
}
_ => prefix_expr.expr().map_or(Self::Scalar(ConstScalar::Unknown), Self::from_expr),
},
ast::Expr::Literal(literal) => Self::Scalar(match literal.kind() {
ast::LiteralKind::IntNumber(num) => {
num.value().map(ConstScalar::UInt).unwrap_or(ConstScalar::Unknown)
}
ast::LiteralKind::Char(c) => {
c.value().map(ConstScalar::Char).unwrap_or(ConstScalar::Unknown)
}
ast::LiteralKind::Bool(f) => ConstScalar::Bool(f),
_ => ConstScalar::Unknown,
}),
_ => Self::Scalar(ConstScalar::Unknown),
}
}
Expand All @@ -427,9 +437,10 @@ impl ConstScalarOrPath {
/// 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),
Int(i128),
UInt(u128),
Bool(bool),
Char(char),

/// 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
Expand All @@ -439,21 +450,37 @@ pub enum ConstScalar {
Unknown,
}

impl std::fmt::Display for ConstScalar {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
impl ConstScalar {
pub fn builtin_type(&self) -> BuiltinType {
match self {
ConstScalar::Usize(us) => us.fmt(f),
ConstScalar::Unknown => f.write_char('_'),
ConstScalar::UInt(_) | ConstScalar::Unknown => BuiltinType::Uint(BuiltinUint::U128),
ConstScalar::Int(_) => BuiltinType::Int(BuiltinInt::I128),
ConstScalar::Char(_) => BuiltinType::Char,
ConstScalar::Bool(_) => BuiltinType::Bool,
}
}
}

impl ConstScalar {
/// Gets a target usize out of the ConstScalar
pub fn as_usize(&self) -> Option<u64> {
impl From<Literal> for ConstScalar {
fn from(literal: Literal) -> Self {
match literal {
Literal::Char(c) => Self::Char(c),
Literal::Bool(flag) => Self::Bool(flag),
Literal::Int(num, _) => Self::Int(num),
Literal::Uint(num, _) => Self::UInt(num),
_ => Self::Unknown,
}
}
}

impl std::fmt::Display for ConstScalar {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
&ConstScalar::Usize(us) => Some(us),
_ => None,
ConstScalar::Int(num) => num.fmt(f),
ConstScalar::UInt(num) => num.fmt(f),
ConstScalar::Bool(flag) => flag.fmt(f),
ConstScalar::Char(c) => write!(f, "'{c}'"),
ConstScalar::Unknown => f.write_str("{unknown}"),
}
}
}
33 changes: 16 additions & 17 deletions crates/hir-ty/src/consteval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,17 +347,6 @@ pub fn eval_const(
}
}

pub fn eval_usize(expr: Idx<Expr>, mut ctx: ConstEvalCtx<'_>) -> Option<u64> {
if let Ok(ce) = eval_const(expr, &mut ctx) {
match ce {
ComputedExpr::Literal(Literal::Int(x, _)) => return x.try_into().ok(),
ComputedExpr::Literal(Literal::Uint(x, _)) => return x.try_into().ok(),
_ => {}
}
}
None
}

pub(crate) fn path_to_const(
db: &dyn HirDatabase,
resolver: &Resolver,
Expand Down Expand Up @@ -406,19 +395,24 @@ pub fn unknown_const_as_generic(ty: Ty) -> GenericArg {
}

/// Interns a constant scalar with the given type
pub fn intern_scalar_const(value: ConstScalar, ty: Ty) -> Const {
pub fn intern_const_scalar_with_type(value: ConstScalar, ty: Ty) -> Const {
ConstData { ty, value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: value }) }
.intern(Interner)
}

/// Interns a possibly-unknown target usize
pub fn usize_const(value: Option<u64>) -> Const {
intern_scalar_const(
value.map(ConstScalar::Usize).unwrap_or(ConstScalar::Unknown),
pub fn usize_const(value: Option<u128>) -> Const {
intern_const_scalar_with_type(
value.map(ConstScalar::UInt).unwrap_or(ConstScalar::Unknown),
TyBuilder::usize(),
)
}

/// Interns a constant scalar with the default type
pub fn intern_const_scalar(value: ConstScalar) -> Const {
intern_const_scalar_with_type(value, TyBuilder::builtin(value.builtin_type()))
}

pub(crate) fn const_eval_recover(
_: &dyn HirDatabase,
_: &[String],
Expand Down Expand Up @@ -463,15 +457,20 @@ pub(crate) fn eval_to_const<'a>(
}
}
let body = ctx.body.clone();
let ctx = ConstEvalCtx {
let mut ctx = ConstEvalCtx {
db: ctx.db,
owner: ctx.owner,
exprs: &body.exprs,
pats: &body.pats,
local_data: HashMap::default(),
infer: &ctx.result,
};
usize_const(eval_usize(expr, ctx))
let computed_expr = eval_const(expr, &mut ctx);
let const_scalar = match computed_expr {
Ok(ComputedExpr::Literal(literal)) => literal.into(),
_ => ConstScalar::Unknown,
};
intern_const_scalar_with_type(const_scalar, TyBuilder::usize())
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion crates/hir-ty/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,10 +605,10 @@ impl<'a> InferenceContext<'a> {
let data = c.data(Interner);
match data.value {
ConstValue::Concrete(cc) => match cc.interned {
hir_def::type_ref::ConstScalar::Usize(_) => c,
hir_def::type_ref::ConstScalar::Unknown => {
self.table.new_const_var(data.ty.clone())
}
_ => c,
},
_ => c,
}
Expand Down
4 changes: 2 additions & 2 deletions crates/hir-ty/src/infer/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,7 @@ impl<'a> InferenceContext<'a> {
let cur_elem_ty = self.infer_expr_inner(expr, &expected);
coerce.coerce(self, Some(expr), &cur_elem_ty);
}
consteval::usize_const(Some(items.len() as u64))
consteval::usize_const(Some(items.len() as u128))
}
&Array::Repeat { initializer, repeat } => {
self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty));
Expand Down Expand Up @@ -766,7 +766,7 @@ impl<'a> InferenceContext<'a> {
Literal::ByteString(bs) => {
let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner);

let len = consteval::usize_const(Some(bs.len() as u64));
let len = consteval::usize_const(Some(bs.len() as u128));

let array_type = TyKind::Array(byte_type, len).intern(Interner);
TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(Interner)
Expand Down
Loading