Skip to content

Commit db6a85d

Browse files
committed
Auto merge of rust-lang#12778 - Logarithmus:feature/fix-negative-const-generics, r=flodiebold
Support negative, `char` & `bool` const generics Before: ![Before](https://user-images.githubusercontent.com/29541480/179379832-0c3b2a74-fef6-427e-b89f-7e31d9c37b3d.png) After: ![After](https://user-images.githubusercontent.com/29541480/179379863-b62475dd-e7bf-41f2-b437-08dfe55951af.png) I tried to implement stuff like `Const<{NUM1 + 3 + NUM2}>` by using already existing constant evaluation mechanism for ordinary constants, but turned out to be harder than I thought, maybe because I've never ever tinkered with compilers before
2 parents 667fd25 + 83177a7 commit db6a85d

File tree

11 files changed

+217
-85
lines changed

11 files changed

+217
-85
lines changed

crates/hir-def/src/body.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
2424
use crate::{
2525
attr::{Attrs, RawAttrs},
2626
db::DefDatabase,
27-
expr::{Expr, ExprId, Label, LabelId, Pat, PatId},
27+
expr::{dummy_expr_id, Expr, ExprId, Label, LabelId, Pat, PatId},
2828
item_scope::BuiltinShadowMode,
2929
macro_id_to_def_id,
3030
nameres::DefMap,
@@ -389,6 +389,21 @@ impl Body {
389389
}
390390
}
391391

392+
impl Default for Body {
393+
fn default() -> Self {
394+
Self {
395+
body_expr: dummy_expr_id(),
396+
exprs: Default::default(),
397+
pats: Default::default(),
398+
or_pats: Default::default(),
399+
labels: Default::default(),
400+
params: Default::default(),
401+
block_scopes: Default::default(),
402+
_c: Default::default(),
403+
}
404+
}
405+
}
406+
392407
impl Index<ExprId> for Body {
393408
type Output = Expr;
394409

crates/hir-def/src/expr.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ use crate::{
2626
pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
2727

2828
pub type ExprId = Idx<Expr>;
29+
30+
/// FIXME: this is a hacky function which should be removed
2931
pub(crate) fn dummy_expr_id() -> ExprId {
30-
ExprId::from_raw(RawIdx::from(!0))
32+
ExprId::from_raw(RawIdx::from(u32::MAX))
3133
}
3234

3335
pub type PatId = Idx<Pat>;

crates/hir-def/src/type_ref.rs

Lines changed: 61 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
//! HIR for references to types. Paths in these are not yet resolved. They can
22
//! be directly created from an ast::TypeRef, without further queries.
33
4+
use std::fmt::Write;
5+
46
use hir_expand::{
57
name::{AsName, Name},
68
AstId, InFile,
79
};
8-
use std::{convert::TryInto, fmt::Write};
910
use syntax::ast::{self, HasName};
1011

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

1320
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
1421
pub enum Mutability {
@@ -178,7 +185,6 @@ impl TypeRef {
178185
// `hir_ty` level, which would allow knowing the type of:
179186
// let v: [u8; 2 + 2] = [0u8; 4];
180187
let len = ConstScalarOrPath::from_expr_opt(inner.expr());
181-
182188
TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
183189
}
184190
ast::Type::SliceType(inner) => {
@@ -403,22 +409,31 @@ impl ConstScalarOrPath {
403409
None => Self::Scalar(ConstScalar::Unknown),
404410
}
405411
}
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-
)
412+
ast::Expr::PrefixExpr(prefix_expr) => match prefix_expr.op_kind() {
413+
Some(ast::UnaryOp::Neg) => {
414+
let unsigned = prefix_expr
415+
.expr()
416+
.map_or(Self::Scalar(ConstScalar::Unknown), Self::from_expr);
417+
// Add sign
418+
match unsigned {
419+
Self::Scalar(ConstScalar::UInt(num)) => {
420+
Self::Scalar(ConstScalar::Int(-(num as i128)))
421+
}
422+
other => other,
418423
}
419-
_ => Self::Scalar(ConstScalar::Unknown),
420424
}
421-
}
425+
_ => prefix_expr.expr().map_or(Self::Scalar(ConstScalar::Unknown), Self::from_expr),
426+
},
427+
ast::Expr::Literal(literal) => Self::Scalar(match literal.kind() {
428+
ast::LiteralKind::IntNumber(num) => {
429+
num.value().map(ConstScalar::UInt).unwrap_or(ConstScalar::Unknown)
430+
}
431+
ast::LiteralKind::Char(c) => {
432+
c.value().map(ConstScalar::Char).unwrap_or(ConstScalar::Unknown)
433+
}
434+
ast::LiteralKind::Bool(f) => ConstScalar::Bool(f),
435+
_ => ConstScalar::Unknown,
436+
}),
422437
_ => Self::Scalar(ConstScalar::Unknown),
423438
}
424439
}
@@ -427,9 +442,10 @@ impl ConstScalarOrPath {
427442
/// A concrete constant value
428443
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
429444
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),
445+
Int(i128),
446+
UInt(u128),
447+
Bool(bool),
448+
Char(char),
433449

