Skip to content

Commit

Permalink
Rollup merge of rust-lang#40224 - nikomatsakis:issue-39808, r=eddyb
Browse files Browse the repository at this point in the history
change the strategy for diverging types

The new strategy is as follows. First, the `!` type is assigned
in two cases:

- a block with a diverging statement and no tail expression (e.g.,
  `{return;}`);
- any expression with the type `!` is considered diverging.

Second, we track when we are in a diverging state, and we permit a value
of any type to be coerced **into** `!` if the expression that produced
it is diverging. This means that `fn foo() -> ! { panic!(); 22 }`
type-checks, even though the block has a type of `usize`.

Finally, coercions **from** the `!` type to any other are always
permitted.

Fixes rust-lang#39808.
Fixes rust-lang#39984.
  • Loading branch information
frewsxcv authored Mar 29, 2017
2 parents 07a3429 + 93a0f54 commit a2af5ff
Show file tree
Hide file tree
Showing 145 changed files with 2,432 additions and 2,592 deletions.
8 changes: 4 additions & 4 deletions src/librustc/cfg/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ pub fn construct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,

impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
fn block(&mut self, blk: &hir::Block, pred: CFGIndex) -> CFGIndex {
if let Some(break_to_expr_id) = blk.break_to_expr_id {
if blk.targeted_by_break {
let expr_exit = self.add_ast_node(blk.id, &[]);

self.breakable_block_scopes.push(BlockScope {
block_expr_id: break_to_expr_id,
block_expr_id: blk.id,
break_index: expr_exit,
});

Expand Down Expand Up @@ -195,7 +195,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
// [..expr..]
//
let cond_exit = self.expr(&cond, pred); // 1
let then_exit = self.block(&then, cond_exit); // 2
let then_exit = self.expr(&then, cond_exit); // 2
self.add_ast_node(expr.id, &[cond_exit, then_exit]) // 3,4
}

Expand All @@ -215,7 +215,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
// [..expr..]
//
let cond_exit = self.expr(&cond, pred); // 1
let then_exit = self.block(&then, cond_exit); // 2
let then_exit = self.expr(&then, cond_exit); // 2
let else_exit = self.expr(&otherwise, cond_exit); // 3
self.add_ast_node(expr.id, &[then_exit, else_exit]) // 4, 5
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/hir/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
}
ExprIf(ref head_expression, ref if_block, ref optional_else) => {
visitor.visit_expr(head_expression);
visitor.visit_block(if_block);
visitor.visit_expr(if_block);
walk_list!(visitor, visit_expr, optional_else);
}
ExprWhile(ref subexpression, ref block, ref opt_sp_name) => {
Expand Down
39 changes: 21 additions & 18 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1146,7 +1146,7 @@ impl<'a> LoweringContext<'a> {
bounds.iter().map(|bound| self.lower_ty_param_bound(bound)).collect()
}

fn lower_block(&mut self, b: &Block, break_to: Option<NodeId>) -> P<hir::Block> {
fn lower_block(&mut self, b: &Block, targeted_by_break: bool) -> P<hir::Block> {
let mut expr = None;

let mut stmts = vec![];
Expand All @@ -1169,7 +1169,7 @@ impl<'a> LoweringContext<'a> {
expr: expr,
rules: self.lower_block_check_mode(&b.rules),
span: b.span,
break_to_expr_id: break_to,
targeted_by_break: targeted_by_break,
})
}

Expand Down Expand Up @@ -1264,7 +1264,7 @@ impl<'a> LoweringContext<'a> {
}
ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => {
self.with_new_scopes(|this| {
let body = this.lower_block(body, None);
let body = this.lower_block(body, false);
let body = this.expr_block(body, ThinVec::new());
let body_id = this.record_body(body, Some(decl));
hir::ItemFn(this.lower_fn_decl(decl),
Expand Down Expand Up @@ -1358,7 +1358,7 @@ impl<'a> LoweringContext<'a> {
hir::TraitMethod::Required(names))
}
TraitItemKind::Method(ref sig, Some(ref body)) => {
let body = this.lower_block(body, None);
let body = this.lower_block(body, false);
let expr = this.expr_block(body, ThinVec::new());
let body_id = this.record_body(expr, Some(&sig.decl));
hir::TraitItemKind::Method(this.lower_method_sig(sig),
Expand Down Expand Up @@ -1414,7 +1414,7 @@ impl<'a> LoweringContext<'a> {
hir::ImplItemKind::Const(this.lower_ty(ty), body_id)
}
ImplItemKind::Method(ref sig, ref body) => {
let body = this.lower_block(body, None);
let body = this.lower_block(body, false);
let expr = this.expr_block(body, ThinVec::new());
let body_id = this.record_body(expr, Some(&sig.decl));
hir::ImplItemKind::Method(this.lower_method_sig(sig), body_id)
Expand Down Expand Up @@ -1838,32 +1838,35 @@ impl<'a> LoweringContext<'a> {
id: id,
rules: hir::DefaultBlock,
span: span,
break_to_expr_id: None,
targeted_by_break: false,
});
P(self.expr_block(blk, ThinVec::new()))
}
_ => P(self.lower_expr(els)),
}
});

hir::ExprIf(P(self.lower_expr(cond)), self.lower_block(blk, None), else_opt)
let then_blk = self.lower_block(blk, false);
let then_expr = self.expr_block(then_blk, ThinVec::new());

hir::ExprIf(P(self.lower_expr(cond)), P(then_expr), else_opt)
}
ExprKind::While(ref cond, ref body, opt_ident) => {
self.with_loop_scope(e.id, |this|
hir::ExprWhile(
this.with_loop_condition_scope(|this| P(this.lower_expr(cond))),
this.lower_block(body, None),
this.lower_block(body, false),
this.lower_opt_sp_ident(opt_ident)))
}
ExprKind::Loop(ref body, opt_ident) => {
self.with_loop_scope(e.id, |this|
hir::ExprLoop(this.lower_block(body, None),
hir::ExprLoop(this.lower_block(body, false),
this.lower_opt_sp_ident(opt_ident),
hir::LoopSource::Loop))
}
ExprKind::Catch(ref body) => {
self.with_catch_scope(e.id, |this|
hir::ExprBlock(this.lower_block(body, Some(e.id))))
self.with_catch_scope(body.id, |this|
hir::ExprBlock(this.lower_block(body, true)))
}
ExprKind::Match(ref expr, ref arms) => {
hir::ExprMatch(P(self.lower_expr(expr)),
Expand All @@ -1881,7 +1884,7 @@ impl<'a> LoweringContext<'a> {
})
})
}
ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk, None)),
ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk, false)),
ExprKind::Assign(ref el, ref er) => {
hir::ExprAssign(P(self.lower_expr(el)), P(self.lower_expr(er)))
}
Expand Down Expand Up @@ -2027,7 +2030,7 @@ impl<'a> LoweringContext<'a> {

// `<pat> => <body>`
{
let body = self.lower_block(body, None);
let body = self.lower_block(body, false);
let body_expr = P(self.expr_block(body, ThinVec::new()));
let pat = self.lower_pat(pat);
arms.push(self.arm(hir_vec![pat], body_expr));
Expand Down Expand Up @@ -2099,7 +2102,7 @@ impl<'a> LoweringContext<'a> {
let (guard, body) = if let ExprKind::If(ref cond,
ref then,
_) = else_expr.node {
let then = self.lower_block(then, None);
let then = self.lower_block(then, false);
(Some(cond),
self.expr_block(then, ThinVec::new()))
} else {
Expand Down Expand Up @@ -2149,7 +2152,7 @@ impl<'a> LoweringContext<'a> {
// Note that the block AND the condition are evaluated in the loop scope.
// This is done to allow `break` from inside the condition of the loop.
let (body, break_expr, sub_expr) = self.with_loop_scope(e.id, |this| (
this.lower_block(body, None),
this.lower_block(body, false),
this.expr_break(e.span, ThinVec::new()),
this.with_loop_condition_scope(|this| P(this.lower_expr(sub_expr))),
));
Expand Down Expand Up @@ -2210,7 +2213,7 @@ impl<'a> LoweringContext<'a> {
// `::std::option::Option::Some(<pat>) => <body>`
let pat_arm = {
let body_block = self.with_loop_scope(e.id,
|this| this.lower_block(body, None));
|this| this.lower_block(body, false));
let body_expr = P(self.expr_block(body_block, ThinVec::new()));
let pat = self.lower_pat(pat);
let some_pat = self.pat_some(e.span, pat);
Expand Down Expand Up @@ -2642,7 +2645,7 @@ impl<'a> LoweringContext<'a> {
id: self.next_id(),
rules: hir::DefaultBlock,
span: span,
break_to_expr_id: None,
targeted_by_break: false,
}
}

Expand Down Expand Up @@ -2750,7 +2753,7 @@ impl<'a> LoweringContext<'a> {
id: id,
stmts: stmts,
expr: Some(expr),
break_to_expr_id: None,
targeted_by_break: false,
});
self.expr_block(block, attrs)
}
Expand Down
12 changes: 7 additions & 5 deletions src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -548,9 +548,11 @@ pub struct Block {
/// Distinguishes between `unsafe { ... }` and `{ ... }`
pub rules: BlockCheckMode,
pub span: Span,
/// The id of the expression that `break` breaks to if the block can be broken out of.
/// Currently only `Some(_)` for `catch {}` blocks
pub break_to_expr_id: Option<NodeId>,
/// If true, then there may exist `break 'a` values that aim to
/// break out of this block early. As of this writing, this is not
/// currently permitted in Rust itself, but it is generated as
/// part of `catch` statements.
pub targeted_by_break: bool,
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)]
Expand Down Expand Up @@ -992,8 +994,8 @@ pub enum Expr_ {
ExprType(P<Expr>, P<Ty>),
/// An `if` block, with an optional else block
///
/// `if expr { block } else { expr }`
ExprIf(P<Expr>, P<Block>, Option<P<Expr>>),
/// `if expr { expr } else { expr }`
ExprIf(P<Expr>, P<Expr>, Option<P<Expr>>),
/// A while loop, with an optional label
///
/// `'label: while expr { block }`
Expand Down
6 changes: 3 additions & 3 deletions src/librustc/hir/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1036,7 +1036,7 @@ impl<'a> State<'a> {
word(&mut self.s, " else if ")?;
self.print_expr(&i)?;
space(&mut self.s)?;
self.print_block(&then)?;
self.print_expr(&then)?;
self.print_else(e.as_ref().map(|e| &**e))
}
// "final else"
Expand All @@ -1058,13 +1058,13 @@ impl<'a> State<'a> {

pub fn print_if(&mut self,
test: &hir::Expr,
blk: &hir::Block,
blk: &hir::Expr,
elseopt: Option<&hir::Expr>)
-> io::Result<()> {
self.head("if")?;
self.print_expr(test)?;
space(&mut self.s)?;
self.print_block(blk)?;
self.print_expr(blk)?;
self.print_else(elseopt)
}

Expand Down
5 changes: 4 additions & 1 deletion src/librustc/infer/type_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub struct TypeVariableTable<'tcx> {
}

/// Reasons to create a type inference variable
#[derive(Debug)]
pub enum TypeVariableOrigin {
MiscVariable(Span),
NormalizeProjectionType(Span),
Expand All @@ -41,6 +42,7 @@ pub enum TypeVariableOrigin {
AdjustmentType(Span),
DivergingStmt(Span),
DivergingBlockExpr(Span),
DivergingFn(Span),
LatticeVariable(Span),
}

Expand Down Expand Up @@ -196,14 +198,15 @@ impl<'tcx> TypeVariableTable<'tcx> {
diverging: bool,
origin: TypeVariableOrigin,
default: Option<Default<'tcx>>,) -> ty::TyVid {
debug!("new_var(diverging={:?}, origin={:?})", diverging, origin);
self.eq_relations.new_key(());
let index = self.values.push(TypeVariableData {
value: Bounded { relations: vec![], default: default },
origin: origin,
diverging: diverging
});
let v = ty::TyVid { index: index as u32 };
debug!("new_var() -> {:?}", v);
debug!("new_var: diverging={:?} index={:?}", diverging, v);
v
}

Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,9 +414,9 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
self.consume_exprs(exprs);
}

hir::ExprIf(ref cond_expr, ref then_blk, ref opt_else_expr) => {
hir::ExprIf(ref cond_expr, ref then_expr, ref opt_else_expr) => {
self.consume_expr(&cond_expr);
self.walk_block(&then_blk);
self.walk_expr(&then_expr);
if let Some(ref else_expr) = *opt_else_expr {
self.consume_expr(&else_expr);
}
Expand Down
6 changes: 3 additions & 3 deletions src/librustc/middle/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -821,8 +821,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {

fn propagate_through_block(&mut self, blk: &hir::Block, succ: LiveNode)
-> LiveNode {
if let Some(break_to_expr_id) = blk.break_to_expr_id {
self.breakable_block_ln.insert(break_to_expr_id, succ);
if blk.targeted_by_break {
self.breakable_block_ln.insert(blk.id, succ);
}
let succ = self.propagate_through_opt_expr(blk.expr.as_ref().map(|e| &**e), succ);
blk.stmts.iter().rev().fold(succ, |succ, stmt| {
Expand Down Expand Up @@ -951,7 +951,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
// ( succ )
//
let else_ln = self.propagate_through_opt_expr(els.as_ref().map(|e| &**e), succ);
let then_ln = self.propagate_through_block(&then, succ);
let then_ln = self.propagate_through_expr(&then, succ);
let ln = self.live_node(expr.id, expr.span);
self.init_from_succ(ln, else_ln);
self.merge_from_succ(ln, then_ln, false);
Expand Down
1 change: 1 addition & 0 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
ObligationCauseCode::StartFunctionType |
ObligationCauseCode::IntrinsicType |
ObligationCauseCode::MethodReceiver |
ObligationCauseCode::ReturnNoExpression |
ObligationCauseCode::MiscObligation => {
}
ObligationCauseCode::SliceOrArrayElem => {
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ pub enum ObligationCauseCode<'tcx> {

// method receiver
MethodReceiver,

// `return` with no expression
ReturnNoExpression,
}

#[derive(Clone, Debug, PartialEq, Eq)]
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/traits/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
type Lifted = traits::ObligationCauseCode<'tcx>;
fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
match *self {
super::ReturnNoExpression => Some(super::ReturnNoExpression),
super::MiscObligation => Some(super::MiscObligation),
super::SliceOrArrayElem => Some(super::SliceOrArrayElem),
super::TupleElem => Some(super::TupleElem),
Expand Down Expand Up @@ -489,6 +490,7 @@ impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> {
super::StructInitializerSized |
super::VariableType(_) |
super::ReturnType |
super::ReturnNoExpression |
super::RepeatVec |
super::FieldSized |
super::ConstSized |
Expand Down Expand Up @@ -533,6 +535,7 @@ impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> {
super::StructInitializerSized |
super::VariableType(_) |
super::ReturnType |
super::ReturnNoExpression |
super::RepeatVec |
super::FieldSized |
super::ConstSized |
Expand Down
Loading

0 comments on commit a2af5ff

Please sign in to comment.