1845: Closure types r=flodiebold a=flodiebold

This adds types for closures and makes them implement the `Fn` traits (we don't currently care or try to infer `Fn` vs. `FnMut` vs. `FnOnce`; this would require move analysis, I think).

This requires some changes in Chalk; one is that we need to know the self type when asked for impls, so we can synthesize `Fn` trait impls for closures; but also there's a problem that prevents us from normalizing the closure output type correctly that I _think_ will be fixed on the Chalk side (basically, we ask too early and try to solve `Normalize(<?1 as FnOnce<(u32,)>>::Output => ?0)` -- note the variable in the self type -- and instead of an ambiguous answer, we get back that it can't be solved, so we don't try again. Niko mentioned he's making all goals where the self type is unconstrained flounder, which I think would mean this would be ambiguous).

Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
bors[bot] 2019-09-24 21:12:26 +00:00 committed by GitHub
commit c7420ddaaa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 385 additions and 72 deletions

View File

@ -551,6 +551,14 @@ pub(crate) fn resolver(self, db: &impl HirDatabase) -> Resolver {
DefWithBody::Static(s) => s.resolver(db), DefWithBody::Static(s) => s.resolver(db),
} }
} }
pub(crate) fn krate(self, db: &impl HirDatabase) -> Option<Crate> {
match self {
DefWithBody::Const(c) => c.krate(db),
DefWithBody::Function(f) => f.krate(db),
DefWithBody::Static(s) => s.krate(db),
}
}
} }
pub trait HasBody: Copy { pub trait HasBody: Copy {
@ -671,6 +679,10 @@ pub fn module(self, db: &impl DefDatabase) -> Module {
self.id.module(db) self.id.module(db)
} }
pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
self.module(db).krate(db)
}
pub fn name(self, db: &impl HirDatabase) -> Name { pub fn name(self, db: &impl HirDatabase) -> Name {
self.data(db).name.clone() self.data(db).name.clone()
} }
@ -745,6 +757,10 @@ pub fn module(self, db: &impl DefDatabase) -> Module {
self.id.module(db) self.id.module(db)
} }
pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
self.module(db).krate(db)
}
pub fn data(self, db: &impl HirDatabase) -> Arc<ConstData> { pub fn data(self, db: &impl HirDatabase) -> Arc<ConstData> {
db.const_data(self) db.const_data(self)
} }
@ -824,6 +840,10 @@ pub fn module(self, db: &impl DefDatabase) -> Module {
self.id.module(db) self.id.module(db)
} }
pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
self.module(db).krate(db)
}
pub fn data(self, db: &impl HirDatabase) -> Arc<ConstData> { pub fn data(self, db: &impl HirDatabase) -> Arc<ConstData> {
db.static_data(self) db.static_data(self)
} }

View File

@ -13,8 +13,8 @@
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

View File

@ -16,7 +16,10 @@
use std::sync::Arc; use std::sync::Arc;
use std::{fmt, mem}; use std::{fmt, mem};
use crate::{db::HirDatabase, type_ref::Mutability, Adt, GenericParams, Name, Trait, TypeAlias}; use crate::{
db::HirDatabase, expr::ExprId, type_ref::Mutability, Adt, DefWithBody, GenericParams, Name,
Trait, TypeAlias,
};
use display::{HirDisplay, HirFormatter}; use display::{HirDisplay, HirFormatter};
pub(crate) use autoderef::autoderef; pub(crate) use autoderef::autoderef;
@ -100,6 +103,12 @@ pub enum TypeCtor {
/// couldn't find a better representation. In that case, we generate /// couldn't find a better representation. In that case, we generate
/// an **application type** like `(Iterator::Item)<T>`. /// an **application type** like `(Iterator::Item)<T>`.
AssociatedType(TypeAlias), AssociatedType(TypeAlias),
/// The type of a specific closure.
///
/// The closure signature is stored in a `FnPtr` type in the first type
/// parameter.
Closure { def: DefWithBody, expr: ExprId },
} }
/// A nominal type with (maybe 0) type parameters. This might be a primitive /// A nominal type with (maybe 0) type parameters. This might be a primitive
@ -481,6 +490,10 @@ fn callable_sig(&self, db: &impl HirDatabase) -> Option<FnSig> {
let sig = db.callable_item_signature(def); let sig = db.callable_item_signature(def);
Some(sig.subst(&a_ty.parameters)) Some(sig.subst(&a_ty.parameters))
} }
TypeCtor::Closure { .. } => {
let sig_param = &a_ty.parameters[0];
sig_param.callable_sig(db)
}
_ => None, _ => None,
}, },
_ => None, _ => None,
@ -720,6 +733,14 @@ fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
write!(f, ">")?; write!(f, ">")?;
} }
} }
TypeCtor::Closure { .. } => {
let sig = self.parameters[0]
.callable_sig(f.db)
.expect("first closure parameter should contain signature");
write!(f, "|")?;
f.write_joined(sig.params(), ", ")?;
write!(f, "| -> {}", sig.ret().display(f.db))?;
}
} }
Ok(()) Ok(())
} }

