Auto merge of #13209 - lowr:feat/inference-for-generator, r=Veykril

feat: type inference for generators

This PR implements basic type inference for generator and yield expressions.

Things not included in this PR:
- Generator upvars and generator witnesses are not implemented. They are only used to determine auto trait impls, so basic type inference should be fine without them, but method resolutions with auto trait bounds may not be resolved correctly.

Open questions:
- I haven't (yet) implemented `HirDisplay` for `TyKind::Generator`, so generator types are just shown as "{{generator}}" (in tests, inlay hints, hovers, etc.), which is not really nice. How should we show them?
- I added moderate amount of stuffs to minicore. I especially didn't want to add `impl<T> Deref for &T` and `impl<T> Deref for &mut T` exclusively for tests for generators; should I move them into the test fixtures or can they be placed in minicore?

cc #4309
This commit is contained in:
bors 2022-09-26 09:28:41 +00:00
commit 1f929659ac
14 changed files with 388 additions and 34 deletions

View file

@ -29,8 +29,9 @@ use crate::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}, builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
db::DefDatabase, db::DefDatabase,
expr::{ expr::{
dummy_expr_id, Array, BindingAnnotation, Expr, ExprId, FloatTypeWrapper, Label, LabelId, dummy_expr_id, Array, BindingAnnotation, ClosureKind, Expr, ExprId, FloatTypeWrapper,
Literal, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, Statement, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat, RecordLitField,
Statement,
}, },
intern::Interned, intern::Interned,
item_scope::BuiltinShadowMode, item_scope::BuiltinShadowMode,
@ -97,6 +98,7 @@ pub(super) fn lower(
name_to_pat_grouping: Default::default(), name_to_pat_grouping: Default::default(),
is_lowering_inside_or_pat: false, is_lowering_inside_or_pat: false,
is_lowering_assignee_expr: false, is_lowering_assignee_expr: false,
is_lowering_generator: false,
} }
.collect(params, body) .collect(params, body)
} }
@ -111,6 +113,7 @@ struct ExprCollector<'a> {
name_to_pat_grouping: FxHashMap<Name, Vec<PatId>>, name_to_pat_grouping: FxHashMap<Name, Vec<PatId>>,
is_lowering_inside_or_pat: bool, is_lowering_inside_or_pat: bool,
is_lowering_assignee_expr: bool, is_lowering_assignee_expr: bool,
is_lowering_generator: bool,
} }
impl ExprCollector<'_> { impl ExprCollector<'_> {
@ -358,6 +361,7 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::Return { expr }, syntax_ptr) self.alloc_expr(Expr::Return { expr }, syntax_ptr)
} }
ast::Expr::YieldExpr(e) => { ast::Expr::YieldExpr(e) => {
self.is_lowering_generator = true;
let expr = e.expr().map(|e| self.collect_expr(e)); let expr = e.expr().map(|e| self.collect_expr(e));
self.alloc_expr(Expr::Yield { expr }, syntax_ptr) self.alloc_expr(Expr::Yield { expr }, syntax_ptr)
} }
@ -459,13 +463,31 @@ impl ExprCollector<'_> {
.ret_type() .ret_type()
.and_then(|r| r.ty()) .and_then(|r| r.ty())
.map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it))); .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 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( self.alloc_expr(
Expr::Closure { Expr::Closure {
args: args.into(), args: args.into(),
arg_types: arg_types.into(), arg_types: arg_types.into(),
ret_type, ret_type,
body, body,
closure_kind,
}, },
syntax_ptr, syntax_ptr,
) )

View file

