From 64a1b26b8df3f154d162c3d75b7ca7f7d4ebae5f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 8 Jul 2021 14:16:23 +0200 Subject: [PATCH] Implement `CoerceMany` --- crates/hir_ty/src/infer/closure.rs | 5 +- crates/hir_ty/src/infer/coerce.rs | 134 ++++++++++++++++++----------- crates/hir_ty/src/infer/expr.rs | 70 +++++++-------- 3 files changed, 120 insertions(+), 89 deletions(-) diff --git a/crates/hir_ty/src/infer/closure.rs b/crates/hir_ty/src/infer/closure.rs index a5c97f25dd..e4fcc56bc4 100644 --- a/crates/hir_ty/src/infer/closure.rs +++ b/crates/hir_ty/src/infer/closure.rs @@ -25,9 +25,8 @@ impl InferenceContext<'_> { }; // Deduction from where-clauses in scope, as well as fn-pointer coercion are handled here. - if let Ok(res) = self.coerce(closure_ty, &expected_ty) { - self.write_expr_adj(closure_expr, res.value.0); - } + let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty); + // 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/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 578e1b07a0..7a624090b6 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -10,7 +10,7 @@ use hir_def::{expr::ExprId, lang_item::LangItemTarget}; use crate::{ autoderef, - infer::{Adjust, Adjustment, AutoBorrow, PointerCast, TypeMismatch}, + infer::{Adjust, Adjustment, AutoBorrow, InferResult, PointerCast, TypeMismatch}, static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Interner, Solution, Substitution, Ty, TyBuilder, TyExt, TyKind, }; @@ -36,23 +36,25 @@ fn success( ) -> CoerceResult { Ok(InferOk { goals, value: (adj, target) }) } +#[derive(Clone, Debug)] +pub(super) struct CoerceMany { + expected_ty: Ty, +} -impl<'a> InferenceContext<'a> { - /// Unify two types, but may coerce the first one to the second one - /// using "implicit coercion rules" if needed. - pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> CoerceResult { - let from_ty = self.resolve_ty_shallow(from_ty); - let to_ty = self.resolve_ty_shallow(to_ty); - match self.coerce_inner(from_ty, &to_ty) { - Ok(InferOk { value, goals }) => { - self.table.register_infer_ok(InferOk { value: (), goals }); - Ok(InferOk { value, goals: Vec::new() }) - } - Err(e) => { - // FIXME deal with error - Err(e) - } - } +impl CoerceMany { + pub(super) fn new(expected: Ty) -> Self { + CoerceMany { expected_ty: expected } + } + + pub(super) fn once( + ctx: &mut InferenceContext<'_>, + expected: Ty, + expr: Option, + expr_ty: &Ty, + ) -> Ty { + let mut this = CoerceMany::new(expected); + this.coerce(ctx, expr, expr_ty); + this.complete() } /// Merge two types from different branches, with possible coercion. @@ -62,51 +64,88 @@ impl<'a> InferenceContext<'a> { /// coerce both to function pointers; /// - if we were concerned with lifetime subtyping, we'd need to look for a /// least upper bound. - pub(super) fn coerce_merge_branch(&mut self, id: Option, ty1: &Ty, ty2: &Ty) -> Ty { - // TODO - let ty1 = self.resolve_ty_shallow(ty1); - let ty2 = self.resolve_ty_shallow(ty2); + pub(super) fn coerce( + &mut self, + ctx: &mut InferenceContext<'_>, + expr: Option, + expr_ty: &Ty, + ) { + let expr_ty = ctx.resolve_ty_shallow(expr_ty); + self.expected_ty = ctx.resolve_ty_shallow(&self.expected_ty); + // Special case: two function types. Try to coerce both to // pointers to have a chance at getting a match. See // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 - let sig = match (ty1.kind(&Interner), ty2.kind(&Interner)) { + let sig = match (self.expected_ty.kind(&Interner), expr_ty.kind(&Interner)) { (TyKind::FnDef(..) | TyKind::Closure(..), TyKind::FnDef(..) | TyKind::Closure(..)) => { // FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure, // we should be coercing the closure to a fn pointer of the safety of the FnDef cov_mark::hit!(coerce_fn_reification); - let sig = ty1.callable_sig(self.db).expect("FnDef without callable sig"); + let sig = + self.expected_ty.callable_sig(ctx.db).expect("FnDef without callable sig"); Some(sig) } _ => None, }; if let Some(sig) = sig { let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(&Interner); - let result1 = self.coerce_inner(ty1.clone(), &target_ty); - let result2 = self.coerce_inner(ty2.clone(), &target_ty); + let result1 = ctx.coerce_inner(self.expected_ty.clone(), &target_ty); + let result2 = ctx.coerce_inner(expr_ty.clone(), &target_ty); if let (Ok(result1), Ok(result2)) = (result1, result2) { - self.table.register_infer_ok(result1); - self.table.register_infer_ok(result2); - return target_ty; + ctx.table.register_infer_ok(result1); + ctx.table.register_infer_ok(result2); + return self.expected_ty = target_ty; } } - // It might not seem like it, but order is important here: ty1 is our - // "previous" type, ty2 is the "new" one being added. If the previous + // It might not seem like it, but order is important here: If the expected // type is a type variable and the new one is `!`, trying it the other // way around first would mean we make the type variable `!`, instead of // just marking it as possibly diverging. - if self.coerce(&ty2, &ty1).is_ok() { - ty1 - } else if self.coerce(&ty1, &ty2).is_ok() { - ty2 + if ctx.coerce(expr, &expr_ty, &self.expected_ty).is_ok() { + /* self.expected_ty is already correct */ + } else if ctx.coerce(expr, &self.expected_ty, &expr_ty).is_ok() { + self.expected_ty = expr_ty; } else { - if let Some(id) = id { - self.result - .type_mismatches - .insert(id.into(), TypeMismatch { expected: ty1.clone(), actual: ty2 }); + if let Some(id) = expr { + ctx.result.type_mismatches.insert( + id.into(), + TypeMismatch { expected: self.expected_ty.clone(), actual: expr_ty }, + ); } cov_mark::hit!(coerce_merge_fail_fallback); - ty1 + /* self.expected_ty is already correct */ + } + } + + pub(super) fn complete(self) -> Ty { + self.expected_ty + } +} + +impl<'a> InferenceContext<'a> { + /// Unify two types, but may coerce the first one to the second one + /// using "implicit coercion rules" if needed. + pub(super) fn coerce( + &mut self, + expr: Option, + from_ty: &Ty, + to_ty: &Ty, + ) -> InferResult { + let from_ty = self.resolve_ty_shallow(from_ty); + let to_ty = self.resolve_ty_shallow(to_ty); + match self.coerce_inner(from_ty, &to_ty) { + Ok(InferOk { value: (adjustments, ty), goals }) => { + if let Some(expr) = expr { + self.write_expr_adj(expr, adjustments); + } + self.table.register_infer_ok(InferOk { value: (), goals }); + Ok(InferOk { value: ty, goals: Vec::new() }) + } + Err(e) => { + // FIXME deal with error + Err(e) + } } } @@ -189,7 +228,6 @@ impl<'a> InferenceContext<'a> { // Check that the types which they point at are compatible. let from_raw = TyKind::Raw(to_mt, from_inner.clone()).intern(&Interner); - // self.table.try_unify(&from_raw, to_ty); // Although references and unsafe ptrs have the same // representation, we still register an Adjust::DerefRef so that @@ -518,15 +556,13 @@ impl<'a> InferenceContext<'a> { // FIXME: should we accept ambiguous results here? _ => return Err(TypeError), }; - // TODO: this is probably wrong? - let coerce_target = self.table.new_type_var(); - self.unify_and(&coerce_target, to_ty, |target| { - let unsize = Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target }; - match reborrow { - None => vec![unsize], - Some((ref deref, ref autoref)) => vec![deref.clone(), autoref.clone(), unsize], - } - }) + let unsize = + Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target: to_ty.clone() }; + let adjustments = match reborrow { + None => vec![unsize], + Some((deref, autoref)) => vec![deref, autoref, unsize], + }; + success(adjustments, to_ty.clone(), vec![]) } } diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index a582d700f2..20830d03e7 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -16,6 +16,7 @@ use syntax::ast::RangeOp; use crate::{ autoderef, consteval, + infer::coerce::CoerceMany, lower::lower_to_chalk_mutability, mapping::from_chalk, method_resolution, op, @@ -56,11 +57,8 @@ impl<'a> InferenceContext<'a> { pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty { let ty = self.infer_expr_inner(expr, expected); let ty = if let Some(target) = expected.only_has_type(&mut self.table) { - match self.coerce(&ty, &target) { - Ok(res) => { - self.result.expr_adjustments.insert(expr, res.value.0); - target - } + match self.coerce(Some(expr), &ty, &target) { + Ok(res) => res.value, Err(_) => { self.result .type_mismatches @@ -128,31 +126,32 @@ impl<'a> InferenceContext<'a> { let body = Arc::clone(&self.body); // avoid borrow checker problem let ty = match &body[tgt_expr] { Expr::Missing => self.err_ty(), - Expr::If { condition, then_branch, else_branch } => { + &Expr::If { condition, then_branch, else_branch } => { // if let is desugared to match, so this is always simple if self.infer_expr( - *condition, + condition, &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(&Interner)), ); let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let mut both_arms_diverge = Diverges::Always; - let mut result_ty = self.table.new_type_var(); - let then_ty = self.infer_expr_inner(*then_branch, expected); + let result_ty = self.table.new_type_var(); + let then_ty = self.infer_expr_inner(then_branch, expected); both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); - result_ty = self.coerce_merge_branch(Some(*then_branch), &result_ty, &then_ty); + let mut coerce = CoerceMany::new(result_ty); + coerce.coerce(self, Some(then_branch), &then_ty); let else_ty = match else_branch { - Some(else_branch) => self.infer_expr_inner(*else_branch, expected), + Some(else_branch) => self.infer_expr_inner(else_branch, expected), None => TyBuilder::unit(), }; both_arms_diverge &= self.diverges; // FIXME: create a synthetic `else {}` so we have something to refer to here instead of None? - result_ty = self.coerce_merge_branch(*else_branch, &result_ty, &else_ty); + coerce.coerce(self, else_branch, &else_ty); self.diverges = condition_diverges | both_arms_diverge; - result_ty + coerce.complete() } Expr::Block { statements, tail, label, id: _ } => { let old_resolver = mem::replace( @@ -193,7 +192,7 @@ impl<'a> InferenceContext<'a> { } Expr::Async { body } => { // Use the first type parameter as the output type of future. - // existenail type AsyncBlockImplTrait: Future + // existential type AsyncBlockImplTrait: Future let inner_ty = self.infer_expr(*body, &Expectation::none()); let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body); let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); @@ -223,6 +222,7 @@ impl<'a> InferenceContext<'a> { self.breakables.push(BreakableContext { may_break: false, break_ty: self.err_ty(), + label: label.map(|label| self.body[label].name.clone()), }); // while let is desugared to a match loop, so this is always simple while @@ -344,7 +344,7 @@ impl<'a> InferenceContext<'a> { let expected = expected.adjust_for_branches(&mut self.table); - let mut result_ty = if arms.is_empty() { + let result_ty = if arms.is_empty() { TyKind::Never.intern(&Interner) } else { match &expected { @@ -352,6 +352,7 @@ impl<'a> InferenceContext<'a> { _ => self.table.new_type_var(), } }; + let mut coerce = CoerceMany::new(result_ty); let matchee_diverges = self.diverges; let mut all_arms_diverge = Diverges::Always; @@ -368,12 +369,12 @@ impl<'a> InferenceContext<'a> { let arm_ty = self.infer_expr_inner(arm.expr, &expected); all_arms_diverge &= self.diverges; - result_ty = self.coerce_merge_branch(Some(arm.expr), &result_ty, &arm_ty); + coerce.coerce(self, Some(arm.expr), &arm_ty); } self.diverges = matchee_diverges | all_arms_diverge; - result_ty + coerce.complete() } Expr::Path(p) => { // FIXME this could be more efficient... @@ -382,6 +383,7 @@ impl<'a> InferenceContext<'a> { } Expr::Continue { .. } => TyKind::Never.intern(&Interner), Expr::Break { expr, label } => { + let expr = *expr; let last_ty = if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) { ctxt.break_ty.clone() @@ -390,13 +392,13 @@ impl<'a> InferenceContext<'a> { }; let val_ty = if let Some(expr) = expr { - self.infer_expr(*expr, &Expectation::none()) + self.infer_expr(expr, &Expectation::none()) } else { TyBuilder::unit() }; // FIXME: create a synthetic `()` during lowering so we have something to refer to here? - let merged_type = self.coerce_merge_branch(*expr, &last_ty, &val_ty); + let merged_type = CoerceMany::once(self, last_ty, expr, &val_ty); if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) { ctxt.break_ty = merged_type; @@ -413,9 +415,7 @@ impl<'a> InferenceContext<'a> { self.infer_expr_coerce(*expr, &Expectation::has_type(self.return_ty.clone())); } else { let unit = TyBuilder::unit(); - if let Ok(ok) = self.coerce(&unit, &self.return_ty.clone()) { - self.write_expr_adj(tgt_expr, ok.value.0); - } + let _ = self.coerce(Some(tgt_expr), &unit, &self.return_ty.clone()); } TyKind::Never.intern(&Interner) } @@ -744,39 +744,37 @@ impl<'a> InferenceContext<'a> { TyKind::Tuple(tys.len(), Substitution::from_iter(&Interner, tys)).intern(&Interner) } Expr::Array(array) => { - let mut elem_ty = + let elem_ty = match expected.to_option(&mut self.table).as_ref().map(|t| t.kind(&Interner)) { Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st.clone(), _ => self.table.new_type_var(), }; + let mut coerce = CoerceMany::new(elem_ty.clone()); let expected = Expectation::has_type(elem_ty.clone()); let len = match array { Array::ElementList(items) => { - for expr in items.iter() { - let cur_elem_ty = self.infer_expr_inner(*expr, &expected); - elem_ty = self.coerce_merge_branch(Some(*expr), &elem_ty, &cur_elem_ty); + for &expr in items.iter() { + let cur_elem_ty = self.infer_expr_inner(expr, &expected); + coerce.coerce(self, Some(expr), &cur_elem_ty); } Some(items.len() as u64) } - Array::Repeat { initializer, repeat } => { - self.infer_expr_coerce( - *initializer, - &Expectation::has_type(elem_ty.clone()), - ); + &Array::Repeat { initializer, repeat } => { + self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty)); self.infer_expr( - *repeat, + repeat, &Expectation::has_type( TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner), ), ); - let repeat_expr = &self.body.exprs[*repeat]; + let repeat_expr = &self.body.exprs[repeat]; consteval::eval_usize(repeat_expr) } }; - TyKind::Array(elem_ty, consteval::usize_const(len)).intern(&Interner) + TyKind::Array(coerce.complete(), consteval::usize_const(len)).intern(&Interner) } Expr::Literal(lit) => match lit { Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner), @@ -872,9 +870,7 @@ impl<'a> InferenceContext<'a> { self.table.new_maybe_never_var() } else { if let Some(t) = expected.only_has_type(&mut self.table) { - if let Ok(ok) = self.coerce(&TyBuilder::unit(), &t) { - self.write_expr_adj(expr, ok.value.0); - } + let _ = self.coerce(Some(expr), &TyBuilder::unit(), &t); } TyBuilder::unit() }