Skip to content

Commit c1305fa

Browse files
committed
Auto merge of rust-lang#13525 - jonas-schievink:generic-call-signature, r=jonas-schievink
feat: show signature help when calling generic types implementing `FnOnce` This queries chalk for the `FnOnce` impl of callees and takes argument and return types from there, making generic `Callable`s available to the IDE. This makes signature help work for them, and potentially allows other features to take generic callables into account in the future.
2 parents a8e97bc + ecad1a9 commit c1305fa

File tree

4 files changed

+105
-9
lines changed

4 files changed

+105
-9
lines changed

crates/hir-ty/src/infer.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1020,7 +1020,7 @@ impl Expectation {
10201020
/// The primary use case is where the expected type is a fat pointer,
10211021
/// like `&[isize]`. For example, consider the following statement:
10221022
///
1023-
/// let x: &[isize] = &[1, 2, 3];
1023+
/// let x: &[isize] = &[1, 2, 3];
10241024
///
10251025
/// In this case, the expected type for the `&[1, 2, 3]` expression is
10261026
/// `&[isize]`. If however we were to say that `[1, 2, 3]` has the

crates/hir-ty/src/lib.rs

+68-1
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@ use std::sync::Arc;
3838
use chalk_ir::{
3939
fold::{Shift, TypeFoldable},
4040
interner::HasInterner,
41-
NoSolution,
41+
NoSolution, UniverseIndex,
4242
};
4343
use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId};
44+
use hir_expand::name;
4445
use itertools::Either;
46+
use traits::FnTrait;
4547
use utils::Generics;
4648

4749
use crate::{consteval::unknown_const, db::HirDatabase, utils::generics};
@@ -508,3 +510,68 @@ where
508510
});
509511
Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(Interner, kinds) }
510512
}
513+
514+
pub fn callable_sig_from_fnonce(
515+
self_ty: &Canonical<Ty>,
516+
env: Arc<TraitEnvironment>,
517+
db: &dyn HirDatabase,
518+
) -> Option<CallableSig> {
519+
let krate = env.krate;
520+
let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
521+
let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
522+
523+
let mut kinds = self_ty.binders.interned().to_vec();
524+
let b = TyBuilder::trait_ref(db, fn_once_trait);
525+
if b.remaining() != 2 {
526+
return None;
527+
}
528+
let fn_once = b
529+
.push(self_ty.value.clone())
530+
.fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len())
531+
.build();
532+
kinds.extend(fn_once.substitution.iter(Interner).skip(1).map(|x| {
533+
let vk = match x.data(Interner) {
534+
chalk_ir::GenericArgData::Ty(_) => {
535+
chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
536+
}
537+
chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime,
538+
chalk_ir::GenericArgData::Const(c) => {
539+
chalk_ir::VariableKind::Const(c.data(Interner).ty.clone())
540+
}
541+
};
542+
chalk_ir::WithKind::new(vk, UniverseIndex::ROOT)
543+
}));
544+
545+
// FIXME: chalk refuses to solve `<Self as FnOnce<^0.0>>::Output == ^0.1`, so we first solve
546+
// `<Self as FnOnce<^0.0>>` and then replace `^0.0` with the concrete argument tuple.
547+
let trait_env = env.env.clone();
548+
let obligation = InEnvironment { goal: fn_once.cast(Interner), environment: trait_env };
549+
let canonical =
550+
Canonical { binders: CanonicalVarKinds::from_iter(Interner, kinds), value: obligation };
551+
let subst = match db.trait_solve(krate, canonical) {
552+
Some(Solution::Unique(vars)) => vars.value.subst,
553+
_ => return None,
554+
};
555+
let args = subst.at(Interner, self_ty.binders.interned().len()).ty(Interner)?;
556+
let params = match args.kind(Interner) {
557+
chalk_ir::TyKind::Tuple(_, subst) => {
558+
subst.iter(Interner).filter_map(|arg| arg.ty(Interner).cloned()).collect::<Vec<_>>()
559+
}
560+
_ => return None,
561+
};
562+
if params.iter().any(|ty| ty.is_unknown()) {
563+
return None;
564+
}
565+
566+
let fn_once = TyBuilder::trait_ref(db, fn_once_trait)
567+
.push(self_ty.value.clone())
568+
.push(args.clone())
569+
.build();
570+
let projection =
571+
TyBuilder::assoc_type_projection(db, output_assoc_type, Some(fn_once.substitution.clone()))
572+
.build();
573+
574+
let ret_ty = db.normalize_projection(projection, env);
575+
576+
Some(CallableSig::from_params_and_return(params, ret_ty.clone(), false))
577+
}