@ -5,7 +5,7 @@ use std::fmt::{self, Write};
use syntax::ast::HasName; use syntax::ast::HasName;
use crate::{ use crate::{
expr::{Array, BindingAnnotation, Literal, Statement}, expr::{Array, BindingAnnotation, ClosureKind, Literal, Movability, Statement},
pretty::{print_generic_args, print_path, print_type_ref}, pretty::{print_generic_args, print_path, print_type_ref},
type_ref::TypeRef, type_ref::TypeRef,
}; };
@ -362,7 +362,10 @@ impl<'a> Printer<'a> {
self.print_expr(*index); self.print_expr(*index);
w!(self, "]"); 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, "|"); w!(self, "|");
for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() { for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
if i != 0 { if i != 0 {

View file

@ -198,6 +198,7 @@ pub enum Expr {
arg_types: Box<[Option<Interned<TypeRef>>]>, arg_types: Box<[Option<Interned<TypeRef>>]>,
ret_type: Option<Interned<TypeRef>>, ret_type: Option<Interned<TypeRef>>,
body: ExprId, body: ExprId,
closure_kind: ClosureKind,
}, },
Tuple { Tuple {
exprs: Box<[ExprId]>, exprs: Box<[ExprId]>,
@ -211,6 +212,18 @@ pub enum Expr {
Underscore, 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)] #[derive(Debug, Clone, Eq, PartialEq)]
pub enum Array { pub enum Array {
ElementList { elements: Box<[ExprId]>, is_assignee_expr: bool }, ElementList { elements: Box<[ExprId]>, is_assignee_expr: bool },

View file

@ -9,8 +9,8 @@ use chalk_ir::{
AdtId, BoundVar, DebruijnIndex, Scalar, AdtId, BoundVar, DebruijnIndex, Scalar,
}; };
use hir_def::{ use hir_def::{
builtin_type::BuiltinType, generics::TypeOrConstParamData, ConstParamId, GenericDefId, TraitId, builtin_type::BuiltinType, generics::TypeOrConstParamData, ConstParamId, DefWithBodyId,
TypeAliasId, GenericDefId, TraitId, TypeAliasId,
}; };
use smallvec::SmallVec; 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 { pub fn build(self) -> Substitution {
let ((), subst) = self.build_internal(); let ((), subst) = self.build_internal();
subst subst

View file

@ -11,6 +11,7 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
use base_db::CrateId; use base_db::CrateId;
use hir_def::{ use hir_def::{
expr::Movability,
lang_item::{lang_attr, LangItemTarget}, lang_item::{lang_attr, LangItemTarget},
AssocItemId, GenericDefId, HasModule, ItemContainerId, Lookup, ModuleId, TypeAliasId, AssocItemId, GenericDefId, HasModule, ItemContainerId, Lookup, ModuleId, TypeAliasId,
}; };
@ -26,9 +27,9 @@ use crate::{
to_assoc_type_id, to_chalk_trait_id, to_assoc_type_id, to_chalk_trait_id,
traits::ChalkContext, traits::ChalkContext,
utils::generics, utils::generics,
AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId, Interner, ProjectionTy, wrap_empty_binders, AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId,
ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef,
TyExt, TyKind, WhereClause, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause,
}; };
pub(crate) type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>; pub(crate) type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>;
@ -372,17 +373,63 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
} }
fn generator_datum( fn generator_datum(
&self, &self,
_: chalk_ir::GeneratorId<Interner>, id: chalk_ir::GeneratorId<Interner>,
) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorDatum<Interner>> { ) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorDatum<Interner>> {
// FIXME let (parent, expr) = self.db.lookup_intern_generator(id.into());
unimplemented!()
// 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( fn generator_witness_datum(
&self, &self,
_: chalk_ir::GeneratorId<Interner>, id: chalk_ir::GeneratorId<Interner>,
) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorWitnessDatum<Interner>> { ) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorWitnessDatum<Interner>> {
// FIXME // FIXME: calculate inner types
unimplemented!() 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<Interner> { fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase<Interner> {

View file

@ -120,6 +120,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId; fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId;
#[salsa::interned] #[salsa::interned]
fn intern_closure(&self, id: (DefWithBodyId, ExprId)) -> InternedClosureId; 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)] #[salsa::invoke(chalk_db::associated_ty_data_query)]
fn associated_ty_data(&self, id: chalk_db::AssocTypeId) -> Arc<chalk_db::AssociatedTyDatum>; fn associated_ty_data(&self, id: chalk_db::AssocTypeId) -> Arc<chalk_db::AssociatedTyDatum>;
@ -233,6 +235,10 @@ impl_intern_key!(InternedOpaqueTyId);
pub struct InternedClosureId(salsa::InternId); pub struct InternedClosureId(salsa::InternId);
impl_intern_key!(InternedClosureId); 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 /// This exists just for Chalk, because Chalk just has a single `FnDefId` where
/// we have different IDs for struct and enum variant constructors. /// we have different IDs for struct and enum variant constructors.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]

View file

@ -20,6 +20,7 @@ use hir_def::{
}; };
use hir_expand::{hygiene::Hygiene, name::Name}; use hir_expand::{hygiene::Hygiene, name::Name};
use itertools::Itertools; use itertools::Itertools;
use smallvec::SmallVec;
use syntax::SmolStr; use syntax::SmolStr;
use crate::{ use crate::{
@ -221,6 +222,7 @@ pub enum DisplaySourceCodeError {
PathNotFound, PathNotFound,
UnknownType, UnknownType,
Closure, Closure,
Generator,
} }
pub enum HirDisplayError { pub enum HirDisplayError {
@ -783,7 +785,34 @@ impl HirDisplay for Ty {
write!(f, "{{unknown}}")?; write!(f, "{{unknown}}")?;
} }
TyKind::InferenceVar(..) => write!(f, "_")?, 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<SmallVec<[&Ty; 3]>> = 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}}")?, TyKind::GeneratorWitness(..) => write!(f, "{{generator witness}}")?,
} }
Ok(()) Ok(())

View file

@ -339,7 +339,7 @@ pub struct InferenceResult {
/// unresolved or missing subpatterns or subpatterns of mismatched types. /// unresolved or missing subpatterns or subpatterns of mismatched types.
pub type_of_pat: ArenaMap<PatId, Ty>, pub type_of_pat: ArenaMap<PatId, Ty>,
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>, type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
/// Interned Unknown to return references to. /// Interned common types to return references to.
standard_types: InternedStandardTypes, standard_types: InternedStandardTypes,
/// Stores the types which were implicitly dereferenced in pattern binding modes. /// Stores the types which were implicitly dereferenced in pattern binding modes.
pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>, pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
@ -419,6 +419,8 @@ pub(crate) struct InferenceContext<'a> {
/// closures, but currently this is the only field that will change there, /// closures, but currently this is the only field that will change there,
/// so it doesn't make sense. /// so it doesn't make sense.
return_ty: Ty, return_ty: Ty,
/// The resume type and the yield type, respectively, of the generator being inferred.
resume_yield_tys: Option<(Ty, Ty)>,
diverges: Diverges, diverges: Diverges,
breakables: Vec<BreakableContext>, breakables: Vec<BreakableContext>,
} }
@ -483,6 +485,7 @@ impl<'a> InferenceContext<'a> {
table: unify::InferenceTable::new(db, trait_env.clone()), table: unify::InferenceTable::new(db, trait_env.clone()),
trait_env, trait_env,
return_ty: TyKind::Error.intern(Interner), // set in collect_fn_signature return_ty: TyKind::Error.intern(Interner), // set in collect_fn_signature
resume_yield_tys: None,
db, db,
owner, owner,
body, body,

View file

@ -12,6 +12,7 @@ use crate::{
use super::{Expectation, InferenceContext}; use super::{Expectation, InferenceContext};
impl InferenceContext<'_> { impl InferenceContext<'_> {
// This function handles both closures and generators.
pub(super) fn deduce_closure_type_from_expectations( pub(super) fn deduce_closure_type_from_expectations(
&mut self, &mut self,
closure_expr: ExprId, closure_expr: ExprId,
@ -27,6 +28,11 @@ impl InferenceContext<'_> {
// Deduction from where-clauses in scope, as well as fn-pointer coercion are handled here. // 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); 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. // Deduction based on the expected `dyn Fn` is done separately.
if let TyKind::Dyn(dyn_ty) = expected_ty.kind(Interner) { if let TyKind::Dyn(dyn_ty) = expected_ty.kind(Interner) {
if let Some(sig) = self.deduce_sig_from_dyn_ty(dyn_ty) { if let Some(sig) = self.deduce_sig_from_dyn_ty(dyn_ty) {

View file

@ -10,7 +10,10 @@ use chalk_ir::{
cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind, cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind,
}; };
use hir_def::{ 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, generics::TypeOrConstParamData,
path::{GenericArg, GenericArgs}, path::{GenericArg, GenericArgs},
resolver::resolver_for_expr, resolver::resolver_for_expr,
@ -216,7 +219,7 @@ impl<'a> InferenceContext<'a> {
self.diverges = Diverges::Maybe; self.diverges = Diverges::Maybe;
TyBuilder::unit() 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()); assert_eq!(args.len(), arg_types.len());
let mut sig_tys = Vec::new(); let mut sig_tys = Vec::new();
@ -244,20 +247,40 @@ impl<'a> InferenceContext<'a> {
), ),
}) })
.intern(Interner); .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_id = self.db.intern_closure((self.owner, tgt_expr)).into();
let closure_ty = let closure_ty =
TyKind::Closure(closure_id, Substitution::from1(Interner, sig_ty.clone())) TyKind::Closure(closure_id, Substitution::from1(Interner, sig_ty.clone()))
.intern(Interner); .intern(Interner);
(closure_ty, None)
};
// Eagerly try to relate the closure type with the expected // Eagerly try to relate the closure type with the expected
// type, otherwise we often won't have enough information to // type, otherwise we often won't have enough information to
// infer the body. // infer the body.
self.deduce_closure_type_from_expectations( self.deduce_closure_type_from_expectations(tgt_expr, &ty, &sig_ty, expected);
tgt_expr,
&closure_ty,
&sig_ty,
expected,
);
// Now go through the argument patterns // Now go through the argument patterns
for (arg_pat, arg_ty) in args.iter().zip(sig_tys) { 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_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); 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| { self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
this.infer_expr_coerce(*body, &Expectation::has_type(ret_ty)); this.infer_expr_coerce(*body, &Expectation::has_type(ret_ty));
@ -273,8 +298,9 @@ impl<'a> InferenceContext<'a> {
self.diverges = prev_diverges; self.diverges = prev_diverges;
self.return_ty = prev_ret_ty; self.return_ty = prev_ret_ty;
self.resume_yield_tys = prev_resume_yield_tys;
closure_ty ty
} }
Expr::Call { callee, args, .. } => { Expr::Call { callee, args, .. } => {
let callee_ty = self.infer_expr(*callee, &Expectation::none()); let callee_ty = self.infer_expr(*callee, &Expectation::none());
@ -423,11 +449,18 @@ impl<'a> InferenceContext<'a> {
TyKind::Never.intern(Interner) TyKind::Never.intern(Interner)
} }
Expr::Yield { expr } => { Expr::Yield { expr } => {
// FIXME: track yield type for coercion if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() {
if let Some(expr) = expr { if let Some(expr) = expr {
self.infer_expr(*expr, &Expectation::none()); 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, .. } => { Expr::RecordLit { path, fields, spread, .. } => {
let (ty, def_id) = self.resolve_variant(path.as_deref(), false); let (ty, def_id) = self.resolve_variant(path.as_deref(), false);

View file

@ -103,6 +103,18 @@ impl From<crate::db::InternedClosureId> for chalk_ir::ClosureId<Interner> {
} }
} }
impl From<chalk_ir::GeneratorId<Interner>> for crate::db::InternedGeneratorId {
fn from(id: chalk_ir::GeneratorId<Interner>) -> Self {
Self::from_intern_id(id.0)
}
}
impl From<crate::db::InternedGeneratorId> for chalk_ir::GeneratorId<Interner> {
fn from(id: crate::db::InternedGeneratorId) -> Self {
chalk_ir::GeneratorId(id.as_intern_id())
}
}
pub fn to_foreign_def_id(id: TypeAliasId) -> ForeignDefId { pub fn to_foreign_def_id(id: TypeAliasId) -> ForeignDefId {
chalk_ir::ForeignDefId(salsa::InternKey::as_intern_id(&id)) chalk_ir::ForeignDefId(salsa::InternKey::as_intern_id(&id))
} }

View file

@ -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] #[test]
fn assign_coerce() { fn assign_coerce() {
check_no_mismatches( check_no_mismatches(

View file

@ -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<i64, &str>
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<i64, &str>
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<i64, &str>
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] #[test]
fn fn_pointer_return() { fn fn_pointer_return() {
check_infer( check_infer(

View file

@ -37,6 +37,7 @@
//! add: //! add:
//! as_ref: sized //! as_ref: sized
//! drop: //! drop:
//! generator: pin
pub mod marker { pub mod marker {
// region:sized // region:sized
@ -182,6 +183,19 @@ pub mod ops {
type Target: ?Sized; type Target: ?Sized;
fn deref(&self) -> &Self::Target; fn deref(&self) -> &Self::Target;
} }
impl<T: ?Sized> Deref for &T {
type Target = T;
fn deref(&self) -> &T {
loop {}
}
}
impl<T: ?Sized> Deref for &mut T {
type Target = T;
fn deref(&self) -> &T {
loop {}
}
}
// region:deref_mut // region:deref_mut
#[lang = "deref_mut"] #[lang = "deref_mut"]
pub trait DerefMut: Deref { pub trait DerefMut: Deref {
@ -347,6 +361,27 @@ pub mod ops {
fn add(self, rhs: Rhs) -> Self::Output; fn add(self, rhs: Rhs) -> Self::Output;
} }
// endregion:add // endregion:add
// region:generator
mod generator {
use crate::pin::Pin;
#[lang = "generator"]
pub trait Generator<R = ()> {
type Yield;
#[lang = "generator_return"]
type Return;
fn resume(self: Pin<&mut Self>, arg: R) -> GeneratorState<Self::Yield, Self::Return>;
}
#[lang = "generator_state"]
pub enum GeneratorState<Y, R> {
Yielded(Y),
Complete(R),
}
}
pub use self::generator::{Generator, GeneratorState};
// endregion:generator
} }
// region:eq // region:eq
@ -455,6 +490,19 @@ pub mod pin {
pub struct Pin<P> { pub struct Pin<P> {
pointer: P, pointer: P,
} }
impl<P> Pin<P> {
pub fn new(pointer: P) -> Pin<P> {
loop {}
}
}
// region:deref
impl<P: crate::ops::Deref> crate::ops::Deref for Pin<P> {
type Target = P::Target;
fn deref(&self) -> &P::Target {
loop {}
}
}
// endregion:deref
} }
// endregion:pin // endregion:pin