Skip to content

Commit a6e6be5

Browse files
committed
port return expressions to use CoerceMany
This slightly affects the error messages in one particular compile-fail test.
1 parent b725272 commit a6e6be5

File tree

4 files changed

+66
-33
lines changed

4 files changed

+66
-33
lines changed

src/librustc_typeck/check/compare_method.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
362362
&infcx.parameter_environment.caller_bounds);
363363
infcx.resolve_regions_and_report_errors(&free_regions, impl_m_body_id);
364364
} else {
365-
let fcx = FnCtxt::new(&inh, Some(tcx.types.err), impl_m_body_id);
365+
let fcx = FnCtxt::new(&inh, impl_m_body_id);
366366
fcx.regionck_item(impl_m_body_id, impl_m_span, &[]);
367367
}
368368

src/librustc_typeck/check/mod.rs

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
448448
// expects the types within the function to be consistent.
449449
err_count_on_creation: usize,
450450

451-
ret_ty: Option<Ty<'tcx>>,
451+
ret_coercion: Option<RefCell<CoerceMany<'gcx, 'tcx>>>,
452452

453453
ps: RefCell<UnsafetyState>,
454454

@@ -679,7 +679,7 @@ fn typeck_tables<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
679679

680680
check_fn(&inh, fn_sig, decl, id, body)
681681
} else {
682-
let fcx = FnCtxt::new(&inh, None, body.value.id);
682+
let fcx = FnCtxt::new(&inh, body.value.id);
683683
let expected_type = tcx.item_type(def_id);
684684
let expected_type = fcx.normalize_associated_types_in(body.value.span, &expected_type);
685685
fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized);
@@ -800,15 +800,16 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
800800

801801
// Create the function context. This is either derived from scratch or,
802802
// in the case of function expressions, based on the outer context.
803-
let mut fcx = FnCtxt::new(inherited, None, body.value.id);
804-
let ret_ty = fn_sig.output();
803+
let mut fcx = FnCtxt::new(inherited, body.value.id);
805804
*fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id);
806805

806+
let ret_ty = fn_sig.output();
807807
fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::ReturnType);
808-
fcx.ret_ty = fcx.instantiate_anon_types(&Some(ret_ty));
808+
let ret_ty = fcx.instantiate_anon_types(&ret_ty);
809+
fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
809810
fn_sig = fcx.tcx.mk_fn_sig(
810811
fn_sig.inputs().iter().cloned(),
811-
fcx.ret_ty.unwrap(),
812+
ret_ty,
812813
fn_sig.variadic,
813814
fn_sig.unsafety,
814815
fn_sig.abi
@@ -833,7 +834,38 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
833834

834835
inherited.tables.borrow_mut().liberated_fn_sigs.insert(fn_id, fn_sig);
835836

836-
fcx.check_expr_coercable_to_type(&body.value, fcx.ret_ty.unwrap());
837+
fcx.check_return_expr(&body.value);
838+
839+
// Finalize the return check by taking the LUB of the return types
840+
// we saw and assigning it to the expected return type. This isn't
841+
// really expected to fail, since the coercions would have failed
842+
// earlier when trying to find a LUB.
843+
//
844+
// However, the behavior around `!` is sort of complex. In the
845+
// event that the `actual_return_ty` comes back as `!`, that
846+
// indicates that the fn either does not return or "returns" only
847+
// values of type `!`. In this case, if there is an expected
848+
// return type that is *not* `!`, that should be ok. But if the
849+
// return type is being inferred, we want to "fallback" to `!`:
850+
//
851+
// let x = move || panic!();
852+
//
853+
// To allow for that, I am creating a type variable with diverging
854+
// fallback. This was deemed ever so slightly better than unifying
855+
// the return value with `!` because it allows for the caller to
856+
// make more assumptions about the return type (e.g., they could do
857+
//
858+
// let y: Option<u32> = Some(x());
859+
//
860+
// which would then cause this return type to become `u32`, not
861+
// `!`).
862+
let coercion = fcx.ret_coercion.take().unwrap().into_inner();
863+
let mut actual_return_ty = coercion.complete(&fcx);
864+
if actual_return_ty.is_never() {
865+
actual_return_ty = fcx.next_diverging_ty_var(
866+
TypeVariableOrigin::DivergingFn(body.value.span));
867+
}
868+
fcx.demand_suptype(body.value.span, ret_ty, actual_return_ty);
837869

838870
fcx
839871
}
@@ -1429,14 +1461,13 @@ enum TupleArgumentsFlag {
14291461

14301462
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
14311463
pub fn new(inh: &'a Inherited<'a, 'gcx, 'tcx>,
1432-
rty: Option<Ty<'tcx>>,
14331464
body_id: ast::NodeId)
14341465
-> FnCtxt<'a, 'gcx, 'tcx> {
14351466
FnCtxt {
14361467
ast_ty_to_ty_cache: RefCell::new(NodeMap()),
14371468
body_id: body_id,
14381469
err_count_on_creation: inh.tcx.sess.err_count(),
1439-
ret_ty: rty,
1470+
ret_coercion: None,
14401471
ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal,
14411472
ast::CRATE_NODE_ID)),
14421473
diverges: Cell::new(Diverges::Maybe),
@@ -2738,6 +2769,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
27382769
ret_ty
27392770
}
27402771

