Skip to content

Commit 6378fbc

Browse files
committed
Check for generic parameter mismatches on trait functions.
1 parent b909c36 commit 6378fbc

File tree

6 files changed

+120
-43
lines changed

6 files changed

+120
-43
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+84-40
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use rustc_middle::ty::visit::TypeVisitableExt;
2121
use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt};
2222
use rustc_middle::{bug, span_bug};
2323
use rustc_session::Session;
24-
use rustc_span::{DUMMY_SP, Ident, Span, kw, sym};
24+
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
2525
use rustc_trait_selection::error_reporting::infer::{FailureCode, ObligationCauseExt};
2626
use rustc_trait_selection::infer::InferCtxtExt;
2727
use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt, SelectionContext};
@@ -2414,11 +2414,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24142414
})
24152415
{
24162416
let Some(generic_param) = generic_param else {
2417-
spans.push_span_label(param.span, "");
2417+
spans.push_span_label(param.span(), "");
24182418
continue;
24192419
};
24202420

2421-
let other_params_matched: Vec<(ExpectedIdx, &hir::Param<'_>)> =
2421+
let other_params_matched: Vec<(ExpectedIdx, FnParam<'_>)> =
24222422
params_with_generics
24232423
.iter_enumerated()
24242424
.filter(|&(other_idx, &(other_generic_param, _))| {
@@ -2447,9 +2447,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24472447
let other_param_matched_names: Vec<String> = other_params_matched
24482448
.iter()
24492449
.map(|(idx, other_param)| {
2450-
if let hir::PatKind::Binding(_, _, ident, _) = other_param.pat.kind
2451-
{
2452-
format!("`{ident}`")
2450+
if let Some(name) = other_param.name() {
2451+
format!("`{name}`")
24532452
} else {
24542453
format!("parameter #{}", idx.as_u32() + 1)
24552454
}
@@ -2462,7 +2461,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24622461

24632462
if matched_inputs[idx].is_some() {
24642463
spans.push_span_label(
2465-
param.span,
2464+
param.span(),
24662465
format!(
24672466
"{} need{} to match the {} type of this parameter",
24682467
listify(&other_param_matched_names, |n| n.to_string())
@@ -2477,7 +2476,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24772476
);
24782477
} else {
24792478
spans.push_span_label(
2480-
param.span,
2479+
param.span(),
24812480
format!(
24822481
"this parameter needs to match the {} type of {}",
24832482
matched_ty,
@@ -2488,7 +2487,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24882487
}
24892488
generics_with_unmatched_params.push(generic_param);
24902489
} else {
2491-
spans.push_span_label(param.span, "");
2490+
spans.push_span_label(param.span(), "");
24922491
}
24932492
}
24942493

@@ -2515,8 +2514,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
25152514
}
25162515
})
25172516
.map(|(idx, &(_, param))| {
2518-
if let hir::PatKind::Binding(_, _, ident, _) = param.pat.kind {
2519-
format!("`{ident}`")
2517+
if let Some(name) = param.name() {
2518+
format!("`{name}`")
25202519
} else {
25212520
format!("parameter #{}", idx.as_u32() + 1)
25222521
}
@@ -2673,35 +2672,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26732672
&self,
26742673
def_id: DefId,
26752674
is_method: bool,
2676-
) -> Option<IndexVec<ExpectedIdx, (Option<&hir::GenericParam<'_>>, &hir::Param<'_>)>> {
2677-
let fn_node = self.tcx.hir().get_if_local(def_id)?;
2678-
let fn_decl = fn_node.fn_decl()?;
2679-
let generic_params = fn_node.generics()?.params;
2680-
2681-
// Remove both the receiver and variadic arguments. Neither can have an unmatched generic
2682-
// parameter.
2683-
let params = self.tcx.hir().body(fn_node.body_id()?).params;
2684-
let params = params.get(is_method as usize..params.len() - fn_decl.c_variadic as usize)?;
2685-
let fn_inputs = fn_decl.inputs.get(is_method as usize..)?;
2686-
debug_assert_eq!(params.len(), fn_inputs.len());
2687-
2688-
Some(
2689-
fn_inputs
2690-
.into_iter()
2691-
.map(|param| {
2692-
if let hir::TyKind::Path(QPath::Resolved(
2693-
_,
2694-
&hir::Path { res: Res::Def(_, res_def_id), .. },
2695-
)) = param.kind
2696-
{
2697-
generic_params.iter().find(|param| param.def_id.to_def_id() == res_def_id)
2698-
} else {
2699-
None
2700-
}
2701-
})
2702-
.zip(params)
2703-
.collect(),
2704-
)
2675+
) -> Option<IndexVec<ExpectedIdx, (Option<&hir::GenericParam<'_>>, FnParam<'_>)>> {
2676+
let (sig, generics, body_id, param_names) = match self.tcx.hir().get_if_local(def_id)? {
2677+
hir::Node::TraitItem(&hir::TraitItem {
2678+
generics,
2679+
kind: hir::TraitItemKind::Fn(sig, trait_fn),
2680+
..
2681+
}) => match trait_fn {
2682+
hir::TraitFn::Required(params) => (sig, generics, None, Some(params)),
2683+
hir::TraitFn::Provided(body) => (sig, generics, Some(body), None),
2684+
},
2685+
hir::Node::ImplItem(&hir::ImplItem {
2686+
generics,
2687+
kind: hir::ImplItemKind::Fn(sig, body),
2688+
..
2689+
})
2690+
| hir::Node::Item(&hir::Item {
2691+
kind: hir::ItemKind::Fn { sig, generics, body, .. },
2692+
..
2693+
}) => (sig, generics, Some(body), None),
2694+
_ => return None,
2695+
};
2696+
2697+
// Make sure to remove both the receiver and variadic argument. Both are removed
2698+
// when matching parameter types.
2699+
let fn_inputs = sig.decl.inputs.get(is_method as usize..)?.iter().map(|param| {
2700+
if let hir::TyKind::Path(QPath::Resolved(
2701+
_,
2702+
&hir::Path { res: Res::Def(_, res_def_id), .. },
2703+
)) = param.kind
2704+
{
2705+
generics.params.iter().find(|param| param.def_id.to_def_id() == res_def_id)
2706+
} else {
2707+
None
2708+
}
2709+
});
2710+
match (body_id, param_names) {
2711+
(Some(_), Some(_)) | (None, None) => unreachable!(),
2712+
(Some(body), None) => {
2713+
let params = self.tcx.hir().body(body).params;
2714+
let params =
2715+
params.get(is_method as usize..params.len() - sig.decl.c_variadic as usize)?;
2716+
debug_assert_eq!(params.len(), fn_inputs.len());
2717+
Some(fn_inputs.zip(params.iter().map(|param| FnParam::Param(param))).collect())
2718+
}
2719+
(None, Some(params)) => {
2720+
let params = params.get(is_method as usize..)?;
2721+
debug_assert_eq!(params.len(), fn_inputs.len());
2722+
Some(fn_inputs.zip(params.iter().map(|param| FnParam::Name(param))).collect())
2723+
}
2724+
}
27052725
}
27062726
}
27072727

@@ -2724,3 +2744,27 @@ impl<'tcx> Visitor<'tcx> for FindClosureArg<'tcx> {
27242744
hir::intravisit::walk_expr(self, ex);
27252745
}
27262746
}
2747+
2748+
#[derive(Clone, Copy)]
2749+
enum FnParam<'hir> {
2750+
Param(&'hir hir::Param<'hir>),
2751+
Name(&'hir Ident),
2752+
}
2753+
impl FnParam<'_> {
2754+
fn span(&self) -> Span {
2755+
match self {
2756+
Self::Param(x) => x.span,
2757+
Self::Name(x) => x.span,
2758+
}
2759+
}
2760+
2761+
fn name(&self) -> Option<Symbol> {
2762+
match self {
2763+
Self::Param(x) if let hir::PatKind::Binding(_, _, ident, _) = x.pat.kind => {
2764+
Some(ident.name)
2765+
}
2766+
Self::Name(x) if x.name != kw::Empty => Some(x.name),
2767+
_ => None,
2768+
}
2769+
}
2770+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
trait Foo {
2+
fn same_type<T>(_: T, _: T);
3+
}
4+
5+
fn f<T: Foo, X, Y>(x: X, y: Y) {
6+
T::same_type([x], Some(y));
7+
//~^ ERROR mismatched types
8+
}
9+
10+
fn main() {}
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/param-mismatch-trait-fn.rs:6:23
3+
|
4+
LL | T::same_type([x], Some(y));
5+
| ------------ --- ^^^^^^^ expected `[X; 1]`, found `Option<Y>`
6+
| | |
7+
| | expected all arguments to be this `[X; 1]` type because they need to match the type of this parameter
8+
| arguments to this function are incorrect
9+
|
10+
= note: expected array `[X; 1]`
11+
found enum `Option<Y>`
12+
note: associated function defined here
13+
--> $DIR/param-mismatch-trait-fn.rs:2:8
14+
|
15+
LL | fn same_type<T>(_: T, _: T);
16+
| ^^^^^^^^^ - - - this parameter needs to match the `[X; 1]` type of parameter #1
17+
| | |
18+
| | parameter #2 needs to match the `[X; 1]` type of this parameter
19+
| parameter #1 and parameter #2 both reference this parameter `T`
20+
21+
error: aborting due to 1 previous error
22+
23+
For more information about this error, try `rustc --explain E0308`.

tests/ui/methods/issues/issue-61525.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ note: method defined here
3232
--> $DIR/issue-61525.rs:2:8
3333
|
3434
LL | fn query<Q>(self, q: Q);
35-
| ^^^^^
35+
| ^^^^^ -
3636

3737
error: aborting due to 2 previous errors
3838

tests/ui/suggestions/trait-with-missing-associated-type-restriction.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ note: method defined here
9494
--> $DIR/trait-with-missing-associated-type-restriction.rs:9:8
9595
|
9696
LL | fn funk(&self, _: Self::A);
97-
| ^^^^
97+
| ^^^^ -
9898
help: consider constraining the associated type `<T as Trait<i32>>::A` to `{integer}`
9999
|
100100
LL | fn bar2<T: Trait<i32, A = {integer}>>(x: T) {

tests/ui/traits/issue-52893.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ note: method defined here
2222
--> $DIR/issue-52893.rs:11:8
2323
|
2424
LL | fn push(self, other: T) -> Self::PushRes;
25-
| ^^^^
25+
| ^^^^ -----
2626

2727
error: aborting due to 1 previous error
2828

0 commit comments

Comments
 (0)