Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #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
bors 2022-07-18
parent 667fd25 · parent 83177a7 · commit db6a85d
-rw-r--r--crates/hir-def/src/body.rs17
-rw-r--r--crates/hir-def/src/expr.rs4
-rw-r--r--crates/hir-def/src/type_ref.rs90
-rw-r--r--crates/hir-ty/src/consteval.rs29
-rw-r--r--crates/hir-ty/src/infer.rs2
-rw-r--r--crates/hir-ty/src/infer/expr.rs4
-rw-r--r--crates/hir-ty/src/infer/pat.rs15
-rw-r--r--crates/hir-ty/src/interner.rs7
-rw-r--r--crates/hir-ty/src/lower.rs4
-rw-r--r--crates/ide/src/hover.rs30
-rw-r--r--crates/ide/src/hover/tests.rs100
11 files changed, 217 insertions, 85 deletions
diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs
index 94210ab33f..33851d90a2 100644
--- a/crates/hir-def/src/body.rs
+++ b/crates/hir-def/src/body.rs
@@ -24,7 +24,7 @@ use syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
use crate::{
attr::{Attrs, RawAttrs},
db::DefDatabase,
- expr::{Expr, ExprId, Label, LabelId, Pat, PatId},
+ expr::{dummy_expr_id, Expr, ExprId, Label, LabelId, Pat, PatId},
item_scope::BuiltinShadowMode,
macro_id_to_def_id,
nameres::DefMap,
@@ -389,6 +389,21 @@ impl Body {
}
}
+impl Default for Body {
+ fn default() -> Self {
+ Self {
+ body_expr: dummy_expr_id(),
+ exprs: Default::default(),
+ pats: Default::default(),
+ or_pats: Default::default(),
+ labels: Default::default(),
+ params: Default::default(),
+ block_scopes: Default::default(),
+ _c: Default::default(),
+ }
+ }
+}
+
impl Index<ExprId> for Body {
type Output = Expr;
diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs
index 4295bacc46..a991365d6b 100644
--- a/crates/hir-def/src/expr.rs
+++ b/crates/hir-def/src/expr.rs
@@ -26,8 +26,10 @@ use crate::{
pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
pub type ExprId = Idx<Expr>;
+
+/// FIXME: this is a hacky function which should be removed
pub(crate) fn dummy_expr_id() -> ExprId {
- ExprId::from_raw(RawIdx::from(!0))
+ ExprId::from_raw(RawIdx::from(u32::MAX))
}
pub type PatId = Idx<Pat>;
diff --git a/crates/hir-def/src/type_ref.rs b/crates/hir-def/src/type_ref.rs
index 81a2f024b3..867d82f45a 100644
--- a/crates/hir-def/src/type_ref.rs
+++ b/crates/hir-def/src/type_ref.rs
@@ -1,14 +1,21 @@
//! HIR for references to types. Paths in these are not yet resolved. They can
//! be directly created from an ast::TypeRef, without further queries.
+use std::fmt::Write;
+
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 {
@@ -178,7 +185,6 @@ impl TypeRef {
// `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());
-
TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
}
ast::Type::SliceType(inner) => {
@@ -403,22 +409,31 @@ impl ConstScalarOrPath {
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),
}
}
@@ -427,9 +442,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
@@ -439,21 +455,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_char('_'),
}
}
}
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 4b58262dac..0495a4e64c 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -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,
@@ -406,17 +395,14 @@ 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(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),
- TyBuilder::usize(),
- )
+pub fn usize_const(value: Option<u128>) -> Const {
+ intern_const_scalar(value.map_or(ConstScalar::Unknown, ConstScalar::UInt), TyBuilder::usize())
}
pub(crate) fn const_eval_recover(
@@ -463,7 +449,7 @@ 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,
@@ -471,7 +457,12 @@ pub(crate) fn eval_to_const<'a>(
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(const_scalar, TyBuilder::usize())
}
#[cfg(test)]
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index ecadfb6ce4..c7c8deaaee 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -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,
}
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 6dee0322a9..2f33467072 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -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));
@@ -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)
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index dc86f696d4..5e7320a5dd 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -11,6 +11,7 @@ use hir_def::{
use hir_expand::name::Name;
use crate::{
+ consteval::intern_const_scalar,
infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
lower::lower_to_chalk_mutability,
static_lifetime, ConcreteConst, ConstValue, Interner, Substitution, Ty, TyBuilder, TyExt,
@@ -262,13 +263,19 @@ impl<'a> InferenceContext<'a> {
if let &Some(slice_pat_id) = slice {
let rest_pat_ty = match expected.kind(Interner) {
TyKind::Array(_, length) => {
- let length = match length.data(Interner).value {
+ let len = match length.data(Interner).value {
ConstValue::Concrete(ConcreteConst {
- interned: ConstScalar::Usize(length),
- }) => length.checked_sub((prefix.len() + suffix.len()) as u64),
+ interned: ConstScalar::UInt(len),
+ }) => len.checked_sub((prefix.len() + suffix.len()) as u128),
_ => None,
};
- TyKind::Array(elem_ty.clone(), crate::consteval::usize_const(length))
+ TyKind::Array(
+ elem_ty.clone(),
+ intern_const_scalar(
+ len.map_or(ConstScalar::Unknown, |len| ConstScalar::UInt(len)),
+ TyBuilder::usize(),
+ ),
+ )
}
_ => TyKind::Slice(elem_ty.clone()),
}
diff --git a/crates/hir-ty/src/interner.rs b/crates/hir-ty/src/interner.rs
index 07197d8a19..c34c4b8a7c 100644
--- a/crates/hir-ty/src/interner.rs
+++ b/crates/hir-ty/src/interner.rs
@@ -257,12 +257,7 @@ impl chalk_ir::interner::Interner for Interner {
c1: &Self::InternedConcreteConst,
c2: &Self::InternedConcreteConst,
) -> bool {
- match (c1, c2) {
- (&ConstScalar::Usize(a), &ConstScalar::Usize(b)) => a == b,
- // we were previously assuming this to be true, I'm not whether true or false on
- // unknown values is safer.
- (_, _) => true,
- }
+ (c1 == &ConstScalar::Unknown) || (c2 == &ConstScalar::Unknown) || (c1 == c2)
}
fn intern_generic_arg(
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index c5304c1821..1605c932d6 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -44,7 +44,7 @@ use syntax::{ast, SmolStr};
use crate::{
all_super_traits,
- consteval::{intern_scalar_const, path_to_const, unknown_const, unknown_const_as_generic},
+ consteval::{intern_const_scalar, path_to_const, unknown_const, unknown_const_as_generic},
db::HirDatabase,
make_binders,
mapping::ToChalk,
@@ -1743,7 +1743,7 @@ pub(crate) fn const_or_path_to_chalk(
debruijn: DebruijnIndex,
) -> Const {
match value {
- ConstScalarOrPath::Scalar(s) => intern_scalar_const(s.clone(), expected_ty),
+ ConstScalarOrPath::Scalar(s) => intern_const_scalar(s.clone(), expected_ty),
ConstScalarOrPath::Path(n) => {
let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
path_to_const(db, resolver, &path, mode, args, debruijn)
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 3372d74af6..592fff322e 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -173,27 +173,17 @@ pub(crate) fn hover_for_definition(
Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(node)?.krate())),
_ => None,
};
- if let Some(markup) = render::definition(sema.db, definition, famous_defs.as_ref(), config) {
- let mut res = HoverResult::default();
- res.markup = render::process_markup(sema.db, definition, &markup, config);
- if let Some(action) = show_implementations_action(sema.db, definition) {
- res.actions.push(action);
+ render::definition(sema.db, definition, famous_defs.as_ref(), config).map(|markup| {
+ HoverResult {
+ markup: render::process_markup(sema.db, definition, &markup, config),
+ actions: show_implementations_action(sema.db, definition)
+ .into_iter()
+ .chain(show_fn_references_action(sema.db, definition))
+ .chain(runnable_action(sema, definition, file_id))
+ .chain(goto_type_action_for_def(sema.db, definition))
+ .collect(),
}
-
- if let Some(action) = show_fn_references_action(sema.db, definition) {
- res.actions.push(action);
- }
-
- if let Some(action) = runnable_action(sema, definition, file_id) {
- res.actions.push(action);
- }
-
- if let Some(action) = goto_type_action_for_def(sema.db, definition) {
- res.actions.push(action);
- }
- return Some(res);
- }
- None
+ })
}
fn hover_ranged(
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index a1484fa19f..867d1f54d4 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -2959,6 +2959,106 @@ struct S$0T<const C: usize = 1, T = Foo>(T);
}
#[test]
+fn const_generic_positive_i8_literal() {
+ check(
+ r#"
+struct Const<const N: i8>;
+
+fn main() {
+ let v$0alue = Const::<1>;
+}
+"#,
+ expect![[r#"
+ *value*
+
+ ```rust
+ let value: Const<1>
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn const_generic_zero_i8_literal() {
+ check(
+ r#"
+struct Const<const N: i8>;
+
+fn main() {
+ let v$0alue = Const::<0>;
+}
+"#,
+ expect![[r#"
+ *value*
+
+ ```rust
+ let value: Const<0>
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn const_generic_negative_i8_literal() {
+ check(
+ r#"
+struct Const<const N: i8>;
+
+fn main() {
+ let v$0alue = Const::<-1>;
+}
+"#,
+ expect![[r#"
+ *value*
+
+ ```rust
+ let value: Const<-1>
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn const_generic_bool_literal() {
+ check(
+ r#"
+struct Const<const F: bool>;
+
+fn main() {
+ let v$0alue = Const::<true>;
+}
+"#,
+ expect![[r#"
+ *value*
+
+ ```rust
+ let value: Const<true>
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn const_generic_char_literal() {
+ check(
+ r#"
+struct Const<const C: char>;
+
+fn main() {
+ let v$0alue = Const::<'🦀'>;
+}
+"#,
+ expect![[r#"
+ *value*
+
+ ```rust
+ let value: Const<'🦀'>
+ ```
+ "#]],
+ );
+}
+
+#[test]
fn hover_self_param_shows_type() {
check(
r#"