diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index 415800b9b79a..71061ba31b8b 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -126,6 +126,7 @@ impl Body { let mut params = None; let mut is_async_fn = false; + let mut is_gen_fn = false; let InFile { file_id, value: body } = { match def { DefWithBodyId::FunctionId(f) => { @@ -146,7 +147,9 @@ impl Body { }), ) }); + // FIXME from here we know this only happens for functions, so maybe we can get the blockexpr directly and also onlive provide the keyword values then is_async_fn = data.has_async_kw(); + is_gen_fn = data.has_gen_kw(); src.map(|it| it.body().map(ast::Expr::from)) } DefWithBodyId::ConstId(c) => { @@ -169,7 +172,7 @@ impl Body { let module = def.module(db); let expander = Expander::new(db, file_id, module); let (mut body, mut source_map) = - Body::new(db, def, expander, params, body, module.krate, is_async_fn); + Body::new(db, def, expander, params, body, module.krate, is_async_fn, is_gen_fn); body.shrink_to_fit(); source_map.shrink_to_fit(); @@ -209,8 +212,9 @@ impl Body { body: Option, krate: CrateId, is_async_fn: bool, + is_gen_fn: bool, ) -> (Body, BodySourceMap) { - lower::lower(db, owner, expander, params, body, krate, is_async_fn) + lower::lower(db, owner, expander, params, body, krate, is_async_fn, is_gen_fn) } fn shrink_to_fit(&mut self) { diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 762bf9fbe0ec..1f90bbbc94c8 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -59,6 +59,7 @@ pub(super) fn lower( body: Option, krate: CrateId, is_async_fn: bool, + is_gen_fn: bool, ) -> (Body, BodySourceMap) { ExprCollector { db, @@ -85,7 +86,7 @@ pub(super) fn lower( label_ribs: Vec::new(), current_binding_owner: None, } - .collect(params, body, is_async_fn) + .collect(params, body, is_async_fn, is_gen_fn) } struct ExprCollector<'a> { @@ -191,6 +192,7 @@ impl ExprCollector<'_> { param_list: Option<(ast::ParamList, impl Iterator)>, body: Option, is_async_fn: bool, + is_gen_fn: bool, ) -> (Body, BodySourceMap) { if let Some((param_list, mut attr_enabled)) = param_list { if let Some(self_param) = @@ -212,23 +214,73 @@ impl ExprCollector<'_> { self.body.params.push(param_pat); } }; - self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| { - if is_async_fn { - match body { + self.body.body_expr = + self.with_label_rib(RibKind::Closure, |this| match (is_async_fn, is_gen_fn) { + (false, false) => this.collect_expr_opt(body), + (false, true) => match body { Some(e) => { let expr = this.collect_expr(e); - this.alloc_expr_desugared(Expr::Async { - id: None, - statements: Box::new([]), - tail: Some(expr), + + this.alloc_expr_desugared(Expr::Closure { + args: Box::new([]), + arg_types: Box::new([]), + ret_type: None, // FIXME maybe unspecified? + body: expr, + closure_kind: ClosureKind::Coroutine( + crate::hir::CoroutineKind::Desugared( + crate::hir::CoroutineDesugaring::Gen, + crate::hir::CoroutineSource::Fn, + ), + Movability::Movable, + ), + capture_by: CaptureBy::Ref, }) } None => this.missing_expr(), - } - } else { - this.collect_expr_opt(body) - } - }); + }, + (true, false) => match body { + Some(e) => { + let expr = this.collect_expr(e); + + this.alloc_expr_desugared(Expr::Closure { + args: Box::new([]), + arg_types: Box::new([]), + ret_type: None, // FIXME maybe unspecified? + body: expr, + closure_kind: ClosureKind::Coroutine( + crate::hir::CoroutineKind::Desugared( + crate::hir::CoroutineDesugaring::Async, + crate::hir::CoroutineSource::Fn, + ), + Movability::Movable, + ), + capture_by: CaptureBy::Ref, + }) + } + None => this.missing_expr(), + }, + (true, true) => match body { + Some(e) => { + let expr = this.collect_expr(e); + + this.alloc_expr_desugared(Expr::Closure { + args: Box::new([]), + arg_types: Box::new([]), + ret_type: None, // FIXME maybe unspecified? + body: expr, + closure_kind: ClosureKind::Coroutine( + crate::hir::CoroutineKind::Desugared( + crate::hir::CoroutineDesugaring::AsyncGen, + crate::hir::CoroutineSource::Fn, + ), + Movability::Movable, + ), + capture_by: CaptureBy::Ref, + }) + } + None => this.missing_expr(), + }, + }); (self.body, self.source_map) } @@ -268,6 +320,7 @@ impl ExprCollector<'_> { let expr = self.collect_expr_opt(e.expr()); self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr) } + // https://github.com/compiler-errors/rust/blob/bd0eec74026d5f967afeadc3611bdb674a7b9de4/compiler/rustc_ast/src/ast.rs#L1430 ast::Expr::BlockExpr(e) => match e.modifier() { Some(ast::BlockModifier::Try(_)) => self.desugar_try_block(e), Some(ast::BlockModifier::Unsafe(_)) => { @@ -288,13 +341,76 @@ impl ExprCollector<'_> { }) }) } + // https://github.com/compiler-errors/rust/blob/closure-kind/compiler/rustc_ast/src/ast.rs#L1430 + // https://github.com/compiler-errors/rust/blob/bd0eec74026d5f967afeadc3611bdb674a7b9de4/compiler/rustc_ast_lowering/src/expr.rs#L186 Some(ast::BlockModifier::Async(_)) => { self.with_label_rib(RibKind::Closure, |this| { - this.collect_block_(e, |id, statements, tail| Expr::Async { - id, - statements, - tail, - }) + let (result_expr_id, _) = this.initialize_binding_owner(syntax_ptr); + + let body = this.collect_block(e); + + this.body.exprs[result_expr_id] = Expr::Closure { + args: Box::new([]), + arg_types: Box::new([]), + ret_type: None, // FIXME maybe unspecified? + body, + closure_kind: ClosureKind::Coroutine( + crate::hir::CoroutineKind::Desugared( + crate::hir::CoroutineDesugaring::Async, + crate::hir::CoroutineSource::Block, + ), + Movability::Movable, + ), + capture_by: CaptureBy::Ref, + }; + + result_expr_id + }) + } + Some(ast::BlockModifier::Gen(_)) => self.with_label_rib(RibKind::Closure, |this| { + let (result_expr_id, _) = this.initialize_binding_owner(syntax_ptr); + + let body = this.collect_block(e); + + this.body.exprs[result_expr_id] = Expr::Closure { + args: Box::new([]), + arg_types: Box::new([]), + ret_type: None, // FIXME maybe unspecified? + body, + closure_kind: ClosureKind::Coroutine( + crate::hir::CoroutineKind::Desugared( + crate::hir::CoroutineDesugaring::Gen, + crate::hir::CoroutineSource::Block, + ), + Movability::Movable, + ), + capture_by: CaptureBy::Ref, + }; + + result_expr_id + }), + Some(ast::BlockModifier::AsyncGen(_)) => { + self.with_label_rib(RibKind::Closure, |this| { + let (result_expr_id, _) = this.initialize_binding_owner(syntax_ptr); + + let body = this.collect_block(e); + + this.body.exprs[result_expr_id] = Expr::Closure { + args: Box::new([]), + arg_types: Box::new([]), + ret_type: None, // FIXME maybe unspecified? + body, + closure_kind: ClosureKind::Coroutine( + crate::hir::CoroutineKind::Desugared( + crate::hir::CoroutineDesugaring::AsyncGen, + crate::hir::CoroutineSource::Block, + ), + Movability::Movable, + ), + capture_by: CaptureBy::Ref, + }; + + result_expr_id }) } Some(ast::BlockModifier::Const(_)) => { @@ -504,6 +620,7 @@ impl ExprCollector<'_> { } } ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| { + // here let (result_expr_id, prev_binding_owner) = this.initialize_binding_owner(syntax_ptr); let mut args = Vec::new(); @@ -536,9 +653,39 @@ impl ExprCollector<'_> { } else { Movability::Movable }; - ClosureKind::Coroutine(movability) + ClosureKind::Coroutine(crate::hir::CoroutineKind::Coroutine, movability) } else if e.async_token().is_some() { - ClosureKind::Async + // https://github.com/compiler-errors/rust/blob/bd0eec74026d5f967afeadc3611bdb674a7b9de4/compiler/rustc_ast_lowering/src/expr.rs#L1199 ? + + let capture_by = + if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; + + let inner = Expr::Closure { + args: Box::new([]), + arg_types: Box::new([]), + ret_type: ret_type.clone(), + body, + closure_kind: ClosureKind::Coroutine( + crate::hir::CoroutineKind::Desugared( + crate::hir::CoroutineDesugaring::Async, + crate::hir::CoroutineSource::Closure, + ), + Movability::Movable, + ), + capture_by, + }; + this.is_lowering_generator = prev_is_lowering_generator; + this.current_binding_owner = prev_binding_owner; + this.current_try_block_label = prev_try_block_label; + this.body.exprs[result_expr_id] = Expr::Closure { + args: args.into(), + arg_types: arg_types.into(), + ret_type, + body: this.alloc_expr_desugared(inner), + closure_kind: ClosureKind::Closure, + capture_by, + }; + return result_expr_id; } else { ClosureKind::Closure }; diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 3e4a31ca0434..79ffb2d2dbea 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -7,8 +7,8 @@ use syntax::ast::HasName; use crate::{ hir::{ - Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Literal, LiteralOrConst, - Movability, Statement, + Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, CoroutineDesugaring, + CoroutineKind, Literal, LiteralOrConst, Movability, Statement, }, pretty::{print_generic_args, print_path, print_type_ref}, type_ref::TypeRef, @@ -383,14 +383,29 @@ impl Printer<'_> { w!(self, "]"); } Expr::Closure { args, arg_types, ret_type, body, closure_kind, capture_by } => { + if let ClosureKind::Coroutine(_, Movability::Static) = closure_kind { + w!(self, "static "); + } match closure_kind { - ClosureKind::Coroutine(Movability::Static) => { - w!(self, "static "); - } - ClosureKind::Async => { + ClosureKind::Coroutine( + CoroutineKind::Desugared(CoroutineDesugaring::Async, _), + _, + ) => { w!(self, "async "); } - _ => (), + ClosureKind::Coroutine( + CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _), + _, + ) => { + w!(self, "async gen "); + } + ClosureKind::Coroutine( + CoroutineKind::Desugared(CoroutineDesugaring::Gen, _), + _, + ) => { + w!(self, "gen "); + } + _ => {} } match capture_by { CaptureBy::Value => { @@ -398,23 +413,25 @@ impl Printer<'_> { } CaptureBy::Ref => (), } - w!(self, "|"); - for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() { - if i != 0 { - w!(self, ", "); + if let ClosureKind::Closure = closure_kind { + w!(self, "|"); + for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() { + if i != 0 { + w!(self, ", "); + } + self.print_pat(*pat); + if let Some(ty) = ty { + w!(self, ": "); + self.print_type_ref(ty); + } } - self.print_pat(*pat); - if let Some(ty) = ty { - w!(self, ": "); - self.print_type_ref(ty); + w!(self, "|"); + if let Some(ret_ty) = ret_type { + w!(self, " -> "); + self.print_type_ref(ret_ty); } + self.whitespace(); } - w!(self, "|"); - if let Some(ret_ty) = ret_type { - w!(self, " -> "); - self.print_type_ref(ret_ty); - } - self.whitespace(); self.print_expr(*body); } Expr::Tuple { exprs, is_assignee_expr: _ } => { @@ -454,9 +471,6 @@ impl Printer<'_> { Expr::Unsafe { id: _, statements, tail } => { self.print_block(Some("unsafe "), statements, tail); } - Expr::Async { id: _, statements, tail } => { - self.print_block(Some("async "), statements, tail); - } Expr::Const(id) => { w!(self, "const {{ /* {id:?} */ }}"); } diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs index ab623250d407..a477dbb8c28d 100644 --- a/crates/hir-def/src/body/scope.rs +++ b/crates/hir-def/src/body/scope.rs @@ -220,7 +220,7 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope Expr::Const(_) => { // FIXME: This is broken. } - Expr::Unsafe { id, statements, tail } | Expr::Async { id, statements, tail } => { + Expr::Unsafe { id, statements, tail } => { let mut scope = scopes.new_block_scope(*scope, *id, None); // Overwrite the old scope for the block expr, so that every block scope can be found // via the block itself (important for blocks that only contain items, no expressions). diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index a46b4bc61508..3a9df6b28801 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs @@ -176,11 +176,6 @@ pub enum Expr { tail: Option, label: Option, }, - Async { - id: Option, - statements: Box<[Statement]>, - tail: Option, - }, Const(ConstBlockId), Unsafe { id: Option, @@ -296,11 +291,59 @@ pub struct InlineAsm { pub e: ExprId, } +// https://github.com/compiler-errors/rust/blob/bd0eec74026d5f967afeadc3611bdb674a7b9de4/compiler/rustc_hir/src/hir.rs#L952 #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ClosureKind { + /// This is a plain closure expression. Closure, - Coroutine(Movability), + /// This is a coroutine expression -- i.e. a closure expression in which + /// we've found a `yield`. These can arise either from "plain" coroutine + /// usage (e.g. `let x = || { yield (); }`) or from a desugared expression + /// (e.g. `async` and `gen` blocks). + // FIXME(coroutines): We could probably remove movability here -- it can be deduced + // from the `CoroutineKind` in all cases (except for "plain" coroutines, which could + // carry the movability in the variant). + Coroutine(CoroutineKind, Movability), +} + +/// The type of source expression that caused this coroutine to be created. +#[derive(Clone, PartialEq, Eq, Debug, Copy, Hash)] +pub enum CoroutineKind { + /// A coroutine that comes from a desugaring. + Desugared(CoroutineDesugaring, CoroutineSource), + + /// A coroutine literal created via a `yield` inside a closure. + Coroutine, +} + +#[derive(Clone, PartialEq, Eq, Debug, Copy, Hash)] +pub enum CoroutineDesugaring { + /// An explicit `async` block or the body of an `async` function. Async, + + /// An explicit `gen` block or the body of a `gen` function. + Gen, + + /// An explicit `async gen` block or the body of an `async gen` function, + /// which is able to both `yield` and `.await`. + AsyncGen, +} + +/// In the case of a coroutine created as part of an async/gen construct, +/// which kind of async/gen construct caused it to be created? +/// +/// This helps error messages but is also used to drive coercions in +/// type-checking (see #60424). +#[derive(Clone, PartialEq, Eq, Hash, Debug, Copy)] +pub enum CoroutineSource { + /// An explicit `async`/`gen` block written by the user. + Block, + + /// An explicit `async`/`gen` closure written by the user. + Closure, + + /// The `async`/`gen` block generated as the body of an async/gen function. + Fn, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -367,9 +410,7 @@ impl Expr { f(*expr); } Expr::Const(_) => (), - Expr::Block { statements, tail, .. } - | Expr::Unsafe { statements, tail, .. } - | Expr::Async { statements, tail, .. } => { + Expr::Block { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } => { for stmt in statements.iter() { match stmt { Statement::Let { initializer, else_branch, .. } => { diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs index 46a0d2cd04aa..3ca23a5601fc 100644 --- a/crates/hir-def/src/item_tree/lower.rs +++ b/crates/hir-def/src/item_tree/lower.rs @@ -354,12 +354,26 @@ impl<'a> Ctx<'a> { None => TypeRef::unit(), }; - let ret_type = if func.async_token().is_some() { - let future_impl = desugar_future_path(ret_type); - let ty_bound = Interned::new(TypeBound::Path(future_impl, TraitBoundModifier::None)); - TypeRef::ImplTrait(vec![ty_bound]) - } else { - ret_type + let ret_type = match (func.async_token(), func.gen_token()) { + (None, None) => ret_type, + (None, Some(_)) => { + let iterator_impl = desugar_iterator_path(ret_type); + let ty_bound = + Interned::new(TypeBound::Path(iterator_impl, TraitBoundModifier::None)); + TypeRef::ImplTrait(vec![ty_bound]) + } + (Some(_), None) => { + let future_impl = desugar_future_path(ret_type); + let ty_bound = + Interned::new(TypeBound::Path(future_impl, TraitBoundModifier::None)); + TypeRef::ImplTrait(vec![ty_bound]) + } + (Some(_), Some(_)) => { + let async_iterator_impl = desugar_async_iterator_path(ret_type); + let ty_bound = + Interned::new(TypeBound::Path(async_iterator_impl, TraitBoundModifier::None)); + TypeRef::ImplTrait(vec![ty_bound]) + } }; let abi = func.abi().map(lower_abi); @@ -717,6 +731,42 @@ fn desugar_future_path(orig: TypeRef) -> Path { Path::from_known_path(path, generic_args) } +fn desugar_iterator_path(orig: TypeRef) -> Path { + let path = path![core::iter::Iterator]; + let mut generic_args: Vec<_> = + std::iter::repeat(None).take(path.segments().len() - 1).collect(); + let binding = AssociatedTypeBinding { + name: name![Item], + args: None, + type_ref: Some(orig), + bounds: Box::default(), + }; + generic_args.push(Some(Interned::new(GenericArgs { + bindings: Box::new([binding]), + ..GenericArgs::empty() + }))); + + Path::from_known_path(path, generic_args) +} + +fn desugar_async_iterator_path(orig: TypeRef) -> Path { + let path = path![core::async_iter::AsyncIterator]; + let mut generic_args: Vec<_> = + std::iter::repeat(None).take(path.segments().len() - 1).collect(); + let binding = AssociatedTypeBinding { + name: name![Item], + args: None, + type_ref: Some(orig), + bounds: Box::default(), + }; + generic_args.push(Some(Interned::new(GenericArgs { + bindings: Box::new([binding]), + ..GenericArgs::empty() + }))); + + Path::from_known_path(path, generic_args) +} + enum HasImplicitSelf { /// Inner list is a type bound list for the implicit `Self`. Yes(Option), diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs index 7e8f094083ee..46c369d36b15 100644 --- a/crates/hir-def/src/item_tree/tests.rs +++ b/crates/hir-def/src/item_tree/tests.rs @@ -391,9 +391,9 @@ gen fn test1() {} async gen fn test2() {} "#, expect![[r#" - pub(self) gen fn test1() -> () { ... } + pub(self) gen fn test1() -> impl ::core::iter::Iterator:: { ... } - pub(self) async gen fn test2() -> impl ::core::future::Future:: { ... } + pub(self) async gen fn test2() -> impl ::core::async_iter::AsyncIterator:: { ... } "#]], ) } diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index b9745b662983..f2df77c4e12a 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -327,6 +327,7 @@ language_item_table! { FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0); + AsyncIterator, sym::asynciterator_trait, asynciterator_trait, Target::Trait, GenericRequirement::Exact(0); CoroutineState, sym::coroutine_state, coroutine_state, Target::Enum, GenericRequirement::None; Coroutine, sym::coroutine, coroutine_trait, Target::Trait, GenericRequirement::Minimum(1); Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; @@ -428,6 +429,7 @@ language_item_table! { IntoFutureIntoFuture, sym::into_future, into_future_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; IntoIterIntoIter, sym::into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + Iterator, sym::iterator_trait, iterator_trait, Target::Trait, GenericRequirement::Exact(0); IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None; PinNewUnchecked, sym::new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None; diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index 30b8c189f52d..607c69e49ed7 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs @@ -308,6 +308,7 @@ pub use crate::name as __name; #[macro_export] macro_rules! __known_path { + (core::async_iter::AsyncIterator) => {}; (core::iter::IntoIterator) => {}; (core::iter::Iterator) => {}; (core::result::Result) => {}; diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 3d8d01e25566..e9f39d2a88cf 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -295,6 +295,7 @@ pub mod known { std, core, alloc, + async_iter, iter, ops, fmt, @@ -323,6 +324,7 @@ pub mod known { unreachable_2015, unreachable_2021, // Components of known path (type name) + AsyncIterator, Iterator, IntoIterator, Item, @@ -447,6 +449,8 @@ pub mod known { fn_mut, fn_once, future_trait, + iterator_trait, + asynciterator_trait, index, index_mut, into_future, diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index 0fa3a188ea53..d378342d2bc4 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/crates/hir-ty/src/chalk_db.rs @@ -339,6 +339,168 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { make_single_type_binders(bound) } } + crate::ImplTraitId::GenBlockTypeImplTrait(..) => { + if let Some((iterator_trait, iterator_item)) = self + .db + .lang_item(self.krate, LangItem::Iterator) + .and_then(|item| item.as_trait()) + .and_then(|trait_| { + let alias = + self.db.trait_data(trait_).associated_type_by_name(&name![Item])?; + Some((trait_, alias)) + }) + { + // Making up Symbol’s value as variable is void: AsyncBlock: + // + // |--------------------OpaqueTyDatum-------------------| + // |-------------OpaqueTyDatumBound--------------| + // for [Future, Future::Output = T] + // ^1 ^0 ^0 ^0 ^1 + let impl_bound = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(iterator_trait), + // Self type as the first parameter. + substitution: Substitution::from1( + Interner, + TyKind::BoundVar(BoundVar { + debruijn: DebruijnIndex::INNERMOST, + index: 0, + }) + .intern(Interner), + ), + }); + let mut binder = vec![]; + binder.push(crate::wrap_empty_binders(impl_bound)); + let sized_trait = self + .db + .lang_item(self.krate, LangItem::Sized) + .and_then(|item| item.as_trait()); + if let Some(sized_trait_) = sized_trait { + let sized_bound = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(sized_trait_), + // Self type as the first parameter. + substitution: Substitution::from1( + Interner, + TyKind::BoundVar(BoundVar { + debruijn: DebruijnIndex::INNERMOST, + index: 0, + }) + .intern(Interner), + ), + }); + binder.push(crate::wrap_empty_binders(sized_bound)); + } + let proj_bound = WhereClause::AliasEq(AliasEq { + alias: AliasTy::Projection(ProjectionTy { + associated_ty_id: to_assoc_type_id(iterator_item), + // Self type as the first parameter. + substitution: Substitution::from1( + Interner, + TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)) + .intern(Interner), + ), + }), + // The parameter of the opaque type. + ty: TyKind::BoundVar(BoundVar { debruijn: DebruijnIndex::ONE, index: 0 }) + .intern(Interner), + }); + binder.push(crate::wrap_empty_binders(proj_bound)); + let bound = OpaqueTyDatumBound { + bounds: make_single_type_binders(binder), + where_clauses: chalk_ir::Binders::empty(Interner, vec![]), + }; + // The opaque type has 1 parameter. + make_single_type_binders(bound) + } else { + // If failed to find Symbol’s value as variable is void: Iterator::Item, return empty bounds as fallback. + let bound = OpaqueTyDatumBound { + bounds: chalk_ir::Binders::empty(Interner, vec![]), + where_clauses: chalk_ir::Binders::empty(Interner, vec![]), + }; + // The opaque type has 1 parameter. + make_single_type_binders(bound) + } + } + crate::ImplTraitId::AsyncGenBlockTypeImplTrait(..) => { + if let Some((async_iterator_trait, async_iterator_item)) = self + .db + .lang_item(self.krate, LangItem::AsyncIterator) + .and_then(|item| item.as_trait()) + .and_then(|trait_| { + let alias = + self.db.trait_data(trait_).associated_type_by_name(&name![Item])?; + Some((trait_, alias)) + }) + { + // Making up Symbol’s value as variable is void: AsyncBlock: + // + // |--------------------OpaqueTyDatum-------------------| + // |-------------OpaqueTyDatumBound--------------| + // for [Future, Future::Output = T] + // ^1 ^0 ^0 ^0 ^1 + let impl_bound = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(async_iterator_trait), + // Self type as the first parameter. + substitution: Substitution::from1( + Interner, + TyKind::BoundVar(BoundVar { + debruijn: DebruijnIndex::INNERMOST, + index: 0, + }) + .intern(Interner), + ), + }); + let mut binder = vec![]; + binder.push(crate::wrap_empty_binders(impl_bound)); + let sized_trait = self + .db + .lang_item(self.krate, LangItem::Sized) + .and_then(|item| item.as_trait()); + if let Some(sized_trait_) = sized_trait { + let sized_bound = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(sized_trait_), + // Self type as the first parameter. + substitution: Substitution::from1( + Interner, + TyKind::BoundVar(BoundVar { + debruijn: DebruijnIndex::INNERMOST, + index: 0, + }) + .intern(Interner), + ), + }); + binder.push(crate::wrap_empty_binders(sized_bound)); + } + let proj_bound = WhereClause::AliasEq(AliasEq { + alias: AliasTy::Projection(ProjectionTy { + associated_ty_id: to_assoc_type_id(async_iterator_item), + // Self type as the first parameter. + substitution: Substitution::from1( + Interner, + TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)) + .intern(Interner), + ), + }), + // The parameter of the opaque type. + ty: TyKind::BoundVar(BoundVar { debruijn: DebruijnIndex::ONE, index: 0 }) + .intern(Interner), + }); + binder.push(crate::wrap_empty_binders(proj_bound)); + let bound = OpaqueTyDatumBound { + bounds: make_single_type_binders(binder), + where_clauses: chalk_ir::Binders::empty(Interner, vec![]), + }; + // The opaque type has 1 parameter. + make_single_type_binders(bound) + } else { + // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback. + let bound = OpaqueTyDatumBound { + bounds: chalk_ir::Binders::empty(Interner, vec![]), + where_clauses: chalk_ir::Binders::empty(Interner, vec![]), + }; + // The opaque type has 1 parameter. + make_single_type_binders(bound) + } + } }; Arc::new(OpaqueTyDatum { opaque_ty_id: id, bound }) @@ -451,10 +613,11 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { let movability = match self.db.body(parent)[expr] { hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::Coroutine(movability), + closure_kind: hir_def::hir::ClosureKind::Coroutine(_, movability), .. } => movability, _ => unreachable!("non coroutine expression interned as coroutine"), + _ => unreachable!("non coroutine expression interned as coroutine"), }; let movability = match movability { Movability::Static => rust_ir::Movability::Static, diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index c9ab356854b8..b98d607928bb 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs @@ -265,6 +265,47 @@ impl TyExt for Ty { None } } + ImplTraitId::GenBlockTypeImplTrait(def, _expr) => { + let krate = def.module(db.upcast()).krate(); + if let Some(iterator_trait) = + db.lang_item(krate, LangItem::Iterator).and_then(|item| item.as_trait()) + { + // This is only used by type walking. + // Parameters will be walked outside, and projection predicate is not used. + // So just provide the Future trait. + let impl_bound = Binders::empty( + Interner, + WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(iterator_trait), + substitution: Substitution::empty(Interner), + }), + ); + Some(vec![impl_bound]) + } else { + None + } + } + ImplTraitId::AsyncGenBlockTypeImplTrait(def, _expr) => { + let krate = def.module(db.upcast()).krate(); + if let Some(async_iterator_trait) = db + .lang_item(krate, LangItem::AsyncIterator) + .and_then(|item| item.as_trait()) + { + // This is only used by type walking. + // Parameters will be walked outside, and projection predicate is not used. + // So just provide the Future trait. + let impl_bound = Binders::empty( + Interner, + WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(async_iterator_trait), + substitution: Substitution::empty(Interner), + }), + ); + Some(vec![impl_bound]) + } else { + None + } + } ImplTraitId::ReturnTypeImplTrait(func, idx) => { db.return_type_impl_traits(func).map(|it| { let data = @@ -285,7 +326,9 @@ impl TyExt for Ty { }) } // It always has an parameter for Future::Output type. - ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(), + ImplTraitId::AsyncBlockTypeImplTrait(..) + | ImplTraitId::GenBlockTypeImplTrait(..) + | ImplTraitId::AsyncGenBlockTypeImplTrait(..) => unreachable!(), }; predicates.map(|it| it.into_value_and_skipped_binders().0) diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 433cf6207e3a..a80db54e71bf 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -1053,6 +1053,60 @@ impl HirDisplay for Ty { parameters.at(Interner, 0).hir_fmt(f)?; write!(f, ">")?; } + ImplTraitId::GenBlockTypeImplTrait(body, ..) => { + let iterator_trait = db + .lang_item(body.module(db.upcast()).krate(), LangItem::Iterator) + .and_then(LangItemTarget::as_trait); + let item = iterator_trait.and_then(|t| { + db.trait_data(t).associated_type_by_name(&hir_expand::name!(Item)) + }); + write!(f, "impl ")?; + if let Some(t) = iterator_trait { + f.start_location_link(t.into()); + } + write!(f, "Iterator")?; + if let Some(_) = iterator_trait { + f.end_location_link(); + } + write!(f, "<")?; + if let Some(t) = item { + f.start_location_link(t.into()); + } + write!(f, "Item")?; + if let Some(_) = item { + f.end_location_link(); + } + write!(f, " = ")?; + parameters.at(Interner, 0).hir_fmt(f)?; + write!(f, ">")?; + } + ImplTraitId::AsyncGenBlockTypeImplTrait(body, ..) => { + let future_trait = db + .lang_item(body.module(db.upcast()).krate(), LangItem::AsyncIterator) + .and_then(LangItemTarget::as_trait); + let output = future_trait.and_then(|t| { + db.trait_data(t).associated_type_by_name(&hir_expand::name!(Item)) + }); + write!(f, "impl ")?; + if let Some(t) = future_trait { + f.start_location_link(t.into()); + } + write!(f, "AsyncIterator")?; + if let Some(_) = future_trait { + f.end_location_link(); + } + write!(f, "<")?; + if let Some(t) = output { + f.start_location_link(t.into()); + } + write!(f, "Item")?; + if let Some(_) = output { + f.end_location_link(); + } + write!(f, " = ")?; + parameters.at(Interner, 0).hir_fmt(f)?; + write!(f, ">")?; + } } } TyKind::Closure(id, substs) => { @@ -1194,6 +1248,12 @@ impl HirDisplay for Ty { ImplTraitId::AsyncBlockTypeImplTrait(..) => { write!(f, "{{async block}}")?; } + ImplTraitId::GenBlockTypeImplTrait(..) => { + write!(f, "{{gen block}}")?; + } + ImplTraitId::AsyncGenBlockTypeImplTrait(..) => { + write!(f, "{{async gen block}}")?; + } }; } TyKind::Error => { diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 8ca8816458b6..2034bfb31d9d 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -461,9 +461,7 @@ impl InferenceContext<'_> { self.consume_expr(expr); } } - Expr::Async { statements, tail, .. } - | Expr::Unsafe { statements, tail, .. } - | Expr::Block { statements, tail, .. } => { + Expr::Unsafe { statements, tail, .. } | Expr::Block { statements, tail, .. } => { for s in statements.iter() { match s { Statement::Let { pat, type_ref: _, initializer, else_branch } => { @@ -605,7 +603,6 @@ impl InferenceContext<'_> { Expr::Closure { .. } => { let ty = self.expr_ty(tgt_expr); let TyKind::Closure(id, _) = ty.kind(Interner) else { - never!("closure type is always closure"); return; }; let (captures, _) = diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 63a06fa40a74..9448483ec525 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -9,7 +9,8 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin use hir_def::{ generics::TypeOrConstParamData, hir::{ - ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp, + ArithOp, Array, BinaryOp, ClosureKind, CoroutineDesugaring, CoroutineKind, Expr, ExprId, + LabelId, Literal, Statement, UnaryOp, }, lang_item::{LangItem, LangItemTarget}, path::{GenericArg, GenericArgs}, @@ -176,9 +177,6 @@ impl InferenceContext<'_> { }) .1 } - Expr::Async { id, statements, tail } => { - self.infer_async_block(tgt_expr, id, statements, tail) - } &Expr::Loop { body, label } => { // FIXME: should be: // let ty = expected.coercion_target_type(&mut self.table); @@ -215,10 +213,26 @@ impl InferenceContext<'_> { Some(type_ref) => self.make_ty(type_ref), None => self.table.new_type_var(), }; - if let ClosureKind::Async = closure_kind { - sig_tys.push(self.lower_async_block_type_impl_trait(ret_ty.clone(), *body)); - } else { - sig_tys.push(ret_ty.clone()); + match closure_kind { + ClosureKind::Coroutine( + CoroutineKind::Desugared(CoroutineDesugaring::Async, _), + _, + ) => { + sig_tys.push(self.lower_async_block_type_impl_trait(ret_ty.clone(), *body)); + } + ClosureKind::Coroutine( + CoroutineKind::Desugared(CoroutineDesugaring::Gen, _), + _, + ) => { + sig_tys.push(self.lower_async_block_type_impl_trait(ret_ty.clone(), *body)); + } + ClosureKind::Coroutine( + CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _), + _, + ) => { + sig_tys.push(self.lower_async_block_type_impl_trait(ret_ty.clone(), *body)); + } + _ => sig_tys.push(ret_ty.clone()), } let sig_ty = TyKind::Function(FnPointer { @@ -232,7 +246,7 @@ impl InferenceContext<'_> { .intern(Interner); let (id, ty, resume_yield_tys) = match closure_kind { - ClosureKind::Coroutine(_) => { + ClosureKind::Coroutine(coroutine_kind, _) => { // FIXME: report error when there are more than 1 parameter. let resume_ty = match sig_tys.first() { // When `sig_tys.len() == 1` the first type is the return type, not the @@ -242,18 +256,45 @@ impl InferenceContext<'_> { }; let yield_ty = self.table.new_type_var(); - let subst = TyBuilder::subst_for_coroutine(self.db, self.owner) - .push(resume_ty.clone()) - .push(yield_ty.clone()) - .push(ret_ty.clone()) - .build(); + // FIXME if async create future, etc# + match coroutine_kind { + CoroutineKind::Desugared(CoroutineDesugaring::Async, _) => { + let ty = self + .lower_async_block_type_impl_trait(ret_ty.clone(), tgt_expr); + + (None, ty, Some((resume_ty, yield_ty))) + } + CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => { + let ty = self + .lower_gen_block_type_impl_trait(yield_ty.clone(), tgt_expr); - let coroutine_id = self.db.intern_coroutine((self.owner, tgt_expr)).into(); - let coroutine_ty = TyKind::Coroutine(coroutine_id, subst).intern(Interner); + (None, ty, Some((resume_ty, yield_ty))) + } + CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _) => { + let ty = self.lower_async_gen_block_type_impl_trait( + yield_ty.clone(), + tgt_expr, + ); - (None, coroutine_ty, Some((resume_ty, yield_ty))) + (None, ty, Some((resume_ty, yield_ty))) + } + CoroutineKind::Coroutine => { + let subst = TyBuilder::subst_for_generator(self.db, self.owner) + .push(resume_ty.clone()) + .push(yield_ty.clone()) + .push(ret_ty.clone()) + .build(); + + let generator_id = + self.db.intern_generator((self.owner, tgt_expr)).into(); + let generator_ty = + TyKind::Generator(generator_id, subst).intern(Interner); + + (None, generator_ty, Some((resume_ty, yield_ty))) + } + } } - ClosureKind::Closure | ClosureKind::Async => { + ClosureKind::Closure => { let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into(); let closure_ty = TyKind::Closure( closure_id, @@ -886,38 +927,34 @@ impl InferenceContext<'_> { ty } - fn infer_async_block( + pub(crate) fn lower_async_block_type_impl_trait( &mut self, + inner_ty: Ty, tgt_expr: ExprId, - id: &Option, - statements: &[Statement], - tail: &Option, ) -> Ty { - let ret_ty = self.table.new_type_var(); - let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); - let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); - let prev_ret_coercion = - mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty.clone()))); - - let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { - this.infer_block(tgt_expr, *id, statements, *tail, None, &Expectation::has_type(ret_ty)) - }); - - self.diverges = prev_diverges; - self.return_ty = prev_ret_ty; - self.return_coercion = prev_ret_coercion; + // Use the first type parameter as the output type of future. + // existential type AsyncBlockImplTrait: Future + let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, tgt_expr); + let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); + TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty)).intern(Interner) + } - self.lower_async_block_type_impl_trait(inner_ty, tgt_expr) + pub(crate) fn lower_gen_block_type_impl_trait(&mut self, inner_ty: Ty, tgt_expr: ExprId) -> Ty { + // Use the first type parameter as the output type of future. + // existential type AsyncBlockImplTrait: Future + let impl_trait_id = crate::ImplTraitId::GenBlockTypeImplTrait(self.owner, tgt_expr); + let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); + TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty)).intern(Interner) } - pub(crate) fn lower_async_block_type_impl_trait( + pub(crate) fn lower_async_gen_block_type_impl_trait( &mut self, inner_ty: Ty, tgt_expr: ExprId, ) -> Ty { // Use the first type parameter as the output type of future. // existential type AsyncBlockImplTrait: Future - let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, tgt_expr); + let impl_trait_id = crate::ImplTraitId::AsyncGenBlockTypeImplTrait(self.owner, tgt_expr); let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty)).intern(Interner) } diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index b8a1af96fba6..e2e9ccc775d9 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -50,7 +50,6 @@ impl InferenceContext<'_> { } Expr::Let { pat, expr } => self.infer_mut_expr(*expr, self.pat_bound_mutability(*pat)), Expr::Block { id: _, statements, tail, label: _ } - | Expr::Async { id: _, statements, tail } | Expr::Unsafe { id: _, statements, tail } => { for st in statements.iter() { match st { diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 344289ed39e1..9c9e5875b5fc 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -386,7 +386,9 @@ pub fn layout_of_ty_query( let infer = db.infer(func.into()); return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env.clone()); } - crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { + crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) + | crate::ImplTraitId::GenBlockTypeImplTrait(_, _) + | crate::ImplTraitId::AsyncGenBlockTypeImplTrait(_, _) => { return Err(LayoutError::NotImplemented) } } diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index cf174feed24b..b2852f1f4b92 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -378,6 +378,8 @@ impl TypeFoldable for CallableSig { pub enum ImplTraitId { ReturnTypeImplTrait(hir_def::FunctionId, RpitId), AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId), + GenBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId), + AsyncGenBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId), } #[derive(Clone, PartialEq, Eq, Debug, Hash)] diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 418280715cda..b8899bb6653b 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -866,7 +866,6 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Await { .. } => not_supported!("await"), Expr::Yeet { .. } => not_supported!("yeet"), - Expr::Async { .. } => not_supported!("async block"), &Expr::Const(id) => { let subst = self.placeholder_subst(); self.lower_const( diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs index d2f8b592bebd..0bab080e2ee5 100644 --- a/crates/hir-ty/src/mir/monomorphization.rs +++ b/crates/hir-ty/src/mir/monomorphization.rs @@ -85,6 +85,12 @@ impl FallibleTypeFolder for Filler<'_> { crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { not_supported!("async block impl trait"); } + crate::ImplTraitId::GenBlockTypeImplTrait(_, _) => { + not_supported!("gen block impl trait"); + } + crate::ImplTraitId::AsyncGenBlockTypeImplTrait(_, _) => { + not_supported!("async gen block impl trait"); + } } } _ => ty.try_super_fold_with(self.as_dyn(), outer_binder), diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs index 579c63368025..d9ae33457848 100644 --- a/crates/hir-ty/src/tests/coercion.rs +++ b/crates/hir-ty/src/tests/coercion.rs @@ -345,6 +345,54 @@ fn test() { ); } +#[test] +fn gen_yield_coerce() { + check_no_mismatches( + r#" +fn test() { + let g = gen { + yield &1u32; + yield &&1u32; + }; +} + "#, + ); + check_no_mismatches( + r#" +fn test() { + let g = gen move { + yield &1u32; + yield &&1u32; + }; +} + "#, + ); +} + +#[test] +fn async_gen_yield_coerce() { + check_no_mismatches( + r#" +fn test() { + let g = async gen { + yield &1u32; + yield &&1u32; + }; +} + "#, + ); + check_no_mismatches( + r#" +fn test() { + let g = async gen move { + yield &1u32; + yield &&1u32; + }; +} + "#, + ); +} + #[test] fn assign_coerce() { check_no_mismatches( diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index f9ab6d8dc8dc..f6860d36cd8b 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -1949,11 +1949,44 @@ fn closure_return_inferred() { } #[test] -fn coroutine_types_inferred() { +fn generator_types_fully_inferred() { check_infer( r#" -//- minicore: coroutine, deref -use core::ops::{Coroutine, CoroutineState}; +//- minicore: generator, deref +use core::pin::Pin; +use core::ops::Generator; + +pub fn test() { + let mut coroutine = || { + yield 1i32; + }; + let _result = Pin::new(&mut coroutine).resume(()); +} + "#, + expect![[r#" + 61..175 '{ ...()); }': () + 71..84 'mut coroutine': |()| yields i32 -> () + 87..117 '|| { ... }': |()| yields i32 -> () + 90..117 '{ ... }': () + 100..110 'yield 1i32': () + 106..110 '1i32': i32 + 127..134 '_result': GeneratorState + 137..145 'Pin::new': fn new<&mut |()| yields i32 -> ()>(&mut |()| yields i32 -> ()) -> Pin<&mut |()| yields i32 -> ()> + 137..161 'Pin::n...utine)': Pin<&mut |()| yields i32 -> ()> + 137..172 'Pin::n...me(())': GeneratorState + 146..160 '&mut coroutine': &mut |()| yields i32 -> () + 151..160 'coroutine': |()| yields i32 -> () + 169..171 '()': () + "#]], + ); +} + +#[test] +fn generator_types_inferred() { + check_infer( + r#" +//- minicore: generator, deref +use core::ops::{Generator, GeneratorState}; use core::pin::Pin; fn f(v: i64) {} @@ -2010,7 +2043,116 @@ fn test() { } #[test] -fn coroutine_resume_yield_return_unit() { +fn gen_types_inferred() { + check_infer( + r#" +//- minicore: iterator, deref +use core::iter::Iterator; + +fn f(v: i64) {} +fn test() { + let mut g = gen { + let a = yield 0; + let a = yield 1; + let a = yield 2; + }; + + match g.next() { + Some(y) => { f(y); } + None => {} + } +} + "#, + expect![[r#" + 32..33 'v': i64 + 40..42 '{}': () + 53..236 '{ ... } }': () + 63..68 'mut g': impl Iterator + 71..157 'gen { ... }': impl Iterator + 71..157 'gen { ... }': () + 89..90 'a': () + 93..100 'yield 0': () + 99..100 '0': i64 + 114..115 'a': () + 118..125 'yield 1': () + 124..125 '1': i64 + 139..140 'a': () + 143..150 'yield 2': () + 149..150 '2': i64 + 164..234 'match ... }': () + 170..171 'g': impl Iterator + 170..178 'g.next()': Option + 189..196 'Some(y)': Option + 194..195 'y': i64 + 200..209 '{ f(y); }': () + 202..203 'f': fn f(i64) + 202..206 'f(y)': () + 204..205 'y': i64 + 218..222 'None': Option + 226..228 '{}': () + "#]], + ); +} + +#[test] +fn async_gen_types_inferred() { + check_infer( + r#" +//- minicore: async_iterator, future, deref +use core::task::{Context, Poll}; +use core::async_iter::AsyncIterator; + +fn f(v: i64) {} +fn test(mut context: Context) { + let mut g = async gen { + let a = yield 0; + let a = yield 1; + let a = yield 2; + }; + + match g.poll_next(&mut context) { + Poll::Ready(Some(y)) => { f(y); } // FIXME test with 1 there I think this infers stuff it should not + Poll::Pending => {} + } +} + "#, + expect![[r#" + 76..77 'v': i64 + 84..86 '{}': () + 95..106 'mut context': Context<'_> + 117..412 '{ ... } }': () + 127..132 'mut g': impl AsyncIterator + 135..227 'async ... }': impl AsyncIterator + 135..227 'async ... }': () + 159..160 'a': () + 163..170 'yield 0': () + 169..170 '0': i32 + 184..185 'a': () + 188..195 'yield 1': () + 194..195 '1': i32 + 209..210 'a': () + 213..220 'yield 2': () + 219..220 '2': i32 + 234..410 'match ... }': () + 240..241 'g': impl AsyncIterator + 240..265 'g.poll...ntext)': Poll> + 252..264 '&mut context': &mut Context<'_> + 257..264 'context': Context<'_> + 276..296 'Poll::...me(y))': Poll> + 288..295 'Some(y)': Option + 293..294 'y': i64 + 300..309 '{ f(y); }': () + 302..303 'f': fn f(i64) + 302..306 'f(y)': () + 304..305 'y': i64 + 385..398 'Poll::Pending': Poll> + 402..404 '{}': () + "#]], + ); +} + +#[test] +fn generator_resume_yield_return_unit() { check_no_mismatches( r#" //- minicore: coroutine, deref @@ -2030,6 +2172,84 @@ fn test() { ); } +#[test] +fn gen_yield_return_unit() { + check_infer( + r#" + //- minicore: iterator, deref + use core::iter::Iterator; + fn test() { + let mut g = gen { + let () = yield; + }; + + match g.next() { + Some(()) => {} + None => {} + } + } +"#, + expect![[r#" + 36..162 '{ ... } }': () + 46..51 'mut g': impl Iterator + 54..89 'gen { ... }': impl Iterator + 54..89 'gen { ... }': () + 72..74 '()': () + 77..82 'yield': () + 96..160 'match ... }': () + 102..103 'g': impl Iterator + 102..110 'g.next()': Option<()> + 121..129 'Some(())': Option<()> + 126..128 '()': () + 133..135 '{}': () + 144..148 'None': Option<()> + 152..154 '{}': () + "#]], + ); +} + +#[test] +fn async_gen_yield_return_unit() { + check_infer( + r#" + //- minicore: async_iterator, future, deref + use core::task::{Context, Poll}; + use core::async_iter::AsyncIterator; + fn test(mut context: Context) { + let mut g = async gen { + let () = yield; + }; + + match g.poll_next(&mut context) { + Poll::Ready(()) => {} // FIXME test with 1 there I think this infers stuff it should not + Poll::Pending => {} + } + g + } +"#, + expect![[r#" + 78..89 'mut context': Context<'_> + 100..338 '{ ... g }': () + 110..115 'mut g': impl AsyncIterator + 118..159 'async ... }': impl AsyncIterator + 118..159 'async ... }': () + 142..144 '()': () + 147..152 'yield': () + 166..330 'match ... }': () + 172..173 'g': impl AsyncIterator + 172..197 'g.poll...ntext)': Poll<()> + 184..196 '&mut context': &mut Context<'_> + 189..196 'context': Context<'_> + 208..223 'Poll::Ready(())': Poll<()> + 220..222 '()': () + 227..229 '{}': () + 305..318 'Poll::Pending': Poll<()> + 322..324 '{}': () + 335..336 'g': impl AsyncIterator + "#]], + ); +} + #[test] fn tuple_pattern_nested_match_ergonomics() { check_no_mismatches( @@ -2126,7 +2346,9 @@ async fn main() { 39..41 '92': i32 53..54 'y': impl Future 57..85 'async ...wait }': impl Future + 57..85 'async ...wait }': () 65..77 'async { () }': impl Future + 65..77 'async { () }': () 65..83 'async ....await': () 73..75 '()': () 95..96 'z': ControlFlow<(), ()> @@ -2183,6 +2405,7 @@ fn main() { 89..91 '{}': () 103..231 '{ ... }); }': () 109..161 'async ... }': impl Future> + 109..161 'async ... }': Result<(), ()> 125..139 'return Err(())': ! 132..135 'Err': Err<(), ()>(()) -> Result<(), ()> 132..139 'Err(())': Result<(), ()> @@ -2194,6 +2417,7 @@ fn main() { 167..228 'test(|... })': () 172..227 '|| asy... }': impl Fn() -> impl Future> 175..227 'async ... }': impl Future> + 175..227 'async ... }': Result<(), ()> 191..205 'return Err(())': ! 198..201 'Err': Err<(), ()>(()) -> Result<(), ()> 198..205 'Err(())': Result<(), ()> diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 003ae60e8e51..ccc244f00336 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -54,6 +54,36 @@ fn test() { ); } +#[test] +fn infer_desugar_gen() { + check_types( + r#" +//- minicore: iterator, future, sized +gen fn foo() -> u64 { yield 128; } + +fn test() { + let r = foo(); + r; +} //^ impl Iterator +"#, + ); +} + +#[test] +fn infer_desugar_async_gen() { + check_types( + r#" +//- minicore: async_iterator, future, sized +async gen fn foo() -> u64 { yield 128; } + +fn test() { + let r = foo(); + r; +} //^ impl AsyncIterator +"#, + ); +} + #[test] fn infer_async_block() { check_types( @@ -82,6 +112,31 @@ async fn test() { ); } +#[test] +fn infer_gen_block() { + check_types( + r#" +//- minicore: iterators, future, option +async fn test2() { + let mut a = gen { yield 42; }; + let x = a.next().unwrap(); + a; +// ^ impl Iterator + x; +// ^ i32 + let mut c = gen { + let y = None; + // ^ Option + yield y; + }; + let _: Option = c.next().unwrap(); + c; +// ^ impl Iterator> +} +"#, + ); +} + #[test] fn infer_async_closure() { check_types( diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 764c50326590..a677cb42e032 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -92,21 +92,24 @@ impl HirDisplay for Function { // `FunctionData::ret_type` will be `::core::future::Future` for async fns. // Use ugly pattern match to strip the Future trait. // Better way? - let ret_type = if !data.has_async_kw() { - &data.ret_type - } else { - match &*data.ret_type { - TypeRef::ImplTrait(bounds) => match bounds[0].as_ref() { - TypeBound::Path(path, _) => { - path.segments().iter().last().unwrap().args_and_bindings.unwrap().bindings - [0] - .type_ref - .as_ref() - .unwrap() - } + let ret_type = match (data.has_async_kw(), data.has_gen_kw()) { + (false, false) => { + &data.ret_type + }, + _ => { + match &*data.ret_type { + TypeRef::ImplTrait(bounds) => match bounds[0].as_ref() { + TypeBound::Path(path, _) => { + path.segments().iter().last().unwrap().args_and_bindings.unwrap().bindings + [0] + .type_ref + .as_ref() + .unwrap() + } + _ => panic!("Async fn ret_type should be impl Future"), + }, _ => panic!("Async fn ret_type should be impl Future"), - }, - _ => panic!("Async fn ret_type should be impl Future"), + } } }; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 6ee6a2915a88..ae2474f4f79a 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2023,6 +2023,40 @@ impl Function { None } + pub fn gen_ret_type(self, db: &dyn HirDatabase) -> Option { + if !self.is_gen(db) { + return None; + } + let resolver = self.id.resolver(db.upcast()); + let substs = TyBuilder::placeholder_subst(db, self.id); + let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs); + let ret_ty = callable_sig.ret().clone(); + for pred in ret_ty.impl_trait_bounds(db).into_iter().flatten() { + if let WhereClause::AliasEq(output_eq) = pred.into_value_and_skipped_binders().0 { + return Type::new_with_resolver_inner(db, &resolver, output_eq.ty).into(); + } + } + never!("gen fn ret_type should be impl Iterator"); + None + } + + pub fn async_gen_ret_type(self, db: &dyn HirDatabase) -> Option { + if !(self.is_async(db) && self.is_gen(db)) { + return None; + } + let resolver = self.id.resolver(db.upcast()); + let substs = TyBuilder::placeholder_subst(db, self.id); + let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs); + let ret_ty = callable_sig.ret().clone(); + for pred in ret_ty.impl_trait_bounds(db).into_iter().flatten() { + if let WhereClause::AliasEq(output_eq) = pred.into_value_and_skipped_binders().0 { + return Type::new_with_resolver_inner(db, &resolver, output_eq.ty).into(); + } + } + never!("async gen fn ret_type should be impl AsyncIterator"); + None + } + pub fn has_self_param(self, db: &dyn HirDatabase) -> bool { db.function_data(self.id).has_self_param() } diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 347a3e9ba074..fea4aa808bbf 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -799,9 +799,22 @@ impl FunctionBody { ast::Fn(fn_) => { let func = sema.to_def(&fn_)?; let mut ret_ty = func.ret_type(sema.db); - if func.is_async(sema.db) { - if let Some(async_ret) = func.async_ret_type(sema.db) { - ret_ty = async_ret; + match (func.is_async(sema.db), func.is_gen(sema.db)) { + (false, false) => {} + (false, true) => { + if let Some(gen_ret) = func.gen_ret_type(sema.db) { + ret_ty = gen_ret; + } + } + (true, false) => { + if let Some(async_ret) = func.async_ret_type(sema.db) { + ret_ty = async_ret; + } + } + (true, true) => { + if let Some(async_gen_ret) = func.async_gen_ret_type(sema.db) { + ret_ty = async_gen_ret; + } } } (fn_.const_token().is_some(), fn_.body().map(ast::Expr::BlockExpr), Some(ret_ty)) diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs index 91421070854e..758a8f26b9f8 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -1088,5 +1088,4 @@ impl Trait for Base { "#, ); } - } diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 5b9cc5f66cde..177ff0a32b30 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -462,6 +462,7 @@ fn inline( } } + // HERE? let is_async_fn = function.is_async(sema.db); if is_async_fn { cov_mark::hit!(inline_call_async_fn); diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index 6bd839cacb6d..2b56acb17145 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -242,14 +242,26 @@ fn detail(db: &dyn HirDatabase, func: hir::Function) -> String { if func.is_const(db) { format_to!(detail, "const "); } - if func.is_async(db) { - format_to!(detail, "async "); - if let Some(async_ret) = func.async_ret_type(db) { - ret_ty = async_ret; + match (func.is_async(db), func.is_gen(db)) { + (false, false) => {} + (false, true) => { + format_to!(detail, "gen "); + if let Some(gen_ret) = func.gen_ret_type(db) { + ret_ty = gen_ret; + } + } + (true, false) => { + format_to!(detail, "async "); + if let Some(async_ret) = func.async_ret_type(db) { + ret_ty = async_ret; + } + } + (true, true) => { + format_to!(detail, "async gen "); + if let Some(async_gen_ret) = func.async_gen_ret_type(db) { + ret_ty = async_gen_ret; + } } - } - if func.is_gen(db) { - format_to!(detail, "gen "); } if func.is_unsafe_to_call(db) { format_to!(detail, "unsafe "); diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index 5b2b93f3093f..ceb6f6521ec3 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs @@ -631,8 +631,21 @@ fn main() { fn detail_gen_fn() { check_empty( r#" -//- minicore: future, sized -gen fn foo() -> u8 {} +//- minicore: iterator, async_iterator, future, sized +async fn foo() -> u8 { } +fn main() { + self::$0 +} +"#, + expect![[r#" + fn foo() async fn() -> u8 + fn main() fn() + "#]], + ); + check_empty( + r#" +//- minicore: iterator, async_iterator, future, sized +gen fn foo() -> u8 { } fn main() { self::$0 } @@ -644,8 +657,8 @@ fn main() { ); check_empty( r#" -//- minicore: future, sized -async gen fn foo() -> u8 {} +//- minicore: iterator, async_iterator, future, sized + async gen fn foo() -> u8 {} fn main() { self::$0 } diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs index e4e735cecd89..c919a9dc6945 100644 --- a/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -265,8 +265,11 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) { match expr { ast::Expr::BlockExpr(b) => { match b.modifier() { + // here? Some( ast::BlockModifier::Async(_) + | ast::BlockModifier::Gen(_) + | ast::BlockModifier::AsyncGen(_) | ast::BlockModifier::Try(_) | ast::BlockModifier::Const(_), ) => return cb(expr), diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index 3aed007f3ea5..e4303dd8bfa7 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -324,6 +324,7 @@ fn highlight_exit_points( closure.param_list().map_or([None; 2], |p| [p.l_paren_token().map(|it| it.text_range()), p.r_paren_token().map(|it| it.text_range())]), closure.body() ), + // here? ast::BlockExpr(block_expr) => if matches!(block_expr.modifier(), Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Try(_)| ast::BlockModifier::Const(_))) { hl( sema, @@ -449,6 +450,7 @@ fn highlight_yield_points(token: SyntaxToken) -> Option> { return match_ast! { match anc { ast::Fn(fn_) => hl(fn_.async_token(), fn_.body().map(ast::Expr::BlockExpr)), + // here? ast::BlockExpr(block_expr) => { if block_expr.async_token().is_none() { continue; diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index d0a02fd0dba2..77e3512e03a4 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -67,6 +67,7 @@ pub(super) fn try_expr( ast::Fn(fn_) => sema.to_def(&fn_)?.ret_type(sema.db), ast::Item(__) => return None, ast::ClosureExpr(closure) => sema.type_of_expr(&closure.body()?)?.original, + // here? ast::BlockExpr(block_expr) => if matches!(block_expr.modifier(), Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Try(_)| ast::BlockModifier::Const(_))) { sema.type_of_expr(&block_expr.into())?.original } else { diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index a94a476dd8a2..431fb6cc4f84 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -1568,7 +1568,7 @@ fn test_hover_function_show_qualifiers() { ``` ```rust - gen fn foo() + gen fn foo() -> impl ::core::iter::Iterator ``` "#]], ); diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs index 36980b146ef5..7b98357bc5d7 100644 --- a/crates/syntax/src/ast/expr_ext.rs +++ b/crates/syntax/src/ast/expr_ext.rs @@ -340,6 +340,8 @@ impl ast::Literal { pub enum BlockModifier { Async(SyntaxToken), + AsyncGen(SyntaxToken), + Gen(SyntaxToken), Unsafe(SyntaxToken), Try(SyntaxToken), Const(SyntaxToken), @@ -348,8 +350,15 @@ pub enum BlockModifier { impl ast::BlockExpr { pub fn modifier(&self) -> Option { - self.async_token() - .map(BlockModifier::Async) + self.gen_token() + .map(|v| { + if self.async_token().is_some() { + BlockModifier::AsyncGen(v) + } else { + BlockModifier::Gen(v) + } + }) + .or_else(|| self.async_token().map(BlockModifier::Async)) .or_else(|| self.unsafe_token().map(BlockModifier::Unsafe)) .or_else(|| self.try_token().map(BlockModifier::Try)) .or_else(|| self.const_token().map(BlockModifier::Const)) diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 7151982138bc..69b5170bfe12 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -448,6 +448,7 @@ pub fn block_expr( ast_from_text(&format!("fn f() {buf}")) } +// interesting pub fn async_move_block_expr( stmts: impl IntoIterator, tail_expr: Option, diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 38186fd3884a..3bbb66f02e5f 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -11,6 +11,7 @@ //! add: //! asm: //! assert: +//! async_iterator: option, future, pin //! as_ref: sized //! bool_impl: option, fn //! builtin_impls: @@ -1201,6 +1202,27 @@ pub mod task { } // endregion:future +// region:async_iterator +pub mod async_iter { + use crate::{ + pin::Pin, + task::{Context, Poll}, + }; + + #[lang = "asynciterator_trait"] + pub trait AsyncIterator { + type Item; + + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_> + ) -> Poll>; + + fn size_hint(&self) -> (usize, Option) { loop {} } + } +} +// endregion:async_iterator + // region:iterator pub mod iter { // region:iterators @@ -1263,6 +1285,7 @@ pub mod iter { mod traits { mod iterator { + #[lang = "iterator_trait"] pub trait Iterator { type Item; #[lang = "next"]