summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_typeck/src/check/_match.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_typeck/src/check/_match.rs')
-rw-r--r--compiler/rustc_typeck/src/check/_match.rs122
1 files changed, 75 insertions, 47 deletions
diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs
index 1b13c98e4..20332e75c 100644
--- a/compiler/rustc_typeck/src/check/_match.rs
+++ b/compiler/rustc_typeck/src/check/_match.rs
@@ -4,7 +4,7 @@ use rustc_errors::{Applicability, MultiSpan};
use rustc_hir::{self as hir, ExprKind};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::traits::Obligation;
-use rustc_middle::ty::{self, ToPredicate, Ty, TypeVisitable};
+use rustc_middle::ty::{self, Subst, ToPredicate, Ty};
use rustc_span::Span;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::{
@@ -12,7 +12,7 @@ use rustc_trait_selection::traits::{
};
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- #[instrument(skip(self), level = "debug")]
+ #[instrument(skip(self), level = "debug", ret)]
pub fn check_match(
&self,
expr: &'tcx hir::Expr<'tcx>,
@@ -94,7 +94,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let arm_ty = self.check_expr_with_expectation(&arm.body, expected);
all_arms_diverge &= self.diverges.get();
- let opt_suggest_box_span = self.opt_suggest_box_span(arm_ty, orig_expected);
+ let opt_suggest_box_span = prior_arm.and_then(|(_, prior_arm_ty, _)| {
+ self.opt_suggest_box_span(prior_arm_ty, arm_ty, orig_expected)
+ });
let (arm_block_id, arm_span) = if let hir::ExprKind::Block(blk, _) = arm.body.kind {
(Some(blk.hir_id), self.find_block_span(blk))
@@ -135,9 +137,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Some(&arm.body),
arm_ty,
Some(&mut |err| {
- let Some(ret) = self.ret_type_span else {
- return;
- };
+ let Some(ret) = self
+ .tcx
+ .hir()
+ .find_by_def_id(self.body_id.owner)
+ .and_then(|owner| owner.fn_decl())
+ .map(|decl| decl.output.span())
+ else { return; };
let Expectation::IsLast(stmt) = orig_expected else {
return
};
@@ -210,9 +216,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// We won't diverge unless the scrutinee or all arms diverge.
self.diverges.set(scrut_diverges | all_arms_diverge);
- let match_ty = coercion.complete(self);
- debug!(?match_ty);
- match_ty
+ coercion.complete(self)
}
/// When the previously checked expression (the scrutinee) diverges,
@@ -468,53 +472,77 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- // When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
- // we check if the different arms would work with boxed trait objects instead and
- // provide a structured suggestion in that case.
+ /// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
+ /// we check if the different arms would work with boxed trait objects instead and
+ /// provide a structured suggestion in that case.
pub(crate) fn opt_suggest_box_span(
&self,
- outer_ty: Ty<'tcx>,
+ first_ty: Ty<'tcx>,
+ second_ty: Ty<'tcx>,
orig_expected: Expectation<'tcx>,
) -> Option<Span> {
+ // FIXME(compiler-errors): This really shouldn't need to be done during the
+ // "good" path of typeck, but here we are.
match orig_expected {
- Expectation::ExpectHasType(expected)
- if self.in_tail_expr
- && self.ret_coercion.as_ref()?.borrow().merged_ty().has_opaque_types()
- && self.can_coerce(outer_ty, expected) =>
- {
- let obligations = self.fulfillment_cx.borrow().pending_obligations();
- let mut suggest_box = !obligations.is_empty();
- for o in obligations {
- match o.predicate.kind().skip_binder() {
- ty::PredicateKind::Trait(t) => {
- let pred =
- ty::Binder::dummy(ty::PredicateKind::Trait(ty::TraitPredicate {
- trait_ref: ty::TraitRef {
- def_id: t.def_id(),
- substs: self.tcx.mk_substs_trait(outer_ty, &[]),
- },
- constness: t.constness,
- polarity: t.polarity,
- }));
- let obl = Obligation::new(
- o.cause.clone(),
- self.param_env,
- pred.to_predicate(self.tcx),
- );
- suggest_box &= self.predicate_must_hold_modulo_regions(&obl);
- if !suggest_box {
- // We've encountered some obligation that didn't hold, so the
- // return expression can't just be boxed. We don't need to
- // evaluate the rest of the obligations.
- break;
+ Expectation::ExpectHasType(expected) => {
+ let TypeVariableOrigin {
+ span,
+ kind: TypeVariableOriginKind::OpaqueTypeInference(rpit_def_id),
+ ..
+ } = self.type_var_origin(expected)? else { return None; };
+
+ let sig = *self
+ .typeck_results
+ .borrow()
+ .liberated_fn_sigs()
+ .get(hir::HirId::make_owner(self.body_id.owner))?;
+
+ let substs = sig.output().walk().find_map(|arg| {
+ if let ty::GenericArgKind::Type(ty) = arg.unpack()
+ && let ty::Opaque(def_id, substs) = *ty.kind()
+ && def_id == rpit_def_id
+ {
+ Some(substs)
+ } else {
+ None
+ }
+ })?;
+ let opaque_ty = self.tcx.mk_opaque(rpit_def_id, substs);
+
+ if !self.can_coerce(first_ty, expected) || !self.can_coerce(second_ty, expected) {
+ return None;
+ }
+
+ for ty in [first_ty, second_ty] {
+ for pred in self.tcx.bound_explicit_item_bounds(rpit_def_id).transpose_iter() {
+ let pred = pred.map_bound(|(pred, _)| *pred).subst(self.tcx, substs);
+ let pred = match pred.kind().skip_binder() {
+ ty::PredicateKind::Trait(mut trait_pred) => {
+ assert_eq!(trait_pred.trait_ref.self_ty(), opaque_ty);
+ trait_pred.trait_ref.substs =
+ self.tcx.mk_substs_trait(ty, &trait_pred.trait_ref.substs[1..]);
+ pred.kind().rebind(trait_pred).to_predicate(self.tcx)
}
+ ty::PredicateKind::Projection(mut proj_pred) => {
+ assert_eq!(proj_pred.projection_ty.self_ty(), opaque_ty);
+ proj_pred.projection_ty.substs = self
+ .tcx
+ .mk_substs_trait(ty, &proj_pred.projection_ty.substs[1..]);
+ pred.kind().rebind(proj_pred).to_predicate(self.tcx)
+ }
+ _ => continue,
+ };
+ if !self.predicate_must_hold_modulo_regions(&Obligation::new(
+ ObligationCause::misc(span, self.body_id),
+ self.param_env,
+ pred,
+ )) {
+ return None;
}
- _ => {}
}
}
- // If all the obligations hold (or there are no obligations) the tail expression
- // we can suggest to return a boxed trait object instead of an opaque type.
- if suggest_box { self.ret_type_span } else { None }
+
+ Some(span)
}
_ => None,
}