From 7e8f17188efcecfdfd1afbbc894a53c65985f836 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 21 Mar 2019 22:13:11 +0300 Subject: diagnostics --- crates/ra_hir/src/ty/infer.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index cff7e7481..269b5162e 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -36,7 +36,8 @@ use crate::{ path::{GenericArgs, GenericArg}, adt::VariantDef, resolve::{Resolver, Resolution}, - nameres::Namespace + nameres::Namespace, + diagnostics::FunctionDiagnostic, }; use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; @@ -96,6 +97,7 @@ pub struct InferenceResult { field_resolutions: FxHashMap, /// For each associated item record what it resolves to assoc_resolutions: FxHashMap, + diagnostics: Vec, pub(super) type_of_expr: ArenaMap, pub(super) type_of_pat: ArenaMap, } @@ -113,6 +115,9 @@ impl InferenceResult { pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option { self.assoc_resolutions.get(&id.into()).map(|it| *it) } + pub(crate) fn diagnostics(&self) -> Vec { + self.diagnostics.clone() + } } impl Index for InferenceResult { @@ -143,6 +148,7 @@ struct InferenceContext<'a, D: HirDatabase> { assoc_resolutions: FxHashMap, type_of_expr: ArenaMap, type_of_pat: ArenaMap, + diagnostics: Vec, /// The return type of the function being inferred. return_ty: Ty, } @@ -155,6 +161,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { assoc_resolutions: FxHashMap::default(), type_of_expr: ArenaMap::default(), type_of_pat: ArenaMap::default(), + diagnostics: Vec::default(), var_unification_table: InPlaceUnificationTable::new(), return_ty: Ty::Unknown, // set in collect_fn_signature db, @@ -181,6 +188,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { assoc_resolutions: self.assoc_resolutions, type_of_expr: expr_types, type_of_pat: pat_types, + diagnostics: self.diagnostics, } } @@ -915,9 +923,18 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Expr::StructLit { path, fields, spread } => { let (ty, def_id) = self.resolve_variant(path.as_ref()); let substs = ty.substs().unwrap_or_else(Substs::empty); - for field in fields { + for (field_idx, field) in fields.into_iter().enumerate() { let field_ty = def_id - .and_then(|it| it.field(self.db, &field.name)) + .and_then(|it| match it.field(self.db, &field.name) { + Some(field) => Some(field), + None => { + self.diagnostics.push(FunctionDiagnostic::NoSuchField { + expr: tgt_expr, + field: field_idx, + }); + None + } + }) .map_or(Ty::Unknown, |field| field.ty(self.db)) .subst(&substs); self.infer_expr(field.expr, &Expectation::has_type(field_ty)); -- cgit v1.2.3 From fcca35969dd7c63a83ee34c4ce7d54cefdb72bbe Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 23 Mar 2019 16:28:47 +0300 Subject: allow dyn diagnostics --- crates/ra_hir/src/ty/infer.rs | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 269b5162e..02708ba0f 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -37,7 +37,8 @@ use crate::{ adt::VariantDef, resolve::{Resolver, Resolution}, nameres::Namespace, - diagnostics::FunctionDiagnostic, + ty::infer::diagnostics::InferenceDiagnostic, + diagnostics::Diagnostics, }; use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; @@ -97,7 +98,7 @@ pub struct InferenceResult { field_resolutions: FxHashMap, /// For each associated item record what it resolves to assoc_resolutions: FxHashMap, - diagnostics: Vec, + diagnostics: Vec, pub(super) type_of_expr: ArenaMap, pub(super) type_of_pat: ArenaMap, } @@ -115,8 +116,13 @@ impl InferenceResult { pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option { self.assoc_resolutions.get(&id.into()).map(|it| *it) } - pub(crate) fn diagnostics(&self) -> Vec { - self.diagnostics.clone() + pub(crate) fn add_diagnostics( + &self, + db: &impl HirDatabase, + owner: Function, + diagnostics: &mut Diagnostics, + ) { + self.diagnostics.iter().for_each(|it| it.add_to(db, owner, diagnostics)) } } @@ -148,7 +154,7 @@ struct InferenceContext<'a, D: HirDatabase> { assoc_resolutions: FxHashMap, type_of_expr: ArenaMap, type_of_pat: ArenaMap, - diagnostics: Vec, + diagnostics: Vec, /// The return type of the function being inferred. return_ty: Ty, } @@ -928,7 +934,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { .and_then(|it| match it.field(self.db, &field.name) { Some(field) => Some(field), None => { - self.diagnostics.push(FunctionDiagnostic::NoSuchField { + self.diagnostics.push(InferenceDiagnostic::NoSuchField { expr: tgt_expr, field: field_idx, }); @@ -1261,3 +1267,24 @@ impl Expectation { Expectation { ty: Ty::Unknown } } } + +mod diagnostics { + use crate::{expr::ExprId, diagnostics::{Diagnostics, NoSuchField}, HirDatabase, Function}; + + #[derive(Debug, PartialEq, Eq, Clone)] + pub(super) enum InferenceDiagnostic { + NoSuchField { expr: ExprId, field: usize }, + } + + impl InferenceDiagnostic { + pub(super) fn add_to(&self, db: &impl HirDatabase, owner: Function, acc: &mut Diagnostics) { + match self { + InferenceDiagnostic::NoSuchField { expr, field } => { + let (file, _) = owner.source(db); + let field = owner.body_source_map(db).field_syntax(*expr, *field); + acc.push(NoSuchField { file, field }) + } + } + } + } +} -- cgit v1.2.3 From 3fb88e95aa5e122a521beec766d5b1264ca4de3b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 23 Mar 2019 18:35:14 +0300 Subject: switch modules to new diagnostics --- crates/ra_hir/src/ty/infer.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 02708ba0f..6dc3edc7a 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -120,9 +120,9 @@ impl InferenceResult { &self, db: &impl HirDatabase, owner: Function, - diagnostics: &mut Diagnostics, + sink: &mut Diagnostics, ) { - self.diagnostics.iter().for_each(|it| it.add_to(db, owner, diagnostics)) + self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) } } @@ -1277,12 +1277,17 @@ mod diagnostics { } impl InferenceDiagnostic { - pub(super) fn add_to(&self, db: &impl HirDatabase, owner: Function, acc: &mut Diagnostics) { + pub(super) fn add_to( + &self, + db: &impl HirDatabase, + owner: Function, + sink: &mut Diagnostics, + ) { match self { InferenceDiagnostic::NoSuchField { expr, field } => { let (file, _) = owner.source(db); let field = owner.body_source_map(db).field_syntax(*expr, *field); - acc.push(NoSuchField { file, field }) + sink.push(NoSuchField { file, field }) } } } -- cgit v1.2.3 From 79df62bc742afa33dcf5bedefd60860ca296b9da Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 23 Mar 2019 20:41:59 +0300 Subject: cleanup --- crates/ra_hir/src/ty/infer.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 6dc3edc7a..5fd602a9e 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -38,7 +38,7 @@ use crate::{ resolve::{Resolver, Resolution}, nameres::Namespace, ty::infer::diagnostics::InferenceDiagnostic, - diagnostics::Diagnostics, + diagnostics::DiagnosticSink, }; use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; @@ -120,7 +120,7 @@ impl InferenceResult { &self, db: &impl HirDatabase, owner: Function, - sink: &mut Diagnostics, + sink: &mut DiagnosticSink, ) { self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) } @@ -1269,7 +1269,7 @@ impl Expectation { } mod diagnostics { - use crate::{expr::ExprId, diagnostics::{Diagnostics, NoSuchField}, HirDatabase, Function}; + use crate::{expr::ExprId, diagnostics::{DiagnosticSink, NoSuchField}, HirDatabase, Function}; #[derive(Debug, PartialEq, Eq, Clone)] pub(super) enum InferenceDiagnostic { @@ -1281,7 +1281,7 @@ mod diagnostics { &self, db: &impl HirDatabase, owner: Function, - sink: &mut Diagnostics, + sink: &mut DiagnosticSink, ) { match self { InferenceDiagnostic::NoSuchField { expr, field } => { -- cgit v1.2.3 From 4c4a714328490d7f2626272663827fd51dfab0bd Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 24 Mar 2019 11:39:47 +0300 Subject: test diagnostics --- crates/ra_hir/src/ty/tests.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 5d8ad4aa7..3aedba243 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2319,3 +2319,27 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() { assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) } } + +#[test] +fn no_such_field_diagnostics() { + let diagnostics = MockDatabase::with_files( + r" + //- /lib.rs + struct S { foo: i32, bar: () } + impl S { + fn new() -> S { + S { + foo: 92, + baz: 62, + } + } + } + ", + ) + .diagnostics(); + + assert_snapshot_matches!(diagnostics, @r###" +"baz: 62": no such field +"### + ); +} -- cgit v1.2.3 From c947c15ce1ec02261803f10568e4659e9396109e Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 24 Mar 2019 17:36:15 +0100 Subject: Basics for trait method resolution --- crates/ra_hir/src/ty/infer.rs | 3 +- crates/ra_hir/src/ty/method_resolution.rs | 60 +++++++++++++++++++++++-------- crates/ra_hir/src/ty/tests.rs | 12 +++---- 3 files changed, 54 insertions(+), 21 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 5fd602a9e..573115321 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -821,7 +821,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } Expr::MethodCall { receiver, args, method_name, generic_args } => { let receiver_ty = self.infer_expr(*receiver, &Expectation::none()); - let resolved = receiver_ty.clone().lookup_method(self.db, method_name); + let resolved = + receiver_ty.clone().lookup_method(self.db, method_name, &self.resolver); let (derefed_receiver_ty, method_ty, def_generics) = match resolved { Some((ty, func)) => { self.write_method_resolution(tgt_expr, func); diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index b1684acf9..708a435b4 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -11,7 +11,7 @@ use crate::{ ids::TraitId, impl_block::{ImplId, ImplBlock, ImplItem}, ty::{Ty, TypeCtor}, - nameres::CrateModuleId, + nameres::CrateModuleId, resolve::Resolver, traits::TraitItem }; @@ -73,18 +73,18 @@ impl CrateImplBlocks { let target_ty = impl_block.target_ty(db); - if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) { - self.impls - .entry(target_ty_fp) - .or_insert_with(Vec::new) - .push((module.module_id, impl_id)); - } - if let Some(tr) = impl_block.target_trait(db) { self.impls_by_trait .entry(tr.id) .or_insert_with(Vec::new) .push((module.module_id, impl_id)); + } else { + if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) { + self.impls + .entry(target_ty_fp) + .or_insert_with(Vec::new) + .push((module.module_id, impl_id)); + } } } @@ -120,20 +120,52 @@ fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option { } impl Ty { - // FIXME: cache this as a query? - // - if so, what signature? (TyFingerprint, Name)? - // - or maybe cache all names and def_ids of methods per fingerprint? /// Look up the method with the given name, returning the actual autoderefed /// receiver type (but without autoref applied yet). - pub fn lookup_method(self, db: &impl HirDatabase, name: &Name) -> Option<(Ty, Function)> { - self.iterate_methods(db, |ty, f| { + pub fn lookup_method( + self, + db: &impl HirDatabase, + name: &Name, + resolver: &Resolver, + ) -> Option<(Ty, Function)> { + // FIXME: what has priority, an inherent method that needs autoderefs or a trait method? + let inherent_method = self.clone().iterate_methods(db, |ty, f| { let sig = f.signature(db); if sig.name() == name && sig.has_self_param() { Some((ty.clone(), f)) } else { None } - }) + }); + inherent_method.or_else(|| self.lookup_trait_method(db, name, resolver)) + } + + fn lookup_trait_method( + self, + db: &impl HirDatabase, + name: &Name, + resolver: &Resolver, + ) -> Option<(Ty, Function)> { + let mut candidates = Vec::new(); + for t in resolver.traits_in_scope() { + let data = t.trait_data(db); + for item in data.items() { + match item { + &TraitItem::Function(m) => { + let sig = m.signature(db); + if sig.name() == name && sig.has_self_param() { + candidates.push((t, m)); + } + } + _ => {} + } + } + } + // FIXME the implements check may result in other obligations or unifying variables? + candidates.retain(|(_t, _m)| /* self implements t */ true); + // FIXME what happens if there are still multiple potential candidates? + let (_chosen_trait, chosen_method) = candidates.first()?; + Some((self.clone(), *chosen_method)) } // This would be nicer if it just returned an iterator, but that runs into diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 3aedba243..971043266 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -1272,8 +1272,8 @@ fn test() { [241; 252) 'Struct::FOO': u32 [262; 263) 'y': u32 [266; 275) 'Enum::BAR': u32 -[285; 286) 'z': u32 -[289; 302) 'TraitTest::ID': u32"### +[285; 286) 'z': {unknown} +[289; 302) 'TraitTest::ID': {unknown}"### ); } @@ -1918,9 +1918,9 @@ fn test() { [110; 114) 'self': &{unknown} [170; 228) '{ ...i128 }': () [176; 178) 'S1': S1 -[176; 187) 'S1.method()': {unknown} +[176; 187) 'S1.method()': u32 [203; 205) 'S2': S2 -[203; 214) 'S2.method()': {unknown}"### +[203; 214) 'S2.method()': u32"### ); } @@ -1964,10 +1964,10 @@ mod bar_test { [169; 173) 'self': &{unknown} [300; 337) '{ ... }': () [310; 311) 'S': S -[310; 320) 'S.method()': {unknown} +[310; 320) 'S.method()': u32 [416; 454) '{ ... }': () [426; 427) 'S': S -[426; 436) 'S.method()': {unknown}"### +[426; 436) 'S.method()': i128"### ); } -- cgit v1.2.3 From 0f7e4a7d2440e7e13a5cdf7e91f262426f0d0d18 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 24 Mar 2019 17:37:27 +0100 Subject: Implement a very naive implements check ... to make the infer_trait_method_simple test have the correct result. --- crates/ra_hir/src/ty/method_resolution.rs | 32 +++++++++++++++++++++++++------ crates/ra_hir/src/ty/tests.rs | 2 +- 2 files changed, 27 insertions(+), 7 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 708a435b4..146e8a02e 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -8,12 +8,12 @@ use rustc_hash::FxHashMap; use crate::{ HirDatabase, Module, Crate, Name, Function, Trait, - ids::TraitId, impl_block::{ImplId, ImplBlock, ImplItem}, ty::{Ty, TypeCtor}, nameres::CrateModuleId, resolve::Resolver, traits::TraitItem }; +use super::{ TraitRef, Substs}; /// This is used as a key for indexing impls. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -38,7 +38,7 @@ pub struct CrateImplBlocks { /// To make sense of the CrateModuleIds, we need the source root. krate: Crate, impls: FxHashMap>, - impls_by_trait: FxHashMap>, + impls_by_trait: FxHashMap>, } impl CrateImplBlocks { @@ -56,8 +56,7 @@ impl CrateImplBlocks { &'a self, tr: &Trait, ) -> impl Iterator + 'a { - let id = tr.id; - self.impls_by_trait.get(&id).into_iter().flat_map(|i| i.iter()).map( + self.impls_by_trait.get(&tr).into_iter().flat_map(|i| i.iter()).map( move |(module_id, impl_id)| { let module = Module { krate: self.krate, module_id: *module_id }; ImplBlock::from_id(module, *impl_id) @@ -75,7 +74,7 @@ impl CrateImplBlocks { if let Some(tr) = impl_block.target_trait(db) { self.impls_by_trait - .entry(tr.id) + .entry(tr) .or_insert_with(Vec::new) .push((module.module_id, impl_id)); } else { @@ -109,6 +108,24 @@ impl CrateImplBlocks { } } +/// Rudimentary check whether an impl exists for a given type and trait; this +/// will actually be done by chalk. +pub(crate) fn implements(db: &impl HirDatabase, trait_ref: TraitRef) -> bool { + // FIXME use all trait impls in the whole crate graph + let krate = trait_ref.trait_.module(db).krate(db); + let krate = match krate { + Some(krate) => krate, + None => return false, + }; + let crate_impl_blocks = db.impls_in_crate(krate); + for impl_block in crate_impl_blocks.lookup_impl_blocks_for_trait(&trait_ref.trait_) { + if &impl_block.target_ty(db) == trait_ref.self_ty() { + return true; + } + } + false +} + fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option { match ty { Ty::Apply(a_ty) => match a_ty.ctor { @@ -162,7 +179,10 @@ impl Ty { } } // FIXME the implements check may result in other obligations or unifying variables? - candidates.retain(|(_t, _m)| /* self implements t */ true); + candidates.retain(|(t, _m)| { + let trait_ref = TraitRef { trait_: *t, substs: Substs::single(self.clone()) }; + db.implements(trait_ref) + }); // FIXME what happens if there are still multiple potential candidates? let (_chosen_trait, chosen_method) = candidates.first()?; Some((self.clone(), *chosen_method)) diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 971043266..655f3c522 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -1920,7 +1920,7 @@ fn test() { [176; 178) 'S1': S1 [176; 187) 'S1.method()': u32 [203; 205) 'S2': S2 -[203; 214) 'S2.method()': u32"### +[203; 214) 'S2.method()': i128"### ); } -- cgit v1.2.3 From 99711c1863fc712dc14ca61809055b283415acbe Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 25 Mar 2019 21:24:20 +0100 Subject: Clean up comments / use nicer Iterator methods --- crates/ra_hir/src/ty/method_resolution.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 146e8a02e..3ac8dc46b 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -118,12 +118,8 @@ pub(crate) fn implements(db: &impl HirDatabase, trait_ref: TraitRef) -> bool { None => return false, }; let crate_impl_blocks = db.impls_in_crate(krate); - for impl_block in crate_impl_blocks.lookup_impl_blocks_for_trait(&trait_ref.trait_) { - if &impl_block.target_ty(db) == trait_ref.self_ty() { - return true; - } - } - false + let mut impl_blocks = crate_impl_blocks.lookup_impl_blocks_for_trait(&trait_ref.trait_); + impl_blocks.any(|impl_block| &impl_block.target_ty(db) == trait_ref.self_ty()) } fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option { @@ -145,7 +141,7 @@ impl Ty { name: &Name, resolver: &Resolver, ) -> Option<(Ty, Function)> { - // FIXME: what has priority, an inherent method that needs autoderefs or a trait method? + // FIXME: trait methods should be used before autoderefs let inherent_method = self.clone().iterate_methods(db, |ty, f| { let sig = f.signature(db); if sig.name() == name && sig.has_self_param() { @@ -178,12 +174,21 @@ impl Ty { } } } - // FIXME the implements check may result in other obligations or unifying variables? + // FIXME: + // - we might not actually be able to determine fully that the type + // implements the trait here; it's enough if we (well, Chalk) determine + // that it's possible. + // - when the trait method is picked, we need to register an + // 'obligation' somewhere so that we later check that it's really + // implemented + // - both points go for additional requirements from where clauses as + // well (in fact, the 'implements' condition could just be considered a + // 'where Self: Trait' clause) candidates.retain(|(t, _m)| { let trait_ref = TraitRef { trait_: *t, substs: Substs::single(self.clone()) }; db.implements(trait_ref) }); - // FIXME what happens if there are still multiple potential candidates? + // FIXME if there's multiple candidates here, that's an ambiguity error let (_chosen_trait, chosen_method) = candidates.first()?; Some((self.clone(), *chosen_method)) } -- cgit v1.2.3