View File

@ -790,11 +790,7 @@ fn infer_method_call(
}; };
self.unify(&expected_receiver_ty, &actual_receiver_ty); self.unify(&expected_receiver_ty, &actual_receiver_ty);
let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown)); self.check_call_arguments(args, &param_tys);
for (arg, param_ty) in args.iter().zip(param_iter) {
let param_ty = self.normalize_associated_types_in(param_ty);
self.infer_expr(*arg, &Expectation::has_type(param_ty));
}
let ret_ty = self.normalize_associated_types_in(ret_ty); let ret_ty = self.normalize_associated_types_in(ret_ty);
ret_ty ret_ty
} }
@ -885,18 +881,37 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
Expr::Lambda { body, args, arg_types } => { Expr::Lambda { body, args, arg_types } => {
assert_eq!(args.len(), arg_types.len()); assert_eq!(args.len(), arg_types.len());
let mut sig_tys = Vec::new();
for (arg_pat, arg_type) in args.iter().zip(arg_types.iter()) { for (arg_pat, arg_type) in args.iter().zip(arg_types.iter()) {
let expected = if let Some(type_ref) = arg_type { let expected = if let Some(type_ref) = arg_type {
self.make_ty(type_ref) self.make_ty(type_ref)
} else { } else {
Ty::Unknown Ty::Unknown
}; };
self.infer_pat(*arg_pat, &expected, BindingMode::default()); let arg_ty = self.infer_pat(*arg_pat, &expected, BindingMode::default());
sig_tys.push(arg_ty);
} }
// FIXME: infer lambda type etc. // add return type
let _body_ty = self.infer_expr(*body, &Expectation::none()); let ret_ty = self.new_type_var();
Ty::Unknown sig_tys.push(ret_ty.clone());
let sig_ty = Ty::apply(
TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1 },
sig_tys.into(),
);
let closure_ty = Ty::apply_one(
TypeCtor::Closure { def: self.body.owner(), expr: tgt_expr },
sig_ty,
);
// Eagerly try to relate the closure type with the expected
// type, otherwise we often won't have enough information to
// infer the body.
self.coerce(&closure_ty, &expected.ty);
self.infer_expr(*body, &Expectation::has_type(ret_ty));
closure_ty
} }
Expr::Call { callee, args } => { Expr::Call { callee, args } => {
let callee_ty = self.infer_expr(*callee, &Expectation::none()); let callee_ty = self.infer_expr(*callee, &Expectation::none());
@ -909,11 +924,7 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
} }
}; };
self.register_obligations_for_call(&callee_ty); self.register_obligations_for_call(&callee_ty);
let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown)); self.check_call_arguments(args, &param_tys);
for (arg, param_ty) in args.iter().zip(param_iter) {
let param_ty = self.normalize_associated_types_in(param_ty);
self.infer_expr(*arg, &Expectation::has_type(param_ty));
}
let ret_ty = self.normalize_associated_types_in(ret_ty); let ret_ty = self.normalize_associated_types_in(ret_ty);
ret_ty ret_ty
} }
@ -1255,6 +1266,30 @@ fn infer_block(
ty ty
} }
fn check_call_arguments(&mut self, args: &[ExprId], param_tys: &[Ty]) {
// Quoting https://github.com/rust-lang/rust/blob/6ef275e6c3cb1384ec78128eceeb4963ff788dca/src/librustc_typeck/check/mod.rs#L3325 --
// We do this in a pretty awful way: first we type-check any arguments
// that are not closures, then we type-check the closures. This is so
// that we have more information about the types of arguments when we
// type-check the functions. This isn't really the right way to do this.
for &check_closures in &[false, true] {
let param_iter = param_tys.iter().cloned().chain(repeat(Ty::Unknown));
for (&arg, param_ty) in args.iter().zip(param_iter) {
let is_closure = match &self.body[arg] {
Expr::Lambda { .. } => true,
_ => false,
};
if is_closure != check_closures {
continue;
}
let param_ty = self.normalize_associated_types_in(param_ty);
self.infer_expr(arg, &Expectation::has_type(param_ty));
}
}
}
fn collect_const(&mut self, data: &ConstData) { fn collect_const(&mut self, data: &ConstData) {
self.return_ty = self.make_ty(data.type_ref()); self.return_ty = self.make_ty(data.type_ref());
} }

