Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/coerce.rs')
-rw-r--r--crates/hir-ty/src/infer/coerce.rs60
1 files changed, 44 insertions, 16 deletions
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 3293534a06..48c9153026 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -50,11 +50,44 @@ fn success(
#[derive(Clone, Debug)]
pub(super) struct CoerceMany {
expected_ty: Ty,
+ final_ty: Option<Ty>,
}
impl CoerceMany {
pub(super) fn new(expected: Ty) -> Self {
- CoerceMany { expected_ty: expected }
+ CoerceMany { expected_ty: expected, final_ty: None }
+ }
+
+ /// Returns the "expected type" with which this coercion was
+ /// constructed. This represents the "downward propagated" type
+ /// that was given to us at the start of typing whatever construct
+ /// we are typing (e.g., the match expression).
+ ///
+ /// Typically, this is used as the expected type when
+ /// type-checking each of the alternative expressions whose types
+ /// we are trying to merge.
+ pub(super) fn expected_ty(&self) -> Ty {
+ self.expected_ty.clone()
+ }
+
+ /// Returns the current "merged type", representing our best-guess
+ /// at the LUB of the expressions we've seen so far (if any). This
+ /// isn't *final* until you call `self.complete()`, which will return
+ /// the merged type.
+ pub(super) fn merged_ty(&self) -> Ty {
+ self.final_ty.clone().unwrap_or_else(|| self.expected_ty.clone())
+ }
+
+ pub(super) fn complete(self, ctx: &mut InferenceContext<'_>) -> Ty {
+ if let Some(final_ty) = self.final_ty {
+ final_ty
+ } else {
+ ctx.result.standard_types.never.clone()
+ }
+ }
+
+ pub(super) fn coerce_forced_unit(&mut self, ctx: &mut InferenceContext<'_>) {
+ self.coerce(ctx, None, &ctx.result.standard_types.unit.clone())
}
/// Merge two types from different branches, with possible coercion.
@@ -76,25 +109,25 @@ impl CoerceMany {
// 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 (self.expected_ty.kind(Interner), expr_ty.kind(Interner)) {
+ let sig = match (self.merged_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 =
- self.expected_ty.callable_sig(ctx.db).expect("FnDef without callable sig");
+ self.merged_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 = ctx.table.coerce_inner(self.expected_ty.clone(), &target_ty);
+ let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
if let (Ok(result1), Ok(result2)) = (result1, result2) {
ctx.table.register_infer_ok(result1);
ctx.table.register_infer_ok(result2);
- return self.expected_ty = target_ty;
+ return self.final_ty = Some(target_ty);
}
}
@@ -102,25 +135,20 @@ impl CoerceMany {
// 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 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;
+ if let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty()) {
+ self.final_ty = Some(res);
+ } else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty) {
+ self.final_ty = Some(res);
} else {
if let Some(id) = expr {
ctx.result.type_mismatches.insert(
id.into(),
- TypeMismatch { expected: self.expected_ty.clone(), actual: expr_ty },
+ TypeMismatch { expected: self.merged_ty().clone(), actual: expr_ty.clone() },
);
}
cov_mark::hit!(coerce_merge_fail_fallback);
- /* self.expected_ty is already correct */
}
}
-
- pub(super) fn complete(self) -> Ty {
- self.expected_ty
- }
}
pub fn could_coerce(
@@ -665,7 +693,7 @@ pub(super) fn auto_deref_adjust_steps(autoderef: &Autoderef<'_, '_>) -> Vec<Adju
.iter()
.map(|(kind, _source)| match kind {
// We do not know what kind of deref we require at this point yet
- AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)),
+ AutoderefKind::Overloaded => Some(OverloadedDeref(None)),
AutoderefKind::Builtin => None,
})
.zip(targets)