From 556c9cebdb91278702263df4ac8c99ec24ab331a Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 21 May 2021 17:41:20 +0200 Subject: Refactor expectation handling So as to not use `TyKind::Error` as "no expectation". --- crates/hir_ty/src/infer.rs | 59 +++++++++++++++++++---------- crates/hir_ty/src/infer/coerce.rs | 4 -- crates/hir_ty/src/infer/expr.rs | 79 ++++++++++++++++++++++++--------------- 3 files changed, 88 insertions(+), 54 deletions(-) diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index db2234018..a137c0f92 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -370,10 +370,6 @@ impl<'a> InferenceContext<'a> { } fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { - // TODO handle expectations properly - if ty2.is_unknown() { - return true; - } self.table.unify(ty1, ty2) } @@ -679,17 +675,23 @@ impl<'a> InferenceContext<'a> { /// When inferring an expression, we propagate downward whatever type hint we /// are able in the form of an `Expectation`. #[derive(Clone, PartialEq, Eq, Debug)] -struct Expectation { - ty: Ty, - /// See the `rvalue_hint` method. - rvalue_hint: bool, +enum Expectation { + None, + HasType(Ty), + // Castable(Ty), // rustc has this, we currently just don't propagate an expectation for casts + RValueLikeUnsized(Ty), } impl Expectation { /// The expectation that the type of the expression needs to equal the given /// type. fn has_type(ty: Ty) -> Self { - Expectation { ty, rvalue_hint: false } + if ty.is_unknown() { + // FIXME: get rid of this? + Expectation::None + } else { + Expectation::HasType(ty) + } } /// The following explanation is copied straight from rustc: @@ -713,24 +715,41 @@ impl Expectation { /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169 /// for examples of where this comes up,. fn rvalue_hint(ty: Ty) -> Self { - Expectation { ty, rvalue_hint: true } + match ty.strip_references().kind(&Interner) { + TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => Expectation::RValueLikeUnsized(ty), + _ => Expectation::has_type(ty), + } } /// This expresses no expectation on the type. fn none() -> Self { - Expectation { - // FIXME - ty: TyKind::Error.intern(&Interner), - rvalue_hint: false, + Expectation::None + } + + fn resolve(&self, table: &mut unify::InferenceTable) -> Expectation { + match self { + Expectation::None => Expectation::None, + Expectation::HasType(t) => Expectation::HasType(table.resolve_ty_shallow(t)), + Expectation::RValueLikeUnsized(t) => { + Expectation::RValueLikeUnsized(table.resolve_ty_shallow(t)) + } } } - fn coercion_target(&self) -> Ty { - if self.rvalue_hint { - // FIXME - TyKind::Error.intern(&Interner) - } else { - self.ty.clone() + fn to_option(&self, table: &mut unify::InferenceTable) -> Option { + match self.resolve(table) { + Expectation::None => None, + Expectation::HasType(t) | + // Expectation::Castable(t) | + Expectation::RValueLikeUnsized(t) => Some(t), + } + } + + fn only_has_type(&self, table: &mut unify::InferenceTable) -> Option { + match self { + Expectation::HasType(t) => Some(table.resolve_ty_shallow(t)), + // Expectation::Castable(_) | + Expectation::RValueLikeUnsized(_) | Expectation::None => None, } } } diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 20c512517..03dd6ae76 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -21,10 +21,6 @@ impl<'a> InferenceContext<'a> { pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { let from_ty = self.resolve_ty_shallow(from_ty); let to_ty = self.resolve_ty_shallow(to_ty); - // TODO handle expectations properly - if to_ty.is_unknown() { - return true; - } match self.coerce_inner(from_ty, &to_ty) { Ok(result) => { self.table.register_infer_ok(result); diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index f5782ab24..4ef847d3a 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -39,12 +39,14 @@ impl<'a> InferenceContext<'a> { // Any expression that produces a value of type `!` must have diverged self.diverges = Diverges::Always; } - let could_unify = self.unify(&ty, &expected.ty); - if !could_unify { - self.result.type_mismatches.insert( - tgt_expr.into(), - TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() }, - ); + if let Some(expected_ty) = expected.only_has_type(&mut self.table) { + let could_unify = self.unify(&ty, &expected_ty); + if !could_unify { + self.result.type_mismatches.insert( + tgt_expr.into(), + TypeMismatch { expected: expected_ty.clone(), actual: ty.clone() }, + ); + } } ty } @@ -53,18 +55,20 @@ impl<'a> InferenceContext<'a> { /// Return the type after possible coercion. pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty { let ty = self.infer_expr_inner(expr, &expected); - let ty = if !self.coerce(&ty, &expected.coercion_target()) { - self.result.type_mismatches.insert( - expr.into(), - TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() }, - ); - // Return actual type when type mismatch. - // This is needed for diagnostic when return type mismatch. - ty - } else if expected.coercion_target().is_unknown() { - ty + let ty = if let Some(target) = expected.only_has_type(&mut self.table) { + if !self.coerce(&ty, &target) { + self.result.type_mismatches.insert( + expr.into(), + TypeMismatch { expected: target.clone(), actual: ty.clone() }, + ); + // Return actual type when type mismatch. + // This is needed for diagnostic when return type mismatch. + ty + } else { + target.clone() + } } else { - expected.ty.clone() + ty }; ty @@ -280,7 +284,9 @@ impl<'a> InferenceContext<'a> { // Eagerly try to relate the closure type with the expected // type, otherwise we often won't have enough information to // infer the body. - self.coerce(&closure_ty, &expected.ty); + if let Some(t) = expected.only_has_type(&mut self.table) { + self.coerce(&closure_ty, &t); + } // Now go through the argument patterns for (arg_pat, arg_ty) in args.iter().zip(sig_tys) { @@ -413,7 +419,9 @@ impl<'a> InferenceContext<'a> { self.write_variant_resolution(tgt_expr.into(), variant); } - self.unify(&ty, &expected.ty); + if let Some(t) = expected.only_has_type(&mut self.table) { + self.unify(&ty, &t); + } let substs = ty .as_adt() @@ -516,6 +524,7 @@ impl<'a> InferenceContext<'a> { self.resolve_associated_type(inner_ty, self.resolve_ops_try_ok()) } Expr::Cast { expr, type_ref } => { + // FIXME: propagate the "castable to" expectation (and find a test case that shows this is necessary) let _inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); let cast_ty = self.make_ty(type_ref); // FIXME check the cast... @@ -523,14 +532,16 @@ impl<'a> InferenceContext<'a> { } Expr::Ref { expr, rawness, mutability } => { let mutability = lower_to_chalk_mutability(*mutability); - let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = - &self.resolve_ty_shallow(&expected.ty).as_reference_or_ptr() + let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = expected + .only_has_type(&mut self.table) + .as_ref() + .and_then(|t| t.as_reference_or_ptr()) { - if *exp_mutability == Mutability::Mut && mutability == Mutability::Not { + if exp_mutability == Mutability::Mut && mutability == Mutability::Not { // FIXME: record type error - expected mut reference but found shared ref, // which cannot be coerced } - if *exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr { + if exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr { // FIXME: record type error - expected reference but found ptr, // which cannot be coerced } @@ -701,8 +712,12 @@ impl<'a> InferenceContext<'a> { } } Expr::Tuple { exprs } => { - let mut tys = match self.resolve_ty_shallow(&expected.ty).kind(&Interner) { - TyKind::Tuple(_, substs) => substs + let mut tys = match expected + .only_has_type(&mut self.table) + .as_ref() + .map(|t| t.kind(&Interner)) + { + Some(TyKind::Tuple(_, substs)) => substs .iter(&Interner) .map(|a| a.assert_ty_ref(&Interner).clone()) .chain(repeat_with(|| self.table.new_type_var())) @@ -718,14 +733,16 @@ impl<'a> InferenceContext<'a> { TyKind::Tuple(tys.len(), Substitution::from_iter(&Interner, tys)).intern(&Interner) } Expr::Array(array) => { - let elem_ty = match self.resolve_ty_shallow(&expected.ty).kind(&Interner) { - TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(), - _ => self.table.new_type_var(), - }; + let elem_ty = + match expected.to_option(&mut self.table).as_ref().map(|t| t.kind(&Interner)) { + Some(TyKind::Array(st, _)) | Some(TyKind::Slice(st)) => st.clone(), + _ => self.table.new_type_var(), + }; let len = match array { Array::ElementList(items) => { for expr in items.iter() { + // FIXME: use CoerceMany (coerce_merge_branch) self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone())); } Some(items.len() as u64) @@ -839,7 +856,9 @@ impl<'a> InferenceContext<'a> { // we don't even make an attempt at coercion self.table.new_maybe_never_var() } else { - self.coerce(&TyBuilder::unit(), &expected.coercion_target()); + if let Some(t) = expected.only_has_type(&mut self.table) { + self.coerce(&TyBuilder::unit(), &t); + } TyBuilder::unit() } }; -- cgit v1.2.3