View File

@ -1077,7 +1077,6 @@ fn test(x: &i32) {
} }
"#), "#),
@r###" @r###"
[9; 10) 'x': &i32 [9; 10) 'x': &i32
[18; 369) '{ ...o_x; }': () [18; 369) '{ ...o_x; }': ()
[28; 29) 'y': &i32 [28; 29) 'y': &i32
@ -1107,8 +1106,8 @@ fn test(x: &i32) {
[177; 205) '{ ... }': () [177; 205) '{ ... }': ()
[191; 192) 'h': {unknown} [191; 192) 'h': {unknown}
[195; 198) 'val': {unknown} [195; 198) 'val': {unknown}
[215; 221) 'lambda': {unknown} [215; 221) 'lambda': |u64, u64, i32| -> i32
[224; 256) '|a: u6...b; c }': {unknown} [224; 256) '|a: u6...b; c }': |u64, u64, i32| -> i32
[225; 226) 'a': u64 [225; 226) 'a': u64
[233; 234) 'b': u64 [233; 234) 'b': u64
[236; 237) 'c': i32 [236; 237) 'c': i32
@ -2836,12 +2835,11 @@ fn test() -> u64 {
} }
"#), "#),
@r###" @r###"
[44; 102) '{ ...0(2) }': u64 [44; 102) '{ ...0(2) }': u64
[54; 55) 'a': S [54; 55) 'a': S
[58; 59) 'S': S(fn(u32) -> u64) -> S [58; 59) 'S': S(fn(u32) -> u64) -> S
[58; 68) 'S(|i| 2*i)': S [58; 68) 'S(|i| 2*i)': S
[60; 67) '|i| 2*i': fn(u32) -> u64 [60; 67) '|i| 2*i': |i32| -> i32
[61; 62) 'i': i32 [61; 62) 'i': i32
[64; 65) '2': i32 [64; 65) '2': i32
[64; 67) '2*i': i32 [64; 67) '2*i': i32
@ -3802,13 +3800,13 @@ fn test<T: Trait<Type = u32>>(x: T, y: impl Trait<Type = i64>) {
[296; 299) 'get': fn get<T>(T) -> <T as Trait>::Type [296; 299) 'get': fn get<T>(T) -> <T as Trait>::Type
[296; 302) 'get(x)': {unknown} [296; 302) 'get(x)': {unknown}
[300; 301) 'x': T [300; 301) 'x': T
[308; 312) 'get2': fn get2<{unknown}, S<{unknown}>>(T) -> U [308; 312) 'get2': fn get2<{unknown}, T>(T) -> U
[308; 315) 'get2(x)': {unknown} [308; 315) 'get2(x)': {unknown}
[313; 314) 'x': T [313; 314) 'x': T
[321; 324) 'get': fn get<impl Trait<Type = i64>>(T) -> <T as Trait>::Type [321; 324) 'get': fn get<impl Trait<Type = i64>>(T) -> <T as Trait>::Type
[321; 327) 'get(y)': {unknown} [321; 327) 'get(y)': {unknown}
[325; 326) 'y': impl Trait<Type = i64> [325; 326) 'y': impl Trait<Type = i64>
[333; 337) 'get2': fn get2<{unknown}, S<{unknown}>>(T) -> U [333; 337) 'get2': fn get2<{unknown}, impl Trait<Type = i64>>(T) -> U
[333; 340) 'get2(y)': {unknown} [333; 340) 'get2(y)': {unknown}
[338; 339) 'y': impl Trait<Type = i64> [338; 339) 'y': impl Trait<Type = i64>
[346; 349) 'get': fn get<S<u64>>(T) -> <T as Trait>::Type [346; 349) 'get': fn get<S<u64>>(T) -> <T as Trait>::Type
@ -3992,49 +3990,50 @@ 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;
} }
enum Option<T> { Some(T), None } enum Option<T> { Some(T), None }
impl<T> Option<T> { impl<T> Option<T> {
fn map<U, F: FnOnce(T) -> U>(self, f: F) -> U {} fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {}
} }
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) '{}': () [173; 175) '{}': ()
[161; 280) '{ ... 1); }': () [189; 308) '{ ... 1); }': ()
[171; 172) 'x': Option<i32> [199; 200) 'x': Option<u32>
[175; 187) 'Option::Some': Some<i32>(T) -> Option<T> [203; 215) 'Option::Some': Some<u32>(T) -> Option<T>
[175; 193) 'Option...(1i32)': Option<i32> [203; 221) 'Option...(1u32)': Option<u32>
[188; 192) '1i32': i32 [216; 220) '1u32': u32
[199; 200) 'x': Option<i32> [227; 228) 'x': Option<u32>
[199; 215) 'x.map(...v + 1)': {unknown} [227; 243) 'x.map(...v + 1)': Option<u32>
[205; 214) '|v| v + 1': {unknown} [233; 242) '|v| v + 1': |u32| -> u32
[206; 207) 'v': {unknown} [234; 235) 'v': u32
[209; 210) 'v': {unknown} [237; 238) 'v': u32
[209; 214) 'v + 1': i32 [237; 242) 'v + 1': u32
[213; 214) '1': i32 [241; 242) '1': u32
[221; 222) 'x': Option<i32> [249; 250) 'x': Option<u32>
[221; 237) 'x.map(... 1u64)': {unknown} [249; 265) 'x.map(... 1u64)': Option<u64>
[227; 236) '|_v| 1u64': {unknown} [255; 264) '|_v| 1u64': |u32| -> u64
[228; 230) '_v': {unknown} [256; 258) '_v': u32
[232; 236) '1u64': u64 [260; 264) '1u64': u64
[247; 248) 'y': Option<i64> [275; 276) 'y': Option<i64>
[264; 265) 'x': Option<i32> [292; 293) 'x': Option<u32>
[264; 277) 'x.map(|_v| 1)': Option<i64> [292; 305) 'x.map(|_v| 1)': Option<i64>
[270; 276) '|_v| 1': {unknown} [298; 304) '|_v| 1': |u32| -> i64
[271; 273) '_v': {unknown} [299; 301) '_v': u32
[275; 276) '1': i32 [303; 304) '1': i64
"### "###
); );
} }
@ -4060,17 +4059,17 @@ fn test<F: FnOnce(u32) -> u64>(f: F) {
[85; 86) 'f': F [85; 86) 'f': F
[85; 89) 'f(1)': {unknown} [85; 89) 'f(1)': {unknown}
[87; 88) '1': i32 [87; 88) '1': i32
[99; 100) 'g': {unknown} [99; 100) 'g': |u64| -> i32
[103; 112) '|v| v + 1': {unknown} [103; 112) '|v| v + 1': |u64| -> i32
[104; 105) 'v': {unknown} [104; 105) 'v': u64
[107; 108) 'v': {unknown} [107; 108) 'v': u64
[107; 112) 'v + 1': i32 [107; 112) 'v + 1': i32
[111; 112) '1': i32 [111; 112) '1': i32
[118; 119) 'g': {unknown} [118; 119) 'g': |u64| -> i32
[118; 125) 'g(1u64)': {unknown} [118; 125) 'g(1u64)': i32
[120; 124) '1u64': u64 [120; 124) '1u64': u64
[135; 136) 'h': {unknown} [135; 136) 'h': |u128| -> u128
[139; 152) '|v| 1u128 + v': {unknown} [139; 152) '|v| 1u128 + v': |u128| -> u128
[140; 141) 'v': u128 [140; 141) 'v': u128
[143; 148) '1u128': u128 [143; 148) '1u128': u128
[143; 152) '1u128 + v': u128 [143; 152) '1u128 + v': u128
@ -4079,6 +4078,86 @@ fn test<F: FnOnce(u32) -> u64>(f: F) {
); );
} }
#[test]
fn closure_as_argument_inference_order() {
assert_snapshot!(
infer(r#"
#[lang = "fn_once"]
trait FnOnce<Args> {
type Output;
}
fn foo1<T, U, F: FnOnce(T) -> U>(x: T, f: F) -> U {}
fn foo2<T, U, F: FnOnce(T) -> U>(f: F, x: T) -> U {}
struct S;
impl S {
fn method(self) -> u64;
fn foo1<T, U, F: FnOnce(T) -> U>(self, x: T, f: F) -> U {}
fn foo2<T, U, F: FnOnce(T) -> U>(self, f: F, x: T) -> U {}
}
fn test() {
let x1 = foo1(S, |s| s.method());
let x2 = foo2(|s| s.method(), S);
let x3 = S.foo1(S, |s| s.method());
let x4 = S.foo2(|s| s.method(), S);
}
"#),
@r###"
[95; 96) 'x': T
[101; 102) 'f': F
[112; 114) '{}': ()
[148; 149) 'f': F
[154; 155) 'x': T
[165; 167) '{}': ()
[202; 206) 'self': S
[254; 258) 'self': S
[260; 261) 'x': T
[266; 267) 'f': F
[277; 279) '{}': ()
[317; 321) 'self': S
[323; 324) 'f': F
[329; 330) 'x': T
[340; 342) '{}': ()
[356; 515) '{ ... S); }': ()
[366; 368) 'x1': u64
[371; 375) 'foo1': fn foo1<S, u64, |S| -> u64>(T, F) -> U
[371; 394) 'foo1(S...hod())': u64
[376; 377) 'S': S
[379; 393) '|s| s.method()': |S| -> u64
[380; 381) 's': S
[383; 384) 's': S
[383; 393) 's.method()': u64
[404; 406) 'x2': u64
[409; 413) 'foo2': fn foo2<S, u64, |S| -> u64>(F, T) -> U
[409; 432) 'foo2(|...(), S)': u64
[414; 428) '|s| s.method()': |S| -> u64
[415; 416) 's': S
[418; 419) 's': S
[418; 428) 's.method()': u64
[430; 431) 'S': S
[442; 444) 'x3': u64
[447; 448) 'S': S
[447; 472) 'S.foo1...hod())': u64
[454; 455) 'S': S
[457; 471) '|s| s.method()': |S| -> u64
[458; 459) 's': S
[461; 462) 's': S
[461; 471) 's.method()': u64
[482; 484) 'x4': u64
[487; 488) 'S': S
[487; 512) 'S.foo2...(), S)': u64
[494; 508) '|s| s.method()': |S| -> u64
[495; 496) 's': S
[498; 499) 's': S
[498; 508) 's.method()': u64
[510; 511) 'S': S
"###
);
}
#[test] #[test]
fn unselected_projection_in_trait_env_1() { fn unselected_projection_in_trait_env_1() {
let t = type_at( let t = type_at(

View File

@ -9,7 +9,7 @@
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};
@ -173,6 +173,14 @@ pub(crate) fn trait_solve_query(
) -> Option<Solution> { ) -> Option<Solution> {
let _p = profile("trait_solve_query"); let _p = profile("trait_solve_query");
debug!("trait_solve_query({})", goal.value.value.display(db)); debug!("trait_solve_query({})", goal.value.value.display(db));
if let Obligation::Projection(pred) = &goal.value.value {
if let Ty::Bound(_) = &pred.projection_ty.parameters[0] {
// Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible
return Some(Solution::Ambig(Guidance::Unknown));
}
}
let canonical = goal.to_chalk(db).cast(); let canonical = goal.to_chalk(db).cast();
// We currently don't deal with universes (I think / hope they're not yet // We currently don't deal with universes (I think / hope they're not yet
// relevant for our use cases?) // relevant for our use cases?)
@ -252,3 +260,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),
}

View File

@ -12,7 +12,7 @@
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 @@ fn from_chalk(db: &impl HirDatabase, chalk: chalk_ir::Ty) -> Self {
} }
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 @@ fn from_chalk(db: &impl HirDatabase, struct_id: chalk_ir::StructId) -> 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 @@ fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> {
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()
{
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
} }
@ -571,6 +588,10 @@ pub(crate) fn struct_datum_query(
type_alias.krate(db) != Some(krate), type_alias.krate(db) != Some(krate),
) )
} }
TypeCtor::Closure { def, .. } => {
let upstream = def.krate(db) != Some(krate);
(1, vec![], upstream)
}
}; };
let flags = chalk_rust_ir::StructFlags { let flags = chalk_rust_ir::StructFlags {
upstream, upstream,
@ -598,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
@ -657,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))
} }