diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 6467adb54dab0..ded03c88a16e9 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -10,7 +10,7 @@ use rustc_trait_selection::traits::{ }; use tracing::{debug, instrument}; -use crate::coercion::{AsCoercionSite, CoerceMany}; +use crate::coercion::CoerceMany; use crate::{Diverges, Expectation, FnCtxt, GatherLocalsVisitor, Needs}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -73,7 +73,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Expectation::ExpectHasType(ety) if ety != tcx.types.unit => ety, _ => self.next_ty_var(expr.span), }; - CoerceMany::with_coercion_sites(coerce_first, arms) + CoerceMany::with_capacity(coerce_first, arms.len()) }; let mut prior_non_diverging_arms = vec![]; // Used only for diagnostics. @@ -269,16 +269,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Handle the fallback arm of a desugared if(-let) like a missing else. /// /// Returns `true` if there was an error forcing the coercion to the `()` type. - pub(super) fn if_fallback_coercion( + pub(super) fn if_fallback_coercion( &self, if_span: Span, cond_expr: &'tcx hir::Expr<'tcx>, then_expr: &'tcx hir::Expr<'tcx>, - coercion: &mut CoerceMany<'tcx, '_, T>, - ) -> bool - where - T: AsCoercionSite, - { + coercion: &mut CoerceMany<'tcx>, + ) -> bool { // If this `if` expr is the parent's function return expr, // the cause of the type coercion is the return type, point at it. (#25228) let hir_id = self.tcx.parent_hir_id(self.tcx.parent_hir_id(then_expr.hir_id)); diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 127965cb4b301..b4a43548dcdad 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1165,17 +1165,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// This is really an internal helper. From outside the coercion /// module, you should instantiate a `CoerceMany` instance. - fn try_find_coercion_lub( + fn try_find_coercion_lub( &self, cause: &ObligationCause<'tcx>, - exprs: &[E], + exprs: &[&'tcx hir::Expr<'tcx>], prev_ty: Ty<'tcx>, new: &hir::Expr<'_>, new_ty: Ty<'tcx>, - ) -> RelateResult<'tcx, Ty<'tcx>> - where - E: AsCoercionSite, - { + ) -> RelateResult<'tcx, Ty<'tcx>> { let prev_ty = self.try_structurally_resolve_type(cause.span, prev_ty); let new_ty = self.try_structurally_resolve_type(new.span, new_ty); debug!( @@ -1269,7 +1266,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::FnDef(..) => Adjust::Pointer(PointerCoercion::ReifyFnPointer(sig.safety())), _ => span_bug!(new.span, "should not try to coerce a {new_ty} to a fn pointer"), }; - for expr in exprs.iter().map(|e| e.as_coercion_site()) { + for expr in exprs.iter() { self.apply_adjustments( expr, vec![Adjustment { kind: prev_adjustment.clone(), target: fn_ptr }], @@ -1316,7 +1313,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (adjustments, target) = self.register_infer_ok_obligations(ok); for expr in exprs { - let expr = expr.as_coercion_site(); self.apply_adjustments(expr, adjustments.clone()); } debug!( @@ -1382,41 +1378,23 @@ pub fn can_coerce<'tcx>( /// } /// let final_ty = coerce.complete(fcx); /// ``` -pub(crate) struct CoerceMany<'tcx, 'exprs, E: AsCoercionSite> { +pub(crate) struct CoerceMany<'tcx> { expected_ty: Ty<'tcx>, final_ty: Option>, - expressions: Expressions<'tcx, 'exprs, E>, - pushed: usize, -} - -/// The type of a `CoerceMany` that is storing up the expressions into -/// a buffer. We use this in `check/mod.rs` for things like `break`. -pub(crate) type DynamicCoerceMany<'tcx> = CoerceMany<'tcx, 'tcx, &'tcx hir::Expr<'tcx>>; - -enum Expressions<'tcx, 'exprs, E: AsCoercionSite> { - Dynamic(Vec<&'tcx hir::Expr<'tcx>>), - UpFront(&'exprs [E]), + expressions: Vec<&'tcx hir::Expr<'tcx>>, } -impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { - /// The usual case; collect the set of expressions dynamically. - /// If the full set of coercion sites is known before hand, - /// consider `with_coercion_sites()` instead to avoid allocation. +impl<'tcx> CoerceMany<'tcx> { + /// Creates a `CoerceMany` with a default capacity of 1. If the full set of + /// coercion sites is known before hand, consider `with_capacity()` instead + /// to avoid allocation. pub(crate) fn new(expected_ty: Ty<'tcx>) -> Self { - Self::make(expected_ty, Expressions::Dynamic(vec![])) + Self::with_capacity(expected_ty, 1) } - /// As an optimization, you can create a `CoerceMany` with a - /// preexisting slice of expressions. In this case, you are - /// expected to pass each element in the slice to `coerce(...)` in - /// order. This is used with arrays in particular to avoid - /// needlessly cloning the slice. - pub(crate) fn with_coercion_sites(expected_ty: Ty<'tcx>, coercion_sites: &'exprs [E]) -> Self { - Self::make(expected_ty, Expressions::UpFront(coercion_sites)) - } - - fn make(expected_ty: Ty<'tcx>, expressions: Expressions<'tcx, 'exprs, E>) -> Self { - CoerceMany { expected_ty, final_ty: None, expressions, pushed: 0 } + /// Creates a `CoerceMany` with a given capacity. + pub(crate) fn with_capacity(expected_ty: Ty<'tcx>, capacity: usize) -> Self { + CoerceMany { expected_ty, final_ty: None, expressions: Vec::with_capacity(capacity) } } /// Returns the "expected type" with which this coercion was @@ -1529,7 +1507,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // Handle the actual type unification etc. let result = if let Some(expression) = expression { - if self.pushed == 0 { + if self.expressions.is_empty() { // Special-case the first expression we are coercing. // To be honest, I'm not entirely sure why we do this. // We don't allow two-phase borrows, see comment in try_find_coercion_lub for why @@ -1541,22 +1519,13 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { Some(cause.clone()), ) } else { - match self.expressions { - Expressions::Dynamic(ref exprs) => fcx.try_find_coercion_lub( - cause, - exprs, - self.merged_ty(), - expression, - expression_ty, - ), - Expressions::UpFront(coercion_sites) => fcx.try_find_coercion_lub( - cause, - &coercion_sites[0..self.pushed], - self.merged_ty(), - expression, - expression_ty, - ), - } + fcx.try_find_coercion_lub( + cause, + &self.expressions, + self.merged_ty(), + expression, + expression_ty, + ) } } else { // this is a hack for cases where we default to `()` because @@ -1591,18 +1560,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { Ok(v) => { self.final_ty = Some(v); if let Some(e) = expression { - match self.expressions { - Expressions::Dynamic(ref mut buffer) => buffer.push(e), - Expressions::UpFront(coercion_sites) => { - // if the user gave us an array to validate, check that we got - // the next expression in the list, as expected - assert_eq!( - coercion_sites[self.pushed].as_coercion_site().hir_id, - e.hir_id - ); - } - } - self.pushed += 1; + self.expressions.push(e); } } Err(coercion_error) => { @@ -1955,45 +1913,12 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } else { // If we only had inputs that were of type `!` (or no // inputs at all), then the final type is `!`. - assert_eq!(self.pushed, 0); + assert!(self.expressions.is_empty()); fcx.tcx.types.never } } } -/// Something that can be converted into an expression to which we can -/// apply a coercion. -pub(crate) trait AsCoercionSite { - fn as_coercion_site(&self) -> &hir::Expr<'_>; -} - -impl AsCoercionSite for hir::Expr<'_> { - fn as_coercion_site(&self) -> &hir::Expr<'_> { - self - } -} - -impl<'a, T> AsCoercionSite for &'a T -where - T: AsCoercionSite, -{ - fn as_coercion_site(&self) -> &hir::Expr<'_> { - (**self).as_coercion_site() - } -} - -impl AsCoercionSite for ! { - fn as_coercion_site(&self) -> &hir::Expr<'_> { - *self - } -} - -impl AsCoercionSite for hir::Arm<'_> { - fn as_coercion_site(&self) -> &hir::Expr<'_> { - self.body - } -} - /// Recursively visit goals to decide whether an unsizing is possible. /// `Break`s when it isn't, and an error should be raised. /// `Continue`s when an unsizing ok based on an implementation of the `Unsize` trait / lang item. diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 48082560bb83b..c4fa39c6c2c84 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -40,7 +40,7 @@ use tracing::{debug, instrument, trace}; use {rustc_ast as ast, rustc_hir as hir}; use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; -use crate::coercion::{CoerceMany, DynamicCoerceMany}; +use crate::coercion::CoerceMany; use crate::errors::{ AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr, BaseExpressionDoubleDotRemove, CantDereference, FieldMultiplySpecifiedInInitializer, @@ -1227,7 +1227,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // (`only_has_type`); otherwise, we just go with a // fresh type variable. let coerce_to_ty = expected.coercion_target_type(self, sp); - let mut coerce: DynamicCoerceMany<'_> = CoerceMany::new(coerce_to_ty); + let mut coerce = CoerceMany::with_capacity(coerce_to_ty, 2); coerce.coerce(self, &self.misc(sp), then_expr, then_ty); @@ -1681,7 +1681,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .to_option(self) .and_then(|uty| self.try_structurally_resolve_type(expr.span, uty).builtin_index()) .unwrap_or_else(|| self.next_ty_var(expr.span)); - let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args); + let mut coerce = CoerceMany::with_capacity(coerce_to, args.len()); for e in args { let e_ty = self.check_expr_with_hint(e, coerce_to); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index d04133ccee976..c07cbfae256dd 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1033,11 +1033,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // break 'a 22; }` would not force the type of the block // to be `()`). let coerce_to_ty = expected.coercion_target_type(self, blk.span); - let coerce = if blk.targeted_by_break { - CoerceMany::new(coerce_to_ty) - } else { - CoerceMany::with_coercion_sites(coerce_to_ty, blk.expr.as_slice()) - }; + let coerce = CoerceMany::new(coerce_to_ty); let prev_diverges = self.diverges.get(); let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false }; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 998c5e6cd25ab..c875e2e50d70c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -25,7 +25,7 @@ use rustc_trait_selection::traits::{ self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, }; -use crate::coercion::DynamicCoerceMany; +use crate::coercion::CoerceMany; use crate::fallback::DivergingFallbackBehavior; use crate::fn_ctxt::checks::DivergingBlockBehavior; use crate::{CoroutineTypes, Diverges, EnclosingBreakables, TypeckRootCtxt}; @@ -56,13 +56,13 @@ pub(crate) struct FnCtxt<'a, 'tcx> { /// expressions. If `None`, this is in a context where return is /// inappropriate, such as a const expression. /// - /// This is a `RefCell`, which means that we + /// This is a `RefCell`, which means that we /// can track all the return expressions and then use them to /// compute a useful coercion from the set, similar to a match /// expression or other branching context. You can use methods /// like `expected_ty` to access the declared return type (if /// any). - pub(super) ret_coercion: Option>>, + pub(super) ret_coercion: Option>>, /// First span of a return site that we find. Used in error messages. pub(super) ret_coercion_span: Cell>, diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 9ca5ddd494aeb..6f9e91bca45a6 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -62,7 +62,7 @@ use tracing::{debug, instrument}; use typeck_root_ctxt::TypeckRootCtxt; use crate::check::check_fn; -use crate::coercion::DynamicCoerceMany; +use crate::coercion::CoerceMany; use crate::diverges::Diverges; use crate::expectation::Expectation; use crate::fn_ctxt::LoweredTy; @@ -350,7 +350,7 @@ pub struct BreakableCtxt<'tcx> { // this is `null` for loops where break with a value is illegal, // such as `while`, `for`, and `while let` - coerce: Option>, + coerce: Option>, } pub struct EnclosingBreakables<'tcx> {