Skip to content

Commit 1015a5a

Browse files
committed
the "add missing members" assists: implemented substitution of default values of const params
1 parent ea02f4c commit 1015a5a

File tree

8 files changed

+127
-52
lines changed

8 files changed

+127
-52
lines changed

crates/hir-def/src/generics.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::{
2424
lower::LowerCtx,
2525
nameres::{DefMap, MacroSubNs},
2626
src::{HasChildSource, HasSource},
27-
type_ref::{LifetimeRef, TypeBound, TypeRef},
27+
type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
2828
AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
2929
LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
3030
};
@@ -48,7 +48,7 @@ pub struct LifetimeParamData {
4848
pub struct ConstParamData {
4949
pub name: Name,
5050
pub ty: Interned<TypeRef>,
51-
pub has_default: bool,
51+
pub default: Option<ConstRef>,
5252
}
5353

5454
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
@@ -75,7 +75,7 @@ impl TypeOrConstParamData {
7575
pub fn has_default(&self) -> bool {
7676
match self {
7777
TypeOrConstParamData::TypeParamData(it) => it.default.is_some(),
78-
TypeOrConstParamData::ConstParamData(it) => it.has_default,
78+
TypeOrConstParamData::ConstParamData(it) => it.default.is_some(),
7979
}
8080
}
8181

@@ -248,7 +248,7 @@ impl GenericParams {
248248
let param = ConstParamData {
249249
name,
250250
ty: Interned::new(ty),
251-
has_default: const_param.default_val().is_some(),
251+
default: ConstRef::from_default_param_value(lower_ctx, const_param),
252252
};
253253
self.type_or_consts.alloc(param.into());
254254
}

crates/hir-def/src/hir/type_ref.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,17 @@ impl ConstRef {
393393
Self::Scalar(LiteralConstRef::Unknown)
394394
}
395395

396+
pub(crate) fn from_default_param_value(
397+
_: &LowerCtx<'_>,
398+
param: ast::ConstParam,
399+
) -> Option<Self> {
400+
if let Some(expr) = param.default_val() {
401+
// FIXME: pass the `ast_id` arg to recognize complex expressions
402+
return Some(Self::from_expr(expr, None));
403+
}
404+
None
405+
}
406+
396407
pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a {
397408
struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRef);
398409
impl fmt::Display for Display<'_> {

crates/hir-ty/src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ use triomphe::Arc;
5757
use utils::Generics;
5858

5959
use crate::{
60-
consteval::unknown_const, db::HirDatabase, infer::unify::InferenceTable, utils::generics,
60+
consteval::unknown_const, db::HirDatabase, display::HirDisplay, infer::unify::InferenceTable,
61+
utils::generics,
6162
};
6263

6364
pub use autoderef::autoderef;
@@ -719,3 +720,12 @@ where
719720
value.visit_with(&mut collector, DebruijnIndex::INNERMOST);
720721
collector.placeholders.into_iter().collect()
721722
}
723+
724+
pub fn known_const_to_string(konst: &Const, db: &dyn HirDatabase) -> Option<String> {
725+
if let ConstValue::Concrete(c) = &konst.interned().value {
726+
if c.interned == ConstScalar::Unknown {
727+
return None;
728+
}
729+
}
730+
Some(konst.display(db).to_string())
731+
}

crates/hir-ty/src/lower.rs

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,19 @@ impl<'a> TyLoweringContext<'a> {
213213
self.lower_ty_ext(type_ref).0
214214
}
215215

216+
pub fn lower_const(&self, const_ref: &ConstRef, const_type: Ty) -> Const {
217+
const_or_path_to_chalk(
218+
self.db,
219+
self.resolver,
220+
self.owner,
221+
const_type,
222+
const_ref,
223+
self.type_param_mode,
224+
|| self.generics(),
225+
self.in_binders,
226+
)
227+
}
228+
216229
fn generics(&self) -> Generics {
217230
generics(
218231
self.db.upcast(),
@@ -242,17 +255,7 @@ impl<'a> TyLoweringContext<'a> {
242255
}
243256
TypeRef::Array(inner, len) => {
244257
let inner_ty = self.lower_ty(inner);
245-
let const_len = const_or_path_to_chalk(
246-
self.db,
247-
self.resolver,
248-
self.owner,
249-
TyBuilder::usize(),
250-
len,
251-
self.type_param_mode,
252-
|| self.generics(),
253-
self.in_binders,
254-
);
255-
258+
let const_len = self.lower_const(len, TyBuilder::usize());
256259
TyKind::Array(inner_ty, const_len).intern(Interner)
257260
}
258261
TypeRef::Slice(inner) => {
@@ -847,18 +850,7 @@ impl<'a> TyLoweringContext<'a> {
847850
arg,
848851
&mut (),
849852
|_, type_ref| self.lower_ty(type_ref),
850-
|_, c, ty| {
851-
const_or_path_to_chalk(
852-
self.db,
853-
self.resolver,
854-
self.owner,
855-
ty,
856-
c,
857-
self.type_param_mode,
858-
|| self.generics(),
859-
self.in_binders,
860-
)
861-
},
853+
|_, const_ref, ty| self.lower_const(const_ref, ty),
862854
) {
863855
had_explicit_args = true;
864856
substs.push(x);
@@ -1604,24 +1596,31 @@ pub(crate) fn generic_defaults_query(
16041596
.iter()
16051597
.enumerate()
16061598
.map(|(idx, (id, p))| {
1607-
let p = match p {
1608-
TypeOrConstParamData::TypeParamData(p) => p,
1609-
TypeOrConstParamData::ConstParamData(_) => {
1610-
// FIXME: implement const generic defaults
1611-
let val = unknown_const_as_generic(
1599+
match p {
1600+
TypeOrConstParamData::TypeParamData(p) => {
1601+
let mut ty = p
1602+
.default
1603+
.as_ref()
1604+
.map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
1605+
// Each default can only refer to previous parameters.
1606+
// Type variable default referring to parameter coming
1607+
// after it is forbidden (FIXME: report diagnostic)
1608+
ty = fallback_bound_vars(ty, idx, parent_start_idx);
1609+
return crate::make_binders(db, &generic_params, ty.cast(Interner));
1610+
}
1611+
TypeOrConstParamData::ConstParamData(p) => {
1612+
let unknown = unknown_const_as_generic(
16121613
db.const_param_ty(ConstParamId::from_unchecked(id)),
16131614
);
1615+
let val = p.default.as_ref().map_or(unknown, |c| {
1616+
let c = ctx.lower_const(c, ctx.lower_ty(&p.ty));
1617+
chalk_ir::GenericArg::new(Interner, GenericArgData::Const(c))
1618+
});
1619+
// FIXME: check if complex default values refer to
1620+
// previous parameters they should not.
16141621
return make_binders(db, &generic_params, val);
16151622
}
16161623
};
1617-
let mut ty =
1618-
p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
1619-
1620-
// Each default can only refer to previous parameters.
1621-
// Type variable default referring to parameter coming
1622-
// after it is forbidden (FIXME: report diagnostic)
1623-
ty = fallback_bound_vars(ty, idx, parent_start_idx);
1624-
crate::make_binders(db, &generic_params, ty.cast(Interner))
16251624
})
16261625
// FIXME: use `Arc::from_iter` when it becomes available
16271626
.collect::<Vec<_>>(),

crates/hir/src/lib.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,13 @@ use hir_expand::{name::name, MacroCallKind};
6161
use hir_ty::{
6262
all_super_traits, autoderef,
6363
consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt},
64-
diagnostics::BodyValidationDiagnostic,
64+
diagnostics::BodyValidationDiagnostic, known_const_to_string,
6565
layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding},
6666
method_resolution::{self, TyFingerprint},
6767
mir::{self, interpret_mir},
6868
primitive::UintTy,
6969
traits::FnTrait,
70-
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId,
70+
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg,
7171
GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
7272
TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId,
7373
WhereClause,
@@ -3095,12 +3095,8 @@ impl TypeParam {
30953095
}
30963096

30973097
pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
3098-
let params = db.generic_defaults(self.id.parent());
3099-
let local_idx = hir_ty::param_idx(db, self.id.into())?;
3098+
let ty = generic_arg_from_param(db, self.id.into())?;
31003099
let resolver = self.id.parent().resolver(db.upcast());
3101-
let ty = params.get(local_idx)?.clone();
3102-
let subst = TyBuilder::placeholder_subst(db, self.id.parent());
3103-
let ty = ty.substitute(Interner, &subst);
31043100
match ty.data(Interner) {
31053101
GenericArgData::Ty(it) => {
31063102
Some(Type::new_with_resolver_inner(db, &resolver, it.clone()))
@@ -3162,6 +3158,19 @@ impl ConstParam {
31623158
pub fn ty(self, db: &dyn HirDatabase) -> Type {
31633159
Type::new(db, self.id.parent(), db.const_param_ty(self.id))
31643160
}
3161+
3162+
pub fn default(self, db: &dyn HirDatabase) -> Option<String> {
3163+
let arg = generic_arg_from_param(db, self.id.into())?;
3164+
known_const_to_string(arg.constant(Interner)?, db)
3165+
}
3166+
}
3167+
3168+
fn generic_arg_from_param(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<GenericArg> {
3169+
let params = db.generic_defaults(id.parent);
3170+
let local_idx = hir_ty::param_idx(db, id)?;
3171+
let ty = params.get(local_idx)?.clone();
3172+
let subst = TyBuilder::placeholder_subst(db, id.parent);
3173+
Some(ty.substitute(Interner, &subst))
31653174
}
31663175

31673176
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]

crates/ide-assists/src/handlers/add_missing_impl_members.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {
405405
check_assist(
406406
add_missing_default_members,
407407
r#"
408-
struct Bar<const: N: bool> {
408+
struct Bar<const N: usize> {
409409
bar: [i32, N]
410410
}
411411
@@ -422,7 +422,7 @@ impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> {
422422
$0
423423
}"#,
424424
r#"
425-
struct Bar<const: N: bool> {
425+
struct Bar<const N: usize> {
426426
bar: [i32, N]
427427
}
428428
@@ -466,6 +466,41 @@ impl<X> Foo<42, {20 + 22}, X> for () {
466466
)
467467
}
468468

469+
#[test]
470+
fn test_const_substitution_with_defaults() {
471+
check_assist(
472+
add_missing_default_members,
473+
r#"
474+
trait Foo<T, const N: usize = 42, const M: bool = false, const P: char = 'a'> {
475+
fn get_n(&self) -> usize { N }
476+
fn get_m(&self) -> bool { M }
477+
fn get_p(&self) -> char { P }
478+
fn get_array(&self, arg: &T) -> [bool; N] { [M; N] }
479+
}
480+
481+
impl<X> Foo<X> for () {
482+
$0
483+
}"#,
484+
r#"
485+
trait Foo<T, const N: usize = 42, const M: bool = false, const P: char = 'a'> {
486+
fn get_n(&self) -> usize { N }
487+
fn get_m(&self) -> bool { M }
488+
fn get_p(&self) -> char { P }
489+
fn get_array(&self, arg: &T) -> [bool; N] { [M; N] }
490+
}
491+
492+
impl<X> Foo<X> for () {
493+
$0fn get_n(&self) -> usize { 42 }
494+
495+
fn get_m(&self) -> bool { false }
496+
497+
fn get_p(&self) -> char { 'a' }
498+
499+
fn get_array(&self, arg: &X) -> [bool; 42] { [false; 42] }
500+
}"#,
501+
);
502+
}
503+
469504
#[test]
470505
fn test_cursor_after_empty_impl_def() {
471506
check_assist(

crates/ide-db/src/path_transform.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,14 @@ impl<'a> PathTransform<'a> {
158158
const_substs.insert(k, expr.syntax().clone());
159159
}
160160
}
161-
(Either::Left(_), None) => (), // FIXME: get default const value
162-
_ => (), // ignore mismatching params
161+
(Either::Left(k), None) => {
162+
if let Some(default) = k.default(db) {
163+
let default = ast::make::expr_const_value(&default);
164+
const_substs.insert(k, default.syntax().clone_for_update());
165+
// FIXME: transform the default value
166+
}
167+
}
168+
_ => (), // ignore mismatching params
163169
});
164170
let lifetime_substs: FxHashMap<_, _> = self
165171
.generic_def

crates/syntax/src/ast/make.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,11 +503,16 @@ pub fn hacky_block_expr(
503503
pub fn expr_unit() -> ast::Expr {
504504
expr_from_text("()")
505505
}
506+
506507
pub fn expr_literal(text: &str) -> ast::Literal {
507508
assert_eq!(text.trim(), text);
508509
ast_from_text(&format!("fn f() {{ let _ = {text}; }}"))
509510
}
510511

512+
pub fn expr_const_value(text: &str) -> ast::Expr {
513+
ast_from_text(&format!("trait Foo<const N: usize = {text}> {{}}"))
514+
}
515+
511516
pub fn expr_empty_block() -> ast::Expr {
512517
expr_from_text("{}")
513518
}

0 commit comments

Comments
 (0)