crates/hir/src/lib.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -2995,7 +2995,17 @@ impl Type {
29952995
let callee = match self.ty.kind(Interner) {
29962996
TyKind::Closure(id, _) => Callee::Closure(*id),
29972997
TyKind::Function(_) => Callee::FnPtr,
2998-
_ => Callee::Def(self.ty.callable_def(db)?),
2998+
TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?),
2999+
_ => {
3000+
let ty = hir_ty::replace_errors_with_variables(&self.ty);
3001+
let sig = hir_ty::callable_sig_from_fnonce(&ty, self.env.clone(), db)?;
3002+
return Some(Callable {
3003+
ty: self.clone(),
3004+
sig,
3005+
callee: Callee::Other,
3006+
is_bound_method: false,
3007+
});
3008+
}
29993009
};
30003010

30013011
let sig = self.ty.callable_sig(db)?;
@@ -3464,6 +3474,7 @@ enum Callee {
34643474
Def(CallableDefId),
34653475
Closure(ClosureId),
34663476
FnPtr,
3477+
Other,
34673478
}
34683479

34693480
pub enum CallableKind {
@@ -3472,6 +3483,8 @@ pub enum CallableKind {
34723483
TupleEnumVariant(Variant),
34733484
Closure,
34743485
FnPtr,
3486+
/// Some other type that implements `FnOnce`.
3487+
Other,
34753488
}
34763489

34773490
impl Callable {
@@ -3483,6 +3496,7 @@ impl Callable {
34833496
Def(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()),
34843497
Closure(_) => CallableKind::Closure,
34853498
FnPtr => CallableKind::FnPtr,
3499+
Other => CallableKind::Other,
34863500
}
34873501
}
34883502
pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> {

crates/ide/src/signature_help.rs

+21-6
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ fn signature_help_for_call(
149149
variant.name(db)
150150
);
151151
}
152-
hir::CallableKind::Closure | hir::CallableKind::FnPtr => (),
152+
hir::CallableKind::Closure | hir::CallableKind::FnPtr | hir::CallableKind::Other => (),
153153
}
154154

155155
res.signature.push('(');
@@ -189,9 +189,10 @@ fn signature_help_for_call(
189189
hir::CallableKind::Function(func) if callable.return_type().contains_unknown() => {
190190
render(func.ret_type(db))
191191
}
192-
hir::CallableKind::Function(_) | hir::CallableKind::Closure | hir::CallableKind::FnPtr => {
193-
render(callable.return_type())
194-
}
192+
hir::CallableKind::Function(_)
193+
| hir::CallableKind::Closure
194+
| hir::CallableKind::FnPtr
195+
| hir::CallableKind::Other => render(callable.return_type()),
195196
hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
196197
}
197198
Some(res)
@@ -387,10 +388,9 @@ mod tests {
387388
}
388389

389390
fn check(ra_fixture: &str, expect: Expect) {
390-
// Implicitly add `Sized` to avoid noisy `T: ?Sized` in the results.
391391
let fixture = format!(
392392
r#"
393-
#[lang = "sized"] trait Sized {{}}
393+
//- minicore: sized, fn
394394
{ra_fixture}
395395
"#
396396
);
@@ -1331,4 +1331,19 @@ fn f() {
13311331
"#]],
13321332
);
13331333
}
1334+
1335+
#[test]
1336+
fn help_for_generic_call() {
1337+
check(
1338+
r#"
1339+
fn f<F: FnOnce(u8, u16) -> i32>(f: F) {
1340+
f($0)
1341+
}
1342+
"#,
1343+
expect![[r#"
1344+
(u8, u16) -> i32
1345+
^^ ---
1346+
"#]],
1347+
);
1348+
}
13341349
}

0 commit comments

Comments
 (0)