Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions compiler/rustc_hir_typeck/src/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<T>(
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));
Expand Down
123 changes: 24 additions & 99 deletions compiler/rustc_hir_typeck/src/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<E>(
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!(
Expand Down Expand Up @@ -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 }],
Expand Down Expand Up @@ -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!(
Expand Down Expand Up @@ -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<Ty<'tcx>>,
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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down
6 changes: 1 addition & 5 deletions compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<DynamicCoerceMany>`, which means that we
/// This is a `RefCell<CoerceMany>`, 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<RefCell<DynamicCoerceMany<'tcx>>>,
pub(super) ret_coercion: Option<RefCell<CoerceMany<'tcx>>>,

/// First span of a return site that we find. Used in error messages.
pub(super) ret_coercion_span: Cell<Option<Span>>,
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_typeck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<DynamicCoerceMany<'tcx>>,
coerce: Option<CoerceMany<'tcx>>,
}

pub struct EnclosingBreakables<'tcx> {
Expand Down
Loading