Skip to content

Commit 74dd65e

Browse files
committed
typeck/expr.rs: extract out check_expr_break.
1 parent 9131f95 commit 74dd65e

File tree

2 files changed

+93
-83
lines changed

2 files changed

+93
-83
lines changed

src/librustc_typeck/check/expr.rs

Lines changed: 92 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -76,89 +76,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7676
tcx.mk_unit()
7777
}
7878
ExprKind::Break(destination, ref expr_opt) => {
79-
if let Ok(target_id) = destination.target_id {
80-
let (e_ty, cause);
81-
if let Some(ref e) = *expr_opt {
82-
// If this is a break with a value, we need to type-check
83-
// the expression. Get an expected type from the loop context.
84-
let opt_coerce_to = {
85-
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
86-
enclosing_breakables.find_breakable(target_id)
87-
.coerce
88-
.as_ref()
89-
.map(|coerce| coerce.expected_ty())
90-
};
91-
92-
// If the loop context is not a `loop { }`, then break with
93-
// a value is illegal, and `opt_coerce_to` will be `None`.
94-
// Just set expectation to error in that case.
95-
let coerce_to = opt_coerce_to.unwrap_or(tcx.types.err);
96-
97-
// Recurse without `enclosing_breakables` borrowed.
98-
e_ty = self.check_expr_with_hint(e, coerce_to);
99-
cause = self.misc(e.span);
100-
} else {
101-
// Otherwise, this is a break *without* a value. That's
102-
// always legal, and is equivalent to `break ()`.
103-
e_ty = tcx.mk_unit();
104-
cause = self.misc(expr.span);
105-
}
106-
107-
// Now that we have type-checked `expr_opt`, borrow
108-
// the `enclosing_loops` field and let's coerce the
109-
// type of `expr_opt` into what is expected.
110-
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
111-
let ctxt = enclosing_breakables.find_breakable(target_id);
112-
if let Some(ref mut coerce) = ctxt.coerce {
113-
if let Some(ref e) = *expr_opt {
114-
coerce.coerce(self, &cause, e, e_ty);
115-
} else {
116-
assert!(e_ty.is_unit());
117-
coerce.coerce_forced_unit(self, &cause, &mut |_| (), true);
118-
}
119-
} else {
120-
// If `ctxt.coerce` is `None`, we can just ignore
121-
// the type of the expresison. This is because
122-
// either this was a break *without* a value, in
123-
// which case it is always a legal type (`()`), or
124-
// else an error would have been flagged by the
125-
// `loops` pass for using break with an expression
126-
// where you are not supposed to.
127-
assert!(expr_opt.is_none() || self.tcx.sess.err_count() > 0);
128-
}
129-
130-
ctxt.may_break = true;
131-
132-
// the type of a `break` is always `!`, since it diverges
133-
tcx.types.never
134-
} else {
135-
// Otherwise, we failed to find the enclosing loop;
136-
// this can only happen if the `break` was not
137-
// inside a loop at all, which is caught by the
138-
// loop-checking pass.
139-
if self.tcx.sess.err_count() == 0 {
140-
self.tcx.sess.delay_span_bug(expr.span,
141-
"break was outside loop, but no error was emitted");
142-
}
143-
144-
// We still need to assign a type to the inner expression to
145-
// prevent the ICE in #43162.
146-
if let Some(ref e) = *expr_opt {
147-
self.check_expr_with_hint(e, tcx.types.err);
148-
149-
// ... except when we try to 'break rust;'.
150-
// ICE this expression in particular (see #43162).
151-
if let ExprKind::Path(QPath::Resolved(_, ref path)) = e.node {
152-
if path.segments.len() == 1 &&
153-
path.segments[0].ident.name == sym::rust {
154-
fatally_break_rust(self.tcx.sess);
155-
}
156-
}
157-
}
158-
// There was an error; make type-check fail.
159-
tcx.types.err
160-
}
161-
79+
self.check_expr_break(destination, expr_opt.deref(), expr)
16280
}
16381
ExprKind::Continue(destination) => {
16482
if destination.target_id.is_ok() {
@@ -725,4 +643,95 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
725643

726644
ty
727645
}
646+
647+
fn check_expr_break(
648+
&self,
649+
destination: hir::Destination,
650+
expr_opt: Option<&'tcx hir::Expr>,
651+
expr: &'tcx hir::Expr,
652+
) -> Ty<'tcx> {
653+
let tcx = self.tcx;
654+
if let Ok(target_id) = destination.target_id {
655+
let (e_ty, cause);
656+
if let Some(ref e) = expr_opt {
657+
// If this is a break with a value, we need to type-check
658+
// the expression. Get an expected type from the loop context.
659+
let opt_coerce_to = {
660+
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
661+
enclosing_breakables.find_breakable(target_id)
662+
.coerce
663+
.as_ref()
664+
.map(|coerce| coerce.expected_ty())
665+
};
666+
667+
// If the loop context is not a `loop { }`, then break with
668+
// a value is illegal, and `opt_coerce_to` will be `None`.
669+
// Just set expectation to error in that case.
670+
let coerce_to = opt_coerce_to.unwrap_or(tcx.types.err);
671+
672+
// Recurse without `enclosing_breakables` borrowed.
673+
e_ty = self.check_expr_with_hint(e, coerce_to);
674+
cause = self.misc(e.span);
675+
} else {
676+
// Otherwise, this is a break *without* a value. That's
677+
// always legal, and is equivalent to `break ()`.
678+
e_ty = tcx.mk_unit();
679+
cause = self.misc(expr.span);
680+
}
681+
682+
// Now that we have type-checked `expr_opt`, borrow
683+
// the `enclosing_loops` field and let's coerce the
684+
// type of `expr_opt` into what is expected.
685+
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
686+
let ctxt = enclosing_breakables.find_breakable(target_id);
687+
if let Some(ref mut coerce) = ctxt.coerce {
688+
if let Some(ref e) = expr_opt {
689+
coerce.coerce(self, &cause, e, e_ty);
690+
} else {
691+
assert!(e_ty.is_unit());
692+
coerce.coerce_forced_unit(self, &cause, &mut |_| (), true);
693+
}
694+
} else {
695+
// If `ctxt.coerce` is `None`, we can just ignore
696+
// the type of the expresison. This is because
697+
// either this was a break *without* a value, in
698+
// which case it is always a legal type (`()`), or
699+
// else an error would have been flagged by the
700+
// `loops` pass for using break with an expression
701+
// where you are not supposed to.
702+
assert!(expr_opt.is_none() || self.tcx.sess.err_count() > 0);
703+
}
704+
705+
ctxt.may_break = true;
706+
707+
// the type of a `break` is always `!`, since it diverges
708+
tcx.types.never
709+
} else {
710+
// Otherwise, we failed to find the enclosing loop;
711+
// this can only happen if the `break` was not
712+
// inside a loop at all, which is caught by the
713+
// loop-checking pass.
714+
if self.tcx.sess.err_count() == 0 {
715+
self.tcx.sess.delay_span_bug(expr.span,
716+
"break was outside loop, but no error was emitted");
717+
}
718+
719+
// We still need to assign a type to the inner expression to
720+
// prevent the ICE in #43162.
721+
if let Some(ref e) = expr_opt {
722+
self.check_expr_with_hint(e, tcx.types.err);
723+
724+
// ... except when we try to 'break rust;'.
725+
// ICE this expression in particular (see #43162).
726+
if let ExprKind::Path(QPath::Resolved(_, ref path)) = e.node {
727+
if path.segments.len() == 1 &&
728+
path.segments[0].ident.name == sym::rust {
729+
fatally_break_rust(self.tcx.sess);
730+
}
731+
}
732+
}
733+
// There was an error; make type-check fail.
734+
tcx.types.err
735+
}
736+
}
728737
}

src/librustc_typeck/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ This API is completely unstable and subject to change.
6868
#![feature(rustc_diagnostic_macros)]
6969
#![feature(slice_patterns)]
7070
#![feature(never_type)]
71+
#![feature(inner_deref)]
7172

7273
#![recursion_limit="256"]
7374

0 commit comments

Comments
 (0)