Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support negative, char & bool const generics #12778

Merged
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};
Logarithmus marked this conversation as resolved.
Show resolved Hide resolved

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(
Logarithmus marked this conversation as resolved.
Show resolved Hide resolved
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(
Logarithmus marked this conversation as resolved.
Show resolved Hide resolved
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
Logarithmus marked this conversation as resolved.
Show resolved Hide resolved
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