Skip to content

Commit 2e956b0

Browse files
committed
Improve error message
1 parent 2e989f4 commit 2e956b0

3 files changed

Lines changed: 62 additions & 14 deletions

File tree

compiler/noirc_frontend/src/elaborator/path_resolution.rs

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -946,18 +946,46 @@ impl Elaborator<'_> {
946946
Some(Ok(PathResolutionItem::TraitConstant(type_id, *trait_id, *def_id)))
947947
}
948948
_ => {
949-
// Multiple matching constants - ambiguous. Multiple impls of the same
950-
// generic trait can produce duplicate trait names here, so dedupe.
951-
let mut traits = vecmap(&in_scope, |(_, trait_id, _)| {
952-
let trait_ = self.interner.get_trait(*trait_id);
953-
self.fully_qualified_trait_path(trait_)
954-
});
955-
traits.sort();
956-
traits.dedup();
957-
Some(Err(PathResolutionError::MultipleTraitsInScope {
958-
ident: ident.clone(),
959-
traits,
960-
}))
949+
// Multiple matching constants - ambiguous. If all candidates are from the
950+
// same trait, this is multiple impls of one trait — report it with the
951+
// specific impl signatures so the user can see what to disambiguate.
952+
let first_trait_id = in_scope[0].1;
953+
let same_trait =
954+
in_scope.iter().all(|(_, trait_id, _)| *trait_id == first_trait_id);
955+
if same_trait {
956+
let trait_name =
957+
self.fully_qualified_trait_path(self.interner.get_trait(first_trait_id));
958+
let type_name = self_type.to_string();
959+
let impls = vecmap(&in_scope, |(_, _, impl_id)| {
960+
let ordered = &self.interner.get_trait_generics_for_impl(*impl_id).ordered;
961+
let signature = if ordered.is_empty() {
962+
trait_name.clone()
963+
} else {
964+
let args = vecmap(ordered, |t| t.to_string()).join(", ");
965+
format!("{trait_name}<{args}>")
966+
};
967+
let location =
968+
self.interner.get_trait_implementation(*impl_id).borrow().location;
969+
(signature, location)
970+
});
971+
Some(Err(PathResolutionError::MultipleApplicableImpls {
972+
ident: ident.clone(),
973+
trait_name,
974+
type_name,
975+
impls,
976+
}))
977+
} else {
978+
let mut traits = vecmap(&in_scope, |(_, trait_id, _)| {
979+
let trait_ = self.interner.get_trait(*trait_id);
980+
self.fully_qualified_trait_path(trait_)
981+
});
982+
traits.sort();
983+
traits.dedup();
984+
Some(Err(PathResolutionError::MultipleTraitsInScope {
985+
ident: ident.clone(),
986+
traits,
987+
}))
988+
}
961989
}
962990
}
963991
}

compiler/noirc_frontend/src/hir/resolution/import.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ pub enum PathResolutionError {
6161
UnresolvedWithPossibleTraitsToImport { ident: Ident, traits: Vec<String> },
6262
#[error("Multiple applicable items in scope")]
6363
MultipleTraitsInScope { ident: Ident, traits: Vec<String> },
64+
#[error("Multiple `impl`s of `{trait_name}` apply to `{type_name}`")]
65+
MultipleApplicableImpls {
66+
ident: Ident,
67+
trait_name: String,
68+
type_name: String,
69+
impls: Vec<(String, Location)>,
70+
},
6471
#[error("No function named '{ident}' found for '{typ}' in the current scope")]
6572
UnresolvedMethodForType { typ: String, ident: Ident, available_impls: Vec<String> },
6673
}
@@ -75,6 +82,7 @@ impl PathResolutionError {
7582
| PathResolutionError::NotAModule { ident, .. }
7683
| PathResolutionError::TraitMethodNotInScope { ident, .. }
7784
| PathResolutionError::MultipleTraitsInScope { ident, .. }
85+
| PathResolutionError::MultipleApplicableImpls { ident, .. }
7886
| PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, .. }
7987
| PathResolutionError::UnresolvedMethodForType { ident, .. } => ident.location(),
8088
}
@@ -142,6 +150,17 @@ impl<'a> From<&'a PathResolutionError> for CustomDiagnostic {
142150
ident.location(),
143151
)
144152
}
153+
PathResolutionError::MultipleApplicableImpls { ident, impls, .. } => {
154+
let mut diag = CustomDiagnostic::simple_error(
155+
error.to_string(),
156+
String::new(),
157+
ident.location(),
158+
);
159+
for (signature, location) in impls {
160+
diag.add_secondary(format!("candidate `{signature}` defined here"), *location);
161+
}
162+
diag
163+
}
145164
PathResolutionError::UnresolvedMethodForType { typ: _, ident, available_impls } => {
146165
let secondary = if available_impls.is_empty() {
147166
String::new()

compiler/noirc_frontend/src/tests/traits/trait_associated_items.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2172,17 +2172,18 @@ fn associated_constant_shorthand_on_generic_trait_is_ambiguous_with_multiple_imp
21722172
pub struct Bar {}
21732173
21742174
impl Foo<u8> for Bar {
2175+
~~~ candidate `Foo<u8>` defined here
21752176
let CONST: u32 = 8;
21762177
}
21772178
21782179
impl Foo<u16> for Bar {
2180+
~~~ candidate `Foo<u16>` defined here
21792181
let CONST: u32 = 16;
21802182
}
21812183
21822184
fn main() {
21832185
let _: u32 = Bar::CONST;
2184-
^^^^^ Multiple applicable items in scope
2185-
~~~~~ Multiple traits which provide `CONST` are implemented and in scope: `Foo`
2186+
^^^^^ Multiple `impl`s of `Foo` apply to `Bar`
21862187
}
21872188
"#;
21882189
check_errors(src);

0 commit comments

Comments
 (0)