1
Fork 0

Rollup merge of #136497 - Jarcho:fn_ctxt, r=compiler-errors

Report generic mismatches when calling bodyless trait functions

Don't know if there's an open issue for this. Just happened to notice this when working in that area.

The awkward extra spans added to the diagnostics of some tests (e.g. `trait-with-missing-associated-type-restriction`) is consistent with what happens for normal functions. Should probably be removed since that span doesn't seem to note anything useful.

First and third commit are both cleanups removing some unnecessary work. Second commit has the actual fix.

fixes #135124
This commit is contained in:
Matthias Krüger 2025-02-04 18:49:40 +01:00 committed by GitHub
commit 29e1dddd83
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 243 additions and 124 deletions

View file

@ -13,7 +13,7 @@ use rustc_hir::{ExprKind, HirId, Node, QPath};
use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt;
use rustc_hir_analysis::check::potentially_plural_count;
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
use rustc_index::IndexVec;
use rustc_index::{Idx, IndexVec};
use rustc_infer::infer::{DefineOpaqueTypes, InferOk, TypeTrace};
use rustc_middle::ty::adjustment::AllowTwoPhase;
use rustc_middle::ty::error::TypeError;
@ -21,7 +21,7 @@ use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_session::Session;
use rustc_span::{DUMMY_SP, Ident, Span, kw, sym};
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
use rustc_trait_selection::error_reporting::infer::{FailureCode, ObligationCauseExt};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt, SelectionContext};
@ -126,7 +126,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Err(guar) => Err(guar),
};
if let Err(guar) = has_error {
let err_inputs = self.err_args(args_no_rcvr.len(), guar);
let err_inputs = self.err_args(
method.map_or(args_no_rcvr.len(), |method| method.sig.inputs().len() - 1),
guar,
);
let err_output = Ty::new_error(self.tcx, guar);
let err_inputs = match tuple_arguments {
@ -2374,11 +2377,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let check_for_matched_generics = || {
if matched_inputs.iter().any(|x| x.is_some())
&& params_with_generics.iter().any(|x| x.1.is_some())
&& params_with_generics.iter().any(|(x, _)| x.is_some())
{
for &(idx, generic, _) in &params_with_generics {
for (idx, (generic, _)) in params_with_generics.iter_enumerated() {
// Param has to have a generic and be matched to be relevant
if matched_inputs[idx.into()].is_none() {
if matched_inputs[idx].is_none() {
continue;
}
@ -2386,10 +2389,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
continue;
};
for unmatching_idx in idx + 1..params_with_generics.len() {
if matched_inputs[unmatching_idx.into()].is_none()
for unmatching_idx in
idx.plus(1)..ExpectedIdx::from_usize(params_with_generics.len())
{
if matched_inputs[unmatching_idx].is_none()
&& let Some(unmatched_idx_param_generic) =
params_with_generics[unmatching_idx].1
params_with_generics[unmatching_idx].0
&& unmatched_idx_param_generic.name.ident()
== generic.name.ident()
{
@ -2404,61 +2409,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let check_for_matched_generics = check_for_matched_generics();
for &(idx, generic_param, param) in
params_with_generics.iter().filter(|&(idx, _, _)| {
for (idx, &(generic_param, param)) in
params_with_generics.iter_enumerated().filter(|&(idx, _)| {
check_for_matched_generics
|| expected_idx.is_none_or(|expected_idx| expected_idx == *idx)
|| expected_idx
.is_none_or(|expected_idx| expected_idx == idx.as_usize())
})
{
let Some(generic_param) = generic_param else {
spans.push_span_label(param.span, "");
spans.push_span_label(param.span(), "");
continue;
};
let other_params_matched: Vec<(usize, &hir::Param<'_>)> = params_with_generics
.iter()
.filter(|(other_idx, other_generic_param, _)| {
if *other_idx == idx {
return false;
}
let Some(other_generic_param) = other_generic_param else {
return false;
};
if matched_inputs[idx.into()].is_none()
&& matched_inputs[(*other_idx).into()].is_none()
{
return false;
}
if matched_inputs[idx.into()].is_some()
&& matched_inputs[(*other_idx).into()].is_some()
{
return false;
}
other_generic_param.name.ident() == generic_param.name.ident()
})
.map(|&(other_idx, _, other_param)| (other_idx, other_param))
.collect();
let other_params_matched: Vec<(ExpectedIdx, FnParam<'_>)> =
params_with_generics
.iter_enumerated()
.filter(|&(other_idx, &(other_generic_param, _))| {
if other_idx == idx {
return false;
}
let Some(other_generic_param) = other_generic_param else {
return false;
};
if matched_inputs[idx].is_none()
&& matched_inputs[other_idx].is_none()
{
return false;
}
if matched_inputs[idx].is_some()
&& matched_inputs[other_idx].is_some()
{
return false;
}
other_generic_param.name.ident() == generic_param.name.ident()
})
.map(|(other_idx, &(_, other_param))| (other_idx, other_param))
.collect();
if !other_params_matched.is_empty() {
let other_param_matched_names: Vec<String> = other_params_matched
.iter()
.map(|(idx, other_param)| {
if let hir::PatKind::Binding(_, _, ident, _) = other_param.pat.kind
{
format!("`{ident}`")
if let Some(name) = other_param.name() {
format!("`{name}`")
} else {
format!("parameter #{}", idx + 1)
format!("parameter #{}", idx.as_u32() + 1)
}
})
.collect();
let matched_ty = self
.resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1)
.resolve_vars_if_possible(formal_and_expected_inputs[idx].1)
.sort_string(self.tcx);
if matched_inputs[idx.into()].is_some() {
if matched_inputs[idx].is_some() {
spans.push_span_label(
param.span,
param.span(),
format!(
"{} need{} to match the {} type of this parameter",
listify(&other_param_matched_names, |n| n.to_string())
@ -2473,7 +2479,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
} else {
spans.push_span_label(
param.span,
param.span(),
format!(
"this parameter needs to match the {} type of {}",
matched_ty,
@ -2484,7 +2490,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
generics_with_unmatched_params.push(generic_param);
} else {
spans.push_span_label(param.span, "");
spans.push_span_label(param.span(), "");
}
}
@ -2502,19 +2508,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
})
{
let param_idents_matching: Vec<String> = params_with_generics
.iter()
.filter(|(_, generic, _)| {
.iter_enumerated()
.filter(|&(_, &(generic, _))| {
if let Some(generic) = generic {
generic.name.ident() == generic_param.name.ident()
} else {
false
}
})
.map(|(idx, _, param)| {
if let hir::PatKind::Binding(_, _, ident, _) = param.pat.kind {
format!("`{ident}`")
.map(|(idx, &(_, param))| {
if let Some(name) = param.name() {
format!("`{name}`")
} else {
format!("parameter #{}", idx + 1)
format!("parameter #{}", idx.as_u32() + 1)
}
})
.collect();
@ -2607,12 +2613,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let Some(params_with_generics) = self.get_hir_params_with_generics(def_id, is_method) {
debug_assert_eq!(params_with_generics.len(), matched_inputs.len());
for &(idx, generic_param, _) in &params_with_generics {
if matched_inputs[idx.into()].is_none() {
for (idx, (generic_param, _)) in params_with_generics.iter_enumerated() {
if matched_inputs[idx].is_none() {
continue;
}
let Some((_, matched_arg_span)) = provided_arg_tys.get(idx.into()) else {
let Some((_, matched_arg_span)) = provided_arg_tys.get(idx.to_provided_idx())
else {
continue;
};
@ -2620,32 +2627,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
continue;
};
let mut idxs_matched: Vec<usize> = vec![];
for &(other_idx, _, _) in
params_with_generics.iter().filter(|&&(other_idx, other_generic_param, _)| {
let idxs_matched = params_with_generics
.iter_enumerated()
.filter(|&(other_idx, (other_generic_param, _))| {
if other_idx == idx {
return false;
}
let Some(other_generic_param) = other_generic_param else {
return false;
};
if matched_inputs[other_idx.into()].is_some() {
if matched_inputs[other_idx].is_some() {
return false;
}
other_generic_param.name.ident() == generic_param.name.ident()
})
{
idxs_matched.push(other_idx);
}
.count();
if idxs_matched.is_empty() {
if idxs_matched == 0 {
continue;
}
let expected_display_type = self
.resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1)
.sort_string(self.tcx);
let label = if idxs_matched.len() == params_with_generics.len() - 1 {
let label = if idxs_matched == params_with_generics.len() - 1 {
format!(
"expected all arguments to be this {} type because they need to match the type of this parameter",
expected_display_type
@ -2664,62 +2669,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
/// Returns the parameters of a function, with their generic parameters if those are the full
/// type of that parameter. Returns `None` if the function body is unavailable (eg is an instrinsic).
/// type of that parameter. Returns `None` if the function has no generics or the body is
/// unavailable (eg is an instrinsic).
fn get_hir_params_with_generics(
&self,
def_id: DefId,
is_method: bool,
) -> Option<Vec<(usize, Option<&hir::GenericParam<'_>>, &hir::Param<'_>)>> {
let fn_node = self.tcx.hir().get_if_local(def_id)?;
let fn_decl = fn_node.fn_decl()?;
let generic_params: Vec<Option<&hir::GenericParam<'_>>> = fn_decl
.inputs
.into_iter()
.skip(if is_method { 1 } else { 0 })
.map(|param| {
if let hir::TyKind::Path(QPath::Resolved(
_,
hir::Path { res: Res::Def(_, res_def_id), .. },
)) = param.kind
{
fn_node
.generics()
.into_iter()
.flat_map(|generics| generics.params)
.find(|param| &param.def_id.to_def_id() == res_def_id)
} else {
None
}
) -> Option<IndexVec<ExpectedIdx, (Option<&hir::GenericParam<'_>>, FnParam<'_>)>> {
let (sig, generics, body_id, param_names) = match self.tcx.hir().get_if_local(def_id)? {
hir::Node::TraitItem(&hir::TraitItem {
generics,
kind: hir::TraitItemKind::Fn(sig, trait_fn),
..
}) => match trait_fn {
hir::TraitFn::Required(params) => (sig, generics, None, Some(params)),
hir::TraitFn::Provided(body) => (sig, generics, Some(body), None),
},
hir::Node::ImplItem(&hir::ImplItem {
generics,
kind: hir::ImplItemKind::Fn(sig, body),
..
})
.collect();
| hir::Node::Item(&hir::Item {
kind: hir::ItemKind::Fn { sig, generics, body, .. },
..
}) => (sig, generics, Some(body), None),
_ => return None,
};
let mut params: Vec<&hir::Param<'_>> = self
.tcx
.hir()
.body(fn_node.body_id()?)
.params
.into_iter()
.skip(if is_method { 1 } else { 0 })
.collect();
// The surrounding code expects variadic functions to not have a parameter representing
// the "..." parameter. This is already true of the FnDecl but not of the body params, so
// we drop it if it exists.
if fn_decl.c_variadic {
params.pop();
// Make sure to remove both the receiver and variadic argument. Both are removed
// when matching parameter types.
let fn_inputs = sig.decl.inputs.get(is_method as usize..)?.iter().map(|param| {
if let hir::TyKind::Path(QPath::Resolved(
_,
&hir::Path { res: Res::Def(_, res_def_id), .. },
)) = param.kind
{
generics.params.iter().find(|param| param.def_id.to_def_id() == res_def_id)
} else {
None
}
});
match (body_id, param_names) {
(Some(_), Some(_)) | (None, None) => unreachable!(),
(Some(body), None) => {
let params = self.tcx.hir().body(body).params;
let params =
params.get(is_method as usize..params.len() - sig.decl.c_variadic as usize)?;
debug_assert_eq!(params.len(), fn_inputs.len());
Some(fn_inputs.zip(params.iter().map(|param| FnParam::Param(param))).collect())
}
(None, Some(params)) => {
let params = params.get(is_method as usize..)?;
debug_assert_eq!(params.len(), fn_inputs.len());
Some(fn_inputs.zip(params.iter().map(|param| FnParam::Name(param))).collect())
}
}
debug_assert_eq!(params.len(), generic_params.len());
Some(
generic_params
.into_iter()
.zip(params)
.enumerate()
.map(|(a, (b, c))| (a, b, c))
.collect(),
)
}
}
@ -2742,3 +2747,27 @@ impl<'tcx> Visitor<'tcx> for FindClosureArg<'tcx> {
hir::intravisit::walk_expr(self, ex);
}
}
#[derive(Clone, Copy)]
enum FnParam<'hir> {
Param(&'hir hir::Param<'hir>),
Name(&'hir Ident),
}
impl FnParam<'_> {
fn span(&self) -> Span {
match self {
Self::Param(x) => x.span,
Self::Name(x) => x.span,
}
}
fn name(&self) -> Option<Symbol> {
match self {
Self::Param(x) if let hir::PatKind::Binding(_, _, ident, _) = x.pat.kind => {
Some(ident.name)
}
Self::Name(x) if x.name != kw::Empty => Some(x.name),
_ => None,
}
}
}

View file

@ -1,9 +0,0 @@
//@ known-bug: #135124
trait A {
fn y(&self)
{
fn call() -> impl Sized {}
self.fold(call());
}
fn fold(&self, &self._) {}
}

View file

@ -0,0 +1,20 @@
// Used to ICE due to a size mismatch between the actual fake signature of `fold` and the
// generated signature used reporting the parameter mismatch at the call site.
// See issue #135124
trait A {
fn y(&self)
{
fn call() -> impl Sized {}
self.fold(call(), call());
}
fn fold<T>(&self, _: T, &self._) {}
//~^ ERROR unexpected `self` parameter in function
//~| ERROR expected one of `)` or `,`, found `.`
//~| ERROR identifier `self` is bound more than once in this parameter list
//~| WARNING anonymous parameters are deprecated
//~| WARNING this is accepted in the current edition
//~| ERROR the placeholder `_` is not allowed within types
}
fn main() {}

View file

@ -0,0 +1,46 @@
error: unexpected `self` parameter in function
--> $DIR/error-recovery-mismatch.rs:11:29
|
LL | fn fold<T>(&self, _: T, &self._) {}
| ^^^^^ must be the first parameter of an associated function
error: expected one of `)` or `,`, found `.`
--> $DIR/error-recovery-mismatch.rs:11:34
|
LL | fn fold<T>(&self, _: T, &self._) {}
| ^
| |
| expected one of `)` or `,`
| help: missing `,`
error[E0415]: identifier `self` is bound more than once in this parameter list
--> $DIR/error-recovery-mismatch.rs:11:30
|
LL | fn fold<T>(&self, _: T, &self._) {}
| ^^^^ used as parameter more than once
warning: anonymous parameters are deprecated and will be removed in the next edition
--> $DIR/error-recovery-mismatch.rs:11:35
|
LL | fn fold<T>(&self, _: T, &self._) {}
| ^ help: try naming the parameter or explicitly ignoring it: `_: _`
|
= warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
= note: for more information, see issue #41686 <https://github.com/rust-lang/rust/issues/41686>
= note: `#[warn(anonymous_parameters)]` on by default
error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
--> $DIR/error-recovery-mismatch.rs:11:35
|
LL | fn fold<T>(&self, _: T, &self._) {}
| ^ not allowed in type signatures
|
help: use type parameters instead
|
LL | fn fold<T, U>(&self, _: T, &self.U) {}
| +++ ~
error: aborting due to 4 previous errors; 1 warning emitted
Some errors have detailed explanations: E0121, E0415.
For more information about an error, try `rustc --explain E0121`.

View file

@ -0,0 +1,10 @@
trait Foo {
fn same_type<T>(_: T, _: T);
}
fn f<T: Foo, X, Y>(x: X, y: Y) {
T::same_type([x], Some(y));
//~^ ERROR mismatched types
}
fn main() {}

View file

@ -0,0 +1,23 @@
error[E0308]: mismatched types
--> $DIR/param-mismatch-trait-fn.rs:6:23
|
LL | T::same_type([x], Some(y));
| ------------ --- ^^^^^^^ expected `[X; 1]`, found `Option<Y>`
| | |
| | expected all arguments to be this `[X; 1]` type because they need to match the type of this parameter
| arguments to this function are incorrect
|
= note: expected array `[X; 1]`
found enum `Option<Y>`
note: associated function defined here
--> $DIR/param-mismatch-trait-fn.rs:2:8
|
LL | fn same_type<T>(_: T, _: T);
| ^^^^^^^^^ - - - this parameter needs to match the `[X; 1]` type of parameter #1
| | |
| | parameter #2 needs to match the `[X; 1]` type of this parameter
| parameter #1 and parameter #2 both reference this parameter `T`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0308`.

View file

@ -32,7 +32,7 @@ note: method defined here
--> $DIR/issue-61525.rs:2:8
|
LL | fn query<Q>(self, q: Q);
| ^^^^^
| ^^^^^ -
error: aborting due to 2 previous errors

View file

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

View file

@ -22,7 +22,7 @@ note: method defined here
--> $DIR/issue-52893.rs:11:8
|
LL | fn push(self, other: T) -> Self::PushRes;
| ^^^^
| ^^^^ -----
error: aborting due to 1 previous error