@@ -448,7 +448,7 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
448
448
// expects the types within the function to be consistent.
449
449
err_count_on_creation : usize ,
450
450
451
- ret_ty : Option < Ty < ' tcx > > ,
451
+ ret_coercion : Option < RefCell < CoerceMany < ' gcx , ' tcx > > > ,
452
452
453
453
ps : RefCell < UnsafetyState > ,
454
454
@@ -679,7 +679,7 @@ fn typeck_tables<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
679
679
680
680
check_fn ( & inh, fn_sig, decl, id, body)
681
681
} else {
682
- let fcx = FnCtxt :: new ( & inh, None , body. value . id ) ;
682
+ let fcx = FnCtxt :: new ( & inh, body. value . id ) ;
683
683
let expected_type = tcx. item_type ( def_id) ;
684
684
let expected_type = fcx. normalize_associated_types_in ( body. value . span , & expected_type) ;
685
685
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>,
800
800
801
801
// Create the function context. This is either derived from scratch or,
802
802
// 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 ) ;
805
804
* fcx. ps . borrow_mut ( ) = UnsafetyState :: function ( fn_sig. unsafety , fn_id) ;
806
805
806
+ let ret_ty = fn_sig. output ( ) ;
807
807
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) ) ) ;
809
810
fn_sig = fcx. tcx . mk_fn_sig (
810
811
fn_sig. inputs ( ) . iter ( ) . cloned ( ) ,
811
- fcx . ret_ty . unwrap ( ) ,
812
+ ret_ty,
812
813
fn_sig. variadic ,
813
814
fn_sig. unsafety ,
814
815
fn_sig. abi
@@ -833,7 +834,38 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
833
834
834
835
inherited. tables . borrow_mut ( ) . liberated_fn_sigs . insert ( fn_id, fn_sig) ;
835
836
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) ;
837
869
838
870
fcx
839
871
}
@@ -1429,14 +1461,13 @@ enum TupleArgumentsFlag {
1429
1461
1430
1462
impl < ' a , ' gcx , ' tcx > FnCtxt < ' a , ' gcx , ' tcx > {
1431
1463
pub fn new ( inh : & ' a Inherited < ' a , ' gcx , ' tcx > ,
1432
- rty : Option < Ty < ' tcx > > ,
1433
1464
body_id : ast:: NodeId )
1434
1465
-> FnCtxt < ' a , ' gcx , ' tcx > {
1435
1466
FnCtxt {
1436
1467
ast_ty_to_ty_cache : RefCell :: new ( NodeMap ( ) ) ,
1437
1468
body_id : body_id,
1438
1469
err_count_on_creation : inh. tcx . sess . err_count ( ) ,
1439
- ret_ty : rty ,
1470
+ ret_coercion : None ,
1440
1471
ps : RefCell :: new ( UnsafetyState :: function ( hir:: Unsafety :: Normal ,
1441
1472
ast:: CRATE_NODE_ID ) ) ,
1442
1473
diverges : Cell :: new ( Diverges :: Maybe ) ,
@@ -2738,6 +2769,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
2738
2769
ret_ty
2739
2770
}
2740
2771
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
+
2741
2789
// A generic function for checking the then and else in an if
2742
2790
// or if-else.
2743
2791
fn check_then_else ( & self ,
@@ -3522,24 +3570,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
3522
3570
}
3523
3571
hir:: ExprAgain ( _) => { tcx. types . never }
3524
3572
hir:: ExprRet ( ref expr_opt) => {
3525
- if self . ret_ty . is_none ( ) {
3573
+ if self . ret_coercion . is_none ( ) {
3526
3574
struct_span_err ! ( self . tcx. sess, expr. span, E0572 ,
3527
3575
"return statement outside of function body" ) . emit ( ) ;
3528
3576
} 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 ) ;
3530
3578
} 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) ;
3543
3582
}
3544
3583
tcx. types . never
3545
3584
}
0 commit comments