Skip to content

Commit dad3140

Browse files
committed
introduce (but do not yet use) a CoerceMany API
The existing pattern for coercions is fairly complex. `CoerceMany` enapsulates it better into a helper.
1 parent eeb6447 commit dad3140

File tree

1 file changed

+227
-0
lines changed

1 file changed

+227
-0
lines changed

src/librustc_typeck/check/coercion.rs

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,3 +837,230 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
837837
}
838838
}
839839
}
840+
841+
/// CoerceMany encapsulates the pattern you should use when you have
842+
/// many expressions that are all getting coerced to a common
843+
/// type. This arises, for example, when you have a match (the result
844+
/// of each arm is coerced to a common type). It also arises in less
845+
/// obvious places, such as when you have many `break foo` expressions
846+
/// that target the same loop, or the various `return` expressions in
847+
/// a function.
848+
///
849+
/// The basic protocol is as follows:
850+
///
851+
/// - Instantiate the `CoerceMany` with an initial `expected_ty`.
852+
/// This will also serve as the "starting LUB". The expectation is
853+
/// that this type is something which all of the expressions *must*
854+
/// be coercible to. Use a fresh type variable if needed.
855+
/// - For each expression whose result is to be coerced, invoke `coerce()` with.
856+
/// - In some cases we wish to coerce "non-expressions" whose types are implicitly
857+
/// unit. This happens for example if you have a `break` with no expression,
858+
/// or an `if` with no `else`. In that case, invoke `coerce_forced_unit()`.
859+
/// - `coerce()` and `coerce_forced_unit()` may report errors. They hide this
860+
/// from you so that you don't have to worry your pretty head about it.
861+
/// But if an error is reported, the final type will be `err`.
862+
/// - Invoking `coerce()` may cause us to go and adjust the "adjustments" on
863+
/// previously coerced expressions.
864+
/// - When all done, invoke `complete()`. This will return the LUB of
865+
/// all your expressions.
866+
/// - WARNING: I don't believe this final type is guaranteed to be
867+
/// related to your initial `expected_ty` in any particular way,
868+
/// although it will typically be a subtype, so you should check it.
869+
/// - Invoking `complete()` may cause us to go and adjust the "adjustments" on
870+
/// previously coerced expressions.
871+
///
872+
/// Example:
873+
///
874+
/// ```
875+
/// let mut coerce = CoerceMany::new(expected_ty);
876+
/// for expr in exprs {
877+
/// let expr_ty = fcx.check_expr_with_expectation(expr, expected);
878+
/// coerce.coerce(fcx, &cause, expr, expr_ty);
879+
/// }
880+
/// let final_ty = coerce.complete(fcx);
881+
/// ```
882+
#[derive(Clone)] // (*)
883+
pub struct CoerceMany<'gcx: 'tcx, 'tcx> {
884+
expected_ty: Ty<'tcx>,
885+
final_ty: Option<Ty<'tcx>>,
886+
expressions: Vec<&'gcx hir::Expr>,
887+
}
888+
889+
// (*) this is clone because `FnCtxt` is clone, but it seems dubious -- nmatsakis
890+
891+
impl<'gcx, 'tcx> CoerceMany<'gcx, 'tcx> {
892+
pub fn new(expected_ty: Ty<'tcx>) -> Self {
893+
CoerceMany {
894+
expected_ty,
895+
final_ty: None,
896+
expressions: vec![],
897+
}
898+
}
899+
900+
pub fn is_empty(&self) -> bool {
901+
self.expressions.is_empty()
902+
}
903+
904+
/// Return the "expected type" with which this coercion was
905+
/// constructed. This represents the "downward propagated" type
906+
/// that was given to us at the start of typing whatever construct
907+
/// we are typing (e.g., the match expression).
908+
///
909+
/// Typically, this is used as the expected type when
910+
/// type-checking each of the alternative expressions whose types
911+
/// we are trying to merge.
912+
pub fn expected_ty(&self) -> Ty<'tcx> {
913+
self.expected_ty
914+
}
915+
916+
/// Returns the current "merged type", representing our best-guess
917+
/// at the LUB of the expressions we've seen so far (if any). This
918+
/// isn't *final* until you call `self.final()`, which will return
919+
/// the merged type.
920+
pub fn merged_ty(&self) -> Ty<'tcx> {
921+
self.final_ty.unwrap_or(self.expected_ty)
922+
}
923+
924+
/// Indicates that the value generated by `expression`, which is
925+
/// of type `expression_ty`, is one of the possibility that we
926+
/// could coerce from. This will record `expression` and later
927+
/// calls to `coerce` may come back and add adjustments and things
928+
/// if necessary.
929+
pub fn coerce<'a>(&mut self,
930+
fcx: &FnCtxt<'a, 'gcx, 'tcx>,
931+
cause: &ObligationCause<'tcx>,
932+
expression: &'gcx hir::Expr,
933+
expression_ty: Ty<'tcx>)
934+
{
935+
self.coerce_inner(fcx, cause, Some(expression), expression_ty)
936+
}
937+
938+
/// Indicates that one of the inputs is a "forced unit". This
939+
/// occurs in a case like `if foo { ... };`, where the issing else
940+
/// generates a "forced unit". Another example is a `loop { break;
941+
/// }`, where the `break` has no argument expression. We treat
942+
/// these cases slightly differently for error-reporting
943+
/// purposes. Note that these tend to correspond to cases where
944+
/// the `()` expression is implicit in the source, and hence we do
945+
/// not take an expression argument.
946+
pub fn coerce_forced_unit<'a>(&mut self,
947+
fcx: &FnCtxt<'a, 'gcx, 'tcx>,
948+
cause: &ObligationCause<'tcx>)
949+
{
950+
self.coerce_inner(fcx,
951+
cause,
952+
None,
953+
fcx.tcx.mk_nil())
954+
}
955+
956+
/// The inner coercion "engine". If `expression` is `None`, this
957+
/// is a forced-unit case, and hence `expression_ty` must be
958+
/// `Nil`.
959+
fn coerce_inner<'a>(&mut self,
960+
fcx: &FnCtxt<'a, 'gcx, 'tcx>,
961+
cause: &ObligationCause<'tcx>,
962+
expression: Option<&'gcx hir::Expr>,
963+
mut expression_ty: Ty<'tcx>)
964+
{
965+
// Incorporate whatever type inference information we have
966+
// until now; in principle we might also want to process
967+
// pending obligations, but doing so should only improve
968+
// compatibility (hopefully that is true) by helping us
969+
// uncover never types better.
970+
if expression_ty.is_ty_var() {
971+
expression_ty = fcx.infcx.shallow_resolve(expression_ty);
972+
}
973+
974+
// If we see any error types, just propagate that error
975+
// upwards.
976+
if expression_ty.references_error() || self.merged_ty().references_error() {
977+
self.final_ty = Some(fcx.tcx.types.err);
978+
return;
979+
}
980+
981+
// Handle the actual type unification etc.
982+
let result = if let Some(expression) = expression {
983+
if self.expressions.is_empty() {
984+
// Special-case the first expression we are coercing.
985+
// To be honest, I'm not entirely sure why we do this.
986+
fcx.try_coerce(expression, expression_ty, self.expected_ty)
987+
} else {
988+
fcx.try_find_coercion_lub(cause,
989+
|| self.expressions.iter().cloned(),
990+
self.merged_ty(),
991+
expression,
992+
expression_ty)
993+
}
994+
} else {
995+
// this is a hack for cases where we default to `()` because
996+
// the expression etc has been omitted from the source. An
997+
// example is an `if let` without an else:
998+
//
999+
// if let Some(x) = ... { }
1000+
//
1001+
// we wind up with a second match arm that is like `_ =>
1002+
// ()`. That is the case we are considering here. We take
1003+
// a different path to get the right "expected, found"
1004+
// message and so forth (and because we know that
1005+
// `expression_ty` will be unit).
1006+
//
1007+
// Another example is `break` with no argument expression.
1008+
assert!(expression_ty.is_nil());
1009+
assert!(expression_ty.is_nil(), "if let hack without unit type");
1010+
fcx.eq_types(true, cause, expression_ty, self.merged_ty())
1011+
.map(|infer_ok| {
1012+
fcx.register_infer_ok_obligations(infer_ok);
1013+
expression_ty
1014+
})
1015+
};
1016+
1017+
match result {
1018+
Ok(v) => {
1019+
self.final_ty = Some(v);
1020+
self.expressions.extend(expression);
1021+
}
1022+
Err(err) => {
1023+
let (expected, found) = if expression.is_none() {
1024+
// In the case where this is a "forced unit", like
1025+
// `break`, we want to call the `()` "expected"
1026+
// since it is implied by the syntax.
1027+
assert!(expression_ty.is_nil());
1028+
(expression_ty, self.final_ty.unwrap_or(self.expected_ty))
1029+
} else {
1030+
// Otherwise, the "expected" type for error
1031+
// reporting is the current unification type,
1032+
// which is basically the LUB of the expressions
1033+
// we've seen so far (combined with the expected
1034+
// type)
1035+
(self.final_ty.unwrap_or(self.expected_ty), expression_ty)
1036+
};
1037+
1038+
match cause.code {
1039+
ObligationCauseCode::ReturnNoExpression => {
1040+
struct_span_err!(fcx.tcx.sess, cause.span, E0069,
1041+
"`return;` in a function whose return type is not `()`")
1042+
.span_label(cause.span, &format!("return type is not ()"))
1043+
.emit();
1044+
}
1045+
_ => {
1046+
fcx.report_mismatched_types(cause, expected, found, err)
1047+
.emit();
1048+
}
1049+
}
1050+
1051+
self.final_ty = Some(fcx.tcx.types.err);
1052+
}
1053+
}
1054+
}
1055+
1056+
pub fn complete<'a>(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> {
1057+
if let Some(final_ty) = self.final_ty {
1058+
final_ty
1059+
} else {
1060+
// If we only had inputs that were of type `!` (or no
1061+
// inputs at all), then the final type is `!`.
1062+
assert!(self.expressions.is_empty());
1063+
fcx.tcx.types.never
1064+
}
1065+
}
1066+
}

0 commit comments

Comments
 (0)