mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 21:35:20 +00:00
Make closures impl closure traits
This commit is contained in:
parent
619a8185a6
commit
3b06faad26
4 changed files with 188 additions and 41 deletions
|
@ -13,8 +13,8 @@ use crate::{
|
||||||
nameres::{CrateDefMap, ImportSourceMap, Namespace, RawItems},
|
nameres::{CrateDefMap, ImportSourceMap, Namespace, RawItems},
|
||||||
traits::TraitData,
|
traits::TraitData,
|
||||||
ty::{
|
ty::{
|
||||||
method_resolution::CrateImplBlocks, CallableDef, FnSig, GenericPredicate, InferenceResult,
|
method_resolution::CrateImplBlocks, traits::Impl, CallableDef, FnSig, GenericPredicate,
|
||||||
Substs, Ty, TypableDef, TypeCtor,
|
InferenceResult, Substs, Ty, TypableDef, TypeCtor,
|
||||||
},
|
},
|
||||||
type_alias::TypeAliasData,
|
type_alias::TypeAliasData,
|
||||||
AstIdMap, Const, ConstData, Crate, DefWithBody, Enum, ErasedFileAstId, ExprScopes, FnData,
|
AstIdMap, Const, ConstData, Crate, DefWithBody, Enum, ErasedFileAstId, ExprScopes, FnData,
|
||||||
|
@ -50,7 +50,7 @@ pub trait InternDatabase: SourceDatabase {
|
||||||
#[salsa::interned]
|
#[salsa::interned]
|
||||||
fn intern_type_ctor(&self, type_ctor: TypeCtor) -> ids::TypeCtorId;
|
fn intern_type_ctor(&self, type_ctor: TypeCtor) -> ids::TypeCtorId;
|
||||||
#[salsa::interned]
|
#[salsa::interned]
|
||||||
fn intern_impl_block(&self, impl_block: ImplBlock) -> ids::GlobalImplId;
|
fn intern_impl(&self, impl_: Impl) -> ids::GlobalImplId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This database has access to source code, so queries here are not really
|
/// This database has access to source code, so queries here are not really
|
||||||
|
|
|
@ -3990,6 +3990,7 @@ fn test<F: FnOnce(u32, u64) -> u128>(f: F) {
|
||||||
fn closure_1() {
|
fn closure_1() {
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
infer(r#"
|
infer(r#"
|
||||||
|
#[lang = "fn_once"]
|
||||||
trait FnOnce<Args> {
|
trait FnOnce<Args> {
|
||||||
type Output;
|
type Output;
|
||||||
}
|
}
|
||||||
|
@ -4000,39 +4001,39 @@ impl<T> Option<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test() {
|
fn test() {
|
||||||
let x = Option::Some(1i32);
|
let x = Option::Some(1u32);
|
||||||
x.map(|v| v + 1);
|
x.map(|v| v + 1);
|
||||||
x.map(|_v| 1u64);
|
x.map(|_v| 1u64);
|
||||||
let y: Option<i64> = x.map(|_v| 1);
|
let y: Option<i64> = x.map(|_v| 1);
|
||||||
}
|
}
|
||||||
"#),
|
"#),
|
||||||
@r###"
|
@r###"
|
||||||
[128; 132) 'self': Option<T>
|
[148; 152) 'self': Option<T>
|
||||||
[134; 135) 'f': F
|
[154; 155) 'f': F
|
||||||
[145; 147) '{}': ()
|
[165; 167) '{}': ()
|
||||||
[161; 280) '{ ... 1); }': ()
|
[181; 300) '{ ... 1); }': ()
|
||||||
[171; 172) 'x': Option<i32>
|
[191; 192) 'x': Option<u32>
|
||||||
[175; 187) 'Option::Some': Some<i32>(T) -> Option<T>
|
[195; 207) 'Option::Some': Some<u32>(T) -> Option<T>
|
||||||
[175; 193) 'Option...(1i32)': Option<i32>
|
[195; 213) 'Option...(1u32)': Option<u32>
|
||||||
[188; 192) '1i32': i32
|
[208; 212) '1u32': u32
|
||||||
[199; 200) 'x': Option<i32>
|
[219; 220) 'x': Option<u32>
|
||||||
[199; 215) 'x.map(...v + 1)': {unknown}
|
[219; 235) 'x.map(...v + 1)': {unknown}
|
||||||
[205; 214) '|v| v + 1': |{unknown}| -> i32
|
[225; 234) '|v| v + 1': |u32| -> i32
|
||||||
[206; 207) 'v': {unknown}
|
[226; 227) 'v': u32
|
||||||
[209; 210) 'v': {unknown}
|
[229; 230) 'v': u32
|
||||||
[209; 214) 'v + 1': i32
|
[229; 234) 'v + 1': i32
|
||||||
[213; 214) '1': i32
|
[233; 234) '1': i32
|
||||||
[221; 222) 'x': Option<i32>
|
[241; 242) 'x': Option<u32>
|
||||||
[221; 237) 'x.map(... 1u64)': {unknown}
|
[241; 257) 'x.map(... 1u64)': {unknown}
|
||||||
[227; 236) '|_v| 1u64': |{unknown}| -> u64
|
[247; 256) '|_v| 1u64': |u32| -> u64
|
||||||
[228; 230) '_v': {unknown}
|
[248; 250) '_v': u32
|
||||||
[232; 236) '1u64': u64
|
[252; 256) '1u64': u64
|
||||||
[247; 248) 'y': Option<i64>
|
[267; 268) 'y': Option<i64>
|
||||||
[264; 265) 'x': Option<i32>
|
[284; 285) 'x': Option<u32>
|
||||||
[264; 277) 'x.map(|_v| 1)': Option<i64>
|
[284; 297) 'x.map(|_v| 1)': Option<i64>
|
||||||
[270; 276) '|_v| 1': |{unknown}| -> i32
|
[290; 296) '|_v| 1': |u32| -> i32
|
||||||
[271; 273) '_v': {unknown}
|
[291; 293) '_v': u32
|
||||||
[275; 276) '1': i32
|
[295; 296) '1': i32
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use ra_prof::profile;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk};
|
use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk};
|
||||||
use crate::{db::HirDatabase, Crate, ImplBlock, Trait};
|
use crate::{db::HirDatabase, expr::ExprId, Crate, DefWithBody, ImplBlock, Trait};
|
||||||
|
|
||||||
use self::chalk::{from_chalk, ToChalk};
|
use self::chalk::{from_chalk, ToChalk};
|
||||||
|
|
||||||
|
@ -252,3 +252,37 @@ pub enum Guidance {
|
||||||
/// There's no useful information to feed back to type inference
|
/// There's no useful information to feed back to type inference
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum FnTrait {
|
||||||
|
FnOnce,
|
||||||
|
FnMut,
|
||||||
|
Fn,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FnTrait {
|
||||||
|
fn lang_item_name(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
FnTrait::FnOnce => "fn_once",
|
||||||
|
FnTrait::FnMut => "fn_mut",
|
||||||
|
FnTrait::Fn => "fn",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct ClosureFnTraitImplData {
|
||||||
|
def: DefWithBody,
|
||||||
|
expr: ExprId,
|
||||||
|
fn_trait: FnTrait,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An impl. Usually this comes from an impl block, but some built-in types get
|
||||||
|
/// synthetic impls.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Impl {
|
||||||
|
/// A normal impl from an impl block.
|
||||||
|
ImplBlock(ImplBlock),
|
||||||
|
/// Closure types implement the Fn traits synthetically.
|
||||||
|
ClosureFnTraitImpl(ClosureFnTraitImplData),
|
||||||
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ use chalk_rust_ir::{AssociatedTyDatum, ImplDatum, StructDatum, TraitDatum};
|
||||||
use ra_db::salsa::{InternId, InternKey};
|
use ra_db::salsa::{InternId, InternKey};
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use super::{Canonical, ChalkContext, Obligation};
|
use super::{Canonical, ChalkContext, Impl, Obligation};
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
generics::GenericDef,
|
generics::GenericDef,
|
||||||
|
@ -111,7 +111,7 @@ impl ToChalk for Ty {
|
||||||
}
|
}
|
||||||
chalk_ir::Ty::ForAll(_) => unimplemented!(),
|
chalk_ir::Ty::ForAll(_) => unimplemented!(),
|
||||||
chalk_ir::Ty::BoundVar(idx) => Ty::Bound(idx as u32),
|
chalk_ir::Ty::BoundVar(idx) => Ty::Bound(idx as u32),
|
||||||
chalk_ir::Ty::InferenceVar(_iv) => panic!("unexpected chalk infer ty"),
|
chalk_ir::Ty::InferenceVar(_iv) => Ty::Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,15 +175,15 @@ impl ToChalk for TypeCtor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToChalk for ImplBlock {
|
impl ToChalk for Impl {
|
||||||
type Chalk = chalk_ir::ImplId;
|
type Chalk = chalk_ir::ImplId;
|
||||||
|
|
||||||
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::ImplId {
|
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::ImplId {
|
||||||
db.intern_impl_block(self).into()
|
db.intern_impl(self).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_chalk(db: &impl HirDatabase, impl_id: chalk_ir::ImplId) -> ImplBlock {
|
fn from_chalk(db: &impl HirDatabase, impl_id: chalk_ir::ImplId) -> Impl {
|
||||||
db.lookup_intern_impl_block(impl_id.into())
|
db.lookup_intern_impl(impl_id.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,19 +388,36 @@ where
|
||||||
fn impls_for_trait(
|
fn impls_for_trait(
|
||||||
&self,
|
&self,
|
||||||
trait_id: chalk_ir::TraitId,
|
trait_id: chalk_ir::TraitId,
|
||||||
_parameters: &[Parameter],
|
parameters: &[Parameter],
|
||||||
) -> Vec<ImplId> {
|
) -> Vec<ImplId> {
|
||||||
debug!("impls_for_trait {:?}", trait_id);
|
debug!("impls_for_trait {:?}", trait_id);
|
||||||
if trait_id == UNKNOWN_TRAIT {
|
if trait_id == UNKNOWN_TRAIT {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
let trait_: Trait = from_chalk(self.db, trait_id);
|
let trait_: Trait = from_chalk(self.db, trait_id);
|
||||||
let result: Vec<_> = self
|
let mut result: Vec<_> = self
|
||||||
.db
|
.db
|
||||||
.impls_for_trait(self.krate, trait_)
|
.impls_for_trait(self.krate, trait_)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|impl_block| impl_block.to_chalk(self.db))
|
.copied()
|
||||||
|
.map(Impl::ImplBlock)
|
||||||
|
.map(|impl_| impl_.to_chalk(self.db))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref().clone());
|
||||||
|
if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { def, expr }, .. }) = ty {
|
||||||
|
for fn_trait in
|
||||||
|
[super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter().copied()
|
||||||
|
{
|
||||||
|
if let Some(actual_trait) = get_fn_trait(self.db, self.krate, fn_trait) {
|
||||||
|
if trait_ == actual_trait {
|
||||||
|
let impl_ = super::ClosureFnTraitImplData { def, expr, fn_trait };
|
||||||
|
result.push(Impl::ClosureFnTraitImpl(impl_).to_chalk(self.db));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
debug!("impls_for_trait returned {} impls", result.len());
|
debug!("impls_for_trait returned {} impls", result.len());
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -602,7 +619,21 @@ pub(crate) fn impl_datum_query(
|
||||||
) -> Arc<ImplDatum> {
|
) -> Arc<ImplDatum> {
|
||||||
let _p = ra_prof::profile("impl_datum");
|
let _p = ra_prof::profile("impl_datum");
|
||||||
debug!("impl_datum {:?}", impl_id);
|
debug!("impl_datum {:?}", impl_id);
|
||||||
let impl_block: ImplBlock = from_chalk(db, impl_id);
|
let impl_: Impl = from_chalk(db, impl_id);
|
||||||
|
match impl_ {
|
||||||
|
Impl::ImplBlock(impl_block) => impl_block_datum(db, krate, impl_id, impl_block),
|
||||||
|
Impl::ClosureFnTraitImpl(data) => {
|
||||||
|
closure_fn_trait_impl_datum(db, krate, impl_id, data).unwrap_or_else(invalid_impl_datum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn impl_block_datum(
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
krate: Crate,
|
||||||
|
impl_id: ImplId,
|
||||||
|
impl_block: ImplBlock,
|
||||||
|
) -> Arc<ImplDatum> {
|
||||||
let generic_params = impl_block.generic_params(db);
|
let generic_params = impl_block.generic_params(db);
|
||||||
let bound_vars = Substs::bound_vars(&generic_params);
|
let bound_vars = Substs::bound_vars(&generic_params);
|
||||||
let trait_ref = impl_block
|
let trait_ref = impl_block
|
||||||
|
@ -661,6 +692,87 @@ pub(crate) fn impl_datum_query(
|
||||||
Arc::new(impl_datum)
|
Arc::new(impl_datum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn invalid_impl_datum() -> Arc<ImplDatum> {
|
||||||
|
let trait_ref = chalk_ir::TraitRef {
|
||||||
|
trait_id: UNKNOWN_TRAIT,
|
||||||
|
parameters: vec![chalk_ir::Ty::BoundVar(0).cast()],
|
||||||
|
};
|
||||||
|
let impl_datum_bound = chalk_rust_ir::ImplDatumBound {
|
||||||
|
trait_ref: chalk_rust_ir::PolarizedTraitRef::Positive(trait_ref),
|
||||||
|
where_clauses: Vec::new(),
|
||||||
|
associated_ty_values: Vec::new(),
|
||||||
|
impl_type: chalk_rust_ir::ImplType::External,
|
||||||
|
};
|
||||||
|
let impl_datum = ImplDatum { binders: make_binders(impl_datum_bound, 1) };
|
||||||
|
Arc::new(impl_datum)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn closure_fn_trait_impl_datum(
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
krate: Crate,
|
||||||
|
impl_id: ImplId,
|
||||||
|
data: super::ClosureFnTraitImplData,
|
||||||
|
) -> Option<Arc<ImplDatum>> {
|
||||||
|
// for some closure |X, Y| -> Z:
|
||||||
|
// impl<T, U, V> Fn<(T, U)> for closure<fn(T, U) -> V> { Output = V }
|
||||||
|
|
||||||
|
let fn_once_trait = get_fn_trait(db, krate, super::FnTrait::FnOnce)?;
|
||||||
|
let trait_ = get_fn_trait(db, krate, data.fn_trait)?; // get corresponding fn trait
|
||||||
|
|
||||||
|
let num_args: u16 = match &db.body_hir(data.def)[data.expr] {
|
||||||
|
crate::expr::Expr::Lambda { args, .. } => args.len() as u16,
|
||||||
|
_ => {
|
||||||
|
log::warn!("closure for closure type {:?} not found", data);
|
||||||
|
0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let arg_ty = Ty::apply(
|
||||||
|
TypeCtor::Tuple { cardinality: num_args },
|
||||||
|
(0..num_args).map(|i| Ty::Bound(i.into())).collect::<Vec<_>>().into(),
|
||||||
|
);
|
||||||
|
let output_ty = Ty::Bound(num_args.into());
|
||||||
|
let sig_ty = Ty::apply(
|
||||||
|
TypeCtor::FnPtr { num_args },
|
||||||
|
(0..num_args + 1).map(|i| Ty::Bound(i.into())).collect::<Vec<_>>().into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let self_ty = Ty::apply_one(TypeCtor::Closure { def: data.def, expr: data.expr }, sig_ty);
|
||||||
|
|
||||||
|
let trait_ref = TraitRef { trait_, substs: vec![self_ty, arg_ty].into() };
|
||||||
|
|
||||||
|
let output_ty_id = fn_once_trait.associated_type_by_name(db, &crate::name::OUTPUT_TYPE)?;
|
||||||
|
|
||||||
|
let output_ty_value = chalk_rust_ir::AssociatedTyValue {
|
||||||
|
associated_ty_id: output_ty_id.to_chalk(db),
|
||||||
|
impl_id,
|
||||||
|
value: make_binders(
|
||||||
|
chalk_rust_ir::AssociatedTyValueBound { ty: output_ty.to_chalk(db) },
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let impl_type = chalk_rust_ir::ImplType::External;
|
||||||
|
|
||||||
|
let impl_datum_bound = chalk_rust_ir::ImplDatumBound {
|
||||||
|
trait_ref: chalk_rust_ir::PolarizedTraitRef::Positive(trait_ref.to_chalk(db)),
|
||||||
|
where_clauses: Vec::new(),
|
||||||
|
associated_ty_values: vec![output_ty_value],
|
||||||
|
impl_type,
|
||||||
|
};
|
||||||
|
let impl_datum = ImplDatum { binders: make_binders(impl_datum_bound, num_args as usize + 1) };
|
||||||
|
Some(Arc::new(impl_datum))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_fn_trait(db: &impl HirDatabase, krate: Crate, fn_trait: super::FnTrait) -> Option<Trait> {
|
||||||
|
let lang_items = db.lang_items(krate);
|
||||||
|
let target = lang_items.target(fn_trait.lang_item_name())?;
|
||||||
|
match target {
|
||||||
|
crate::lang_item::LangItemTarget::Trait(t) => Some(*t),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn id_from_chalk<T: InternKey>(chalk_id: chalk_ir::RawId) -> T {
|
fn id_from_chalk<T: InternKey>(chalk_id: chalk_ir::RawId) -> T {
|
||||||
T::from_intern_id(InternId::from(chalk_id.index))
|
T::from_intern_id(InternId::from(chalk_id.index))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue