mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 12:29:21 +00:00
feat: support negative const generic parameters
* feat: support `bool` & `char` const generics
This commit is contained in:
parent
b9b42e8670
commit
a96f0aa7cd
15 changed files with 153 additions and 126 deletions
|
@ -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>,
|
||||
|
|
|
@ -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};
|
||||
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
|
@ -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::{
|
||||
|
@ -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,
|
||||
|
@ -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> {
|
||||
|
@ -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>,
|
||||
|
@ -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.
|
||||
|
@ -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));
|
||||
|
|
|
@ -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},
|
||||
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
@ -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(
|
||||
ConstScalarOrPath::Scalar(ConstScalar::Unknown),
|
||||
ConstScalarOrPath::from_expr,
|
||||
);
|
||||
args.push(GenericArg::Const(arg))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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(
|
||||
ConstScalarOrPath::Scalar(ConstScalar::Unknown),
|
||||
ConstScalarOrPath::from_expr,
|
||||
);
|
||||
|
||||
TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
|
||||
}
|
||||
|
@ -386,16 +394,9 @@ 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()) {
|
||||
|
@ -403,22 +404,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 +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
|
||||
|
@ -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> {
|
||||
match self {
|
||||
&ConstScalar::Usize(us) => Some(us),
|
||||
_ => None,
|
||||
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::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}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue