@@ -415,15 +415,16 @@ impl Diverges {
415
415
}
416
416
417
417
#[ derive( Clone ) ]
418
- pub struct BreakableCtxt < ' gcx , ' tcx > {
419
- unified : Ty < ' tcx > ,
420
- coerce_to : Ty < ' tcx > ,
421
- break_exprs : Vec < & ' gcx hir:: Expr > ,
418
+ pub struct BreakableCtxt < ' gcx : ' tcx , ' tcx > {
422
419
may_break : bool ,
420
+
421
+ // this is `null` for loops where break with a value is illegal,
422
+ // such as `while`, `for`, and `while let`
423
+ coerce : Option < CoerceMany < ' gcx , ' tcx > > ,
423
424
}
424
425
425
426
#[ derive( Clone ) ]
426
- pub struct EnclosingBreakables < ' gcx , ' tcx > {
427
+ pub struct EnclosingBreakables < ' gcx : ' tcx , ' tcx > {
427
428
stack : Vec < BreakableCtxt < ' gcx , ' tcx > > ,
428
429
by_id : NodeMap < usize > ,
429
430
}
@@ -3547,60 +3548,66 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
3547
3548
tcx. mk_nil ( )
3548
3549
}
3549
3550
hir:: ExprBreak ( destination, ref expr_opt) => {
3550
- if let Some ( target_id) = destination. target_id . opt_id ( ) {
3551
- let coerce_to = {
3552
- let mut enclosing_breakables = self . enclosing_breakables . borrow_mut ( ) ;
3553
- enclosing_breakables. find_breakable ( target_id) . coerce_to
3554
- } ;
3555
-
3556
- let e_ty;
3557
- let cause;
3558
- if let Some ( ref e) = * expr_opt {
3559
- // Recurse without `enclosing_loops` borrowed.
3560
- e_ty = self . check_expr_with_hint ( e, coerce_to) ;
3561
- cause = self . misc ( e. span ) ;
3562
- // Notably, the recursive call may alter coerce_to - must not keep using it!
3563
- } else {
3564
- // `break` without argument acts like `break ()`.
3565
- e_ty = tcx. mk_nil ( ) ;
3566
- cause = self . misc ( expr. span ) ;
3567
- }
3568
-
3569
- let mut enclosing_breakables = self . enclosing_breakables . borrow_mut ( ) ;
3570
- let ctxt = enclosing_breakables. find_breakable ( target_id) ;
3551
+ if let Some ( target_id) = destination. target_id . opt_id ( ) {
3552
+ let ( e_ty, cause) ;
3553
+ if let Some ( ref e) = * expr_opt {
3554
+ // If this is a break with a value, we need to type-check
3555
+ // the expression. Get an expected type from the loop context.
3556
+ let opt_coerce_to = {
3557
+ let mut enclosing_breakables = self . enclosing_breakables . borrow_mut ( ) ;
3558
+ enclosing_breakables. find_breakable ( target_id)
3559
+ . coerce
3560
+ . as_ref ( )
3561
+ . map ( |coerce| coerce. expected_ty ( ) )
3562
+ } ;
3563
+
3564
+ // If the loop context is not a `loop { }`, then break with
3565
+ // a value is illegal, and `opt_coerce_to` will be `None`.
3566
+ // Just set expectation to error in that case.
3567
+ let coerce_to = opt_coerce_to. unwrap_or ( tcx. types . err ) ;
3568
+
3569
+ // Recurse without `enclosing_breakables` borrowed.
3570
+ e_ty = self . check_expr_with_hint ( e, coerce_to) ;
3571
+ cause = self . misc ( e. span ) ;
3572
+ } else {
3573
+ // Otherwise, this is a break *without* a value. That's
3574
+ // always legal, and is equivalent to `break ()`.
3575
+ e_ty = tcx. mk_nil ( ) ;
3576
+ cause = self . misc ( expr. span ) ;
3577
+ }
3571
3578
3572
- let result = if let Some ( ref e) = * expr_opt {
3573
- // Special-case the first element, as it has no "previous expressions".
3574
- let result = if !ctxt. may_break {
3575
- self . try_coerce ( e, e_ty, ctxt. coerce_to )
3576
- } else {
3577
- self . try_find_coercion_lub ( & cause, || ctxt. break_exprs . iter ( ) . cloned ( ) ,
3578
- ctxt. unified , e, e_ty)
3579
- } ;
3579
+ // Now that we have type-checked `expr_opt`, borrow
3580
+ // the `enclosing_loops` field and let's coerce the
3581
+ // type of `expr_opt` into what is expected.
3582
+ let mut enclosing_breakables = self . enclosing_breakables . borrow_mut ( ) ;
3583
+ let ctxt = enclosing_breakables. find_breakable ( target_id) ;
3584
+ if let Some ( ref mut coerce) = ctxt. coerce {
3585
+ if let Some ( ref e) = * expr_opt {
3586
+ coerce. coerce ( self , & cause, e, e_ty) ;
3587
+ } else {
3588
+ assert ! ( e_ty. is_nil( ) ) ;
3589
+ coerce. coerce_forced_unit ( self , & cause) ;
3590
+ }
3591
+ } else {
3592
+ // If `ctxt.coerce` is `None`, we can just ignore
3593
+ // the type of the expresison. This is because
3594
+ // either this was a break *without* a value, in
3595
+ // which case it is always a legal type (`()`), or
3596
+ // else an error would have been flagged by the
3597
+ // `loops` pass for using break with an expression
3598
+ // where you are not supposed to.
3599
+ assert ! ( expr_opt. is_none( ) || self . tcx. sess. err_count( ) > 0 ) ;
3600
+ }
3580
3601
3581
- ctxt. break_exprs . push ( e) ;
3582
- result
3583
- } else {
3584
- self . eq_types ( true , & cause, e_ty, ctxt. unified )
3585
- . map ( |InferOk { obligations, .. } | {
3586
- // FIXME(#32730) propagate obligations
3587
- assert ! ( obligations. is_empty( ) ) ;
3588
- e_ty
3589
- } )
3590
- } ;
3591
- match result {
3592
- Ok ( ty) => ctxt. unified = ty,
3593
- Err ( err) => {
3594
- self . report_mismatched_types ( & cause, ctxt. unified , e_ty, err) . emit ( ) ;
3595
- }
3596
- }
3602
+ ctxt. may_break = true ;
3603
+ } else {
3604
+ // Otherwise, we failed to find the enclosing loop; this can only happen if the
3605
+ // `break` was not inside a loop at all, which is caught by the loop-checking pass.
3606
+ assert ! ( self . tcx. sess. err_count( ) > 0 ) ;
3607
+ }
3597
3608
3598
- ctxt. may_break = true ;
3599
- }
3600
- // Otherwise, we failed to find the enclosing breakable; this can only happen if the
3601
- // `break` target was not found, which is caught in HIR lowering and reported by the
3602
- // loop-checking pass.
3603
- tcx. types . never
3609
+ // the type of a `break` is always `!`, since it diverges
3610
+ tcx. types . never
3604
3611
}
3605
3612
hir:: ExprAgain ( _) => { tcx. types . never }
3606
3613
hir:: ExprRet ( ref expr_opt) => {
@@ -3645,51 +3652,59 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
3645
3652
expr. span , expected)
3646
3653
}
3647
3654
hir:: ExprWhile ( ref cond, ref body, _) => {
3648
- let unified = self . tcx . mk_nil ( ) ;
3649
- let coerce_to = unified;
3650
- let ctxt = BreakableCtxt {
3651
- unified : unified,
3652
- coerce_to : coerce_to,
3653
- break_exprs : vec ! [ ] ,
3654
- may_break : true ,
3655
- } ;
3656
- self . with_breakable_ctxt ( expr. id , ctxt, || {
3657
- self . check_expr_has_type ( & cond, tcx. types . bool ) ;
3658
- let cond_diverging = self . diverges . get ( ) ;
3659
- self . check_block_no_value ( & body) ;
3655
+ let ctxt = BreakableCtxt {
3656
+ // cannot use break with a value from a while loop
3657
+ coerce : None ,
3658
+ may_break : true ,
3659
+ } ;
3660
3660
3661
- // We may never reach the body so it diverging means nothing.
3662
- self . diverges . set ( cond_diverging) ;
3663
- } ) ;
3661
+ self . with_breakable_ctxt ( expr. id , ctxt, || {
3662
+ self . check_expr_has_type ( & cond, tcx. types . bool ) ;
3663
+ let cond_diverging = self . diverges . get ( ) ;
3664
+ self . check_block_no_value ( & body) ;
3664
3665
3665
- if self . has_errors . get ( ) {
3666
- tcx . types . err
3667
- } else {
3668
- tcx . mk_nil ( )
3669
- }
3666
+ // We may never reach the body so it diverging means nothing.
3667
+ self . diverges . set ( cond_diverging ) ;
3668
+ } ) ;
3669
+
3670
+ self . tcx . mk_nil ( )
3670
3671
}
3671
- hir:: ExprLoop ( ref body, _, _) => {
3672
- let unified = self . next_ty_var ( TypeVariableOrigin :: TypeInference ( body. span ) ) ;
3673
- let coerce_to = expected. only_has_type ( self ) . unwrap_or ( unified) ;
3674
- let ctxt = BreakableCtxt {
3675
- unified : unified,
3676
- coerce_to : coerce_to,
3677
- break_exprs : vec ! [ ] ,
3678
- may_break : false ,
3679
- } ;
3672
+ hir:: ExprLoop ( ref body, _, source) => {
3673
+ let coerce = match source {
3674
+ // you can only use break with a value from a normal `loop { }`
3675
+ hir:: LoopSource :: Loop => {
3676
+ let coerce_to = expected. only_has_type_or_fresh_var ( self , body. span ) ;
3677
+ Some ( CoerceMany :: new ( coerce_to) )
3678
+ }
3680
3679
3681
- let ( ctxt, ( ) ) = self . with_breakable_ctxt ( expr. id , ctxt, || {
3682
- self . check_block_no_value ( & body) ;
3683
- } ) ;
3684
- if ctxt. may_break {
3685
- // No way to know whether it's diverging because
3686
- // of a `break` or an outer `break` or `return.
3687
- self . diverges . set ( Diverges :: Maybe ) ;
3680
+ hir:: LoopSource :: WhileLet |
3681
+ hir:: LoopSource :: ForLoop => {
3682
+ None
3683
+ }
3684
+ } ;
3688
3685
3689
- ctxt. unified
3690
- } else {
3691
- tcx. types . never
3692
- }
3686
+ let ctxt = BreakableCtxt {
3687
+ coerce : coerce,
3688
+ may_break : false , // will get updated if/when we find a `break`
3689
+ } ;
3690
+
3691
+ let ( ctxt, ( ) ) = self . with_breakable_ctxt ( expr. id , ctxt, || {
3692
+ self . check_block_no_value ( & body) ;
3693
+ } ) ;
3694
+
3695
+ if ctxt. may_break {
3696
+ // No way to know whether it's diverging because
3697
+ // of a `break` or an outer `break` or `return.
3698
+ self . diverges . set ( Diverges :: Maybe ) ;
3699
+ }
3700
+
3701
+ // If we permit break with a value, then result type is
3702
+ // the LUB of the breaks (possibly ! if none); else, it
3703
+ // is nil. This makes sense because infinite loops
3704
+ // (which would have type !) are only possible iff we
3705
+ // permit break with a value [1].
3706
+ assert ! ( ctxt. coerce. is_some( ) || ctxt. may_break) ; // [1]
3707
+ ctxt. coerce . map ( |c| c. complete ( self ) ) . unwrap_or ( self . tcx . mk_nil ( ) )
3693
3708
}
3694
3709
hir:: ExprMatch ( ref discrim, ref arms, match_src) => {
3695
3710
self . check_match ( expr, & discrim, arms, expected, match_src)
0 commit comments