Skip to content

Commit a96f0aa

Browse files
committed
feat: support negative const generic parameters
* feat: support `bool` & `char` const generics
1 parent b9b42e8 commit a96f0aa

File tree

15 files changed

+152
-125
lines changed

15 files changed

+152
-125
lines changed

crates/hir-def/src/body.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ pub struct Mark {
238238
}
239239

240240
/// The body of an item (function, const etc.).
241-
#[derive(Debug, Eq, PartialEq)]
241+
#[derive(Debug, Default, Eq, PartialEq)]
242242
pub struct Body {
243243
pub exprs: Arena<Expr>,
244244
pub pats: Arena<Pat>,

crates/hir-def/src/body/lower.rs

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
22
//! representation.
33
4-
use std::{mem, sync::Arc};
4+
use std::{collections::HashMap, mem, sync::Arc};
55

66
use either::Either;
77
use hir_expand::{
@@ -10,8 +10,6 @@ use hir_expand::{
1010
name::{name, AsName, Name},
1111
ExpandError, HirFileId, InFile,
1212
};
13-
use la_arena::Arena;
14-
use profile::Count;
1513
use rustc_hash::FxHashMap;
1614
use syntax::{
1715
ast::{
@@ -28,8 +26,8 @@ use crate::{
2826
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
2927
db::DefDatabase,
3028
expr::{
31-
dummy_expr_id, Array, BindingAnnotation, Expr, ExprId, FloatTypeWrapper, Label, LabelId,
32-
Literal, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
29+
Array, BindingAnnotation, Expr, ExprId, FloatTypeWrapper, Label, LabelId, Literal,
30+
MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
3331
},
3432
intern::Interned,
3533
item_scope::BuiltinShadowMode,
@@ -82,24 +80,8 @@ pub(super) fn lower(
8280
params: Option<ast::ParamList>,
8381
body: Option<ast::Expr>,
8482
) -> (Body, BodySourceMap) {
85-
ExprCollector {
86-
db,
87-
source_map: BodySourceMap::default(),
88-
body: Body {
89-
exprs: Arena::default(),
90-
pats: Arena::default(),
91-
labels: Arena::default(),
92-
params: Vec::new(),
93-
body_expr: dummy_expr_id(),
94-
block_scopes: Vec::new(),
95-
_c: Count::new(),
96-
or_pats: Default::default(),
97-
},
98-
expander,
99-
name_to_pat_grouping: Default::default(),
100-
is_lowering_inside_or_pat: false,
101-
}
102-
.collect(params, body)
83+
let collector = ExprCollector::new(db, expander);
84+
collector.collect(params, body)
10385
}
10486

10587
struct ExprCollector<'a> {
@@ -112,7 +94,18 @@ struct ExprCollector<'a> {
11294
is_lowering_inside_or_pat: bool,
11395
}
11496

115-
impl ExprCollector<'_> {
97+
impl<'a> ExprCollector<'a> {
98+
pub(crate) fn new(db: &'a dyn DefDatabase, expander: Expander) -> Self {
99+
Self {
100+
db,
101+
expander,
102+
body: Body::default(),
103+
source_map: BodySourceMap::default(),
104+
name_to_pat_grouping: HashMap::default(),
105+
is_lowering_inside_or_pat: false,
106+
}
107+
}
108+
116109
fn collect(
117110
mut self,
118111
param_list: Option<ast::ParamList>,
@@ -197,7 +190,8 @@ impl ExprCollector<'_> {
197190
}
198191

199192
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
200-
self.maybe_collect_expr(expr).unwrap_or_else(|| self.missing_expr())
193+
let expr_id = self.maybe_collect_expr(expr).unwrap_or_else(|| self.missing_expr());
194+
expr_id
201195
}
202196

203197
/// Returns `None` if and only if the expression is `#[cfg]`d out.
@@ -689,7 +683,6 @@ impl ExprCollector<'_> {
689683
};
690684
let prev_def_map = mem::replace(&mut self.expander.def_map, def_map);
691685
let prev_local_module = mem::replace(&mut self.expander.module, module);
692-
693686
let mut statements: Vec<_> =
694687
block.statements().filter_map(|s| self.collect_stmt(s)).collect();
695688
let tail = block.tail_expr().and_then(|e| self.maybe_collect_expr(e));

crates/hir-def/src/expr.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
//! See also a neighboring `body` module.
1414
1515
use hir_expand::name::Name;
16-
use la_arena::{Idx, RawIdx};
16+
use la_arena::Idx;
1717

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

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

3330
pub type PatId = Idx<Pat>;
3431

crates/hir-def/src/path/lower.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
//! Transforms syntax into `Path` objects, ideally with accounting for hygiene
22
3-
use crate::{intern::Interned, type_ref::ConstScalarOrPath};
3+
use crate::{
4+
intern::Interned,
5+
type_ref::{ConstScalar, ConstScalarOrPath},
6+
};
47

58
use either::Either;
69
use hir_expand::name::{name, AsName};
@@ -181,7 +184,10 @@ pub(super) fn lower_generic_args(
181184
}
182185
}
183186
ast::GenericArg::ConstArg(arg) => {
184-
let arg = ConstScalarOrPath::from_expr_opt(arg.expr());
187+
let arg = arg.expr().map_or(
188+
ConstScalarOrPath::Scalar(ConstScalar::Unknown),
189+
ConstScalarOrPath::from_expr,
190+
);
185191
args.push(GenericArg::Const(arg))
186192
}
187193
}

crates/hir-def/src/type_ref.rs

Lines changed: 64 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ use hir_expand::{
55
name::{AsName, Name},
66
AstId, InFile,
77
};
8-
use std::{convert::TryInto, fmt::Write};
98
use syntax::ast::{self, HasName};
109

11-
use crate::{body::LowerCtx, intern::Interned, path::Path};
10+
use crate::{
11+
body::LowerCtx,
12+
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
13+
expr::Literal,
14+
intern::Interned,
15+
path::Path,
16+
};
1217

1318
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
1419
pub enum Mutability {
@@ -177,7 +182,10 @@ impl TypeRef {
177182
// `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
178183
// `hir_ty` level, which would allow knowing the type of:
179184
// let v: [u8; 2 + 2] = [0u8; 4];
180-
let len = ConstScalarOrPath::from_expr_opt(inner.expr());
185+
let len = inner.expr().map_or(
186+
ConstScalarOrPath::Scalar(ConstScalar::Unknown),
187+
ConstScalarOrPath::from_expr,
188+
);
181189

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

388396
impl ConstScalarOrPath {
389-
pub(crate) fn from_expr_opt(expr: Option<ast::Expr>) -> Self {
390-
match expr {
391-
Some(x) => Self::from_expr(x),
392-
None => Self::Scalar(ConstScalar::Unknown),
393-
}
394-
}
395-
396397
// FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
397398
// parse stage.
398-
fn from_expr(expr: ast::Expr) -> Self {
399+
pub(crate) fn from_expr(expr: ast::Expr) -> Self {
399400
match expr {
400401
ast::Expr::PathExpr(p) => {
401402
match p.path().and_then(|x| x.segment()).and_then(|x| x.name_ref()) {
402403
Some(x) => Self::Path(x.as_name()),
403404
None => Self::Scalar(ConstScalar::Unknown),
404405
}
405406
}
406-
ast::Expr::Literal(lit) => {
407-
let lkind = lit.kind();
408-
match lkind {
409-
ast::LiteralKind::IntNumber(num)
410-
if num.suffix() == None || num.suffix() == Some("usize") =>
411-
{
412-
Self::Scalar(
413-
num.value()
414-
.and_then(|v| v.try_into().ok())
415-
.map(ConstScalar::Usize)
416-
.unwrap_or(ConstScalar::Unknown),
417-
)
407+
ast::Expr::PrefixExpr(prefix_expr) => match prefix_expr.op_kind() {
408+
Some(ast::UnaryOp::Neg) => {
409+
let unsigned = prefix_expr
410+
.expr()
411+
.map_or(Self::Scalar(ConstScalar::Unknown), Self::from_expr);
412+
// Add sign
413+
match unsigned {
414+
Self::Scalar(ConstScalar::UInt(num)) => {
415+
Self::Scalar(ConstScalar::Int(-(num as i128)))
416+
}
417+
other => other,
418418
}
419-
_ => Self::Scalar(ConstScalar::Unknown),
420419
}
421-
}
420+
_ => prefix_expr.expr().map_or(Self::Scalar(ConstScalar::Unknown), Self::from_expr),
421+
},
422+
ast::Expr::Literal(literal) => Self::Scalar(match literal.kind() {
423+
ast::LiteralKind::IntNumber(num) => {
424+
num.value().map(ConstScalar::UInt).unwrap_or(ConstScalar::Unknown)
425+
}
426+
ast::LiteralKind::Char(c) => {
427+
c.value().map(ConstScalar::Char).unwrap_or(ConstScalar::Unknown)
428+
}
429+
ast::LiteralKind::Bool(f) => ConstScalar::Bool(f),
430+
_ => ConstScalar::Unknown,
431+
}),
422432
_ => Self::Scalar(ConstScalar::Unknown),
423433
}
424434
}
@@ -427,9 +437,10 @@ impl ConstScalarOrPath {
427437
/// A concrete constant value
428438
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
429439
pub enum ConstScalar {
430-
// for now, we only support the trivial case of constant evaluating the length of an array
431-
// Note that this is u64 because the target usize may be bigger than our usize
432-
Usize(u64),
440+
Int(i128),
441+
UInt(u128),
442+
Bool(bool),
443+
Char(char),
433444

434445
/// Case of an unknown value that rustc might know but we don't
435446
// FIXME: this is a hack to get around chalk not being able to represent unevaluatable
@@ -439,21 +450,37 @@ pub enum ConstScalar {
439450
Unknown,
440451
}
441452

442-
impl std::fmt::Display for ConstScalar {
443-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
453+
impl ConstScalar {
454+
pub fn builtin_type(&self) -> BuiltinType {
444455
match self {
445-
ConstScalar::Usize(us) => us.fmt(f),
446-
ConstScalar::Unknown => f.write_char('_'),
456+
ConstScalar::UInt(_) | ConstScalar::Unknown => BuiltinType::Uint(BuiltinUint::U128),
457+
ConstScalar::Int(_) => BuiltinType::Int(BuiltinInt::I128),
458+
ConstScalar::Char(_) => BuiltinType::Char,
459+
ConstScalar::Bool(_) => BuiltinType::Bool,
447460
}
448461
}
449462
}
450463

451-
impl ConstScalar {
452-
/// Gets a target usize out of the ConstScalar
453-
pub fn as_usize(&self) -> Option<u64> {
464+
impl From<Literal> for ConstScalar {
465+
fn from(literal: Literal) -> Self {
466+
match literal {
467+
Literal::Char(c) => Self::Char(c),
468+
Literal::Bool(flag) => Self::Bool(flag),
469+
Literal::Int(num, _) => Self::Int(num),
470+
Literal::Uint(num, _) => Self::UInt(num),
471+
_ => Self::Unknown,
472+
}
473+
}
474+
}
475+
476+
impl std::fmt::Display for ConstScalar {
477+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
454478
match self {
455-
&ConstScalar::Usize(us) => Some(us),
456-
_ => None,
479+
ConstScalar::Int(num) => num.fmt(f),
480+
ConstScalar::UInt(num) => num.fmt(f),
481+
ConstScalar::Bool(flag) => flag.fmt(f),
482+
ConstScalar::Char(c) => write!(f, "'{c}'"),
483+
ConstScalar::Unknown => f.write_str("{unknown}"),
457484
}
458485
}
459486
}

crates/hir-ty/src/consteval.rs

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -347,17 +347,6 @@ pub fn eval_const(
347347
}
348348
}
349349

350-
pub fn eval_usize(expr: Idx<Expr>, mut ctx: ConstEvalCtx<'_>) -> Option<u64> {
351-
if let Ok(ce) = eval_const(expr, &mut ctx) {
352-
match ce {
353-
ComputedExpr::Literal(Literal::Int(x, _)) => return x.try_into().ok(),
354-
ComputedExpr::Literal(Literal::Uint(x, _)) => return x.try_into().ok(),
355-
_ => {}
356-
}
357-
}
358-
None
359-
}
360-
361350
pub(crate) fn path_to_const(
362351
db: &dyn HirDatabase,
363352
resolver: &Resolver,
@@ -406,19 +395,24 @@ pub fn unknown_const_as_generic(ty: Ty) -> GenericArg {
406395
}
407396

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

414403
/// Interns a possibly-unknown target usize
415-
pub fn usize_const(value: Option<u64>) -> Const {
416-
intern_scalar_const(
417-
value.map(ConstScalar::Usize).unwrap_or(ConstScalar::Unknown),
404+
pub fn usize_const(value: Option<u128>) -> Const {
405+
intern_const_scalar_with_type(
406+
value.map(ConstScalar::UInt).unwrap_or(ConstScalar::Unknown),
418407
TyBuilder::usize(),
419408
)
420409
}
421410

411+
/// Interns a constant scalar with the default type
412+
pub fn intern_const_scalar(value: ConstScalar) -> Const {
413+
intern_const_scalar_with_type(value, TyBuilder::builtin(value.builtin_type()))
414+
}
415+
422416
pub(crate) fn const_eval_recover(
423417
_: &dyn HirDatabase,
424418
_: &[String],
@@ -463,15 +457,20 @@ pub(crate) fn eval_to_const<'a>(
463457
}
464458
}
465459
let body = ctx.body.clone();
466-
let ctx = ConstEvalCtx {
460+
let mut ctx = ConstEvalCtx {
467461
db: ctx.db,
468462
owner: ctx.owner,
469463
exprs: &body.exprs,
470464
pats: &body.pats,
471465
local_data: HashMap::default(),
472466
infer: &ctx.result,
473467
};
474-
usize_const(eval_usize(expr, ctx))
468+
let computed_expr = eval_const(expr, &mut ctx);
469+
let const_scalar = match computed_expr {
470+
Ok(ComputedExpr::Literal(literal)) => literal.into(),
471+
_ => ConstScalar::Unknown,
472+
};
473+
intern_const_scalar_with_type(const_scalar, TyBuilder::usize())
475474
}
476475

477476
#[cfg(test)]

crates/hir-ty/src/infer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,10 +605,10 @@ impl<'a> InferenceContext<'a> {
605605
let data = c.data(Interner);
606606
match data.value {
607607
ConstValue::Concrete(cc) => match cc.interned {
608-
hir_def::type_ref::ConstScalar::Usize(_) => c,
609608
hir_def::type_ref::ConstScalar::Unknown => {
610609
self.table.new_const_var(data.ty.clone())
611610
}
611+
_ => c,
612612
},
613613
_ => c,
614614
}

crates/hir-ty/src/infer/expr.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -729,7 +729,7 @@ impl<'a> InferenceContext<'a> {
729729
let cur_elem_ty = self.infer_expr_inner(expr, &expected);
730730
coerce.coerce(self, Some(expr), &cur_elem_ty);
731731
}
732-
consteval::usize_const(Some(items.len() as u64))
732+
consteval::usize_const(Some(items.len() as u128))
733733
}
734734
&Array::Repeat { initializer, repeat } => {
735735
self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty));
@@ -766,7 +766,7 @@ impl<'a> InferenceContext<'a> {
766766
Literal::ByteString(bs) => {
767767
let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner);
768768

769-
let len = consteval::usize_const(Some(bs.len() as u64));
769+
let len = consteval::usize_const(Some(bs.len() as u128));
770770

771771
let array_type = TyKind::Array(byte_type, len).intern(Interner);
772772
TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(Interner)

0 commit comments

Comments
 (0)