mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-10 15:14:32 +00:00
Merge #2661
2661: Implement infer await from async function r=flodiebold a=edwin0cheng This PR is my attempt for trying to add support for infer `.await` expression from an `async` function, by desugaring its return type to `Impl Future<Output=RetType>`. Note that I don't know it is supposed to desugaring it in that phase, if it is not suitable in current design, just feel free to reject it :) r=@flodiebold Co-authored-by: Edwin Cheng <edwin0cheng@gmail.com>
This commit is contained in:
commit
3f7e5cde0b
5 changed files with 110 additions and 3 deletions
|
@ -10,8 +10,9 @@ use ra_syntax::ast::{self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAs
|
|||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
path::{path, GenericArgs, Path},
|
||||
src::HasSource,
|
||||
type_ref::{Mutability, TypeRef},
|
||||
type_ref::{Mutability, TypeBound, TypeRef},
|
||||
AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule,
|
||||
ImplId, Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
|
||||
};
|
||||
|
@ -62,11 +63,29 @@ impl FunctionData {
|
|||
TypeRef::unit()
|
||||
};
|
||||
|
||||
let ret_type = if src.value.is_async() {
|
||||
let future_impl = desugar_future_path(ret_type);
|
||||
let ty_bound = TypeBound::Path(future_impl);
|
||||
TypeRef::ImplTrait(vec![ty_bound])
|
||||
} else {
|
||||
ret_type
|
||||
};
|
||||
|
||||
let sig = FunctionData { name, params, ret_type, has_self_param };
|
||||
Arc::new(sig)
|
||||
}
|
||||
}
|
||||
|
||||
fn desugar_future_path(orig: TypeRef) -> Path {
|
||||
let path = path![std::future::Future];
|
||||
let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
|
||||
let mut last = GenericArgs::empty();
|
||||
last.bindings.push((name![Output], orig));
|
||||
generic_args.push(Some(Arc::new(last)));
|
||||
|
||||
Path::from_known_path(path, generic_args)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TypeAliasData {
|
||||
pub name: Name,
|
||||
|
|
|
@ -130,6 +130,14 @@ impl Path {
|
|||
Path { type_anchor: None, mod_path: name_ref.as_name().into(), generic_args: vec![None] }
|
||||
}
|
||||
|
||||
/// Converts a known mod path to `Path`.
|
||||
pub(crate) fn from_known_path(
|
||||
path: ModPath,
|
||||
generic_args: Vec<Option<Arc<GenericArgs>>>,
|
||||
) -> Path {
|
||||
Path { type_anchor: None, mod_path: path, generic_args }
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &PathKind {
|
||||
&self.mod_path.kind
|
||||
}
|
||||
|
|
|
@ -37,8 +37,8 @@ use test_utils::tested_by;
|
|||
use super::{
|
||||
primitive::{FloatTy, IntTy},
|
||||
traits::{Guidance, Obligation, ProjectionPredicate, Solution},
|
||||
ApplicationTy, InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor,
|
||||
TypeWalk, Uncertain,
|
||||
ApplicationTy, GenericPredicate, InEnvironment, ProjectionTy, Substs, TraitEnvironment,
|
||||
TraitRef, Ty, TypeCtor, TypeWalk, Uncertain,
|
||||
};
|
||||
use crate::{db::HirDatabase, infer::diagnostics::InferenceDiagnostic};
|
||||
|
||||
|
@ -379,6 +379,25 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
) -> Ty {
|
||||
match assoc_ty {
|
||||
Some(res_assoc_ty) => {
|
||||
// FIXME:
|
||||
// Check if inner_ty is is `impl Trait` and contained input TypeAlias id
|
||||
// this is a workaround while Chalk assoc type projection doesn't always work yet,
|
||||
// but once that is fixed I don't think we should keep this
|
||||
// (we'll probably change how associated types are resolved anyway)
|
||||
if let Ty::Opaque(ref predicates) = inner_ty {
|
||||
for p in predicates.iter() {
|
||||
if let GenericPredicate::Projection(projection) = p {
|
||||
if projection.projection_ty.associated_ty == res_assoc_ty {
|
||||
if let ty_app!(_, params) = &projection.ty {
|
||||
if params.len() == 0 {
|
||||
return projection.ty.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ty = self.table.new_type_var();
|
||||
let builder = Substs::build_for_def(self.db, res_assoc_ty)
|
||||
.push(inner_ty)
|
||||
|
|
|
@ -37,6 +37,63 @@ mod future {
|
|||
assert_eq!("u64", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_async() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
|
||||
async fn foo() -> u64 {
|
||||
128
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let r = foo();
|
||||
let v = r.await;
|
||||
v<|>;
|
||||
}
|
||||
|
||||
//- /std.rs crate:std
|
||||
#[prelude_import] use future::*;
|
||||
mod future {
|
||||
trait Future {
|
||||
type Output;
|
||||
}
|
||||
}
|
||||
|
||||
"#,
|
||||
);
|
||||
assert_eq!("u64", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_desugar_async() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
|
||||
async fn foo() -> u64 {
|
||||
128
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let r = foo();
|
||||
r<|>;
|
||||
}
|
||||
|
||||
//- /std.rs crate:std
|
||||
#[prelude_import] use future::*;
|
||||
mod future {
|
||||
trait Future {
|
||||
type Output;
|
||||
}
|
||||
}
|
||||
|
||||
"#,
|
||||
);
|
||||
assert_eq!("impl Future<Output = u64>", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_try() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
|
|
|
@ -221,6 +221,10 @@ impl ast::FnDef {
|
|||
.and_then(|it| it.into_token())
|
||||
.filter(|it| it.kind() == T![;])
|
||||
}
|
||||
|
||||
pub fn is_async(&self) -> bool {
|
||||
self.syntax().children_with_tokens().any(|it| it.kind() == T![async])
|
||||
}
|
||||
}
|
||||
|
||||
impl ast::LetStmt {
|
||||
|
|
Loading…
Reference in a new issue