2772+
fn check_return_expr(&self, return_expr: &'gcx hir::Expr) {
2773+
let ret_coercion =
2774+
self.ret_coercion
2775+
.as_ref()
2776+
.unwrap_or_else(|| span_bug!(return_expr.span,
2777+
"check_return_expr called outside fn body"));
2778+
2779+
let ret_ty = ret_coercion.borrow().expected_ty();
2780+
let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty);
2781+
ret_coercion.borrow_mut()
2782+
.coerce(self,
2783+
&self.misc(return_expr.span),
2784+
return_expr,
2785+
return_expr_ty);
2786+
}
2787+
2788+
27412789
// A generic function for checking the then and else in an if
27422790
// or if-else.
27432791
fn check_then_else(&self,
@@ -3522,24 +3570,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
35223570
}
35233571
hir::ExprAgain(_) => { tcx.types.never }
35243572
hir::ExprRet(ref expr_opt) => {
3525-
if self.ret_ty.is_none() {
3573+
if self.ret_coercion.is_none() {
35263574
struct_span_err!(self.tcx.sess, expr.span, E0572,
35273575
"return statement outside of function body").emit();
35283576
} else if let Some(ref e) = *expr_opt {
3529-
self.check_expr_coercable_to_type(&e, self.ret_ty.unwrap());
3577+
self.check_return_expr(e);
35303578
} else {
3531-
match self.eq_types(false,
3532-
&self.misc(expr.span),
3533-
self.ret_ty.unwrap(),
3534-
tcx.mk_nil()) {
3535-
Ok(ok) => self.register_infer_ok_obligations(ok),
3536-
Err(_) => {
3537-
struct_span_err!(tcx.sess, expr.span, E0069,
3538-
"`return;` in a function whose return type is not `()`")
3539-
.span_label(expr.span, &format!("return type is not ()"))
3540-
.emit();
3541-
}
3542-
}
3579+
let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut();
3580+
let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression);
3581+
coercion.coerce_forced_unit(self, &cause);
35433582
}
35443583
tcx.types.never
35453584
}

src/librustc_typeck/check/wfcheck.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ impl<'a, 'gcx, 'tcx> CheckWfFcxBuilder<'a, 'gcx, 'tcx> {
5050
let id = self.id;
5151
let span = self.span;
5252
self.inherited.enter(|inh| {
53-
let fcx = FnCtxt::new(&inh, None, id);
53+
let fcx = FnCtxt::new(&inh, id);
5454
let wf_tys = f(&fcx, &mut CheckTypeWellFormedVisitor {
5555
tcx: fcx.tcx.global_tcx(),
5656
code: code

src/test/compile-fail/regions-bounds.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,11 @@ struct an_enum<'a>(&'a isize);
1616
struct a_class<'a> { x:&'a isize }
1717

1818
fn a_fn1<'a,'b>(e: an_enum<'a>) -> an_enum<'b> {
19-
return e; //~ ERROR mismatched types
20-
//~| expected type `an_enum<'b>`
21-
//~| found type `an_enum<'a>`
22-
//~| lifetime mismatch
19+
return e; //~^ ERROR mismatched types
2320
}
2421

2522
fn a_fn3<'a,'b>(e: a_class<'a>) -> a_class<'b> {
26-
return e; //~ ERROR mismatched types
27-
//~| expected type `a_class<'b>`
28-
//~| found type `a_class<'a>`
29-
//~| lifetime mismatch
23+
return e; //~^ ERROR mismatched types
3024
}
3125

3226
fn main() { }

0 commit comments

Comments
 (0)