From 619a8185a607b216c64b58d230c3949ccef98a37 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 7 Sep 2019 21:03:03 +0200 Subject: Give closures types --- crates/ra_hir/src/code_model.rs | 20 ++++++++++++++++++++ crates/ra_hir/src/ty.rs | 23 ++++++++++++++++++++++- crates/ra_hir/src/ty/infer.rs | 22 ++++++++++++++++++---- crates/ra_hir/src/ty/tests.rs | 30 ++++++++++++++---------------- crates/ra_hir/src/ty/traits/chalk.rs | 4 ++++ 5 files changed, 78 insertions(+), 21 deletions(-) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 12399c6ac..99c247a0b 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -551,6 +551,14 @@ impl DefWithBody { DefWithBody::Static(s) => s.resolver(db), } } + + pub(crate) fn krate(self, db: &impl HirDatabase) -> Option { + match self { + DefWithBody::Const(c) => c.krate(db), + DefWithBody::Function(f) => f.krate(db), + DefWithBody::Static(s) => s.krate(db), + } + } } pub trait HasBody: Copy { @@ -671,6 +679,10 @@ impl Function { self.id.module(db) } + pub fn krate(self, db: &impl DefDatabase) -> Option { + self.module(db).krate(db) + } + pub fn name(self, db: &impl HirDatabase) -> Name { self.data(db).name.clone() } @@ -745,6 +757,10 @@ impl Const { self.id.module(db) } + pub fn krate(self, db: &impl DefDatabase) -> Option { + self.module(db).krate(db) + } + pub fn data(self, db: &impl HirDatabase) -> Arc { db.const_data(self) } @@ -824,6 +840,10 @@ impl Static { self.id.module(db) } + pub fn krate(self, db: &impl DefDatabase) -> Option { + self.module(db).krate(db) + } + pub fn data(self, db: &impl HirDatabase) -> Arc { db.static_data(self) } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 36bfb10ce..e6ecbe1ea 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -16,7 +16,10 @@ use std::ops::Deref; use std::sync::Arc; 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}; pub(crate) use autoderef::autoderef; @@ -100,6 +103,12 @@ pub enum TypeCtor { /// couldn't find a better representation. In that case, we generate /// an **application type** like `(Iterator::Item)`. 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 @@ -481,6 +490,10 @@ impl Ty { let sig = db.callable_item_signature(def); Some(sig.subst(&a_ty.parameters)) } + TypeCtor::Closure { .. } => { + let sig_param = &a_ty.parameters[0]; + sig_param.callable_sig(db) + } _ => None, }, _ => None, @@ -720,6 +733,14 @@ impl HirDisplay for ApplicationTy { 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(()) } diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 81a8623bf..c04f2a0c4 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -885,18 +885,32 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Expr::Lambda { body, args, arg_types } => { 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()) { let expected = if let Some(type_ref) = arg_type { self.make_ty(type_ref) } else { 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. - let _body_ty = self.infer_expr(*body, &Expectation::none()); - Ty::Unknown + // add return type + let ret_ty = self.new_type_var(); + 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, + ); + + self.infer_expr(*body, &Expectation::has_type(ret_ty)); + closure_ty } Expr::Call { callee, args } => { let callee_ty = self.infer_expr(*callee, &Expectation::none()); diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 3ac1fbdd5..eb8770ec4 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -1077,7 +1077,6 @@ fn test(x: &i32) { } "#), @r###" - [9; 10) 'x': &i32 [18; 369) '{ ...o_x; }': () [28; 29) 'y': &i32 @@ -1107,8 +1106,8 @@ fn test(x: &i32) { [177; 205) '{ ... }': () [191; 192) 'h': {unknown} [195; 198) 'val': {unknown} - [215; 221) 'lambda': {unknown} - [224; 256) '|a: u6...b; c }': {unknown} + [215; 221) 'lambda': |u64, u64, i32| -> i32 + [224; 256) '|a: u6...b; c }': |u64, u64, i32| -> i32 [225; 226) 'a': u64 [233; 234) 'b': u64 [236; 237) 'c': i32 @@ -2836,12 +2835,11 @@ fn test() -> u64 { } "#), @r###" - [44; 102) '{ ...0(2) }': u64 [54; 55) 'a': S [58; 59) 'S': S(fn(u32) -> u64) -> 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 [64; 65) '2': i32 [64; 67) '2*i': i32 @@ -4019,20 +4017,20 @@ fn test() { [188; 192) '1i32': i32 [199; 200) 'x': Option [199; 215) 'x.map(...v + 1)': {unknown} - [205; 214) '|v| v + 1': {unknown} + [205; 214) '|v| v + 1': |{unknown}| -> i32 [206; 207) 'v': {unknown} [209; 210) 'v': {unknown} [209; 214) 'v + 1': i32 [213; 214) '1': i32 [221; 222) 'x': Option [221; 237) 'x.map(... 1u64)': {unknown} - [227; 236) '|_v| 1u64': {unknown} + [227; 236) '|_v| 1u64': |{unknown}| -> u64 [228; 230) '_v': {unknown} [232; 236) '1u64': u64 [247; 248) 'y': Option [264; 265) 'x': Option [264; 277) 'x.map(|_v| 1)': Option - [270; 276) '|_v| 1': {unknown} + [270; 276) '|_v| 1': |{unknown}| -> i32 [271; 273) '_v': {unknown} [275; 276) '1': i32 "### @@ -4060,17 +4058,17 @@ fn test u64>(f: F) { [85; 86) 'f': F [85; 89) 'f(1)': {unknown} [87; 88) '1': i32 - [99; 100) 'g': {unknown} - [103; 112) '|v| v + 1': {unknown} - [104; 105) 'v': {unknown} - [107; 108) 'v': {unknown} + [99; 100) 'g': |u64| -> i32 + [103; 112) '|v| v + 1': |u64| -> i32 + [104; 105) 'v': u64 + [107; 108) 'v': u64 [107; 112) 'v + 1': i32 [111; 112) '1': i32 - [118; 119) 'g': {unknown} - [118; 125) 'g(1u64)': {unknown} + [118; 119) 'g': |u64| -> i32 + [118; 125) 'g(1u64)': i32 [120; 124) '1u64': u64 - [135; 136) 'h': {unknown} - [139; 152) '|v| 1u128 + v': {unknown} + [135; 136) 'h': |u128| -> u128 + [139; 152) '|v| 1u128 + v': |u128| -> u128 [140; 141) 'v': u128 [143; 148) '1u128': u128 [143; 152) '1u128 + v': u128 diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index 462156021..34e623931 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -571,6 +571,10 @@ pub(crate) fn struct_datum_query( 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 { upstream, -- cgit v1.2.3 From 3b06faad26fdb1485c542970374ff8bbcb0e01c4 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 9 Sep 2019 22:10:58 +0200 Subject: Make closures impl closure traits --- crates/ra_hir/src/db.rs | 6 +- crates/ra_hir/src/ty/tests.rs | 55 ++++++++------- crates/ra_hir/src/ty/traits.rs | 36 +++++++++- crates/ra_hir/src/ty/traits/chalk.rs | 132 ++++++++++++++++++++++++++++++++--- 4 files changed, 188 insertions(+), 41 deletions(-) diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 05259dcbb..2b20ae02b 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -13,8 +13,8 @@ use crate::{ nameres::{CrateDefMap, ImportSourceMap, Namespace, RawItems}, traits::TraitData, ty::{ - method_resolution::CrateImplBlocks, CallableDef, FnSig, GenericPredicate, InferenceResult, - Substs, Ty, TypableDef, TypeCtor, + method_resolution::CrateImplBlocks, traits::Impl, CallableDef, FnSig, GenericPredicate, + InferenceResult, Substs, Ty, TypableDef, TypeCtor, }, type_alias::TypeAliasData, AstIdMap, Const, ConstData, Crate, DefWithBody, Enum, ErasedFileAstId, ExprScopes, FnData, @@ -50,7 +50,7 @@ pub trait InternDatabase: SourceDatabase { #[salsa::interned] fn intern_type_ctor(&self, type_ctor: TypeCtor) -> ids::TypeCtorId; #[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 diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index eb8770ec4..13090f89d 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3990,6 +3990,7 @@ fn test u128>(f: F) { fn closure_1() { assert_snapshot!( infer(r#" +#[lang = "fn_once"] trait FnOnce { type Output; } @@ -4000,39 +4001,39 @@ impl Option { } fn test() { - let x = Option::Some(1i32); + let x = Option::Some(1u32); x.map(|v| v + 1); x.map(|_v| 1u64); let y: Option = x.map(|_v| 1); } "#), @r###" - [128; 132) 'self': Option - [134; 135) 'f': F - [145; 147) '{}': () - [161; 280) '{ ... 1); }': () - [171; 172) 'x': Option - [175; 187) 'Option::Some': Some(T) -> Option - [175; 193) 'Option...(1i32)': Option - [188; 192) '1i32': i32 - [199; 200) 'x': Option - [199; 215) 'x.map(...v + 1)': {unknown} - [205; 214) '|v| v + 1': |{unknown}| -> i32 - [206; 207) 'v': {unknown} - [209; 210) 'v': {unknown} - [209; 214) 'v + 1': i32 - [213; 214) '1': i32 - [221; 222) 'x': Option - [221; 237) 'x.map(... 1u64)': {unknown} - [227; 236) '|_v| 1u64': |{unknown}| -> u64 - [228; 230) '_v': {unknown} - [232; 236) '1u64': u64 - [247; 248) 'y': Option - [264; 265) 'x': Option - [264; 277) 'x.map(|_v| 1)': Option - [270; 276) '|_v| 1': |{unknown}| -> i32 - [271; 273) '_v': {unknown} - [275; 276) '1': i32 + [148; 152) 'self': Option + [154; 155) 'f': F + [165; 167) '{}': () + [181; 300) '{ ... 1); }': () + [191; 192) 'x': Option + [195; 207) 'Option::Some': Some(T) -> Option + [195; 213) 'Option...(1u32)': Option + [208; 212) '1u32': u32 + [219; 220) 'x': Option + [219; 235) 'x.map(...v + 1)': {unknown} + [225; 234) '|v| v + 1': |u32| -> i32 + [226; 227) 'v': u32 + [229; 230) 'v': u32 + [229; 234) 'v + 1': i32 + [233; 234) '1': i32 + [241; 242) 'x': Option + [241; 257) 'x.map(... 1u64)': {unknown} + [247; 256) '|_v| 1u64': |u32| -> u64 + [248; 250) '_v': u32 + [252; 256) '1u64': u64 + [267; 268) 'y': Option + [284; 285) 'x': Option + [284; 297) 'x.map(|_v| 1)': Option + [290; 296) '|_v| 1': |u32| -> i32 + [291; 293) '_v': u32 + [295; 296) '1': i32 "### ); } diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs index c0c132809..8e256341d 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -9,7 +9,7 @@ use ra_prof::profile; use rustc_hash::FxHashSet; 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}; @@ -252,3 +252,37 @@ pub enum Guidance { /// There's no useful information to feed back to type inference 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), +} diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index 34e623931..f229b1aef 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -12,7 +12,7 @@ use chalk_rust_ir::{AssociatedTyDatum, ImplDatum, StructDatum, TraitDatum}; use ra_db::salsa::{InternId, InternKey}; use test_utils::tested_by; -use super::{Canonical, ChalkContext, Obligation}; +use super::{Canonical, ChalkContext, Impl, Obligation}; use crate::{ db::HirDatabase, generics::GenericDef, @@ -111,7 +111,7 @@ impl ToChalk for Ty { } chalk_ir::Ty::ForAll(_) => unimplemented!(), 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; 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 { - db.lookup_intern_impl_block(impl_id.into()) + fn from_chalk(db: &impl HirDatabase, impl_id: chalk_ir::ImplId) -> Impl { + db.lookup_intern_impl(impl_id.into()) } } @@ -388,19 +388,36 @@ where fn impls_for_trait( &self, trait_id: chalk_ir::TraitId, - _parameters: &[Parameter], + parameters: &[Parameter], ) -> Vec { debug!("impls_for_trait {:?}", trait_id); if trait_id == UNKNOWN_TRAIT { return Vec::new(); } let trait_: Trait = from_chalk(self.db, trait_id); - let result: Vec<_> = self + let mut result: Vec<_> = self .db .impls_for_trait(self.krate, trait_) .iter() - .map(|impl_block| impl_block.to_chalk(self.db)) + .copied() + .map(Impl::ImplBlock) + .map(|impl_| impl_.to_chalk(self.db)) .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()); result } @@ -602,7 +619,21 @@ pub(crate) fn impl_datum_query( ) -> Arc { let _p = ra_prof::profile("impl_datum"); 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 { let generic_params = impl_block.generic_params(db); let bound_vars = Substs::bound_vars(&generic_params); let trait_ref = impl_block @@ -661,6 +692,87 @@ pub(crate) fn impl_datum_query( Arc::new(impl_datum) } +fn invalid_impl_datum() -> Arc { + 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> { + // for some closure |X, Y| -> Z: + // impl Fn<(T, U)> for closure 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::>().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::>().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 { + 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(chalk_id: chalk_ir::RawId) -> T { T::from_intern_id(InternId::from(chalk_id.index)) } -- cgit v1.2.3 From a0aeb6e7ad7385811a4bb75577513339c9a9ed91 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 24 Sep 2019 19:04:53 +0200 Subject: Make the closure_1 test work --- crates/ra_hir/src/ty/infer.rs | 5 ++++ crates/ra_hir/src/ty/tests.rs | 54 +++++++++++++++++++++--------------------- crates/ra_hir/src/ty/traits.rs | 8 +++++++ 3 files changed, 40 insertions(+), 27 deletions(-) diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index c04f2a0c4..4784fad85 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -909,6 +909,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { 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 } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 13090f89d..112b3d73f 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3800,13 +3800,13 @@ fn test>(x: T, y: impl Trait) { [296; 299) 'get': fn get(T) -> ::Type [296; 302) 'get(x)': {unknown} [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} [313; 314) 'x': T [321; 324) 'get': fn get>(T) -> ::Type [321; 327) 'get(y)': {unknown} [325; 326) 'y': impl Trait - [333; 337) 'get2': fn get2<{unknown}, S<{unknown}>>(T) -> U + [333; 337) 'get2': fn get2<{unknown}, impl Trait>(T) -> U [333; 340) 'get2(y)': {unknown} [338; 339) 'y': impl Trait [346; 349) 'get': fn get>(T) -> ::Type @@ -3997,7 +3997,7 @@ trait FnOnce { enum Option { Some(T), None } impl Option { - fn map U>(self, f: F) -> U {} + fn map U>(self, f: F) -> Option {} } fn test() { @@ -4010,30 +4010,30 @@ fn test() { @r###" [148; 152) 'self': Option [154; 155) 'f': F - [165; 167) '{}': () - [181; 300) '{ ... 1); }': () - [191; 192) 'x': Option - [195; 207) 'Option::Some': Some(T) -> Option - [195; 213) 'Option...(1u32)': Option - [208; 212) '1u32': u32 - [219; 220) 'x': Option - [219; 235) 'x.map(...v + 1)': {unknown} - [225; 234) '|v| v + 1': |u32| -> i32 - [226; 227) 'v': u32 - [229; 230) 'v': u32 - [229; 234) 'v + 1': i32 - [233; 234) '1': i32 - [241; 242) 'x': Option - [241; 257) 'x.map(... 1u64)': {unknown} - [247; 256) '|_v| 1u64': |u32| -> u64 - [248; 250) '_v': u32 - [252; 256) '1u64': u64 - [267; 268) 'y': Option - [284; 285) 'x': Option - [284; 297) 'x.map(|_v| 1)': Option - [290; 296) '|_v| 1': |u32| -> i32 - [291; 293) '_v': u32 - [295; 296) '1': i32 + [173; 175) '{}': () + [189; 308) '{ ... 1); }': () + [199; 200) 'x': Option + [203; 215) 'Option::Some': Some(T) -> Option + [203; 221) 'Option...(1u32)': Option + [216; 220) '1u32': u32 + [227; 228) 'x': Option + [227; 243) 'x.map(...v + 1)': Option + [233; 242) '|v| v + 1': |u32| -> u32 + [234; 235) 'v': u32 + [237; 238) 'v': u32 + [237; 242) 'v + 1': u32 + [241; 242) '1': u32 + [249; 250) 'x': Option + [249; 265) 'x.map(... 1u64)': Option + [255; 264) '|_v| 1u64': |u32| -> u64 + [256; 258) '_v': u32 + [260; 264) '1u64': u64 + [275; 276) 'y': Option + [292; 293) 'x': Option + [292; 305) 'x.map(|_v| 1)': Option + [298; 304) '|_v| 1': |u32| -> i64 + [299; 301) '_v': u32 + [303; 304) '1': i64 "### ); } diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs index 8e256341d..d11dab294 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -173,6 +173,14 @@ pub(crate) fn trait_solve_query( ) -> Option { let _p = profile("trait_solve_query"); 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(); // We currently don't deal with universes (I think / hope they're not yet // relevant for our use cases?) -- cgit v1.2.3 From 6a8670665032f6103ca14e38ed9106126b20063d Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 24 Sep 2019 23:04:33 +0200 Subject: Implement the call argument checking order hack for closures --- crates/ra_hir/src/ty/infer.rs | 36 +++++++++++----- crates/ra_hir/src/ty/tests.rs | 80 ++++++++++++++++++++++++++++++++++++ crates/ra_hir/src/ty/traits/chalk.rs | 4 +- 3 files changed, 108 insertions(+), 12 deletions(-) diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 4784fad85..378d2f829 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -790,11 +790,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { }; self.unify(&expected_receiver_ty, &actual_receiver_ty); - let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown)); - 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)); - } + self.check_call_arguments(args, ¶m_tys); let ret_ty = self.normalize_associated_types_in(ret_ty); ret_ty } @@ -928,11 +924,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } }; self.register_obligations_for_call(&callee_ty); - let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown)); - 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)); - } + self.check_call_arguments(args, ¶m_tys); let ret_ty = self.normalize_associated_types_in(ret_ty); ret_ty } @@ -1274,6 +1266,30 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { 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) { self.return_ty = self.make_ty(data.type_ref()); } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 112b3d73f..2872cd27b 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -4078,6 +4078,86 @@ fn test u64>(f: F) { ); } +#[test] +fn closure_as_argument_inference_order() { + assert_snapshot!( + infer(r#" +#[lang = "fn_once"] +trait FnOnce { + type Output; +} + +fn foo1 U>(x: T, f: F) -> U {} +fn foo2 U>(f: F, x: T) -> U {} + +struct S; +impl S { + fn method(self) -> u64; + + fn foo1 U>(self, x: T, f: F) -> U {} + fn foo2 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 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 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] fn unselected_projection_in_trait_env_1() { let t = type_at( diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index f229b1aef..d83706f86 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -406,8 +406,8 @@ where 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() + 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 { -- cgit v1.2.3