Skip to content

Commit

Permalink
Auto merge of #15179 - ponyii:fix/default-values-of-const-params-are-…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
bors committed Aug 15, 2023
2 parents 7ca45dc + 68e8379 commit b771de3
Show file tree
Hide file tree
Showing 18 changed files with 265 additions and 103 deletions.
8 changes: 4 additions & 4 deletions crates/hir-def/src/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::{
lower::LowerCtx,
nameres::{DefMap, MacroSubNs},
src::{HasChildSource, HasSource},
type_ref::{LifetimeRef, TypeBound, TypeRef},
type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
};
Expand All @@ -49,7 +49,7 @@ pub struct LifetimeParamData {
pub struct ConstParamData {
pub name: Name,
pub ty: Interned<TypeRef>,
pub has_default: bool,
pub default: Option<ConstRef>,
}

#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
Expand All @@ -76,7 +76,7 @@ impl TypeOrConstParamData {
pub fn has_default(&self) -> bool {
match self {
TypeOrConstParamData::TypeParamData(it) => it.default.is_some(),
TypeOrConstParamData::ConstParamData(it) => it.has_default,
TypeOrConstParamData::ConstParamData(it) => it.default.is_some(),
}
}

Expand Down Expand Up @@ -307,7 +307,7 @@ impl GenericParams {
let param = ConstParamData {
name,
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());
add_param_attrs(idx.into(), ast::GenericParam::ConstParam(const_param));
Expand Down
11 changes: 11 additions & 0 deletions crates/hir-def/src/hir/type_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,17 @@ impl ConstRef {
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 {
struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRef);
impl fmt::Display for Display<'_> {
Expand Down
17 changes: 16 additions & 1 deletion crates/hir-ty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,14 @@ use hir_expand::name;
use la_arena::{Arena, Idx};
use mir::{MirEvalError, VTableMap};
use rustc_hash::FxHashSet;
use syntax::ast::{make, ConstArg};
use traits::FnTrait;
use triomphe::Arc;
use utils::Generics;

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;
Expand Down Expand Up @@ -719,3 +721,16 @@ where
value.visit_with(&mut collector, DebruijnIndex::INNERMOST);
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()))
}
81 changes: 42 additions & 39 deletions crates/hir-ty/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,19 @@ impl<'a> TyLoweringContext<'a> {
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 {
generics(
self.db.upcast(),
Expand Down Expand Up @@ -241,17 +254,7 @@ impl<'a> TyLoweringContext<'a> {
}
TypeRef::Array(inner, len) => {
let inner_ty = self.lower_ty(inner);
let const_len = const_or_path_to_chalk(
self.db,
self.resolver,
self.owner,
TyBuilder::usize(),
len,
self.type_param_mode,
|| self.generics(),
self.in_binders,
);

let const_len = self.lower_const(len, TyBuilder::usize());
TyKind::Array(inner_ty, const_len).intern(Interner)
}
TypeRef::Slice(inner) => {
Expand Down Expand Up @@ -846,18 +849,7 @@ impl<'a> TyLoweringContext<'a> {
arg,
&mut (),
|_, type_ref| self.lower_ty(type_ref),
|_, c, ty| {
const_or_path_to_chalk(
self.db,
self.resolver,
self.owner,
ty,
c,
self.type_param_mode,
|| self.generics(),
self.in_binders,
)
},
|_, const_ref, ty| self.lower_const(const_ref, ty),
) {
had_explicit_args = true;
substs.push(x);
Expand Down Expand Up @@ -1603,24 +1595,35 @@ pub(crate) fn generic_defaults_query(
.iter()
.enumerate()
.map(|(idx, (id, p))| {
let p = match p {
TypeOrConstParamData::TypeParamData(p) => p,
TypeOrConstParamData::ConstParamData(_) => {
// FIXME: implement const generic defaults
let val = unknown_const_as_generic(
db.const_param_ty(ConstParamId::from_unchecked(id)),
match p {
TypeOrConstParamData::TypeParamData(p) => {
let mut ty = p
.default
.as_ref()
.map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
// Each default can only refer to previous parameters.
// 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 val = p.default.as_ref().map_or_else(
|| {
unknown_const_as_generic(
db.const_param_ty(ConstParamId::from_unchecked(id)),
)
},
|c| {
let c = ctx.lower_const(c, ctx.lower_ty(&p.ty));
c.cast(Interner)
},
);
return make_binders(db, &generic_params, val);
// 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)
}
};
let mut ty =
p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));

// Each default can only refer to previous parameters.
// 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))
}
})
// FIXME: use `Arc::from_iter` when it becomes available
.collect::<Vec<_>>(),
Expand Down
22 changes: 16 additions & 6 deletions crates/hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,13 @@ use hir_ty::{
all_super_traits, autoderef,
consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt},
diagnostics::BodyValidationDiagnostic,
known_const_to_ast,
layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding},
method_resolution::{self, TyFingerprint},
mir::{self, interpret_mir},
primitive::UintTy,
traits::FnTrait,
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId,
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg,
GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId,
WhereClause,
Expand Down Expand Up @@ -3128,12 +3129,8 @@ impl TypeParam {
}

pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
let params = db.generic_defaults(self.id.parent());
let local_idx = hir_ty::param_idx(db, self.id.into())?;
let ty = generic_arg_from_param(db, self.id.into())?;
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) {
GenericArgData::Ty(it) => {
Some(Type::new_with_resolver_inner(db, &resolver, it.clone()))
Expand Down Expand Up @@ -3195,6 +3192,19 @@ impl ConstParam {
pub fn ty(self, db: &dyn HirDatabase) -> Type {
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)]
Expand Down
105 changes: 103 additions & 2 deletions crates/ide-assists/src/handlers/add_missing_impl_members.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {
check_assist(
add_missing_default_members,
r#"
struct Bar<const: N: bool> {
struct Bar<const N: usize> {
bar: [i32, N]
}
Expand All @@ -439,7 +439,7 @@ impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> {
$0
}"#,
r#"
struct Bar<const: N: bool> {
struct Bar<const N: usize> {
bar: [i32, N]
}
Expand Down Expand Up @@ -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]
fn test_cursor_after_empty_impl_def() {
check_assist(
Expand Down
2 changes: 1 addition & 1 deletion crates/ide-assists/src/handlers/extract_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -810,7 +810,7 @@ impl FunctionBody {
(true, konst.body(), Some(sema.to_def(&konst)?.ty(sema.db)))
},
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) => {
let expr = cbp.block_expr().map(ast::Expr::BlockExpr);
Expand Down
4 changes: 2 additions & 2 deletions crates/ide-db/src/imports/import_assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use hir::{
use itertools::Itertools;
use rustc_hash::FxHashSet;
use syntax::{
ast::{self, HasName},
ast::{self, make, HasName},
utils::path_to_string_stripping_turbo_fish,
AstNode, SyntaxNode,
};
Expand Down Expand Up @@ -607,7 +607,7 @@ impl ImportCandidate {
fn for_name(sema: &Semantics<'_, RootDatabase>, name: &ast::Name) -> Option<Self> {
if sema
.scope(name.syntax())?
.speculative_resolve(&ast::make::ext::ident_path(&name.text()))
.speculative_resolve(&make::ext::ident_path(&name.text()))
.is_some()
{
return None;
Expand Down

0 comments on commit b771de3

Please sign in to comment.