434450
/// Case of an unknown value that rustc might know but we don't
435451
// FIXME: this is a hack to get around chalk not being able to represent unevaluatable
@@ -439,21 +455,37 @@ pub enum ConstScalar {
439455
Unknown,
440456
}
441457

442-
impl std::fmt::Display for ConstScalar {
443-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
458+
impl ConstScalar {
459+
pub fn builtin_type(&self) -> BuiltinType {
444460
match self {
445-
ConstScalar::Usize(us) => us.fmt(f),
446-
ConstScalar::Unknown => f.write_char('_'),
461+
ConstScalar::UInt(_) | ConstScalar::Unknown => BuiltinType::Uint(BuiltinUint::U128),
462+
ConstScalar::Int(_) => BuiltinType::Int(BuiltinInt::I128),
463+
ConstScalar::Char(_) => BuiltinType::Char,
464+
ConstScalar::Bool(_) => BuiltinType::Bool,
447465
}
448466
}
449467
}
450468

451-
impl ConstScalar {
452-
/// Gets a target usize out of the ConstScalar
453-
pub fn as_usize(&self) -> Option<u64> {
469+
impl From<Literal> for ConstScalar {
470+
fn from(literal: Literal) -> Self {
471+
match literal {
472+
Literal::Char(c) => Self::Char(c),
473+
Literal::Bool(flag) => Self::Bool(flag),
474+
Literal::Int(num, _) => Self::Int(num),
475+
Literal::Uint(num, _) => Self::UInt(num),
476+
_ => Self::Unknown,
477+
}
478+
}
479+
}
480+
481+
impl std::fmt::Display for ConstScalar {
482+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
454483
match self {
455-
&ConstScalar::Usize(us) => Some(us),
456-
_ => None,
484+
ConstScalar::Int(num) => num.fmt(f),
485+
ConstScalar::UInt(num) => num.fmt(f),
486+
ConstScalar::Bool(flag) => flag.fmt(f),
487+
ConstScalar::Char(c) => write!(f, "'{c}'"),
488+
ConstScalar::Unknown => f.write_char('_'),
457489
}
458490
}
459491
}

crates/hir-ty/src/consteval.rs

Lines changed: 10 additions & 19 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,17 +395,14 @@ 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(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),
418-
TyBuilder::usize(),
419-
)
404+
pub fn usize_const(value: Option<u128>) -> Const {
405+
intern_const_scalar(value.map_or(ConstScalar::Unknown, ConstScalar::UInt), TyBuilder::usize())
420406
}
421407

422408
pub(crate) fn const_eval_recover(
@@ -463,15 +449,20 @@ pub(crate) fn eval_to_const<'a>(
463449
}
464450
}
465451
let body = ctx.body.clone();
466-
let ctx = ConstEvalCtx {
452+
let mut ctx = ConstEvalCtx {
467453
db: ctx.db,
468454
owner: ctx.owner,
469455
exprs: &body.exprs,
470456
pats: &body.pats,
471457
local_data: HashMap::default(),
472458
infer: &ctx.result,
473459
};
474-
usize_const(eval_usize(expr, ctx))
460+
let computed_expr = eval_const(expr, &mut ctx);
461+
let const_scalar = match computed_expr {
462+
Ok(ComputedExpr::Literal(literal)) => literal.into(),
463+
_ => ConstScalar::Unknown,
464+
};
465+
intern_const_scalar(const_scalar, TyBuilder::usize())
475466
}
476467

