mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 21:43:37 +00:00
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:
commit
1f929659ac
14 changed files with 388 additions and 34 deletions
|
@ -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<Name, Vec<PatId>>,
|
||||
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,
|
||||
)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -198,6 +198,7 @@ pub enum Expr {
|
|||
arg_types: Box<[Option<Interned<TypeRef>>]>,
|
||||
ret_type: Option<Interned<TypeRef>>,
|
||||
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 },
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Interner>;
|
||||
|
@ -372,17 +373,63 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||
}
|
||||
fn generator_datum(
|
||||
&self,
|
||||
_: chalk_ir::GeneratorId<Interner>,
|
||||
id: chalk_ir::GeneratorId<Interner>,
|
||||
) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorDatum<Interner>> {
|
||||
// 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<Interner>,
|
||||
id: chalk_ir::GeneratorId<Interner>,
|
||||
) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorWitnessDatum<Interner>> {
|
||||
// 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<Interner> {
|
||||
|
|
|
@ -120,6 +120,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
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<chalk_db::AssociatedTyDatum>;
|
||||
|
@ -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)]
|
||||
|
|
|
@ -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<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}}")?,
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -339,7 +339,7 @@ pub struct InferenceResult {
|
|||
/// unresolved or missing subpatterns or subpatterns of mismatched types.
|
||||
pub type_of_pat: ArenaMap<PatId, Ty>,
|
||||
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
|
||||
/// 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<PatId, Vec<Ty>>,
|
||||
|
@ -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<BreakableContext>,
|
||||
}
|
||||
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
chalk_ir::ForeignDefId(salsa::InternKey::as_intern_id(&id))
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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]
|
||||
fn fn_pointer_return() {
|
||||
check_infer(
|
||||
|
|
|
@ -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<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
|
||||
#[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<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
|
||||
|
@ -455,6 +490,19 @@ pub mod pin {
|
|||
pub struct Pin<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
|
||||
|
||||
|
|
Loading…
Reference in a new issue