diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index c43291557f7..40ae169a94e 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -837,3 +837,230 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } } + +/// CoerceMany encapsulates the pattern you should use when you have +/// many expressions that are all getting coerced to a common +/// type. This arises, for example, when you have a match (the result +/// of each arm is coerced to a common type). It also arises in less +/// obvious places, such as when you have many `break foo` expressions +/// that target the same loop, or the various `return` expressions in +/// a function. +/// +/// The basic protocol is as follows: +/// +/// - Instantiate the `CoerceMany` with an initial `expected_ty`. +/// This will also serve as the "starting LUB". The expectation is +/// that this type is something which all of the expressions *must* +/// be coercible to. Use a fresh type variable if needed. +/// - For each expression whose result is to be coerced, invoke `coerce()` with. +/// - In some cases we wish to coerce "non-expressions" whose types are implicitly +/// unit. This happens for example if you have a `break` with no expression, +/// or an `if` with no `else`. In that case, invoke `coerce_forced_unit()`. +/// - `coerce()` and `coerce_forced_unit()` may report errors. They hide this +/// from you so that you don't have to worry your pretty head about it. +/// But if an error is reported, the final type will be `err`. +/// - Invoking `coerce()` may cause us to go and adjust the "adjustments" on +/// previously coerced expressions. +/// - When all done, invoke `complete()`. This will return the LUB of +/// all your expressions. +/// - WARNING: I don't believe this final type is guaranteed to be +/// related to your initial `expected_ty` in any particular way, +/// although it will typically be a subtype, so you should check it. +/// - Invoking `complete()` may cause us to go and adjust the "adjustments" on +/// previously coerced expressions. +/// +/// Example: +/// +/// ``` +/// let mut coerce = CoerceMany::new(expected_ty); +/// for expr in exprs { +/// let expr_ty = fcx.check_expr_with_expectation(expr, expected); +/// coerce.coerce(fcx, &cause, expr, expr_ty); +/// } +/// let final_ty = coerce.complete(fcx); +/// ``` +#[derive(Clone)] // (*) +pub struct CoerceMany<'gcx: 'tcx, 'tcx> { + expected_ty: Ty<'tcx>, + final_ty: Option>, + expressions: Vec<&'gcx hir::Expr>, +} + +// (*) this is clone because `FnCtxt` is clone, but it seems dubious -- nmatsakis + +impl<'gcx, 'tcx> CoerceMany<'gcx, 'tcx> { + pub fn new(expected_ty: Ty<'tcx>) -> Self { + CoerceMany { + expected_ty, + final_ty: None, + expressions: vec![], + } + } + + pub fn is_empty(&self) -> bool { + self.expressions.is_empty() + } + + /// Return 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 fn expected_ty(&self) -> Ty<'tcx> { + self.expected_ty + } + + /// 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.final()`, which will return + /// the merged type. + pub fn merged_ty(&self) -> Ty<'tcx> { + self.final_ty.unwrap_or(self.expected_ty) + } + + /// Indicates that the value generated by `expression`, which is + /// of type `expression_ty`, is one of the possibility that we + /// could coerce from. This will record `expression` and later + /// calls to `coerce` may come back and add adjustments and things + /// if necessary. + pub fn coerce<'a>(&mut self, + fcx: &FnCtxt<'a, 'gcx, 'tcx>, + cause: &ObligationCause<'tcx>, + expression: &'gcx hir::Expr, + expression_ty: Ty<'tcx>) + { + self.coerce_inner(fcx, cause, Some(expression), expression_ty) + } + + /// Indicates that one of the inputs is a "forced unit". This + /// occurs in a case like `if foo { ... };`, where the issing else + /// generates a "forced unit". Another example is a `loop { break; + /// }`, where the `break` has no argument expression. We treat + /// these cases slightly differently for error-reporting + /// purposes. Note that these tend to correspond to cases where + /// the `()` expression is implicit in the source, and hence we do + /// not take an expression argument. + pub fn coerce_forced_unit<'a>(&mut self, + fcx: &FnCtxt<'a, 'gcx, 'tcx>, + cause: &ObligationCause<'tcx>) + { + self.coerce_inner(fcx, + cause, + None, + fcx.tcx.mk_nil()) + } + + /// The inner coercion "engine". If `expression` is `None`, this + /// is a forced-unit case, and hence `expression_ty` must be + /// `Nil`. + fn coerce_inner<'a>(&mut self, + fcx: &FnCtxt<'a, 'gcx, 'tcx>, + cause: &ObligationCause<'tcx>, + expression: Option<&'gcx hir::Expr>, + mut expression_ty: Ty<'tcx>) + { + // Incorporate whatever type inference information we have + // until now; in principle we might also want to process + // pending obligations, but doing so should only improve + // compatibility (hopefully that is true) by helping us + // uncover never types better. + if expression_ty.is_ty_var() { + expression_ty = fcx.infcx.shallow_resolve(expression_ty); + } + + // If we see any error types, just propagate that error + // upwards. + if expression_ty.references_error() || self.merged_ty().references_error() { + self.final_ty = Some(fcx.tcx.types.err); + return; + } + + // Handle the actual type unification etc. + let result = if let Some(expression) = expression { + 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. + fcx.try_coerce(expression, expression_ty, self.expected_ty) + } else { + fcx.try_find_coercion_lub(cause, + || self.expressions.iter().cloned(), + self.merged_ty(), + expression, + expression_ty) + } + } else { + // this is a hack for cases where we default to `()` because + // the expression etc has been omitted from the source. An + // example is an `if let` without an else: + // + // if let Some(x) = ... { } + // + // we wind up with a second match arm that is like `_ => + // ()`. That is the case we are considering here. We take + // a different path to get the right "expected, found" + // message and so forth (and because we know that + // `expression_ty` will be unit). + // + // Another example is `break` with no argument expression. + assert!(expression_ty.is_nil()); + assert!(expression_ty.is_nil(), "if let hack without unit type"); + fcx.eq_types(true, cause, expression_ty, self.merged_ty()) + .map(|infer_ok| { + fcx.register_infer_ok_obligations(infer_ok); + expression_ty + }) + }; + + match result { + Ok(v) => { + self.final_ty = Some(v); + self.expressions.extend(expression); + } + Err(err) => { + let (expected, found) = if expression.is_none() { + // In the case where this is a "forced unit", like + // `break`, we want to call the `()` "expected" + // since it is implied by the syntax. + assert!(expression_ty.is_nil()); + (expression_ty, self.final_ty.unwrap_or(self.expected_ty)) + } else { + // Otherwise, the "expected" type for error + // reporting is the current unification type, + // which is basically the LUB of the expressions + // we've seen so far (combined with the expected + // type) + (self.final_ty.unwrap_or(self.expected_ty), expression_ty) + }; + + match cause.code { + ObligationCauseCode::ReturnNoExpression => { + struct_span_err!(fcx.tcx.sess, cause.span, E0069, + "`return;` in a function whose return type is not `()`") + .span_label(cause.span, &format!("return type is not ()")) + .emit(); + } + _ => { + fcx.report_mismatched_types(cause, expected, found, err) + .emit(); + } + } + + self.final_ty = Some(fcx.tcx.types.err); + } + } + } + + pub fn complete<'a>(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { + if let Some(final_ty) = self.final_ty { + final_ty + } else { + // If we only had inputs that were of type `!` (or no + // inputs at all), then the final type is `!`. + assert!(self.expressions.is_empty()); + fcx.tcx.types.never + } + } +}