]> git.lizzy.rs Git - rust.git/commitdiff
Implement `CoerceMany`
authorLukas Wirth <lukastw97@gmail.com>
Thu, 8 Jul 2021 12:16:23 +0000 (14:16 +0200)
committerLukas Wirth <lukastw97@gmail.com>
Thu, 8 Jul 2021 12:16:23 +0000 (14:16 +0200)
crates/hir_ty/src/infer/closure.rs
crates/hir_ty/src/infer/coerce.rs
crates/hir_ty/src/infer/expr.rs

index a5c97f25dd01d31f2ccbe730b814daf08ae111d2..e4fcc56bc45bfb514cfa59f57bcc743cf1078941 100644 (file)
@@ -25,9 +25,8 @@ pub(super) fn deduce_closure_type_from_expectations(
         };
 
         // 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) {
index 578e1b07a0ebbe53f45cd93cbf1f5734e59cd0cc..7a624090b65fc3d0c19d13abfd11993b8dc4559f 100644 (file)
@@ -10,7 +10,7 @@
 
 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<ExprId>,
+        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 @@ pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> CoerceResult {
     ///    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<ExprId>, 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<ExprId>,
+        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<ExprId>,
+        from_ty: &Ty,
+        to_ty: &Ty,
+    ) -> InferResult<Ty> {
+        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 @@ fn coerce_ptr(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> CoerceRe
 
         // 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 @@ fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> CoerceResult {
             // 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![])
     }
 }
 
index a582d700f20beb40fabbd52bd0679fb774a14a22..20830d03e777f6729ea7c8d1fc57fbccafe87969 100644 (file)
@@ -16,6 +16,7 @@
 
 use crate::{
     autoderef, consteval,
+    infer::coerce::CoerceMany,
     lower::lower_to_chalk_mutability,
     mapping::from_chalk,
     method_resolution, op,
@@ -56,11 +57,8 @@ pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) ->
     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 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
         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 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
             }
             Expr::Async { body } => {
                 // Use the first type parameter as the output type of future.
-                // existenail type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
+                // existential type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
                 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 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
                 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 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
 
                 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 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
                         _ => 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 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
 
                     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 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
             }
             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 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
                     };
 
                 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 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
                     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 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
                 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 @@ fn infer_block(
                 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()
             }