Skip to content

Commit

Permalink
Auto merge of rust-lang#13725 - bvanjoi:resolve-const-triat-impls, r=…
Browse files Browse the repository at this point in the history
…flodiebold

feat: resolve const for trait impls

Fixed rust-lang#13694
  • Loading branch information
bors committed Dec 10, 2022
2 parents 632f804 + 7012b50 commit a3ea20a
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 18 deletions.
1 change: 1 addition & 0 deletions crates/hir-ty/src/consteval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ pub fn eval_const(
.infer
.assoc_resolutions_for_expr(expr_id)
.ok_or(ConstEvalError::SemanticError("unresolved assoc item"))?
.0
{
hir_def::AssocItemId::FunctionId(_) => {
Err(ConstEvalError::NotSupported("assoc function"))
Expand Down
25 changes: 18 additions & 7 deletions crates/hir-ty/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ pub struct InferenceResult {
/// For each struct literal or pattern, records the variant it resolves to.
variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
/// For each associated item record what it resolves to
assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>,
assoc_resolutions: FxHashMap<ExprOrPatId, (AssocItemId, Option<Substitution>)>,
pub diagnostics: Vec<InferenceDiagnostic>,
pub type_of_expr: ArenaMap<ExprId, Ty>,
/// For each pattern record the type it resolves to.
Expand Down Expand Up @@ -379,11 +379,17 @@ impl InferenceResult {
pub fn variant_resolution_for_pat(&self, id: PatId) -> Option<VariantId> {
self.variant_resolutions.get(&id.into()).copied()
}
pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option<AssocItemId> {
self.assoc_resolutions.get(&id.into()).copied()
pub fn assoc_resolutions_for_expr(
&self,
id: ExprId,
) -> Option<(AssocItemId, Option<Substitution>)> {
self.assoc_resolutions.get(&id.into()).cloned()
}
pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<AssocItemId> {
self.assoc_resolutions.get(&id.into()).copied()
pub fn assoc_resolutions_for_pat(
&self,
id: PatId,
) -> Option<(AssocItemId, Option<Substitution>)> {
self.assoc_resolutions.get(&id.into()).cloned()
}
pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> {
self.type_mismatches.get(&expr.into())
Expand Down Expand Up @@ -647,8 +653,13 @@ impl<'a> InferenceContext<'a> {
self.result.variant_resolutions.insert(id, variant);
}

fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: AssocItemId) {
self.result.assoc_resolutions.insert(id, item);
fn write_assoc_resolution(
&mut self,
id: ExprOrPatId,
item: AssocItemId,
subs: Option<Substitution>,
) {
self.result.assoc_resolutions.insert(id, (item, subs));
}

fn write_pat_ty(&mut self, pat: PatId, ty: Ty) {
Expand Down
4 changes: 2 additions & 2 deletions crates/hir-ty/src/infer/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ impl<'a> InferenceContext<'a> {
AssocItemId::TypeAliasId(_) => unreachable!(),
};

self.write_assoc_resolution(id, item);
self.write_assoc_resolution(id, item, Some(trait_ref.substitution.clone()));
Some((def, Some(trait_ref.substitution)))
}

Expand Down Expand Up @@ -273,7 +273,7 @@ impl<'a> InferenceContext<'a> {
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None,
};

self.write_assoc_resolution(id, item);
self.write_assoc_resolution(id, item, substs.clone());
Some((def, substs))
},
)
Expand Down
42 changes: 38 additions & 4 deletions crates/hir-ty/src/method_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,30 @@ pub(crate) fn iterate_method_candidates<T>(
slot
}

pub fn lookup_impl_const(
db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
const_id: ConstId,
subs: Substitution,
) -> ConstId {
let trait_id = match const_id.lookup(db.upcast()).container {
ItemContainerId::TraitId(id) => id,
_ => return const_id,
};
let substitution = Substitution::from_iter(Interner, subs.iter(Interner));
let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution };

let const_data = db.const_data(const_id);
let name = match const_data.name.as_ref() {
Some(name) => name,
None => return const_id,
};

lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name)
.and_then(|assoc| if let AssocItemId::ConstId(id) = assoc { Some(id) } else { None })
.unwrap_or(const_id)
}

/// Looks up the impl method that actually runs for the trait method `func`.
///
/// Returns `func` if it's not a method defined in a trait or the lookup failed.
Expand All @@ -663,15 +687,17 @@ pub fn lookup_impl_method(
};

let name = &db.function_data(func).name;
lookup_impl_method_for_trait_ref(trait_ref, db, env, name).unwrap_or(func)
lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name)
.and_then(|assoc| if let AssocItemId::FunctionId(id) = assoc { Some(id) } else { None })
.unwrap_or(func)
}

fn lookup_impl_method_for_trait_ref(
fn lookup_impl_assoc_item_for_trait_ref(
trait_ref: TraitRef,
db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
name: &Name,
) -> Option<FunctionId> {
) -> Option<AssocItemId> {
let self_ty = trait_ref.self_type_parameter(Interner);
let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?;
let impls = db.trait_impls_in_deps(env.krate);
Expand All @@ -681,7 +707,15 @@ fn lookup_impl_method_for_trait_ref(

let impl_data = find_matching_impl(impls, table, trait_ref)?;
impl_data.items.iter().find_map(|it| match it {
AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f),
AssocItemId::FunctionId(f) => {
(db.function_data(*f).name == *name).then(|| AssocItemId::FunctionId(*f))
}
AssocItemId::ConstId(c) => db
.const_data(*c)
.name
.as_ref()
.map(|n| *n == *name)
.and_then(|result| if result { Some(AssocItemId::ConstId(*c)) } else { None }),
_ => None,
})
}
Expand Down
34 changes: 29 additions & 5 deletions crates/hir/src/source_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use hir_def::{
path::{ModPath, Path, PathKind},
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
type_ref::Mutability,
AsMacroCall, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, LocalFieldId,
Lookup, ModuleDefId, TraitId, VariantId,
AsMacroCall, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, ItemContainerId,
LocalFieldId, Lookup, ModuleDefId, TraitId, VariantId,
};
use hir_expand::{
builtin_fn_macro::BuiltinFnLikeExpander,
Expand Down Expand Up @@ -482,7 +482,7 @@ impl SourceAnalyzer {
let infer = self.infer.as_deref()?;
if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) {
let expr_id = self.expr_id(db, &path_expr.into())?;
if let Some(assoc) = infer.assoc_resolutions_for_expr(expr_id) {
if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr(expr_id) {
let assoc = match assoc {
AssocItemId::FunctionId(f_in_trait) => {
match infer.type_of_expr.get(expr_id) {
Expand All @@ -501,7 +501,13 @@ impl SourceAnalyzer {
}
}
}

AssocItemId::ConstId(const_id) => {
if let Some(subs) = subs {
self.resolve_impl_const_or_trait_def(db, const_id, subs).into()
} else {
assoc
}
}
_ => assoc,
};

Expand All @@ -515,7 +521,7 @@ impl SourceAnalyzer {
prefer_value_ns = true;
} else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) {
let pat_id = self.pat_id(&path_pat.into())?;
if let Some(assoc) = infer.assoc_resolutions_for_pat(pat_id) {
if let Some((assoc, _)) = infer.assoc_resolutions_for_pat(pat_id) {
return Some(PathResolution::Def(AssocItem::from(assoc).into()));
}
if let Some(VariantId::EnumVariantId(variant)) =
Expand Down Expand Up @@ -792,6 +798,24 @@ impl SourceAnalyzer {
method_resolution::lookup_impl_method(db, env, func, substs)
}

fn resolve_impl_const_or_trait_def(
&self,
db: &dyn HirDatabase,
const_id: ConstId,
subs: Substitution,
) -> ConstId {
let krate = self.resolver.krate();
let owner = match self.resolver.body_owner() {
Some(it) => it,
None => return const_id,
};
let env = owner.as_generic_def_id().map_or_else(
|| Arc::new(hir_ty::TraitEnvironment::empty(krate)),
|d| db.trait_environment(d),
);
method_resolution::lookup_impl_const(db, env, const_id, subs)
}

fn lang_trait_fn(
&self,
db: &dyn HirDatabase,
Expand Down
157 changes: 157 additions & 0 deletions crates/ide/src/hover/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3657,6 +3657,163 @@ enum E {

#[test]
fn hover_const_eval() {
check(
r#"
trait T {
const B: bool = false;
}
impl T for <()> {
/// true
const B: bool = true;
}
fn main() {
<()>::B$0;
}
"#,
expect![[r#"
*B*
```rust
test
```
```rust
const B: bool = true
```
---
true
"#]],
);

check(
r#"
struct A {
i: i32
};
trait T {
const AA: A = A {
i: 1
};
}
impl T for i32 {
const AA: A = A {
i: 2
}
}
fn main() {
<i32>::AA$0;
}
"#,
expect![[r#"
*AA*
```rust
test
```
```rust
const AA: A = A {
i: 2
}
```
"#]],
);

check(
r#"
trait T {
/// false
const B: bool = false;
}
impl T for () {
/// true
const B: bool = true;
}
fn main() {
T::B$0;
}
"#,
expect![[r#"
*B*
```rust
test
```
```rust
const B: bool = false
```
---
false
"#]],
);

check(
r#"
trait T {
/// false
const B: bool = false;
}
impl T for () {
}
fn main() {
<()>::B$0;
}
"#,
expect![[r#"
*B*
```rust
test
```
```rust
const B: bool = false
```
---
false
"#]],
);

check(
r#"
trait T {
/// false
const B: bool = false;
}
impl T for () {
/// true
const B: bool = true;
}
impl T for i32 {}
fn main() {
<i32>::B$0;
}
"#,
expect![[r#"
*B*
```rust
test
```
```rust
const B: bool = false
```
---
false
"#]],
);

// show hex for <10
check(
r#"
Expand Down

0 comments on commit a3ea20a

Please sign in to comment.