477468
#[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)

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use hir_def::{
1111
use hir_expand::name::Name;
1212

1313
use crate::{
14+
consteval::intern_const_scalar,
1415
infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
1516
lower::lower_to_chalk_mutability,
1617
static_lifetime, ConcreteConst, ConstValue, Interner, Substitution, Ty, TyBuilder, TyExt,
@@ -262,13 +263,19 @@ impl<'a> InferenceContext<'a> {
262263
if let &Some(slice_pat_id) = slice {
263264
let rest_pat_ty = match expected.kind(Interner) {
264265
TyKind::Array(_, length) => {
265-
let length = match length.data(Interner).value {
266+
let len = match length.data(Interner).value {
266267
ConstValue::Concrete(ConcreteConst {
267-
interned: ConstScalar::Usize(length),
268-
}) => length.checked_sub((prefix.len() + suffix.len()) as u64),
268+
interned: ConstScalar::UInt(len),
269+
}) => len.checked_sub((prefix.len() + suffix.len()) as u128),
269270
_ => None,
270271
};
271-
TyKind::Array(elem_ty.clone(), crate::consteval::usize_const(length))
272+
TyKind::Array(
273+
elem_ty.clone(),
274+
intern_const_scalar(
275+
len.map_or(ConstScalar::Unknown, |len| ConstScalar::UInt(len)),
276+
TyBuilder::usize(),
277+
),
278+
)
272279
}
273280
_ => TyKind::Slice(elem_ty.clone()),
274281
}

crates/hir-ty/src/interner.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -257,12 +257,7 @@ impl chalk_ir::interner::Interner for Interner {
257257
c1: &Self::InternedConcreteConst,
258258
c2: &Self::InternedConcreteConst,
259259
) -> bool {
260-
match (c1, c2) {
261-
(&ConstScalar::Usize(a), &ConstScalar::Usize(b)) => a == b,
262-
// we were previously assuming this to be true, I'm not whether true or false on
263-
// unknown values is safer.
264-
(_, _) => true,
265-
}
260+
(c1 == &ConstScalar::Unknown) || (c2 == &ConstScalar::Unknown) || (c1 == c2)
266261
}
267262

268263
fn intern_generic_arg(

crates/hir-ty/src/lower.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ use syntax::{ast, SmolStr};
4444

4545
use crate::{
4646
all_super_traits,
47-
consteval::{intern_scalar_const, path_to_const, unknown_const, unknown_const_as_generic},
47+
consteval::{intern_const_scalar, path_to_const, unknown_const, unknown_const_as_generic},
4848
db::HirDatabase,
4949
make_binders,
5050
mapping::ToChalk,
@@ -1743,7 +1743,7 @@ pub(crate) fn const_or_path_to_chalk(
17431743
debruijn: DebruijnIndex,
17441744
) -> Const {
17451745
match value {
1746-
ConstScalarOrPath::Scalar(s) => intern_scalar_const(s.clone(), expected_ty),
1746+
ConstScalarOrPath::Scalar(s) => intern_const_scalar(s.clone(), expected_ty),
17471747
ConstScalarOrPath::Path(n) => {
17481748
let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
17491749
path_to_const(db, resolver, &path, mode, args, debruijn)

crates/ide/src/hover.rs

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -173,27 +173,17 @@ pub(crate) fn hover_for_definition(
173173
Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(node)?.krate())),
174174
_ => None,
175175
};
176-
if let Some(markup) = render::definition(sema.db, definition, famous_defs.as_ref(), config) {
177-
let mut res = HoverResult::default();
178-
res.markup = render::process_markup(sema.db, definition, &markup, config);
179-
if let Some(action) = show_implementations_action(sema.db, definition) {
180-
res.actions.push(action);
176+
render::definition(sema.db, definition, famous_defs.as_ref(), config).map(|markup| {
177+
HoverResult {
178+
markup: render::process_markup(sema.db, definition, &markup, config),
179+
actions: show_implementations_action(sema.db, definition)
180+
.into_iter()
181+
.chain(show_fn_references_action(sema.db, definition))
182+
.chain(runnable_action(sema, definition, file_id))
183+
.chain(goto_type_action_for_def(sema.db, definition))
184+
.collect(),
181185
}
182-
183-
if let Some(action) = show_fn_references_action(sema.db, definition) {
184-
res.actions.push(action);
185-
}
186-
187-
if let Some(action) = runnable_action(sema, definition, file_id) {
188-
res.actions.push(action);
189-
}
190-
191-
if let Some(action) = goto_type_action_for_def(sema.db, definition) {
192-
res.actions.push(action);
193-
}
194-
return Some(res);
195-
}
196-
None
186+
})
197187
}
198188

199189
fn hover_ranged(

0 commit comments

Comments
 (0)