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 all 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
17 changes: 16 additions & 1 deletion crates/hir-def/src/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;

Expand Down
4 changes: 3 additions & 1 deletion crates/hir-def/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down
90 changes: 61 additions & 29 deletions crates/hir-def/src/type_ref.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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),
}
}
Expand All @@ -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
Expand All @@ -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('_'),
}
}
}
29 changes: 10 additions & 19 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,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(
Expand Down Expand Up @@ -463,15 +449,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(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
15 changes: 11 additions & 4 deletions crates/hir-ty/src/infer/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Logarithmus marked this conversation as resolved.
Show resolved Hide resolved
len.map_or(ConstScalar::Unknown, |len| ConstScalar::UInt(len)),
TyBuilder::usize(),
),
)
}
_ => TyKind::Slice(elem_ty.clone()),
}
Expand Down
7 changes: 1 addition & 6 deletions crates/hir-ty/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Logarithmus marked this conversation as resolved.
Show resolved Hide resolved
}
(c1 == &ConstScalar::Unknown) || (c2 == &ConstScalar::Unknown) || (c1 == c2)
}

fn intern_generic_arg(
Expand Down
4 changes: 2 additions & 2 deletions crates/hir-ty/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -1742,7 +1742,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)
Expand Down
30 changes: 10 additions & 20 deletions crates/ide/src/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Loading