diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 3b3297f781..c4f91e49a6 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -29,8 +29,9 @@ use crate::{ builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}, db::DefDatabase, expr::{ - dummy_expr_id, Array, BindingAnnotation, Expr, ExprId, FloatTypeWrapper, Label, LabelId, - Literal, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, Statement, + dummy_expr_id, Array, BindingAnnotation, ClosureKind, Expr, ExprId, FloatTypeWrapper, + Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat, RecordLitField, + Statement, }, intern::Interned, item_scope::BuiltinShadowMode, @@ -97,6 +98,7 @@ pub(super) fn lower( name_to_pat_grouping: Default::default(), is_lowering_inside_or_pat: false, is_lowering_assignee_expr: false, + is_lowering_generator: false, } .collect(params, body) } @@ -111,6 +113,7 @@ struct ExprCollector<'a> { name_to_pat_grouping: FxHashMap>, is_lowering_inside_or_pat: bool, is_lowering_assignee_expr: bool, + is_lowering_generator: bool, } impl ExprCollector<'_> { @@ -358,6 +361,7 @@ impl ExprCollector<'_> { self.alloc_expr(Expr::Return { expr }, syntax_ptr) } ast::Expr::YieldExpr(e) => { + self.is_lowering_generator = true; let expr = e.expr().map(|e| self.collect_expr(e)); self.alloc_expr(Expr::Yield { expr }, syntax_ptr) } @@ -459,13 +463,31 @@ impl ExprCollector<'_> { .ret_type() .and_then(|r| r.ty()) .map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it))); + + let prev_is_lowering_generator = self.is_lowering_generator; + self.is_lowering_generator = false; + let body = self.collect_expr_opt(e.body()); + + let closure_kind = if self.is_lowering_generator { + let movability = if e.static_token().is_some() { + Movability::Static + } else { + Movability::Movable + }; + ClosureKind::Generator(movability) + } else { + ClosureKind::Closure + }; + self.is_lowering_generator = prev_is_lowering_generator; + self.alloc_expr( Expr::Closure { args: args.into(), arg_types: arg_types.into(), ret_type, body, + closure_kind, }, syntax_ptr, ) diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 9121fb50fd..162d173d52 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -5,7 +5,7 @@ use std::fmt::{self, Write}; use syntax::ast::HasName; use crate::{ - expr::{Array, BindingAnnotation, Literal, Statement}, + expr::{Array, BindingAnnotation, ClosureKind, Literal, Movability, Statement}, pretty::{print_generic_args, print_path, print_type_ref}, type_ref::TypeRef, }; @@ -362,7 +362,10 @@ impl<'a> Printer<'a> { self.print_expr(*index); w!(self, "]"); } - Expr::Closure { args, arg_types, ret_type, body } => { + Expr::Closure { args, arg_types, ret_type, body, closure_kind } => { + if let ClosureKind::Generator(Movability::Static) = closure_kind { + w!(self, "static "); + } w!(self, "|"); for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() { if i != 0 { diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs index 419d3feec3..1626465502 100644 --- a/crates/hir-def/src/expr.rs +++ b/crates/hir-def/src/expr.rs @@ -198,6 +198,7 @@ pub enum Expr { arg_types: Box<[Option>]>, ret_type: Option>, body: ExprId, + closure_kind: ClosureKind, }, Tuple { exprs: Box<[ExprId]>, @@ -211,6 +212,18 @@ pub enum Expr { Underscore, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ClosureKind { + Closure, + Generator(Movability), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Movability { + Static, + Movable, +} + #[derive(Debug, Clone, Eq, PartialEq)] pub enum Array { ElementList { elements: Box<[ExprId]>, is_assignee_expr: bool }, diff --git a/crates/hir-ty/src/builder.rs b/crates/hir-ty/src/builder.rs index 94d7806cb6..3ae7fb2a61 100644 --- a/crates/hir-ty/src/builder.rs +++ b/crates/hir-ty/src/builder.rs @@ -9,8 +9,8 @@ use chalk_ir::{ AdtId, BoundVar, DebruijnIndex, Scalar, }; use hir_def::{ - builtin_type::BuiltinType, generics::TypeOrConstParamData, ConstParamId, GenericDefId, TraitId, - TypeAliasId, + builtin_type::BuiltinType, generics::TypeOrConstParamData, ConstParamId, DefWithBodyId, + GenericDefId, TraitId, TypeAliasId, }; use smallvec::SmallVec; @@ -205,6 +205,38 @@ impl TyBuilder<()> { ) } + /// Creates a `TyBuilder` to build `Substitution` for a generator defined in `parent`. + /// + /// A generator's substitution consists of: + /// - generic parameters in scope on `parent` + /// - resume type of generator + /// - yield type of generator ([`Generator::Yield`](std::ops::Generator::Yield)) + /// - return type of generator ([`Generator::Return`](std::ops::Generator::Return)) + /// in this order. + /// + /// This method prepopulates the builder with placeholder substitution of `parent`, so you + /// should only push exactly 3 `GenericArg`s before building. + pub fn subst_for_generator(db: &dyn HirDatabase, parent: DefWithBodyId) -> TyBuilder<()> { + let parent_subst = match parent.as_generic_def_id() { + Some(parent) => generics(db.upcast(), parent).placeholder_subst(db), + // Static initializers *may* contain generators. + None => Substitution::empty(Interner), + }; + let builder = TyBuilder::new( + (), + parent_subst + .iter(Interner) + .map(|arg| match arg.constant(Interner) { + Some(c) => ParamKind::Const(c.data(Interner).ty.clone()), + None => ParamKind::Type, + }) + // These represent resume type, yield type, and return type of generator. + .chain(std::iter::repeat(ParamKind::Type).take(3)) + .collect(), + ); + builder.use_parent_substs(&parent_subst) + } + pub fn build(self) -> Substitution { let ((), subst) = self.build_internal(); subst diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index faec99c7d3..c5cf6729d1 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/crates/hir-ty/src/chalk_db.rs @@ -11,6 +11,7 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; use base_db::CrateId; use hir_def::{ + expr::Movability, lang_item::{lang_attr, LangItemTarget}, AssocItemId, GenericDefId, HasModule, ItemContainerId, Lookup, ModuleId, TypeAliasId, }; @@ -26,9 +27,9 @@ use crate::{ to_assoc_type_id, to_chalk_trait_id, traits::ChalkContext, utils::generics, - AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId, Interner, ProjectionTy, - ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, - TyExt, TyKind, WhereClause, + wrap_empty_binders, AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId, + Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef, + TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause, }; pub(crate) type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum; @@ -372,17 +373,63 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { } fn generator_datum( &self, - _: chalk_ir::GeneratorId, + id: chalk_ir::GeneratorId, ) -> std::sync::Arc> { - // FIXME - unimplemented!() + let (parent, expr) = self.db.lookup_intern_generator(id.into()); + + // We fill substitution with unknown type, because we only need to know whether the generic + // params are types or consts to build `Binders` and those being filled up are for + // `resume_type`, `yield_type`, and `return_type` of the generator in question. + let subst = TyBuilder::subst_for_generator(self.db, parent).fill_with_unknown().build(); + + let len = subst.len(Interner); + let input_output = rust_ir::GeneratorInputOutputDatum { + resume_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, len - 3)) + .intern(Interner), + yield_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, len - 2)) + .intern(Interner), + return_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, len - 1)) + .intern(Interner), + // FIXME: calculate upvars + upvars: vec![], + }; + + let it = subst + .iter(Interner) + .map(|it| it.constant(Interner).map(|c| c.data(Interner).ty.clone())); + let input_output = crate::make_type_and_const_binders(it, input_output); + + let movability = match self.db.body(parent)[expr] { + hir_def::expr::Expr::Closure { + closure_kind: hir_def::expr::ClosureKind::Generator(movability), + .. + } => movability, + _ => unreachable!("non generator expression interned as generator"), + }; + let movability = match movability { + Movability::Static => rust_ir::Movability::Static, + Movability::Movable => rust_ir::Movability::Movable, + }; + + Arc::new(rust_ir::GeneratorDatum { movability, input_output }) } fn generator_witness_datum( &self, - _: chalk_ir::GeneratorId, + id: chalk_ir::GeneratorId, ) -> std::sync::Arc> { - // FIXME - unimplemented!() + // FIXME: calculate inner types + let inner_types = + rust_ir::GeneratorWitnessExistential { types: wrap_empty_binders(vec![]) }; + + let (parent, _) = self.db.lookup_intern_generator(id.into()); + // See the comment in `generator_datum()` for unknown types. + let subst = TyBuilder::subst_for_generator(self.db, parent).fill_with_unknown().build(); + let it = subst + .iter(Interner) + .map(|it| it.constant(Interner).map(|c| c.data(Interner).ty.clone())); + let inner_types = crate::make_type_and_const_binders(it, inner_types); + + Arc::new(rust_ir::GeneratorWitnessDatum { inner_types }) } fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase { diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 72abcc2b4b..932fce8356 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -120,6 +120,8 @@ pub trait HirDatabase: DefDatabase + Upcast { fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId; #[salsa::interned] fn intern_closure(&self, id: (DefWithBodyId, ExprId)) -> InternedClosureId; + #[salsa::interned] + fn intern_generator(&self, id: (DefWithBodyId, ExprId)) -> InternedGeneratorId; #[salsa::invoke(chalk_db::associated_ty_data_query)] fn associated_ty_data(&self, id: chalk_db::AssocTypeId) -> Arc; @@ -233,6 +235,10 @@ impl_intern_key!(InternedOpaqueTyId); pub struct InternedClosureId(salsa::InternId); impl_intern_key!(InternedClosureId); +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InternedGeneratorId(salsa::InternId); +impl_intern_key!(InternedGeneratorId); + /// This exists just for Chalk, because Chalk just has a single `FnDefId` where /// we have different IDs for struct and enum variant constructors. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 874abdaea8..a5058f71a4 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -20,6 +20,7 @@ use hir_def::{ }; use hir_expand::{hygiene::Hygiene, name::Name}; use itertools::Itertools; +use smallvec::SmallVec; use syntax::SmolStr; use crate::{ @@ -221,6 +222,7 @@ pub enum DisplaySourceCodeError { PathNotFound, UnknownType, Closure, + Generator, } pub enum HirDisplayError { @@ -783,7 +785,34 @@ impl HirDisplay for Ty { write!(f, "{{unknown}}")?; } TyKind::InferenceVar(..) => write!(f, "_")?, - TyKind::Generator(..) => write!(f, "{{generator}}")?, + TyKind::Generator(_, subst) => { + if f.display_target.is_source_code() { + return Err(HirDisplayError::DisplaySourceCodeError( + DisplaySourceCodeError::Generator, + )); + } + + let subst = subst.as_slice(Interner); + let a: Option> = subst + .get(subst.len() - 3..) + .map(|args| args.iter().map(|arg| arg.ty(Interner)).collect()) + .flatten(); + + if let Some([resume_ty, yield_ty, ret_ty]) = a.as_deref() { + write!(f, "|")?; + resume_ty.hir_fmt(f)?; + write!(f, "|")?; + + write!(f, " yields ")?; + yield_ty.hir_fmt(f)?; + + write!(f, " -> ")?; + ret_ty.hir_fmt(f)?; + } else { + // This *should* be unreachable, but fallback just in case. + write!(f, "{{generator}}")?; + } + } TyKind::GeneratorWitness(..) => write!(f, "{{generator witness}}")?, } Ok(()) diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index daf1e2f0c6..039824694a 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -339,7 +339,7 @@ pub struct InferenceResult { /// unresolved or missing subpatterns or subpatterns of mismatched types. pub type_of_pat: ArenaMap, type_mismatches: FxHashMap, - /// Interned Unknown to return references to. + /// Interned common types to return references to. standard_types: InternedStandardTypes, /// Stores the types which were implicitly dereferenced in pattern binding modes. pub pat_adjustments: FxHashMap>, @@ -419,6 +419,8 @@ pub(crate) struct InferenceContext<'a> { /// closures, but currently this is the only field that will change there, /// so it doesn't make sense. return_ty: Ty, + /// The resume type and the yield type, respectively, of the generator being inferred. + resume_yield_tys: Option<(Ty, Ty)>, diverges: Diverges, breakables: Vec, } @@ -483,6 +485,7 @@ impl<'a> InferenceContext<'a> { table: unify::InferenceTable::new(db, trait_env.clone()), trait_env, return_ty: TyKind::Error.intern(Interner), // set in collect_fn_signature + resume_yield_tys: None, db, owner, body, diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 3ead929098..094e460dbf 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -12,6 +12,7 @@ use crate::{ use super::{Expectation, InferenceContext}; impl InferenceContext<'_> { + // This function handles both closures and generators. pub(super) fn deduce_closure_type_from_expectations( &mut self, closure_expr: ExprId, @@ -27,6 +28,11 @@ impl InferenceContext<'_> { // Deduction from where-clauses in scope, as well as fn-pointer coercion are handled here. let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty); + // Generators are not Fn* so return early. + if matches!(closure_ty.kind(Interner), TyKind::Generator(..)) { + return; + } + // Deduction based on the expected `dyn Fn` is done separately. if let TyKind::Dyn(dyn_ty) = expected_ty.kind(Interner) { if let Some(sig) = self.deduce_sig_from_dyn_ty(dyn_ty) { diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 2d04a864a2..e3d6be23e6 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -10,7 +10,10 @@ use chalk_ir::{ cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind, }; use hir_def::{ - expr::{ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, LabelId, Literal, Statement, UnaryOp}, + expr::{ + ArithOp, Array, BinaryOp, ClosureKind, CmpOp, Expr, ExprId, LabelId, Literal, Statement, + UnaryOp, + }, generics::TypeOrConstParamData, path::{GenericArg, GenericArgs}, resolver::resolver_for_expr, @@ -216,7 +219,7 @@ impl<'a> InferenceContext<'a> { self.diverges = Diverges::Maybe; TyBuilder::unit() } - Expr::Closure { body, args, ret_type, arg_types } => { + Expr::Closure { body, args, ret_type, arg_types, closure_kind } => { assert_eq!(args.len(), arg_types.len()); let mut sig_tys = Vec::new(); @@ -244,20 +247,40 @@ impl<'a> InferenceContext<'a> { ), }) .intern(Interner); - let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into(); - let closure_ty = - TyKind::Closure(closure_id, Substitution::from1(Interner, sig_ty.clone())) - .intern(Interner); + + let (ty, resume_yield_tys) = if matches!(closure_kind, ClosureKind::Generator(_)) { + // 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 + // first parameter type. + Some(ty) if sig_tys.len() > 1 => ty.clone(), + _ => self.result.standard_types.unit.clone(), + }; + let yield_ty = self.table.new_type_var(); + + 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); + + (generator_ty, Some((resume_ty, yield_ty))) + } else { + let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into(); + let closure_ty = + TyKind::Closure(closure_id, Substitution::from1(Interner, sig_ty.clone())) + .intern(Interner); + + (closure_ty, None) + }; // Eagerly try to relate the closure type with the expected // type, otherwise we often won't have enough information to // infer the body. - self.deduce_closure_type_from_expectations( - tgt_expr, - &closure_ty, - &sig_ty, - expected, - ); + self.deduce_closure_type_from_expectations(tgt_expr, &ty, &sig_ty, expected); // Now go through the argument patterns for (arg_pat, arg_ty) in args.iter().zip(sig_tys) { @@ -266,6 +289,8 @@ impl<'a> InferenceContext<'a> { 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_resume_yield_tys = + mem::replace(&mut self.resume_yield_tys, resume_yield_tys); self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| { this.infer_expr_coerce(*body, &Expectation::has_type(ret_ty)); @@ -273,8 +298,9 @@ impl<'a> InferenceContext<'a> { self.diverges = prev_diverges; self.return_ty = prev_ret_ty; + self.resume_yield_tys = prev_resume_yield_tys; - closure_ty + ty } Expr::Call { callee, args, .. } => { let callee_ty = self.infer_expr(*callee, &Expectation::none()); @@ -423,11 +449,18 @@ impl<'a> InferenceContext<'a> { TyKind::Never.intern(Interner) } Expr::Yield { expr } => { - // FIXME: track yield type for coercion - if let Some(expr) = expr { - self.infer_expr(*expr, &Expectation::none()); + if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() { + if let Some(expr) = expr { + self.infer_expr_coerce(*expr, &Expectation::has_type(yield_ty)); + } else { + let unit = self.result.standard_types.unit.clone(); + let _ = self.coerce(Some(tgt_expr), &unit, &yield_ty); + } + resume_ty + } else { + // FIXME: report error (yield expr in non-generator) + TyKind::Error.intern(Interner) } - TyKind::Never.intern(Interner) } Expr::RecordLit { path, fields, spread, .. } => { let (ty, def_id) = self.resolve_variant(path.as_deref(), false); diff --git a/crates/hir-ty/src/mapping.rs b/crates/hir-ty/src/mapping.rs index d765fee0e1..f80fb39c1f 100644 --- a/crates/hir-ty/src/mapping.rs +++ b/crates/hir-ty/src/mapping.rs @@ -103,6 +103,18 @@ impl From for chalk_ir::ClosureId { } } +impl From> for crate::db::InternedGeneratorId { + fn from(id: chalk_ir::GeneratorId) -> Self { + Self::from_intern_id(id.0) + } +} + +impl From for chalk_ir::GeneratorId { + fn from(id: crate::db::InternedGeneratorId) -> Self { + chalk_ir::GeneratorId(id.as_intern_id()) + } +} + pub fn to_foreign_def_id(id: TypeAliasId) -> ForeignDefId { chalk_ir::ForeignDefId(salsa::InternKey::as_intern_id(&id)) } diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs index bf59fadc2c..d301595bcd 100644 --- a/crates/hir-ty/src/tests/coercion.rs +++ b/crates/hir-ty/src/tests/coercion.rs @@ -294,6 +294,24 @@ fn foo() { ); } +#[test] +fn generator_yield_return_coerce() { + check_no_mismatches( + r#" +fn test() { + let g = || { + yield &1u32; + yield &&1u32; + if true { + return &1u32; + } + &&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 1757327b92..080e2ac1b8 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -1952,6 +1952,88 @@ fn closure_return_inferred() { ); } +#[test] +fn generator_types_inferred() { + check_infer( + r#" +//- minicore: generator, deref +use core::ops::{Generator, GeneratorState}; +use core::pin::Pin; + +fn f(v: i64) {} +fn test() { + let mut g = |r| { + let a = yield 0; + let a = yield 1; + let a = yield 2; + "return value" + }; + + match Pin::new(&mut g).resume(0usize) { + GeneratorState::Yielded(y) => { f(y); } + GeneratorState::Complete(r) => {} + } +} + "#, + expect![[r#" + 70..71 'v': i64 + 78..80 '{}': () + 91..362 '{ ... } }': () + 101..106 'mut g': |usize| yields i64 -> &str + 109..218 '|r| { ... }': |usize| yields i64 -> &str + 110..111 'r': usize + 113..218 '{ ... }': &str + 127..128 'a': usize + 131..138 'yield 0': usize + 137..138 '0': i64 + 152..153 'a': usize + 156..163 'yield 1': usize + 162..163 '1': i64 + 177..178 'a': usize + 181..188 'yield 2': usize + 187..188 '2': i64 + 198..212 '"return value"': &str + 225..360 'match ... }': () + 231..239 'Pin::new': fn new<&mut |usize| yields i64 -> &str>(&mut |usize| yields i64 -> &str) -> Pin<&mut |usize| yields i64 -> &str> + 231..247 'Pin::n...mut g)': Pin<&mut |usize| yields i64 -> &str> + 231..262 'Pin::n...usize)': GeneratorState + 240..246 '&mut g': &mut |usize| yields i64 -> &str + 245..246 'g': |usize| yields i64 -> &str + 255..261 '0usize': usize + 273..299 'Genera...ded(y)': GeneratorState + 297..298 'y': i64 + 303..312 '{ f(y); }': () + 305..306 'f': fn f(i64) + 305..309 'f(y)': () + 307..308 'y': i64 + 321..348 'Genera...ete(r)': GeneratorState + 346..347 'r': &str + 352..354 '{}': () + "#]], + ); +} + +#[test] +fn generator_resume_yield_return_unit() { + check_no_mismatches( + r#" +//- minicore: generator, deref +use core::ops::{Generator, GeneratorState}; +use core::pin::Pin; +fn test() { + let mut g = || { + let () = yield; + }; + + match Pin::new(&mut g).resume(()) { + GeneratorState::Yielded(()) => {} + GeneratorState::Complete(()) => {} + } +} + "#, + ); +} + #[test] fn fn_pointer_return() { check_infer( diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 6df29db474..10386b5b7b 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -37,6 +37,7 @@ //! add: //! as_ref: sized //! drop: +//! generator: pin pub mod marker { // region:sized @@ -182,6 +183,19 @@ pub mod ops { type Target: ?Sized; fn deref(&self) -> &Self::Target; } + + impl Deref for &T { + type Target = T; + fn deref(&self) -> &T { + loop {} + } + } + impl Deref for &mut T { + type Target = T; + fn deref(&self) -> &T { + loop {} + } + } // region:deref_mut #[lang = "deref_mut"] pub trait DerefMut: Deref { @@ -347,6 +361,27 @@ pub mod ops { fn add(self, rhs: Rhs) -> Self::Output; } // endregion:add + + // region:generator + mod generator { + use crate::pin::Pin; + + #[lang = "generator"] + pub trait Generator { + type Yield; + #[lang = "generator_return"] + type Return; + fn resume(self: Pin<&mut Self>, arg: R) -> GeneratorState; + } + + #[lang = "generator_state"] + pub enum GeneratorState { + Yielded(Y), + Complete(R), + } + } + pub use self::generator::{Generator, GeneratorState}; + // endregion:generator } // region:eq @@ -455,6 +490,19 @@ pub mod pin { pub struct Pin

{ pointer: P, } + impl

Pin

{ + pub fn new(pointer: P) -> Pin

{ + loop {} + } + } + // region:deref + impl crate::ops::Deref for Pin

{ + type Target = P::Target; + fn deref(&self) -> &P::Target { + loop {} + } + } + // endregion:deref } // endregion:pin