Fix lifetimes len diagnostics for fn pointers

This commit is contained in:
Chayim Refael Friedman 2026-01-09 13:47:13 +02:00
parent d87c468b42
commit e80fbd4bca
3 changed files with 47 additions and 27 deletions

View file

@ -27,8 +27,8 @@ use hir_def::{
resolver::{HasResolver, LifetimeNs, Resolver, TypeNs, ValueNs},
signatures::{FunctionSignature, TraitFlags, TypeAliasFlags},
type_ref::{
ConstRef, LifetimeRefId, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound,
TypeRef, TypeRefId,
ConstRef, FnType, LifetimeRefId, PathId, TraitBoundModifier, TraitRef as HirTraitRef,
TypeBound, TypeRef, TypeRefId,
},
};
use hir_expand::name::Name;
@ -98,7 +98,7 @@ impl ImplTraitLoweringState {
}
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub enum LifetimeElisionKind<'db> {
/// Create a new anonymous lifetime parameter and reference it.
///
@ -437,26 +437,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
Ty::new_ref(interner, lifetime, inner_ty, lower_mutability(ref_.mutability))
}
TypeRef::Placeholder => Ty::new_error(interner, ErrorGuaranteed),
TypeRef::Fn(fn_) => {
let substs = self.with_shifted_in(
DebruijnIndex::from_u32(1),
|ctx: &mut TyLoweringContext<'_, '_>| {
Tys::new_from_iter(
interner,
fn_.params.iter().map(|&(_, tr)| ctx.lower_ty(tr)),
)
},
);
Ty::new_fn_ptr(
interner,
Binder::dummy(FnSig {
abi: fn_.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol),
safety: if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe },
c_variadic: fn_.is_varargs,
inputs_and_output: substs,
}),
)
}
TypeRef::Fn(fn_) => self.lower_fn_ptr(fn_),
TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds),
TypeRef::ImplTrait(bounds) => {
match self.impl_trait_mode.mode {
@ -517,6 +498,30 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
(ty, res)
}
fn lower_fn_ptr(&mut self, fn_: &FnType) -> Ty<'db> {
let interner = self.interner;
let (params, ret_ty) = fn_.split_params_and_ret();
let old_lifetime_elision = self.lifetime_elision;
let mut args = Vec::with_capacity(fn_.params.len());
self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx: &mut TyLoweringContext<'_, '_>| {
ctx.lifetime_elision =
LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false };
args.extend(params.iter().map(|&(_, tr)| ctx.lower_ty(tr)));
ctx.lifetime_elision = LifetimeElisionKind::for_fn_ret(interner);
args.push(ctx.lower_ty(ret_ty));
});
self.lifetime_elision = old_lifetime_elision;
Ty::new_fn_ptr(
interner,
Binder::dummy(FnSig {
abi: fn_.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol),
safety: if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe },
c_variadic: fn_.is_varargs,
inputs_and_output: Tys::new_from_slice(&args),
}),
)
}
/// This is only for `generic_predicates_for_param`, where we can't just
/// lower the self types of the predicates since that could lead to cycles.
/// So we just check here if the `type_ref` resolves to a generic param, and which.

View file

@ -599,7 +599,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
explicit_self_ty: Option<Ty<'db>>,
lowering_assoc_type_generics: bool,
) -> GenericArgs<'db> {
let old_lifetime_elision = self.ctx.lifetime_elision.clone();
let old_lifetime_elision = self.ctx.lifetime_elision;
if let Some(args) = self.current_or_prev_segment.args_and_bindings
&& args.parenthesized != GenericArgsParentheses::No
@ -640,7 +640,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
explicit_self_ty,
PathGenericsSource::Segment(self.current_segment_u32()),
lowering_assoc_type_generics,
self.ctx.lifetime_elision.clone(),
self.ctx.lifetime_elision,
);
self.ctx.lifetime_elision = old_lifetime_elision;
result
@ -884,7 +884,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
assoc_type: binding_idx as u32,
},
false,
this.ctx.lifetime_elision.clone(),
this.ctx.lifetime_elision,
)
});
let args = GenericArgs::new_from_iter(
@ -902,7 +902,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
// `Fn()`-style generics are elided like functions. This is `Output` (we lower to it in hir-def).
LifetimeElisionKind::for_fn_ret(self.ctx.interner)
} else {
self.ctx.lifetime_elision.clone()
self.ctx.lifetime_elision
};
self.with_lifetime_elision(lifetime_elision, |this| {
match (&this.ctx.store[type_ref], this.ctx.impl_trait_mode.mode) {

View file

@ -100,4 +100,19 @@ fn foo<T: Fn(WithLifetime) -> WithLifetime>() {}
"#,
);
}
#[test]
fn regression_21430() {
check_diagnostics(
r#"
struct S {
f: fn(A<()>),
}
struct A<'a, T> {
a: &'a T,
}
"#,
);
}
}