Auto merge of #15179 - ponyii:fix/default-values-of-const-params-are-ignored, r=HKalbasi
the "add missing members" assists: implemented substitution of default values of const params To achieve this, I've made `hir::ConstParamData` store the default values
This commit is contained in:
commit
b771de3fdc
@ -25,7 +25,7 @@ use crate::{
|
|||||||
lower::LowerCtx,
|
lower::LowerCtx,
|
||||||
nameres::{DefMap, MacroSubNs},
|
nameres::{DefMap, MacroSubNs},
|
||||||
src::{HasChildSource, HasSource},
|
src::{HasChildSource, HasSource},
|
||||||
type_ref::{LifetimeRef, TypeBound, TypeRef},
|
type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
|
||||||
AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
|
AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
|
||||||
LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
|
LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
|
||||||
};
|
};
|
||||||
@ -49,7 +49,7 @@ pub struct LifetimeParamData {
|
|||||||
pub struct ConstParamData {
|
pub struct ConstParamData {
|
||||||
pub name: Name,
|
pub name: Name,
|
||||||
pub ty: Interned<TypeRef>,
|
pub ty: Interned<TypeRef>,
|
||||||
pub has_default: bool,
|
pub default: Option<ConstRef>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||||
@ -76,7 +76,7 @@ impl TypeOrConstParamData {
|
|||||||
pub fn has_default(&self) -> bool {
|
pub fn has_default(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
TypeOrConstParamData::TypeParamData(it) => it.default.is_some(),
|
TypeOrConstParamData::TypeParamData(it) => it.default.is_some(),
|
||||||
TypeOrConstParamData::ConstParamData(it) => it.has_default,
|
TypeOrConstParamData::ConstParamData(it) => it.default.is_some(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,7 +307,7 @@ impl GenericParams {
|
|||||||
let param = ConstParamData {
|
let param = ConstParamData {
|
||||||
name,
|
name,
|
||||||
ty: Interned::new(ty),
|
ty: Interned::new(ty),
|
||||||
has_default: const_param.default_val().is_some(),
|
default: ConstRef::from_const_param(lower_ctx, &const_param),
|
||||||
};
|
};
|
||||||
let idx = self.type_or_consts.alloc(param.into());
|
let idx = self.type_or_consts.alloc(param.into());
|
||||||
add_param_attrs(idx.into(), ast::GenericParam::ConstParam(const_param));
|
add_param_attrs(idx.into(), ast::GenericParam::ConstParam(const_param));
|
||||||
|
@ -393,6 +393,17 @@ impl ConstRef {
|
|||||||
Self::Scalar(LiteralConstRef::Unknown)
|
Self::Scalar(LiteralConstRef::Unknown)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_const_param(
|
||||||
|
lower_ctx: &LowerCtx<'_>,
|
||||||
|
param: &ast::ConstParam,
|
||||||
|
) -> Option<Self> {
|
||||||
|
let default = param.default_val();
|
||||||
|
match default {
|
||||||
|
Some(_) => Some(Self::from_const_arg(lower_ctx, default)),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a {
|
pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a {
|
||||||
struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRef);
|
struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRef);
|
||||||
impl fmt::Display for Display<'_> {
|
impl fmt::Display for Display<'_> {
|
||||||
|
@ -52,12 +52,14 @@ use hir_expand::name;
|
|||||||
use la_arena::{Arena, Idx};
|
use la_arena::{Arena, Idx};
|
||||||
use mir::{MirEvalError, VTableMap};
|
use mir::{MirEvalError, VTableMap};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
use syntax::ast::{make, ConstArg};
|
||||||
use traits::FnTrait;
|
use traits::FnTrait;
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
use utils::Generics;
|
use utils::Generics;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consteval::unknown_const, db::HirDatabase, infer::unify::InferenceTable, utils::generics,
|
consteval::unknown_const, db::HirDatabase, display::HirDisplay, infer::unify::InferenceTable,
|
||||||
|
utils::generics,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use autoderef::autoderef;
|
pub use autoderef::autoderef;
|
||||||
@ -719,3 +721,16 @@ where
|
|||||||
value.visit_with(&mut collector, DebruijnIndex::INNERMOST);
|
value.visit_with(&mut collector, DebruijnIndex::INNERMOST);
|
||||||
collector.placeholders.into_iter().collect()
|
collector.placeholders.into_iter().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn known_const_to_ast(konst: &Const, db: &dyn HirDatabase) -> Option<ConstArg> {
|
||||||
|
if let ConstValue::Concrete(c) = &konst.interned().value {
|
||||||
|
match c.interned {
|
||||||
|
ConstScalar::UnevaluatedConst(GeneralConstId::InTypeConstId(cid), _) => {
|
||||||
|
return Some(cid.source(db.upcast()));
|
||||||
|
}
|
||||||
|
ConstScalar::Unknown => return None,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(make::expr_const_value(konst.display(db).to_string().as_str()))
|
||||||
|
}
|
||||||
|
@ -212,6 +212,19 @@ impl<'a> TyLoweringContext<'a> {
|
|||||||
self.lower_ty_ext(type_ref).0
|
self.lower_ty_ext(type_ref).0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lower_const(&self, const_ref: &ConstRef, const_type: Ty) -> Const {
|
||||||
|
const_or_path_to_chalk(
|
||||||
|
self.db,
|
||||||
|
self.resolver,
|
||||||
|
self.owner,
|
||||||
|
const_type,
|
||||||
|
const_ref,
|
||||||
|
self.type_param_mode,
|
||||||
|
|| self.generics(),
|
||||||
|
self.in_binders,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn generics(&self) -> Generics {
|
fn generics(&self) -> Generics {
|
||||||
generics(
|
generics(
|
||||||
self.db.upcast(),
|
self.db.upcast(),
|
||||||
@ -241,17 +254,7 @@ impl<'a> TyLoweringContext<'a> {
|
|||||||
}
|
}
|
||||||
TypeRef::Array(inner, len) => {
|
TypeRef::Array(inner, len) => {
|
||||||
let inner_ty = self.lower_ty(inner);
|
let inner_ty = self.lower_ty(inner);
|
||||||
let const_len = const_or_path_to_chalk(
|
let const_len = self.lower_const(len, TyBuilder::usize());
|
||||||
self.db,
|
|
||||||
self.resolver,
|
|
||||||
self.owner,
|
|
||||||
TyBuilder::usize(),
|
|
||||||
len,
|
|
||||||
self.type_param_mode,
|
|
||||||
|| self.generics(),
|
|
||||||
self.in_binders,
|
|
||||||
);
|
|
||||||
|
|
||||||
TyKind::Array(inner_ty, const_len).intern(Interner)
|
TyKind::Array(inner_ty, const_len).intern(Interner)
|
||||||
}
|
}
|
||||||
TypeRef::Slice(inner) => {
|
TypeRef::Slice(inner) => {
|
||||||
@ -846,18 +849,7 @@ impl<'a> TyLoweringContext<'a> {
|
|||||||
arg,
|
arg,
|
||||||
&mut (),
|
&mut (),
|
||||||
|_, type_ref| self.lower_ty(type_ref),
|
|_, type_ref| self.lower_ty(type_ref),
|
||||||
|_, c, ty| {
|
|_, const_ref, ty| self.lower_const(const_ref, ty),
|
||||||
const_or_path_to_chalk(
|
|
||||||
self.db,
|
|
||||||
self.resolver,
|
|
||||||
self.owner,
|
|
||||||
ty,
|
|
||||||
c,
|
|
||||||
self.type_param_mode,
|
|
||||||
|| self.generics(),
|
|
||||||
self.in_binders,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
) {
|
) {
|
||||||
had_explicit_args = true;
|
had_explicit_args = true;
|
||||||
substs.push(x);
|
substs.push(x);
|
||||||
@ -1603,24 +1595,35 @@ pub(crate) fn generic_defaults_query(
|
|||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(idx, (id, p))| {
|
.map(|(idx, (id, p))| {
|
||||||
let p = match p {
|
match p {
|
||||||
TypeOrConstParamData::TypeParamData(p) => p,
|
TypeOrConstParamData::TypeParamData(p) => {
|
||||||
TypeOrConstParamData::ConstParamData(_) => {
|
let mut ty = p
|
||||||
// FIXME: implement const generic defaults
|
.default
|
||||||
let val = unknown_const_as_generic(
|
.as_ref()
|
||||||
db.const_param_ty(ConstParamId::from_unchecked(id)),
|
.map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
|
||||||
);
|
// Each default can only refer to previous parameters.
|
||||||
return make_binders(db, &generic_params, val);
|
// Type variable default referring to parameter coming
|
||||||
|
// after it is forbidden (FIXME: report diagnostic)
|
||||||
|
ty = fallback_bound_vars(ty, idx, parent_start_idx);
|
||||||
|
crate::make_binders(db, &generic_params, ty.cast(Interner))
|
||||||
}
|
}
|
||||||
};
|
TypeOrConstParamData::ConstParamData(p) => {
|
||||||
let mut ty =
|
let mut val = p.default.as_ref().map_or_else(
|
||||||
p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
|
|| {
|
||||||
|
unknown_const_as_generic(
|
||||||
// Each default can only refer to previous parameters.
|
db.const_param_ty(ConstParamId::from_unchecked(id)),
|
||||||
// Type variable default referring to parameter coming
|
)
|
||||||
// after it is forbidden (FIXME: report diagnostic)
|
},
|
||||||
ty = fallback_bound_vars(ty, idx, parent_start_idx);
|
|c| {
|
||||||
crate::make_binders(db, &generic_params, ty.cast(Interner))
|
let c = ctx.lower_const(c, ctx.lower_ty(&p.ty));
|
||||||
|
c.cast(Interner)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// Each default can only refer to previous parameters, see above.
|
||||||
|
val = fallback_bound_vars(val, idx, parent_start_idx);
|
||||||
|
make_binders(db, &generic_params, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
// FIXME: use `Arc::from_iter` when it becomes available
|
// FIXME: use `Arc::from_iter` when it becomes available
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
|
@ -63,12 +63,13 @@ use hir_ty::{
|
|||||||
all_super_traits, autoderef,
|
all_super_traits, autoderef,
|
||||||
consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt},
|
consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt},
|
||||||
diagnostics::BodyValidationDiagnostic,
|
diagnostics::BodyValidationDiagnostic,
|
||||||
|
known_const_to_ast,
|
||||||
layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding},
|
layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding},
|
||||||
method_resolution::{self, TyFingerprint},
|
method_resolution::{self, TyFingerprint},
|
||||||
mir::{self, interpret_mir},
|
mir::{self, interpret_mir},
|
||||||
primitive::UintTy,
|
primitive::UintTy,
|
||||||
traits::FnTrait,
|
traits::FnTrait,
|
||||||
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId,
|
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg,
|
||||||
GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
|
GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
|
||||||
TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId,
|
TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId,
|
||||||
WhereClause,
|
WhereClause,
|
||||||
@ -3128,12 +3129,8 @@ impl TypeParam {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
|
pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
|
||||||
let params = db.generic_defaults(self.id.parent());
|
let ty = generic_arg_from_param(db, self.id.into())?;
|
||||||
let local_idx = hir_ty::param_idx(db, self.id.into())?;
|
|
||||||
let resolver = self.id.parent().resolver(db.upcast());
|
let resolver = self.id.parent().resolver(db.upcast());
|
||||||
let ty = params.get(local_idx)?.clone();
|
|
||||||
let subst = TyBuilder::placeholder_subst(db, self.id.parent());
|
|
||||||
let ty = ty.substitute(Interner, &subst);
|
|
||||||
match ty.data(Interner) {
|
match ty.data(Interner) {
|
||||||
GenericArgData::Ty(it) => {
|
GenericArgData::Ty(it) => {
|
||||||
Some(Type::new_with_resolver_inner(db, &resolver, it.clone()))
|
Some(Type::new_with_resolver_inner(db, &resolver, it.clone()))
|
||||||
@ -3195,6 +3192,19 @@ impl ConstParam {
|
|||||||
pub fn ty(self, db: &dyn HirDatabase) -> Type {
|
pub fn ty(self, db: &dyn HirDatabase) -> Type {
|
||||||
Type::new(db, self.id.parent(), db.const_param_ty(self.id))
|
Type::new(db, self.id.parent(), db.const_param_ty(self.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn default(self, db: &dyn HirDatabase) -> Option<ast::ConstArg> {
|
||||||
|
let arg = generic_arg_from_param(db, self.id.into())?;
|
||||||
|
known_const_to_ast(arg.constant(Interner)?, db)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic_arg_from_param(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<GenericArg> {
|
||||||
|
let params = db.generic_defaults(id.parent);
|
||||||
|
let local_idx = hir_ty::param_idx(db, id)?;
|
||||||
|
let ty = params.get(local_idx)?.clone();
|
||||||
|
let subst = TyBuilder::placeholder_subst(db, id.parent);
|
||||||
|
Some(ty.substitute(Interner, &subst))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
@ -422,7 +422,7 @@ impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {
|
|||||||
check_assist(
|
check_assist(
|
||||||
add_missing_default_members,
|
add_missing_default_members,
|
||||||
r#"
|
r#"
|
||||||
struct Bar<const: N: bool> {
|
struct Bar<const N: usize> {
|
||||||
bar: [i32, N]
|
bar: [i32, N]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,7 +439,7 @@ impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> {
|
|||||||
$0
|
$0
|
||||||
}"#,
|
}"#,
|
||||||
r#"
|
r#"
|
||||||
struct Bar<const: N: bool> {
|
struct Bar<const N: usize> {
|
||||||
bar: [i32, N]
|
bar: [i32, N]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,6 +483,107 @@ impl<X> Foo<42, {20 + 22}, X> for () {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_const_substitution_with_defaults() {
|
||||||
|
check_assist(
|
||||||
|
add_missing_default_members,
|
||||||
|
r#"
|
||||||
|
trait Foo<T, const N: usize = 42, const M: bool = false, const P: char = 'a'> {
|
||||||
|
fn get_n(&self) -> usize { N }
|
||||||
|
fn get_m(&self) -> bool { M }
|
||||||
|
fn get_p(&self) -> char { P }
|
||||||
|
fn get_array(&self, arg: &T) -> [bool; N] { [M; N] }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<X> Foo<X> for () {
|
||||||
|
$0
|
||||||
|
}"#,
|
||||||
|
r#"
|
||||||
|
trait Foo<T, const N: usize = 42, const M: bool = false, const P: char = 'a'> {
|
||||||
|
fn get_n(&self) -> usize { N }
|
||||||
|
fn get_m(&self) -> bool { M }
|
||||||
|
fn get_p(&self) -> char { P }
|
||||||
|
fn get_array(&self, arg: &T) -> [bool; N] { [M; N] }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<X> Foo<X> for () {
|
||||||
|
$0fn get_n(&self) -> usize { 42 }
|
||||||
|
|
||||||
|
fn get_m(&self) -> bool { false }
|
||||||
|
|
||||||
|
fn get_p(&self) -> char { 'a' }
|
||||||
|
|
||||||
|
fn get_array(&self, arg: &X) -> [bool; 42] { [false; 42] }
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_const_substitution_with_defaults_2() {
|
||||||
|
check_assist(
|
||||||
|
add_missing_impl_members,
|
||||||
|
r#"
|
||||||
|
mod m {
|
||||||
|
pub const LEN: usize = 42;
|
||||||
|
pub trait Foo<const M: usize = LEN, const N: usize = M, T = [bool; N]> {
|
||||||
|
fn get_t(&self) -> T;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl m::Foo for () {
|
||||||
|
$0
|
||||||
|
}"#,
|
||||||
|
r#"
|
||||||
|
mod m {
|
||||||
|
pub const LEN: usize = 42;
|
||||||
|
pub trait Foo<const M: usize = LEN, const N: usize = M, T = [bool; N]> {
|
||||||
|
fn get_t(&self) -> T;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl m::Foo for () {
|
||||||
|
fn get_t(&self) -> [bool; m::LEN] {
|
||||||
|
${0:todo!()}
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_const_substitution_with_defaults_3() {
|
||||||
|
check_assist(
|
||||||
|
add_missing_default_members,
|
||||||
|
r#"
|
||||||
|
mod m {
|
||||||
|
pub const VAL: usize = 0;
|
||||||
|
|
||||||
|
pub trait Foo<const N: usize = {40 + 2}, const M: usize = {VAL + 1}> {
|
||||||
|
fn get_n(&self) -> usize { N }
|
||||||
|
fn get_m(&self) -> usize { M }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl m::Foo for () {
|
||||||
|
$0
|
||||||
|
}"#,
|
||||||
|
r#"
|
||||||
|
mod m {
|
||||||
|
pub const VAL: usize = 0;
|
||||||
|
|
||||||
|
pub trait Foo<const N: usize = {40 + 2}, const M: usize = {VAL + 1}> {
|
||||||
|
fn get_n(&self) -> usize { N }
|
||||||
|
fn get_m(&self) -> usize { M }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl m::Foo for () {
|
||||||
|
$0fn get_n(&self) -> usize { {40 + 2} }
|
||||||
|
|
||||||
|
fn get_m(&self) -> usize { {m::VAL + 1} }
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cursor_after_empty_impl_def() {
|
fn test_cursor_after_empty_impl_def() {
|
||||||
check_assist(
|
check_assist(
|
||||||
|
@ -810,7 +810,7 @@ impl FunctionBody {
|
|||||||
(true, konst.body(), Some(sema.to_def(&konst)?.ty(sema.db)))
|
(true, konst.body(), Some(sema.to_def(&konst)?.ty(sema.db)))
|
||||||
},
|
},
|
||||||
ast::ConstParam(cp) => {
|
ast::ConstParam(cp) => {
|
||||||
(true, cp.default_val(), Some(sema.to_def(&cp)?.ty(sema.db)))
|
(true, cp.default_val()?.expr(), Some(sema.to_def(&cp)?.ty(sema.db)))
|
||||||
},
|
},
|
||||||
ast::ConstBlockPat(cbp) => {
|
ast::ConstBlockPat(cbp) => {
|
||||||
let expr = cbp.block_expr().map(ast::Expr::BlockExpr);
|
let expr = cbp.block_expr().map(ast::Expr::BlockExpr);
|
||||||
|
@ -6,7 +6,7 @@ use hir::{
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, HasName},
|
ast::{self, make, HasName},
|
||||||
utils::path_to_string_stripping_turbo_fish,
|
utils::path_to_string_stripping_turbo_fish,
|
||||||
AstNode, SyntaxNode,
|
AstNode, SyntaxNode,
|
||||||
};
|
};
|
||||||
@ -607,7 +607,7 @@ impl ImportCandidate {
|
|||||||
fn for_name(sema: &Semantics<'_, RootDatabase>, name: &ast::Name) -> Option<Self> {
|
fn for_name(sema: &Semantics<'_, RootDatabase>, name: &ast::Name) -> Option<Self> {
|
||||||
if sema
|
if sema
|
||||||
.scope(name.syntax())?
|
.scope(name.syntax())?
|
||||||
.speculative_resolve(&ast::make::ext::ident_path(&name.text()))
|
.speculative_resolve(&make::ext::ident_path(&name.text()))
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
|
@ -5,7 +5,7 @@ use either::Either;
|
|||||||
use hir::{AsAssocItem, HirDisplay, SemanticsScope};
|
use hir::{AsAssocItem, HirDisplay, SemanticsScope};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode},
|
ast::{self, make, AstNode},
|
||||||
ted, SyntaxNode,
|
ted, SyntaxNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -21,6 +21,7 @@ enum TypeOrConst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LifetimeName = String;
|
type LifetimeName = String;
|
||||||
|
type DefaultedParam = Either<hir::TypeParam, hir::ConstParam>;
|
||||||
|
|
||||||
/// `PathTransform` substitutes path in SyntaxNodes in bulk.
|
/// `PathTransform` substitutes path in SyntaxNodes in bulk.
|
||||||
///
|
///
|
||||||
@ -115,7 +116,7 @@ impl<'a> PathTransform<'a> {
|
|||||||
};
|
};
|
||||||
let mut type_substs: FxHashMap<hir::TypeParam, ast::Type> = Default::default();
|
let mut type_substs: FxHashMap<hir::TypeParam, ast::Type> = Default::default();
|
||||||
let mut const_substs: FxHashMap<hir::ConstParam, SyntaxNode> = Default::default();
|
let mut const_substs: FxHashMap<hir::ConstParam, SyntaxNode> = Default::default();
|
||||||
let mut default_types: Vec<hir::TypeParam> = Default::default();
|
let mut defaulted_params: Vec<DefaultedParam> = Default::default();
|
||||||
self.generic_def
|
self.generic_def
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|it| it.type_params(db))
|
.flat_map(|it| it.type_params(db))
|
||||||
@ -138,8 +139,8 @@ impl<'a> PathTransform<'a> {
|
|||||||
if let Some(default) =
|
if let Some(default) =
|
||||||
&default.display_source_code(db, source_module.into(), false).ok()
|
&default.display_source_code(db, source_module.into(), false).ok()
|
||||||
{
|
{
|
||||||
type_substs.insert(k, ast::make::ty(default).clone_for_update());
|
type_substs.insert(k, make::ty(default).clone_for_update());
|
||||||
default_types.push(k);
|
defaulted_params.push(Either::Left(k));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,11 +156,19 @@ impl<'a> PathTransform<'a> {
|
|||||||
// is a standalone statement or a part of another expresson)
|
// is a standalone statement or a part of another expresson)
|
||||||
// and sometimes require slight modifications; see
|
// and sometimes require slight modifications; see
|
||||||
// https://doc.rust-lang.org/reference/statements.html#expression-statements
|
// https://doc.rust-lang.org/reference/statements.html#expression-statements
|
||||||
|
// (default values in curly brackets can cause the same problem)
|
||||||
const_substs.insert(k, expr.syntax().clone());
|
const_substs.insert(k, expr.syntax().clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Either::Left(_), None) => (), // FIXME: get default const value
|
(Either::Left(k), None) => {
|
||||||
_ => (), // ignore mismatching params
|
if let Some(default) = k.default(db) {
|
||||||
|
if let Some(default) = default.expr() {
|
||||||
|
const_substs.insert(k, default.syntax().clone_for_update());
|
||||||
|
defaulted_params.push(Either::Right(k));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (), // ignore mismatching params
|
||||||
});
|
});
|
||||||
let lifetime_substs: FxHashMap<_, _> = self
|
let lifetime_substs: FxHashMap<_, _> = self
|
||||||
.generic_def
|
.generic_def
|
||||||
@ -175,7 +184,7 @@ impl<'a> PathTransform<'a> {
|
|||||||
target_module,
|
target_module,
|
||||||
source_scope: self.source_scope,
|
source_scope: self.source_scope,
|
||||||
};
|
};
|
||||||
ctx.transform_default_type_substs(default_types);
|
ctx.transform_default_values(defaulted_params);
|
||||||
ctx
|
ctx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,13 +221,19 @@ impl Ctx<'_> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform_default_type_substs(&self, default_types: Vec<hir::TypeParam>) {
|
fn transform_default_values(&self, defaulted_params: Vec<DefaultedParam>) {
|
||||||
for k in default_types {
|
// By now the default values are simply copied from where they are declared
|
||||||
let v = self.type_substs.get(&k).unwrap();
|
// and should be transformed. As any value is allowed to refer to previous
|
||||||
|
// generic (both type and const) parameters, they should be all iterated left-to-right.
|
||||||
|
for param in defaulted_params {
|
||||||
|
let value = match param {
|
||||||
|
Either::Left(k) => self.type_substs.get(&k).unwrap().syntax(),
|
||||||
|
Either::Right(k) => self.const_substs.get(&k).unwrap(),
|
||||||
|
};
|
||||||
// `transform_path` may update a node's parent and that would break the
|
// `transform_path` may update a node's parent and that would break the
|
||||||
// tree traversal. Thus all paths in the tree are collected into a vec
|
// tree traversal. Thus all paths in the tree are collected into a vec
|
||||||
// so that such operation is safe.
|
// so that such operation is safe.
|
||||||
let paths = postorder(&v.syntax()).filter_map(ast::Path::cast).collect::<Vec<_>>();
|
let paths = postorder(value).filter_map(ast::Path::cast).collect::<Vec<_>>();
|
||||||
for path in paths {
|
for path in paths {
|
||||||
self.transform_path(path);
|
self.transform_path(path);
|
||||||
}
|
}
|
||||||
@ -263,15 +278,14 @@ impl Ctx<'_> {
|
|||||||
hir::ModuleDef::Trait(trait_ref),
|
hir::ModuleDef::Trait(trait_ref),
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
match ast::make::ty_path(mod_path_to_ast(&found_path)) {
|
match make::ty_path(mod_path_to_ast(&found_path)) {
|
||||||
ast::Type::PathType(path_ty) => Some(path_ty),
|
ast::Type::PathType(path_ty) => Some(path_ty),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let segment = ast::make::path_segment_ty(subst.clone(), trait_ref);
|
let segment = make::path_segment_ty(subst.clone(), trait_ref);
|
||||||
let qualified =
|
let qualified = make::path_from_segments(std::iter::once(segment), false);
|
||||||
ast::make::path_from_segments(std::iter::once(segment), false);
|
|
||||||
ted::replace(path.syntax(), qualified.clone_for_update().syntax());
|
ted::replace(path.syntax(), qualified.clone_for_update().syntax());
|
||||||
} else if let Some(path_ty) = ast::PathType::cast(parent) {
|
} else if let Some(path_ty) = ast::PathType::cast(parent) {
|
||||||
ted::replace(
|
ted::replace(
|
||||||
|
@ -1,31 +1,29 @@
|
|||||||
//! Functionality for generating trivial constructors
|
//! Functionality for generating trivial constructors
|
||||||
|
|
||||||
use hir::StructKind;
|
use hir::StructKind;
|
||||||
use syntax::ast;
|
use syntax::ast::{make, Expr, Path};
|
||||||
|
|
||||||
/// given a type return the trivial constructor (if one exists)
|
/// given a type return the trivial constructor (if one exists)
|
||||||
pub fn use_trivial_constructor(
|
pub fn use_trivial_constructor(
|
||||||
db: &crate::RootDatabase,
|
db: &crate::RootDatabase,
|
||||||
path: ast::Path,
|
path: Path,
|
||||||
ty: &hir::Type,
|
ty: &hir::Type,
|
||||||
) -> Option<ast::Expr> {
|
) -> Option<Expr> {
|
||||||
match ty.as_adt() {
|
match ty.as_adt() {
|
||||||
Some(hir::Adt::Enum(x)) => {
|
Some(hir::Adt::Enum(x)) => {
|
||||||
if let &[variant] = &*x.variants(db) {
|
if let &[variant] = &*x.variants(db) {
|
||||||
if variant.kind(db) == hir::StructKind::Unit {
|
if variant.kind(db) == hir::StructKind::Unit {
|
||||||
let path = ast::make::path_qualified(
|
let path = make::path_qualified(
|
||||||
path,
|
path,
|
||||||
syntax::ast::make::path_segment(ast::make::name_ref(
|
make::path_segment(make::name_ref(&variant.name(db).to_smol_str())),
|
||||||
&variant.name(db).to_smol_str(),
|
|
||||||
)),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return Some(syntax::ast::make::expr_path(path));
|
return Some(make::expr_path(path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(hir::Adt::Struct(x)) if x.kind(db) == StructKind::Unit => {
|
Some(hir::Adt::Struct(x)) if x.kind(db) == StructKind::Unit => {
|
||||||
return Some(syntax::ast::make::expr_path(path));
|
return Some(make::expr_path(path));
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ fn const_param(p: &mut Parser<'_>, m: Marker) {
|
|||||||
|
|
||||||
// test const_param_default_path
|
// test const_param_default_path
|
||||||
// struct A<const N: i32 = i32::MAX>;
|
// struct A<const N: i32 = i32::MAX>;
|
||||||
generic_args::const_arg_expr(p);
|
generic_args::const_arg(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
m.complete(p, CONST_PARAM);
|
m.complete(p, CONST_PARAM);
|
||||||
|
@ -20,7 +20,8 @@ SOURCE_FILE
|
|||||||
IDENT "i32"
|
IDENT "i32"
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
EQ "="
|
EQ "="
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
|
CONST_ARG
|
||||||
COMMA ","
|
COMMA ","
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
CONST_PARAM
|
CONST_PARAM
|
||||||
@ -37,8 +38,9 @@ SOURCE_FILE
|
|||||||
IDENT "i32"
|
IDENT "i32"
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
EQ "="
|
EQ "="
|
||||||
|
CONST_ARG
|
||||||
R_ANGLE ">"
|
R_ANGLE ">"
|
||||||
SEMICOLON ";"
|
SEMICOLON ";"
|
||||||
WHITESPACE "\n"
|
WHITESPACE "\n"
|
||||||
error 23: expected a generic const argument
|
error 24: expected a generic const argument
|
||||||
error 40: expected a generic const argument
|
error 40: expected a generic const argument
|
||||||
|
@ -21,16 +21,17 @@ SOURCE_FILE
|
|||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
EQ "="
|
EQ "="
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
PATH_EXPR
|
CONST_ARG
|
||||||
PATH
|
PATH_EXPR
|
||||||
PATH
|
PATH
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "i32"
|
||||||
|
COLON2 "::"
|
||||||
PATH_SEGMENT
|
PATH_SEGMENT
|
||||||
NAME_REF
|
NAME_REF
|
||||||
IDENT "i32"
|
IDENT "MAX"
|
||||||
COLON2 "::"
|
|
||||||
PATH_SEGMENT
|
|
||||||
NAME_REF
|
|
||||||
IDENT "MAX"
|
|
||||||
R_ANGLE ">"
|
R_ANGLE ">"
|
||||||
SEMICOLON ";"
|
SEMICOLON ";"
|
||||||
WHITESPACE "\n"
|
WHITESPACE "\n"
|
||||||
|
@ -21,14 +21,15 @@ SOURCE_FILE
|
|||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
EQ "="
|
EQ "="
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
BLOCK_EXPR
|
CONST_ARG
|
||||||
STMT_LIST
|
BLOCK_EXPR
|
||||||
L_CURLY "{"
|
STMT_LIST
|
||||||
WHITESPACE " "
|
L_CURLY "{"
|
||||||
LITERAL
|
WHITESPACE " "
|
||||||
INT_NUMBER "1"
|
LITERAL
|
||||||
WHITESPACE " "
|
INT_NUMBER "1"
|
||||||
R_CURLY "}"
|
WHITESPACE " "
|
||||||
|
R_CURLY "}"
|
||||||
R_ANGLE ">"
|
R_ANGLE ">"
|
||||||
SEMICOLON ";"
|
SEMICOLON ";"
|
||||||
WHITESPACE "\n"
|
WHITESPACE "\n"
|
||||||
|
@ -21,10 +21,11 @@ SOURCE_FILE
|
|||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
EQ "="
|
EQ "="
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
PREFIX_EXPR
|
CONST_ARG
|
||||||
MINUS "-"
|
PREFIX_EXPR
|
||||||
LITERAL
|
MINUS "-"
|
||||||
INT_NUMBER "1"
|
LITERAL
|
||||||
|
INT_NUMBER "1"
|
||||||
R_ANGLE ">"
|
R_ANGLE ">"
|
||||||
SEMICOLON ";"
|
SEMICOLON ";"
|
||||||
WHITESPACE "\n"
|
WHITESPACE "\n"
|
||||||
|
@ -296,7 +296,7 @@ TypeParam =
|
|||||||
|
|
||||||
ConstParam =
|
ConstParam =
|
||||||
Attr* 'const' Name ':' Type
|
Attr* 'const' Name ':' Type
|
||||||
('=' default_val:Expr)?
|
('=' default_val:ConstArg)?
|
||||||
|
|
||||||
LifetimeParam =
|
LifetimeParam =
|
||||||
Attr* Lifetime (':' TypeBoundList?)?
|
Attr* Lifetime (':' TypeBoundList?)?
|
||||||
|
@ -709,7 +709,7 @@ impl ConstParam {
|
|||||||
pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
|
pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
|
||||||
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
|
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
|
||||||
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
|
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
|
||||||
pub fn default_val(&self) -> Option<Expr> { support::child(&self.syntax) }
|
pub fn default_val(&self) -> Option<ConstArg> { support::child(&self.syntax) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
@ -503,11 +503,16 @@ pub fn hacky_block_expr(
|
|||||||
pub fn expr_unit() -> ast::Expr {
|
pub fn expr_unit() -> ast::Expr {
|
||||||
expr_from_text("()")
|
expr_from_text("()")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expr_literal(text: &str) -> ast::Literal {
|
pub fn expr_literal(text: &str) -> ast::Literal {
|
||||||
assert_eq!(text.trim(), text);
|
assert_eq!(text.trim(), text);
|
||||||
ast_from_text(&format!("fn f() {{ let _ = {text}; }}"))
|
ast_from_text(&format!("fn f() {{ let _ = {text}; }}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expr_const_value(text: &str) -> ast::ConstArg {
|
||||||
|
ast_from_text(&format!("trait Foo<const N: usize = {text}> {{}}"))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn expr_empty_block() -> ast::Expr {
|
pub fn expr_empty_block() -> ast::Expr {
|
||||||
expr_from_text("{}")
|
expr_from_text("{}")
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user