From 276bba9039e90a2c06ec0c811d80108a52a26e0b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 9 Mar 2017 09:53:00 -0500 Subject: [PATCH 01/36] refactor if so that the "then type" is an expression --- src/librustc/cfg/construct.rs | 4 +-- src/librustc/hir/intravisit.rs | 2 +- src/librustc/hir/lowering.rs | 5 +++- src/librustc/hir/mod.rs | 2 +- src/librustc/hir/print.rs | 6 ++--- src/librustc/middle/expr_use_visitor.rs | 4 +-- src/librustc/middle/liveness.rs | 2 +- src/librustc_mir/hair/cx/expr.rs | 2 +- src/librustc_typeck/check/mod.rs | 35 ++++++++----------------- 9 files changed, 26 insertions(+), 36 deletions(-) diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index 189a7344c3130..388d019f654a4 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -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 } @@ -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 } diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index f59b8b757f5cc..c7ad143c94979 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -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) => { diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 029b27b40f04d..fbc8a97dacc4c 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -1856,7 +1856,10 @@ impl<'a> LoweringContext<'a> { } }); - hir::ExprIf(P(self.lower_expr(cond)), self.lower_block(blk, None), else_opt) + let then_blk = self.lower_block(blk, None); + 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| diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 3ca75dc6c00c9..59cefa36cbf1d 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -994,7 +994,7 @@ pub enum Expr_ { /// An `if` block, with an optional else block /// /// `if expr { block } else { expr }` - ExprIf(P, P, Option>), + ExprIf(P, P, Option>), /// A while loop, with an optional label /// /// `'label: while expr { block }` diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 3411de9bb5df1..04a65fd5e3aa4 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -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" @@ -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) } diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index a44679b0b3e0e..c7cf4a35a4bc7 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -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); } diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 769dc8aeb54dd..e146663d4c8d6 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -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); diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 44858a98e36f9..d9b8d04ad386f 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -636,7 +636,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, hir::ExprIf(ref cond, ref then, ref otherwise) => { ExprKind::If { condition: cond.to_ref(), - then: block::to_expr_ref(cx, then), + then: then.to_ref(), otherwise: otherwise.to_ref(), } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index afa1e0557dc8b..54da0b2b0047e 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2739,7 +2739,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // or if-else. fn check_then_else(&self, cond_expr: &'gcx hir::Expr, - then_blk: &'gcx hir::Block, + then_expr: &'gcx hir::Expr, opt_else_expr: Option<&'gcx hir::Expr>, sp: Span, expected: Expectation<'tcx>) -> Ty<'tcx> { @@ -2748,7 +2748,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.diverges.set(Diverges::Maybe); let expected = expected.adjust_for_branches(self); - let then_ty = self.check_block_with_expected(then_blk, expected); + let then_ty = self.check_expr_with_expectation(then_expr, expected); let then_diverges = self.diverges.get(); self.diverges.set(Diverges::Maybe); @@ -2763,26 +2763,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // to assign coercions to, otherwise it's () or diverging. expected_ty = then_ty; found_ty = else_ty; - result = if let Some(ref then) = then_blk.expr { - let res = self.try_find_coercion_lub(&cause, || Some(&**then), - then_ty, else_expr, else_ty); - - // In case we did perform an adjustment, we have to update - // the type of the block, because old trans still uses it. - if res.is_ok() { - let adj = self.tables.borrow().adjustments.get(&then.id).cloned(); - if let Some(adj) = adj { - self.write_ty(then_blk.id, adj.target); - } - } - res - } else { - self.commit_if_ok(|_| { - let trace = TypeTrace::types(&cause, true, then_ty, else_ty); - self.lub(true, trace, &then_ty, &else_ty) - .map(|ok| self.register_infer_ok_obligations(ok)) - }) + let coerce_to = expected.only_has_type(self).unwrap_or(then_ty); + result = { + self.try_coerce(then_expr, then_ty, coerce_to) + .and_then(|t| { + self.try_find_coercion_lub(&cause, || Some(then_expr), t, else_expr, else_ty) + }) }; // We won't diverge unless both branches do (or the condition does). @@ -3587,9 +3574,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { tcx.mk_nil() } } - hir::ExprIf(ref cond, ref then_blk, ref opt_else_expr) => { - self.check_then_else(&cond, &then_blk, opt_else_expr.as_ref().map(|e| &**e), - expr.span, expected) + hir::ExprIf(ref cond, ref then_expr, ref opt_else_expr) => { + self.check_then_else(&cond, then_expr, opt_else_expr.as_ref().map(|e| &**e), + expr.span, expected) } hir::ExprWhile(ref cond, ref body, _) => { let unified = self.tcx.mk_nil(); From 6c022729264a54f1b90ab8801000dc783cdf1e6d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 9 Mar 2017 09:57:26 -0500 Subject: [PATCH 02/36] update comment --- src/librustc/hir/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 59cefa36cbf1d..560fa18a40502 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -993,7 +993,7 @@ pub enum Expr_ { ExprType(P, P), /// An `if` block, with an optional else block /// - /// `if expr { block } else { expr }` + /// `if expr { expr } else { expr }` ExprIf(P, P, Option>), /// A while loop, with an optional label /// From 6a47fa1d3d4594ddd3077d0e73d29aa056493b0d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 29 Mar 2017 23:52:45 -0400 Subject: [PATCH 03/36] remove unneeded `&` that now fails to coerce cc https://github.com/rust-lang/rust/issues/40924 --- src/librustc_trans/abi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index 1530708b4b888..453f65eb762f8 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -369,7 +369,7 @@ impl FnType { match sig.inputs().last().unwrap().sty { ty::TyTuple(ref tupled_arguments, _) => { inputs = &sig.inputs()[0..sig.inputs().len() - 1]; - &tupled_arguments + tupled_arguments } _ => { bug!("argument to function with \"rust-call\" ABI \ From 066d44bc0d1680e2094867631920104049fd4948 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 22 Mar 2017 11:40:29 -0400 Subject: [PATCH 04/36] refactor the `targeted_by_break` field In master, this field was an arbitrary node-id (in fact, an id for something that doesn't even exist in the HIR -- the `catch` node). Breaks targeting this block used that id. In the newer system, this field is a boolean, and any breaks targeted this block will use the id of the block. --- src/librustc/cfg/construct.rs | 4 +- src/librustc/hir/lowering.rs | 36 +++--- src/librustc/hir/mod.rs | 8 +- src/librustc/middle/liveness.rs | 4 +- src/librustc_mir/build/block.rs | 170 ++++++++++++++++------------ src/librustc_mir/build/expr/into.rs | 14 +-- src/librustc_mir/hair/cx/block.rs | 1 + src/librustc_mir/hair/mod.rs | 1 + src/librustc_typeck/check/mod.rs | 36 +++--- 9 files changed, 143 insertions(+), 131 deletions(-) diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index 388d019f654a4..20b322ec18951 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -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, }); diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index fbc8a97dacc4c..17185a6ab69f4 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -1156,7 +1156,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) -> P { + fn lower_block(&mut self, b: &Block, targeted_by_break: bool) -> P { let mut expr = None; let mut stmts = vec![]; @@ -1179,7 +1179,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, }) } @@ -1274,7 +1274,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), @@ -1368,7 +1368,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), @@ -1424,7 +1424,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) @@ -1848,7 +1848,7 @@ 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())) } @@ -1856,7 +1856,7 @@ impl<'a> LoweringContext<'a> { } }); - let then_blk = self.lower_block(blk, None); + 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) @@ -1865,18 +1865,18 @@ impl<'a> LoweringContext<'a> { 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)), @@ -1894,7 +1894,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))) } @@ -2040,7 +2040,7 @@ impl<'a> LoweringContext<'a> { // ` => ` { - 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)); @@ -2112,7 +2112,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 { @@ -2162,7 +2162,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))), )); @@ -2223,7 +2223,7 @@ impl<'a> LoweringContext<'a> { // `::std::option::Option::Some() => ` 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); @@ -2655,7 +2655,7 @@ impl<'a> LoweringContext<'a> { id: self.next_id(), rules: hir::DefaultBlock, span: span, - break_to_expr_id: None, + targeted_by_break: false, } } @@ -2763,7 +2763,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) } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 560fa18a40502..d5000ac9c1866 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -549,9 +549,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, + /// 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)] diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index e146663d4c8d6..7cae08efc0de0 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -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| { diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index 3305cfc0dfe1a..7739766182cfa 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -12,90 +12,116 @@ use build::{BlockAnd, BlockAndExtension, Builder}; use hair::*; use rustc::mir::*; use rustc::hir; +use syntax_pos::Span; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { pub fn ast_block(&mut self, destination: &Lvalue<'tcx>, - mut block: BasicBlock, - ast_block: &'tcx hir::Block) + block: BasicBlock, + ast_block: &'tcx hir::Block, + source_info: SourceInfo) -> BlockAnd<()> { - let Block { extent, span, stmts, expr } = self.hir.mirror(ast_block); + let Block { extent, span, stmts, expr, targeted_by_break } = self.hir.mirror(ast_block); self.in_scope(extent, block, move |this| { - // This convoluted structure is to avoid using recursion as we walk down a list - // of statements. Basically, the structure we get back is something like: - // - // let x = in { - // expr1; - // let y = in { - // expr2; - // expr3; - // ... - // } - // } - // - // The let bindings are valid till the end of block so all we have to do is to pop all - // the let-scopes at the end. - // - // First we build all the statements in the block. - let mut let_extent_stack = Vec::with_capacity(8); - let outer_visibility_scope = this.visibility_scope; - for stmt in stmts { - let Stmt { span: _, kind } = this.hir.mirror(stmt); - match kind { - StmtKind::Expr { scope, expr } => { - unpack!(block = this.in_scope(scope, block, |this| { - let expr = this.hir.mirror(expr); - this.stmt_expr(block, expr) - })); - } - StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => { - let tcx = this.hir.tcx(); + if targeted_by_break { + // This is a `break`-able block (currently only `catch { ... }`) + let exit_block = this.cfg.start_new_block(); + let block_exit = this.in_breakable_scope(None, exit_block, + destination.clone(), |this| { + this.ast_block_stmts(destination, block, span, stmts, expr) + }); + this.cfg.terminate(unpack!(block_exit), source_info, + TerminatorKind::Goto { target: exit_block }); + exit_block.unit() + } else { + this.ast_block_stmts(destination, block, span, stmts, expr) + } + }) + } - // Enter the remainder scope, i.e. the bindings' destruction scope. - this.push_scope(remainder_scope); - let_extent_stack.push(remainder_scope); + fn ast_block_stmts(&mut self, + destination: &Lvalue<'tcx>, + mut block: BasicBlock, + span: Span, + stmts: Vec>, + expr: Option>) + -> BlockAnd<()> { + let this = self; + + // This convoluted structure is to avoid using recursion as we walk down a list + // of statements. Basically, the structure we get back is something like: + // + // let x = in { + // expr1; + // let y = in { + // expr2; + // expr3; + // ... + // } + // } + // + // The let bindings are valid till the end of block so all we have to do is to pop all + // the let-scopes at the end. + // + // First we build all the statements in the block. + let mut let_extent_stack = Vec::with_capacity(8); + let outer_visibility_scope = this.visibility_scope; + for stmt in stmts { + let Stmt { span: _, kind } = this.hir.mirror(stmt); + match kind { + StmtKind::Expr { scope, expr } => { + unpack!(block = this.in_scope(scope, block, |this| { + let expr = this.hir.mirror(expr); + this.stmt_expr(block, expr) + })); + } + StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => { + let tcx = this.hir.tcx(); - // Declare the bindings, which may create a visibility scope. - let remainder_span = remainder_scope.span(&tcx.region_maps, &tcx.hir); - let remainder_span = remainder_span.unwrap_or(span); - let scope = this.declare_bindings(None, remainder_span, &pattern); + // Enter the remainder scope, i.e. the bindings' destruction scope. + this.push_scope(remainder_scope); + let_extent_stack.push(remainder_scope); - // Evaluate the initializer, if present. - if let Some(init) = initializer { - unpack!(block = this.in_scope(init_scope, block, move |this| { - // FIXME #30046 ^~~~ - this.expr_into_pattern(block, pattern, init) - })); - } else { - this.visit_bindings(&pattern, &mut |this, _, _, node, span, _| { - this.storage_live_binding(block, node, span); - this.schedule_drop_for_binding(node, span); - }) - } + // Declare the bindings, which may create a visibility scope. + let remainder_span = remainder_scope.span(&tcx.region_maps, &tcx.hir); + let remainder_span = remainder_span.unwrap_or(span); + let scope = this.declare_bindings(None, remainder_span, &pattern); - // Enter the visibility scope, after evaluating the initializer. - if let Some(visibility_scope) = scope { - this.visibility_scope = visibility_scope; - } + // Evaluate the initializer, if present. + if let Some(init) = initializer { + unpack!(block = this.in_scope(init_scope, block, move |this| { + // FIXME #30046 ^~~~ + this.expr_into_pattern(block, pattern, init) + })); + } else { + this.visit_bindings(&pattern, &mut |this, _, _, node, span, _| { + this.storage_live_binding(block, node, span); + this.schedule_drop_for_binding(node, span); + }) + } + + // Enter the visibility scope, after evaluating the initializer. + if let Some(visibility_scope) = scope { + this.visibility_scope = visibility_scope; } } } - // Then, the block may have an optional trailing expression which is a “return” value - // of the block. - if let Some(expr) = expr { - unpack!(block = this.into(destination, block, expr)); - } else { - let source_info = this.source_info(span); - this.cfg.push_assign_unit(block, source_info, destination); - } - // Finally, we pop all the let scopes before exiting out from the scope of block - // itself. - for extent in let_extent_stack.into_iter().rev() { - unpack!(block = this.pop_scope(extent, block)); - } - // Restore the original visibility scope. - this.visibility_scope = outer_visibility_scope; - block.unit() - }) + } + // Then, the block may have an optional trailing expression which is a “return” value + // of the block. + if let Some(expr) = expr { + unpack!(block = this.into(destination, block, expr)); + } else { + let source_info = this.source_info(span); + this.cfg.push_assign_unit(block, source_info, destination); + } + // Finally, we pop all the let scopes before exiting out from the scope of block + // itself. + for extent in let_extent_stack.into_iter().rev() { + unpack!(block = this.pop_scope(extent, block)); + } + // Restore the original visibility scope. + this.visibility_scope = outer_visibility_scope; + block.unit() } } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index e1b0c6a6f042e..a5a114c61bcf6 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -40,19 +40,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { this.in_scope(extent, block, |this| this.into(destination, block, value)) } ExprKind::Block { body: ast_block } => { - if let Some(_) = ast_block.break_to_expr_id { - // This is a `break`-able block (currently only `catch { ... }`) - let exit_block = this.cfg.start_new_block(); - let block_exit = this.in_breakable_scope(None, exit_block, - destination.clone(), |this| { - this.ast_block(destination, block, ast_block) - }); - this.cfg.terminate(unpack!(block_exit), source_info, - TerminatorKind::Goto { target: exit_block }); - exit_block.unit() - } else { - this.ast_block(destination, block, ast_block) - } + this.ast_block(destination, block, ast_block, source_info) } ExprKind::Match { discriminant, arms } => { this.match_expr(destination, expr_span, block, discriminant, arms) diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs index ba6b9361a83f4..d2465331df353 100644 --- a/src/librustc_mir/hair/cx/block.rs +++ b/src/librustc_mir/hair/cx/block.rs @@ -23,6 +23,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Block { // in order to get the lexical scoping correctly. let stmts = mirror_stmts(cx, self.id, &*self.stmts); Block { + targeted_by_break: self.targeted_by_break, extent: cx.tcx.region_maps.node_extent(self.id), span: self.span, stmts: stmts, diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 2ee375dee08ac..a3982efd2d695 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -31,6 +31,7 @@ pub use rustc_const_eval::pattern::{BindingMode, Pattern, PatternKind, FieldPatt #[derive(Clone, Debug)] pub struct Block<'tcx> { + pub targeted_by_break: bool, pub extent: CodeExtent, pub span: Span, pub stmts: Vec>, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 54da0b2b0047e..000398a4bce9f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -416,15 +416,11 @@ pub struct EnclosingBreakables<'gcx, 'tcx> { } impl<'gcx, 'tcx> EnclosingBreakables<'gcx, 'tcx> { - fn find_breakable(&mut self, target: hir::ScopeTarget) - -> Option<&mut BreakableCtxt<'gcx, 'tcx>> - { - let opt_index = target.opt_id().and_then(|id| self.by_id.get(&id).cloned()); - if let Some(ix) = opt_index { - Some(&mut self.stack[ix]) - } else { - None - } + fn find_breakable(&mut self, target_id: ast::NodeId) -> &mut BreakableCtxt<'gcx, 'tcx> { + let ix = *self.by_id.get(&target_id).unwrap_or_else(|| { + bug!("could not find enclosing breakable with id {}", target_id); + }); + &mut self.stack[ix] } } @@ -3472,12 +3468,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { tcx.mk_nil() } hir::ExprBreak(destination, ref expr_opt) => { - let coerce_to = { - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - enclosing_breakables - .find_breakable(destination.target_id).map(|ctxt| ctxt.coerce_to) - }; - if let Some(coerce_to) = coerce_to { + if let Some(target_id) = destination.target_id.opt_id() { + let coerce_to = { + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + enclosing_breakables.find_breakable(target_id).coerce_to + }; + let e_ty; let cause; if let Some(ref e) = *expr_opt { @@ -3492,7 +3488,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - let ctxt = enclosing_breakables.find_breakable(destination.target_id).unwrap(); + let ctxt = enclosing_breakables.find_breakable(target_id); let result = if let Some(ref e) = *expr_opt { // Special-case the first element, as it has no "previous expressions". @@ -4024,7 +4020,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { replace(&mut *fcx_ps, unsafety_state) }; - let mut ty = if let Some(break_to_expr_id) = blk.break_to_expr_id { + let mut ty = if blk.targeted_by_break { let unified = self.next_ty_var(TypeVariableOrigin::TypeInference(blk.span)); let coerce_to = expected.only_has_type(self).unwrap_or(unified); let ctxt = BreakableCtxt { @@ -4034,15 +4030,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { may_break: false, }; - let (mut ctxt, (e_ty, cause)) = self.with_breakable_ctxt(break_to_expr_id, ctxt, || { + let (mut ctxt, (e_ty, cause)) = self.with_breakable_ctxt(blk.id, ctxt, || { for s in &blk.stmts { self.check_stmt(s); } let coerce_to = { let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - enclosing_breakables.find_breakable( - hir::ScopeTarget::Block(break_to_expr_id) - ).unwrap().coerce_to + enclosing_breakables.find_breakable(blk.id).coerce_to }; let e_ty; let cause; From 4c6c26eba1ca315677b024026a54001a5ab96cd0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 2 Mar 2017 21:15:26 -0500 Subject: [PATCH 05/36] 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 #39808. --- src/librustc_typeck/check/demand.rs | 24 +++++++++++++++++- src/librustc_typeck/check/mod.rs | 24 ++++++++++-------- .../diverging-tuple-parts-39485.rs | 25 +++++++++++++++++++ .../compile-fail/never-assign-wrong-type.rs | 1 + src/test/run-pass/issue-39808.rs | 21 ++++++++++++++++ 5 files changed, 83 insertions(+), 12 deletions(-) create mode 100644 src/test/compile-fail/diverging-tuple-parts-39485.rs create mode 100644 src/test/run-pass/issue-39808.rs diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 232c4c4db7c97..25d689b3c2c45 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -67,8 +67,30 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } // Checks that the type of `expr` can be coerced to `expected`. - pub fn demand_coerce(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, expected: Ty<'tcx>) { + // + // NB: This code relies on `self.diverges` to be accurate. In + // particular, assignments to `!` will be permitted if the + // diverges flag is currently "always". + pub fn demand_coerce(&self, + expr: &hir::Expr, + checked_ty: Ty<'tcx>, + expected: Ty<'tcx>) { let expected = self.resolve_type_vars_with_obligations(expected); + + // If we are "assigning" to a `!` location, then we can permit + // any type to be assigned there, so long as we are in + // dead-code. This applies to e.g. `fn foo() -> ! { return; 22 + // }` but also `fn foo() { let x: ! = { return; 22 }; }`. + // + // You might imagine that we could just *always* bail if we + // are in dead-code, but we don't want to do that, because + // that leaves a lot of type variables unconstrained. See + // e.g. #39808 and #39984. + let in_dead_code = self.diverges.get().always(); + if expected.is_never() && in_dead_code { + return; + } + if let Err(e) = self.try_coerce(expr, checked_ty, expected) { let cause = self.misc(expr.span); let expr_ty = self.resolve_type_vars_with_obligations(checked_ty); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 000398a4bce9f..5d002dba78c24 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1533,18 +1533,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { #[inline] pub fn write_ty(&self, node_id: ast::NodeId, ty: Ty<'tcx>) { debug!("write_ty({}, {:?}) in fcx {}", - node_id, ty, self.tag()); + node_id, self.resolve_type_vars_if_possible(&ty), self.tag()); self.tables.borrow_mut().node_types.insert(node_id, ty); if ty.references_error() { self.has_errors.set(true); self.set_tainted_by_errors(); } - - // FIXME(canndrew): This is_never should probably be an is_uninhabited - if ty.is_never() || self.type_var_diverges(ty) { - self.diverges.set(self.diverges.get() | Diverges::Always); - } } pub fn write_substs(&self, node_id: ast::NodeId, substs: ty::ItemSubsts<'tcx>) { @@ -3282,6 +3277,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { _ => self.warn_if_unreachable(expr.id, expr.span, "expression") } + // Any expression that produces a value of type `!` must have diverged + if ty.is_never() { + self.diverges.set(self.diverges.get() | Diverges::Always); + } + // Record the type, which applies it effects. // We need to do this after the warning above, so that // we don't warn for the diverging expression itself. @@ -3967,7 +3967,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.diverges.set(Diverges::Maybe); self.has_errors.set(false); - let (node_id, span) = match stmt.node { + let (node_id, _span) = match stmt.node { hir::StmtDecl(ref decl, id) => { let span = match decl.node { hir::DeclLocal(ref l) => { @@ -3993,9 +3993,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if self.has_errors.get() { self.write_error(node_id); - } else if self.diverges.get().always() { - self.write_ty(node_id, self.next_diverging_ty_var( - TypeVariableOrigin::DivergingStmt(span))); } else { self.write_nil(node_id); } @@ -4046,7 +4043,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { cause = self.misc(e.span); }, None => { - e_ty = self.tcx.mk_nil(); + e_ty = if self.diverges.get().always() { + self.next_diverging_ty_var(TypeVariableOrigin::DivergingBlockExpr(blk.span)) + } else { + self.tcx.mk_nil() + }; cause = self.misc(blk.span); } }; @@ -4054,6 +4055,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { (e_ty, cause) }); + if let ExpectHasType(ety) = expected { if let Some(ref e) = blk.expr { let result = if !ctxt.may_break { self.try_coerce(e, e_ty, ctxt.coerce_to) diff --git a/src/test/compile-fail/diverging-tuple-parts-39485.rs b/src/test/compile-fail/diverging-tuple-parts-39485.rs new file mode 100644 index 0000000000000..eedad08ab5536 --- /dev/null +++ b/src/test/compile-fail/diverging-tuple-parts-39485.rs @@ -0,0 +1,25 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// After #39485, this test used to pass, but that change was reverted +// due to numerous inference failures like #39808, so it now fails +// again. #39485 made it so that diverging types never propagate +// upward; but we now do propagate such types upward in many more +// cases. + +fn g() { + &panic!() //~ ERROR mismatched types +} + +fn f() -> isize { + (return 1, return 2) //~ ERROR mismatched types +} + +fn main() {} diff --git a/src/test/compile-fail/never-assign-wrong-type.rs b/src/test/compile-fail/never-assign-wrong-type.rs index 53d96aaf4fe89..d854e6eb20388 100644 --- a/src/test/compile-fail/never-assign-wrong-type.rs +++ b/src/test/compile-fail/never-assign-wrong-type.rs @@ -11,6 +11,7 @@ // Test that we can't use another type in place of ! #![feature(never_type)] +#![deny(warnings)] fn main() { let x: ! = "hello"; //~ ERROR mismatched types diff --git a/src/test/run-pass/issue-39808.rs b/src/test/run-pass/issue-39808.rs new file mode 100644 index 0000000000000..f83e9328e5879 --- /dev/null +++ b/src/test/run-pass/issue-39808.rs @@ -0,0 +1,21 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test: even though `Ok` is dead-code, its type needs to +// be influenced by the result of `Err` or else we get a "type +// variable unconstrained" error. + +fn main() { + let _ = if false { + Ok(return) + } else { + Err("") + }; +} From 555b6d69b2420862388e36992d7e099625ca6fe4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 09:35:50 -0400 Subject: [PATCH 06/36] add some debug logs to type_variable.rs --- src/librustc/infer/type_variable.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs index 9c8419d9546d2..5aab0fdf6c951 100644 --- a/src/librustc/infer/type_variable.rs +++ b/src/librustc/infer/type_variable.rs @@ -30,6 +30,7 @@ pub struct TypeVariableTable<'tcx> { } /// Reasons to create a type inference variable +#[derive(Debug)] pub enum TypeVariableOrigin { MiscVariable(Span), NormalizeProjectionType(Span), @@ -196,6 +197,7 @@ impl<'tcx> TypeVariableTable<'tcx> { diverging: bool, origin: TypeVariableOrigin, default: Option>,) -> 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 }, @@ -203,7 +205,7 @@ impl<'tcx> TypeVariableTable<'tcx> { diverging: diverging }); let v = ty::TyVid { index: index as u32 }; - debug!("new_var() -> {:?}", v); + debug!("new_var: diverging={:?} index={:?}", diverging, v); v } From 27f4b57602a01c2b49c09b161e1ddca26752fec8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 09:36:44 -0400 Subject: [PATCH 07/36] add a `TypeVariableOrigin` we'll use later (`DerivingFn`) --- src/librustc/infer/type_variable.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs index 5aab0fdf6c951..67f37e5f9272e 100644 --- a/src/librustc/infer/type_variable.rs +++ b/src/librustc/infer/type_variable.rs @@ -42,6 +42,7 @@ pub enum TypeVariableOrigin { AdjustmentType(Span), DivergingStmt(Span), DivergingBlockExpr(Span), + DivergingFn(Span), LatticeVariable(Span), } From eeb6447bbfbb34ec093b723ff6a9dd40df2287bb Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 09:37:18 -0400 Subject: [PATCH 08/36] add an `ObligationCauseCode` we'll use later (`ReturnNoExpression`) --- src/librustc/traits/error_reporting.rs | 1 + src/librustc/traits/mod.rs | 3 +++ src/librustc/traits/structural_impls.rs | 3 +++ 3 files changed, 7 insertions(+) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 27525d550ff20..152dd6ac3000f 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -904,6 +904,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { ObligationCauseCode::StartFunctionType | ObligationCauseCode::IntrinsicType | ObligationCauseCode::MethodReceiver | + ObligationCauseCode::ReturnNoExpression | ObligationCauseCode::MiscObligation => { } ObligationCauseCode::SliceOrArrayElem => { diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index c71fc28b4d6b3..47cbccdd2ab10 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -173,6 +173,9 @@ pub enum ObligationCauseCode<'tcx> { // method receiver MethodReceiver, + + // `return` with no expression + ReturnNoExpression, } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index 717c171db2ac7..44ef461327ddb 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -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 { match *self { + super::ReturnNoExpression => Some(super::ReturnNoExpression), super::MiscObligation => Some(super::MiscObligation), super::SliceOrArrayElem => Some(super::SliceOrArrayElem), super::TupleElem => Some(super::TupleElem), @@ -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 | @@ -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 | From dad314040764122fc78a7f8b70ffa9ef77bde3c5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 09:39:13 -0400 Subject: [PATCH 09/36] introduce (but do not yet use) a `CoerceMany` API The existing pattern for coercions is fairly complex. `CoerceMany` enapsulates it better into a helper. --- src/librustc_typeck/check/coercion.rs | 227 ++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index c43291557f7fa..40ae169a94e0d 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -837,3 +837,230 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } } + +/// CoerceMany encapsulates the pattern you should use when you have +/// many expressions that are all getting coerced to a common +/// type. This arises, for example, when you have a match (the result +/// of each arm is coerced to a common type). It also arises in less +/// obvious places, such as when you have many `break foo` expressions +/// that target the same loop, or the various `return` expressions in +/// a function. +/// +/// The basic protocol is as follows: +/// +/// - Instantiate the `CoerceMany` with an initial `expected_ty`. +/// This will also serve as the "starting LUB". The expectation is +/// that this type is something which all of the expressions *must* +/// be coercible to. Use a fresh type variable if needed. +/// - For each expression whose result is to be coerced, invoke `coerce()` with. +/// - In some cases we wish to coerce "non-expressions" whose types are implicitly +/// unit. This happens for example if you have a `break` with no expression, +/// or an `if` with no `else`. In that case, invoke `coerce_forced_unit()`. +/// - `coerce()` and `coerce_forced_unit()` may report errors. They hide this +/// from you so that you don't have to worry your pretty head about it. +/// But if an error is reported, the final type will be `err`. +/// - Invoking `coerce()` may cause us to go and adjust the "adjustments" on +/// previously coerced expressions. +/// - When all done, invoke `complete()`. This will return the LUB of +/// all your expressions. +/// - WARNING: I don't believe this final type is guaranteed to be +/// related to your initial `expected_ty` in any particular way, +/// although it will typically be a subtype, so you should check it. +/// - Invoking `complete()` may cause us to go and adjust the "adjustments" on +/// previously coerced expressions. +/// +/// Example: +/// +/// ``` +/// let mut coerce = CoerceMany::new(expected_ty); +/// for expr in exprs { +/// let expr_ty = fcx.check_expr_with_expectation(expr, expected); +/// coerce.coerce(fcx, &cause, expr, expr_ty); +/// } +/// let final_ty = coerce.complete(fcx); +/// ``` +#[derive(Clone)] // (*) +pub struct CoerceMany<'gcx: 'tcx, 'tcx> { + expected_ty: Ty<'tcx>, + final_ty: Option>, + expressions: Vec<&'gcx hir::Expr>, +} + +// (*) this is clone because `FnCtxt` is clone, but it seems dubious -- nmatsakis + +impl<'gcx, 'tcx> CoerceMany<'gcx, 'tcx> { + pub fn new(expected_ty: Ty<'tcx>) -> Self { + CoerceMany { + expected_ty, + final_ty: None, + expressions: vec![], + } + } + + pub fn is_empty(&self) -> bool { + self.expressions.is_empty() + } + + /// Return the "expected type" with which this coercion was + /// constructed. This represents the "downward propagated" type + /// that was given to us at the start of typing whatever construct + /// we are typing (e.g., the match expression). + /// + /// Typically, this is used as the expected type when + /// type-checking each of the alternative expressions whose types + /// we are trying to merge. + pub fn expected_ty(&self) -> Ty<'tcx> { + self.expected_ty + } + + /// Returns the current "merged type", representing our best-guess + /// at the LUB of the expressions we've seen so far (if any). This + /// isn't *final* until you call `self.final()`, which will return + /// the merged type. + pub fn merged_ty(&self) -> Ty<'tcx> { + self.final_ty.unwrap_or(self.expected_ty) + } + + /// Indicates that the value generated by `expression`, which is + /// of type `expression_ty`, is one of the possibility that we + /// could coerce from. This will record `expression` and later + /// calls to `coerce` may come back and add adjustments and things + /// if necessary. + pub fn coerce<'a>(&mut self, + fcx: &FnCtxt<'a, 'gcx, 'tcx>, + cause: &ObligationCause<'tcx>, + expression: &'gcx hir::Expr, + expression_ty: Ty<'tcx>) + { + self.coerce_inner(fcx, cause, Some(expression), expression_ty) + } + + /// Indicates that one of the inputs is a "forced unit". This + /// occurs in a case like `if foo { ... };`, where the issing else + /// generates a "forced unit". Another example is a `loop { break; + /// }`, where the `break` has no argument expression. We treat + /// these cases slightly differently for error-reporting + /// purposes. Note that these tend to correspond to cases where + /// the `()` expression is implicit in the source, and hence we do + /// not take an expression argument. + pub fn coerce_forced_unit<'a>(&mut self, + fcx: &FnCtxt<'a, 'gcx, 'tcx>, + cause: &ObligationCause<'tcx>) + { + self.coerce_inner(fcx, + cause, + None, + fcx.tcx.mk_nil()) + } + + /// The inner coercion "engine". If `expression` is `None`, this + /// is a forced-unit case, and hence `expression_ty` must be + /// `Nil`. + fn coerce_inner<'a>(&mut self, + fcx: &FnCtxt<'a, 'gcx, 'tcx>, + cause: &ObligationCause<'tcx>, + expression: Option<&'gcx hir::Expr>, + mut expression_ty: Ty<'tcx>) + { + // Incorporate whatever type inference information we have + // until now; in principle we might also want to process + // pending obligations, but doing so should only improve + // compatibility (hopefully that is true) by helping us + // uncover never types better. + if expression_ty.is_ty_var() { + expression_ty = fcx.infcx.shallow_resolve(expression_ty); + } + + // If we see any error types, just propagate that error + // upwards. + if expression_ty.references_error() || self.merged_ty().references_error() { + self.final_ty = Some(fcx.tcx.types.err); + return; + } + + // Handle the actual type unification etc. + let result = if let Some(expression) = expression { + if self.expressions.is_empty() { + // Special-case the first expression we are coercing. + // To be honest, I'm not entirely sure why we do this. + fcx.try_coerce(expression, expression_ty, self.expected_ty) + } else { + fcx.try_find_coercion_lub(cause, + || self.expressions.iter().cloned(), + self.merged_ty(), + expression, + expression_ty) + } + } else { + // this is a hack for cases where we default to `()` because + // the expression etc has been omitted from the source. An + // example is an `if let` without an else: + // + // if let Some(x) = ... { } + // + // we wind up with a second match arm that is like `_ => + // ()`. That is the case we are considering here. We take + // a different path to get the right "expected, found" + // message and so forth (and because we know that + // `expression_ty` will be unit). + // + // Another example is `break` with no argument expression. + assert!(expression_ty.is_nil()); + assert!(expression_ty.is_nil(), "if let hack without unit type"); + fcx.eq_types(true, cause, expression_ty, self.merged_ty()) + .map(|infer_ok| { + fcx.register_infer_ok_obligations(infer_ok); + expression_ty + }) + }; + + match result { + Ok(v) => { + self.final_ty = Some(v); + self.expressions.extend(expression); + } + Err(err) => { + let (expected, found) = if expression.is_none() { + // In the case where this is a "forced unit", like + // `break`, we want to call the `()` "expected" + // since it is implied by the syntax. + assert!(expression_ty.is_nil()); + (expression_ty, self.final_ty.unwrap_or(self.expected_ty)) + } else { + // Otherwise, the "expected" type for error + // reporting is the current unification type, + // which is basically the LUB of the expressions + // we've seen so far (combined with the expected + // type) + (self.final_ty.unwrap_or(self.expected_ty), expression_ty) + }; + + match cause.code { + ObligationCauseCode::ReturnNoExpression => { + struct_span_err!(fcx.tcx.sess, cause.span, E0069, + "`return;` in a function whose return type is not `()`") + .span_label(cause.span, &format!("return type is not ()")) + .emit(); + } + _ => { + fcx.report_mismatched_types(cause, expected, found, err) + .emit(); + } + } + + self.final_ty = Some(fcx.tcx.types.err); + } + } + } + + pub fn complete<'a>(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { + if let Some(final_ty) = self.final_ty { + final_ty + } else { + // If we only had inputs that were of type `!` (or no + // inputs at all), then the final type is `!`. + assert!(self.expressions.is_empty()); + fcx.tcx.types.never + } + } +} From 56847af9163284f928d5632a3d0d29399716414f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 09:51:31 -0400 Subject: [PATCH 10/36] port the match code to use `CoerceMany` `match { }` now (correctly?) indicates divergence, which results in more unreachable warnings. We also avoid fallback to `!` if there is just one arm (see new test: `match-unresolved-one-arm.rs`). --- src/librustc_typeck/check/_match.rs | 90 ++++++++----------- src/libsyntax/parse/obsolete.rs | 1 + .../match-no-arms-unreachable-after.rs | 22 +++++ ...eachable-warning-with-diverging-discrim.rs | 16 ++++ .../compile-fail/match-unresolved-one-arm.rs | 17 ++++ 5 files changed, 95 insertions(+), 51 deletions(-) create mode 100644 src/test/compile-fail/match-no-arms-unreachable-after.rs create mode 100644 src/test/compile-fail/match-unreachable-warning-with-diverging-discrim.rs create mode 100644 src/test/compile-fail/match-unresolved-one-arm.rs diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index feed5752cf8fb..f0d2598a0fb2a 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -16,6 +16,7 @@ use rustc::infer::type_variable::TypeVariableOrigin; use rustc::traits::ObligationCauseCode; use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference}; use check::{FnCtxt, Expectation, Diverges}; +use check::coercion::CoerceMany; use util::nodemap::FxHashMap; use std::collections::hash_map::Entry::{Occupied, Vacant}; @@ -414,6 +415,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { discrim_ty = self.next_ty_var(TypeVariableOrigin::TypeInference(discrim.span)); self.check_expr_has_type(discrim, discrim_ty); }; + + // If the discriminant diverges, the match is pointless (e.g., + // `match (return) { }`). + self.warn_if_unreachable(expr.id, expr.span, "expression"); + + // If there are no arms, that is a diverging match; a special case. + if arms.is_empty() { + self.diverges.set(self.diverges.get() | Diverges::Always); + return tcx.types.never; + } + + // Otherwise, we have to union together the types that the + // arms produce and so forth. + let discrim_diverges = self.diverges.get(); self.diverges.set(Diverges::Maybe); @@ -426,6 +441,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.check_pat(&p, discrim_ty); all_pats_diverge &= self.diverges.get(); } + // As discussed with @eddyb, this is for disabling unreachable_code // warnings on patterns (they're now subsumed by unreachable_patterns // warnings). @@ -444,20 +460,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // on any empty type and is therefore unreachable; should the flow // of execution reach it, we will panic, so bottom is an appropriate // type in that case) - let expected = expected.adjust_for_branches(self); - let mut result_ty = self.next_diverging_ty_var( - TypeVariableOrigin::DivergingBlockExpr(expr.span)); let mut all_arms_diverge = Diverges::WarnedAlways; - let coerce_first = match expected { - // We don't coerce to `()` so that if the match expression is a - // statement it's branches can have any consistent type. That allows - // us to give better error messages (pointing to a usually better - // arm for inconsistent arms or to the whole match when a `()` type - // is required). - Expectation::ExpectHasType(ety) if ety != self.tcx.mk_nil() => { - ety - } - _ => result_ty + + let expected = expected.adjust_for_branches(self); + + let mut coercion = { + let coerce_first = match expected { + // We don't coerce to `()` so that if the match expression is a + // statement it's branches can have any consistent type. That allows + // us to give better error messages (pointing to a usually better + // arm for inconsistent arms or to the whole match when a `()` type + // is required). + Expectation::ExpectHasType(ety) if ety != self.tcx.mk_nil() => ety, + _ => self.next_ty_var(TypeVariableOrigin::MiscVariable(expr.span)), + }; + CoerceMany::new(coerce_first) }; for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() { @@ -470,11 +487,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let arm_ty = self.check_expr_with_expectation(&arm.body, expected); all_arms_diverge &= self.diverges.get(); - if result_ty.references_error() || arm_ty.references_error() { - result_ty = tcx.types.err; - continue; - } - // Handle the fallback arm of a desugared if-let like a missing else. let is_if_let_fallback = match match_src { hir::MatchSource::IfLetDesugar { contains_else_clause: false } => { @@ -483,47 +495,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { _ => false }; - let cause = if is_if_let_fallback { - self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse) + if is_if_let_fallback { + let cause = self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse); + assert!(arm_ty.is_nil()); + coercion.coerce_forced_unit(self, &cause); } else { - self.cause(expr.span, ObligationCauseCode::MatchExpressionArm { + let cause = self.cause(expr.span, ObligationCauseCode::MatchExpressionArm { arm_span: arm.body.span, source: match_src - }) - }; - - let result = if is_if_let_fallback { - self.eq_types(true, &cause, arm_ty, result_ty) - .map(|infer_ok| { - self.register_infer_ok_obligations(infer_ok); - arm_ty - }) - } else if i == 0 { - // Special-case the first arm, as it has no "previous expressions". - self.try_coerce(&arm.body, arm_ty, coerce_first) - } else { - let prev_arms = || arms[..i].iter().map(|arm| &*arm.body); - self.try_find_coercion_lub(&cause, prev_arms, result_ty, &arm.body, arm_ty) - }; - - result_ty = match result { - Ok(ty) => ty, - Err(e) => { - let (expected, found) = if is_if_let_fallback { - (arm_ty, result_ty) - } else { - (result_ty, arm_ty) - }; - self.report_mismatched_types(&cause, expected, found, e).emit(); - self.tcx.types.err - } - }; + }); + coercion.coerce(self, &cause, &arm.body, arm_ty); + } } // We won't diverge unless the discriminant or all arms diverge. self.diverges.set(discrim_diverges | all_arms_diverge); - result_ty + coercion.complete(self) } fn check_pat_struct(&self, diff --git a/src/libsyntax/parse/obsolete.rs b/src/libsyntax/parse/obsolete.rs index a46a788ca0808..d5baec675e44f 100644 --- a/src/libsyntax/parse/obsolete.rs +++ b/src/libsyntax/parse/obsolete.rs @@ -36,6 +36,7 @@ pub trait ParserObsoleteMethods { impl<'a> ParserObsoleteMethods for parser::Parser<'a> { /// Reports an obsolete syntax non-fatal error. #[allow(unused_variables)] + #[allow(unreachable_code)] fn obsolete(&mut self, sp: Span, kind: ObsoleteSyntax) { let (kind_str, desc, error) = match kind { // Nothing here at the moment diff --git a/src/test/compile-fail/match-no-arms-unreachable-after.rs b/src/test/compile-fail/match-no-arms-unreachable-after.rs new file mode 100644 index 0000000000000..db08f5e5e66a3 --- /dev/null +++ b/src/test/compile-fail/match-no-arms-unreachable-after.rs @@ -0,0 +1,22 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(warnings)] +#![deny(unreachable_code)] + +enum Void { } + +fn foo(v: Void) { + match v { } + let x = 2; //~ ERROR unreachable +} + +fn main() { +} diff --git a/src/test/compile-fail/match-unreachable-warning-with-diverging-discrim.rs b/src/test/compile-fail/match-unreachable-warning-with-diverging-discrim.rs new file mode 100644 index 0000000000000..aae0f3135d8f5 --- /dev/null +++ b/src/test/compile-fail/match-unreachable-warning-with-diverging-discrim.rs @@ -0,0 +1,16 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_parens)] +#![deny(unreachable_code)] + +fn main() { + match (return) { } //~ ERROR unreachable expression +} diff --git a/src/test/compile-fail/match-unresolved-one-arm.rs b/src/test/compile-fail/match-unresolved-one-arm.rs new file mode 100644 index 0000000000000..ea0f8db99e893 --- /dev/null +++ b/src/test/compile-fail/match-unresolved-one-arm.rs @@ -0,0 +1,17 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn foo() -> T { panic!("Rocks for my pillow") } + +fn main() { + let x = match () { //~ ERROR type annotations needed + () => foo() // T here should be unresolved + }; +} From b725272b265268fbe4f3220111ae1a053b3a37a7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 09:52:38 -0400 Subject: [PATCH 11/36] port if-then-else to use `CoerceMany` --- src/librustc_typeck/check/mod.rs | 71 +++++++++++++++----------------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 5d002dba78c24..69f1466c47518 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -77,6 +77,7 @@ type parameter). */ pub use self::Expectation::*; +use self::coercion::CoerceMany; pub use self::compare_method::{compare_impl_method, compare_const_impl}; use self::TupleArgumentsFlag::*; @@ -299,12 +300,23 @@ impl<'a, 'gcx, 'tcx> Expectation<'tcx> { } } + /// It sometimes happens that we want to turn an expectation into + /// a **hard constraint** (i.e., something that must be satisfied + /// for the program to type-check). `only_has_type` will return + /// such a constraint, if it exists. fn only_has_type(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Option> { match self.resolve(fcx) { ExpectHasType(ty) => Some(ty), _ => None } } + + /// Like `only_has_type`, but instead of returning `None` if no + /// hard constraint exists, creates a fresh type variable. + fn only_has_type_or_fresh_var(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>, span: Span) -> Ty<'tcx> { + self.only_has_type(fcx) + .unwrap_or_else(|| fcx.next_ty_var(TypeVariableOrigin::MiscVariable(span))) + } } #[derive(Copy, Clone)] @@ -2743,54 +2755,39 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let then_diverges = self.diverges.get(); self.diverges.set(Diverges::Maybe); - let unit = self.tcx.mk_nil(); - let (cause, expected_ty, found_ty, result); + // We've already taken the expected type's preferences + // into account when typing the `then` branch. To figure + // out the initial shot at a LUB, we thus only consider + // `expected` if it represents a *hard* constraint + // (`only_has_type`); otherwise, we just go with a + // fresh type variable. + let coerce_to_ty = expected.only_has_type_or_fresh_var(self, sp); + let mut coerce = CoerceMany::new(coerce_to_ty); + + let if_cause = self.cause(sp, ObligationCauseCode::IfExpression); + coerce.coerce(self, &if_cause, then_expr, then_ty); + if let Some(else_expr) = opt_else_expr { let else_ty = self.check_expr_with_expectation(else_expr, expected); let else_diverges = self.diverges.get(); - cause = self.cause(sp, ObligationCauseCode::IfExpression); - - // Only try to coerce-unify if we have a then expression - // to assign coercions to, otherwise it's () or diverging. - expected_ty = then_ty; - found_ty = else_ty; - - let coerce_to = expected.only_has_type(self).unwrap_or(then_ty); - result = { - self.try_coerce(then_expr, then_ty, coerce_to) - .and_then(|t| { - self.try_find_coercion_lub(&cause, || Some(then_expr), t, else_expr, else_ty) - }) - }; + + coerce.coerce(self, &if_cause, else_expr, else_ty); // We won't diverge unless both branches do (or the condition does). self.diverges.set(cond_diverges | then_diverges & else_diverges); } else { + let else_cause = self.cause(sp, ObligationCauseCode::IfExpressionWithNoElse); + coerce.coerce_forced_unit(self, &else_cause); + // If the condition is false we can't diverge. self.diverges.set(cond_diverges); - - cause = self.cause(sp, ObligationCauseCode::IfExpressionWithNoElse); - expected_ty = unit; - found_ty = then_ty; - result = self.eq_types(true, &cause, unit, then_ty) - .map(|ok| { - self.register_infer_ok_obligations(ok); - unit - }); } - match result { - Ok(ty) => { - if cond_ty.references_error() { - self.tcx.types.err - } else { - ty - } - } - Err(e) => { - self.report_mismatched_types(&cause, expected_ty, found_ty, e).emit(); - self.tcx.types.err - } + let result_ty = coerce.complete(self); + if cond_ty.references_error() { + self.tcx.types.err + } else { + result_ty } } From a6e6be5f88bc773f50016cc1cf1baaa7b3e6cd11 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 09:54:26 -0400 Subject: [PATCH 12/36] port `return` expressions to use `CoerceMany` This slightly affects the error messages in one particular compile-fail test. --- src/librustc_typeck/check/compare_method.rs | 2 +- src/librustc_typeck/check/mod.rs | 85 +++++++++++++++------ src/librustc_typeck/check/wfcheck.rs | 2 +- src/test/compile-fail/regions-bounds.rs | 10 +-- 4 files changed, 66 insertions(+), 33 deletions(-) diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index 0e9abaf1cf955..905d8688ea194 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -362,7 +362,7 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, &infcx.parameter_environment.caller_bounds); infcx.resolve_regions_and_report_errors(&free_regions, impl_m_body_id); } else { - let fcx = FnCtxt::new(&inh, Some(tcx.types.err), impl_m_body_id); + let fcx = FnCtxt::new(&inh, impl_m_body_id); fcx.regionck_item(impl_m_body_id, impl_m_span, &[]); } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 69f1466c47518..619f4fb10c544 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -448,7 +448,7 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { // expects the types within the function to be consistent. err_count_on_creation: usize, - ret_ty: Option>, + ret_coercion: Option>>, ps: RefCell, @@ -679,7 +679,7 @@ fn typeck_tables<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, check_fn(&inh, fn_sig, decl, id, body) } else { - let fcx = FnCtxt::new(&inh, None, body.value.id); + let fcx = FnCtxt::new(&inh, body.value.id); let expected_type = tcx.item_type(def_id); let expected_type = fcx.normalize_associated_types_in(body.value.span, &expected_type); 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>, // Create the function context. This is either derived from scratch or, // in the case of function expressions, based on the outer context. - let mut fcx = FnCtxt::new(inherited, None, body.value.id); - let ret_ty = fn_sig.output(); + let mut fcx = FnCtxt::new(inherited, body.value.id); *fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id); + let ret_ty = fn_sig.output(); fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::ReturnType); - fcx.ret_ty = fcx.instantiate_anon_types(&Some(ret_ty)); + let ret_ty = fcx.instantiate_anon_types(&ret_ty); + fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty))); fn_sig = fcx.tcx.mk_fn_sig( fn_sig.inputs().iter().cloned(), - fcx.ret_ty.unwrap(), + ret_ty, fn_sig.variadic, fn_sig.unsafety, fn_sig.abi @@ -833,7 +834,38 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, inherited.tables.borrow_mut().liberated_fn_sigs.insert(fn_id, fn_sig); - fcx.check_expr_coercable_to_type(&body.value, fcx.ret_ty.unwrap()); + fcx.check_return_expr(&body.value); + + // Finalize the return check by taking the LUB of the return types + // we saw and assigning it to the expected return type. This isn't + // really expected to fail, since the coercions would have failed + // earlier when trying to find a LUB. + // + // However, the behavior around `!` is sort of complex. In the + // event that the `actual_return_ty` comes back as `!`, that + // indicates that the fn either does not return or "returns" only + // values of type `!`. In this case, if there is an expected + // return type that is *not* `!`, that should be ok. But if the + // return type is being inferred, we want to "fallback" to `!`: + // + // let x = move || panic!(); + // + // To allow for that, I am creating a type variable with diverging + // fallback. This was deemed ever so slightly better than unifying + // the return value with `!` because it allows for the caller to + // make more assumptions about the return type (e.g., they could do + // + // let y: Option = Some(x()); + // + // which would then cause this return type to become `u32`, not + // `!`). + let coercion = fcx.ret_coercion.take().unwrap().into_inner(); + let mut actual_return_ty = coercion.complete(&fcx); + if actual_return_ty.is_never() { + actual_return_ty = fcx.next_diverging_ty_var( + TypeVariableOrigin::DivergingFn(body.value.span)); + } + fcx.demand_suptype(body.value.span, ret_ty, actual_return_ty); fcx } @@ -1429,14 +1461,13 @@ enum TupleArgumentsFlag { impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn new(inh: &'a Inherited<'a, 'gcx, 'tcx>, - rty: Option>, body_id: ast::NodeId) -> FnCtxt<'a, 'gcx, 'tcx> { FnCtxt { ast_ty_to_ty_cache: RefCell::new(NodeMap()), body_id: body_id, err_count_on_creation: inh.tcx.sess.err_count(), - ret_ty: rty, + ret_coercion: None, ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, ast::CRATE_NODE_ID)), diverges: Cell::new(Diverges::Maybe), @@ -2738,6 +2769,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ret_ty } + fn check_return_expr(&self, return_expr: &'gcx hir::Expr) { + let ret_coercion = + self.ret_coercion + .as_ref() + .unwrap_or_else(|| span_bug!(return_expr.span, + "check_return_expr called outside fn body")); + + let ret_ty = ret_coercion.borrow().expected_ty(); + let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty); + ret_coercion.borrow_mut() + .coerce(self, + &self.misc(return_expr.span), + return_expr, + return_expr_ty); + } + + // A generic function for checking the then and else in an if // or if-else. fn check_then_else(&self, @@ -3522,24 +3570,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } hir::ExprAgain(_) => { tcx.types.never } hir::ExprRet(ref expr_opt) => { - if self.ret_ty.is_none() { + if self.ret_coercion.is_none() { struct_span_err!(self.tcx.sess, expr.span, E0572, "return statement outside of function body").emit(); } else if let Some(ref e) = *expr_opt { - self.check_expr_coercable_to_type(&e, self.ret_ty.unwrap()); + self.check_return_expr(e); } else { - match self.eq_types(false, - &self.misc(expr.span), - self.ret_ty.unwrap(), - tcx.mk_nil()) { - Ok(ok) => self.register_infer_ok_obligations(ok), - Err(_) => { - struct_span_err!(tcx.sess, expr.span, E0069, - "`return;` in a function whose return type is not `()`") - .span_label(expr.span, &format!("return type is not ()")) - .emit(); - } - } + let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut(); + let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression); + coercion.coerce_forced_unit(self, &cause); } tcx.types.never } diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index a4cb4071b4d88..85c87adf9be68 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -50,7 +50,7 @@ impl<'a, 'gcx, 'tcx> CheckWfFcxBuilder<'a, 'gcx, 'tcx> { let id = self.id; let span = self.span; self.inherited.enter(|inh| { - let fcx = FnCtxt::new(&inh, None, id); + let fcx = FnCtxt::new(&inh, id); let wf_tys = f(&fcx, &mut CheckTypeWellFormedVisitor { tcx: fcx.tcx.global_tcx(), code: code diff --git a/src/test/compile-fail/regions-bounds.rs b/src/test/compile-fail/regions-bounds.rs index 64dbf27b78e48..810a8671c536b 100644 --- a/src/test/compile-fail/regions-bounds.rs +++ b/src/test/compile-fail/regions-bounds.rs @@ -16,17 +16,11 @@ struct an_enum<'a>(&'a isize); struct a_class<'a> { x:&'a isize } fn a_fn1<'a,'b>(e: an_enum<'a>) -> an_enum<'b> { - return e; //~ ERROR mismatched types - //~| expected type `an_enum<'b>` - //~| found type `an_enum<'a>` - //~| lifetime mismatch + return e; //~^ ERROR mismatched types } fn a_fn3<'a,'b>(e: a_class<'a>) -> a_class<'b> { - return e; //~ ERROR mismatched types - //~| expected type `a_class<'b>` - //~| found type `a_class<'a>` - //~| lifetime mismatch + return e; //~^ ERROR mismatched types } fn main() { } From 4967f1ae57120b02639f3d1493e8f0c9e168b0dc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 11:10:12 -0400 Subject: [PATCH 13/36] document the diverges flag etc --- src/librustc_typeck/check/mod.rs | 43 +++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 619f4fb10c544..e186daf9f408e 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -360,11 +360,12 @@ impl UnsafetyState { } } -/// Whether a node ever exits normally or not. -/// Tracked semi-automatically (through type variables -/// marked as diverging), with some manual adjustments -/// for control-flow primitives (approximating a CFG). -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +/// Tracks whether executing a node may exit normally (versus +/// return/break/panic, which "diverge", leaving dead code in their +/// wake). Tracked semi-automatically (through type variables marked +/// as diverging), with some manual adjustments for control-flow +/// primitives (approximating a CFG). +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] enum Diverges { /// Potentially unknown, some cases converge, /// others require a CFG to determine them. @@ -452,7 +453,37 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { ps: RefCell, - /// Whether the last checked node can ever exit. + /// Whether the last checked node generates a divergence (e.g., + /// `return` will set this to Always). In general, this is + /// typically set to *Maybe* on the way **down** the tree, and + /// then values are propagated **up** the tree. In a block, we + /// combine the results from statements and propagate the + /// end-result up. + /// + /// We use this flag for two purposes: + /// + /// - To warn about unreachable code: if, after processing a + /// sub-expression but before we have applied the effects of the + /// current node, we see that the flag is set to `Always`, we + /// can issue a warning. This corresponds to something like + /// `foo(return)`; we warn on the `foo()` expression. (We then + /// update the flag to `WarnedAlways` to suppress duplicate + /// reports.) Similarly, if we traverse to a fresh statement (or + /// tail expression) from a `Always` setting, we will isssue a + /// warning. This corresponds to something like `{return; + /// foo();}` or `{return; 22}`, where we would warn on the + /// `foo()` or `22`. + /// + /// - To permit assignment into a local variable or other lvalue + /// (including the "return slot") of type `!`. This is allowed + /// if **either** the type of value being assigned is `!`, which + /// means the current code is dead, **or** the expression's + /// divering flag is true, which means that a divering value was + /// wrapped (e.g., `let x: ! = foo(return)`). + /// + /// To repeat the last point: an expression represents dead-code + /// if, after checking it, **either** its type is `!` OR the + /// diverges flag is set to something other than `Maybe`. diverges: Cell, /// Whether any child nodes have any type errors. From 1ae620bbebadbe4362a28b37bcecd41cfed66cea Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 11:48:18 -0400 Subject: [PATCH 14/36] do not eagerly convert `!` to a diverging variable Instead, wait until coercion time. This has some small effects on a few tests (one less temporary, generally better errors when trying to call methods or otherwise "force" the type). --- src/librustc_typeck/check/coercion.rs | 26 +++++++-- src/librustc_typeck/check/mod.rs | 79 +++++++++++++++------------ src/test/compile-fail/index-bot.rs | 2 +- src/test/compile-fail/issue-13847.rs | 2 +- src/test/compile-fail/issue-15207.rs | 2 +- src/test/compile-fail/issue-15965.rs | 2 +- src/test/compile-fail/issue-17373.rs | 2 +- src/test/compile-fail/issue-18532.rs | 3 +- 8 files changed, 71 insertions(+), 47 deletions(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 40ae169a94e0d..a08faf2610e33 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -169,7 +169,23 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { } if a.is_never() { - return success(Adjust::NeverToAny, b, vec![]); + // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound + // type variable, we want `?T` to fallback to `!` if not + // otherwise constrained. An example where this arises: + // + // let _: Option = Some({ return; }); + // + // here, we would coerce from `!` to `?T`. + let b = self.shallow_resolve(b); + return if self.shallow_resolve(b).is_ty_var() { + // micro-optimization: no need for this if `b` is + // already resolved in some way. + let diverging_ty = self.next_diverging_ty_var( + TypeVariableOrigin::AdjustmentType(self.cause.span)); + self.unify_and(&b, &diverging_ty, Adjust::NeverToAny) + } else { + success(Adjust::NeverToAny, b, vec![]) + }; } // Consider coercing the subtype to a DST @@ -687,11 +703,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let adjustment = self.register_infer_ok_obligations(ok); if !adjustment.is_identity() { debug!("Success, coerced with {:?}", adjustment); - match self.tables.borrow().adjustments.get(&expr.id) { - None | - Some(&Adjustment { kind: Adjust::NeverToAny, .. }) => (), - _ => bug!("expr already has an adjustment on it!"), - }; + if self.tables.borrow().adjustments.get(&expr.id).is_some() { + bug!("expr already has an adjustment on it!"); + } self.write_adjustment(expr.id, adjustment); } Ok(adjustment.target) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index e186daf9f408e..ae0ed81ac778b 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2664,7 +2664,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn check_expr_has_type(&self, expr: &'gcx hir::Expr, expected: Ty<'tcx>) -> Ty<'tcx> { - let ty = self.check_expr_with_hint(expr, expected); + let mut ty = self.check_expr_with_hint(expr, expected); + + // While we don't allow *arbitrary* coercions here, we *do* allow + // coercions from ! to `expected`. + if ty.is_never() { + assert!(!self.tables.borrow().adjustments.contains_key(&expr.id), + "expression with never type wound up being adjusted"); + let adj_ty = self.next_diverging_ty_var( + TypeVariableOrigin::AdjustmentType(expr.span)); + self.write_adjustment(expr.id, adjustment::Adjustment { + kind: adjustment::Adjust::NeverToAny, + target: adj_ty + }); + ty = adj_ty; + } + self.demand_suptype(expr.span, expected, ty); ty } @@ -3370,18 +3385,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { debug!("type of {} is...", self.tcx.hir.node_to_string(expr.id)); debug!("... {:?}, expected is {:?}", ty, expected); - // Add adjustments to !-expressions - if ty.is_never() { - if let Some(hir::map::NodeExpr(node_expr)) = self.tcx.hir.find(expr.id) { - let adj_ty = self.next_diverging_ty_var( - TypeVariableOrigin::AdjustmentType(node_expr.span)); - self.write_adjustment(expr.id, adjustment::Adjustment { - kind: adjustment::Adjust::NeverToAny, - target: adj_ty - }); - return adj_ty; - } - } ty } @@ -4072,7 +4075,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn check_block_no_value(&self, blk: &'gcx hir::Block) { let unit = self.tcx.mk_nil(); let ty = self.check_block_with_expected(blk, ExpectHasType(unit)); - self.demand_suptype(blk.span, unit, ty); + + // if the block produces a `!` value, that can always be + // (effectively) coerced to unit. + if !ty.is_never() { + self.demand_suptype(blk.span, unit, ty); + } } fn check_block_with_expected(&self, @@ -4111,7 +4119,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }, None => { e_ty = if self.diverges.get().always() { - self.next_diverging_ty_var(TypeVariableOrigin::DivergingBlockExpr(blk.span)) + self.tcx.types.never } else { self.tcx.mk_nil() }; @@ -4135,6 +4143,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Err(err) => self.report_mismatched_types(&cause, ctxt.unified, e_ty, err).emit(), } + } else if self.diverges.get().always() { + // No tail expression and the body diverges; ignore + // the expected type, and keep `!` as the type of the + // block. } else { self.check_block_no_expr(blk, self.tcx.mk_nil(), e_ty); }; @@ -4147,33 +4159,32 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mut ty = match blk.expr { Some(ref e) => self.check_expr_with_expectation(e, expected), - None => self.tcx.mk_nil() + None => if self.diverges.get().always() { + self.tcx.types.never + } else { + self.tcx.mk_nil() + }, }; - if self.diverges.get().always() { - if let ExpectHasType(ety) = expected { - // Avoid forcing a type (only `!` for now) in unreachable code. - // FIXME(aburka) do we need this special case? and should it be is_uninhabited? - if !ety.is_never() { - if let Some(ref e) = blk.expr { - // Coerce the tail expression to the right type. - self.demand_coerce(e, ty, ety); - } - } - } - - ty = self.next_diverging_ty_var(TypeVariableOrigin::DivergingBlockExpr(blk.span)); - } else if let ExpectHasType(ety) = expected { + if let ExpectHasType(ety) = expected { if let Some(ref e) = blk.expr { // Coerce the tail expression to the right type. self.demand_coerce(e, ty, ety); + + // We already applied the type (and potentially errored), + // use the expected type to avoid further errors out. + ty = ety; + } else if self.diverges.get().always() { + // No tail expression and the body diverges; ignore + // the expected type, and keep `!` as the type of the + // block. } else { self.check_block_no_expr(blk, ty, ety); - } - // We already applied the type (and potentially errored), - // use the expected type to avoid further errors out. - ty = ety; + // We already applied the type (and potentially errored), + // use the expected type to avoid further errors out. + ty = ety; + } } ty }; diff --git a/src/test/compile-fail/index-bot.rs b/src/test/compile-fail/index-bot.rs index 70c362303ae30..05b0472330048 100644 --- a/src/test/compile-fail/index-bot.rs +++ b/src/test/compile-fail/index-bot.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - (return)[0]; //~ ERROR the type of this value must be known in this context + (return)[0]; //~ ERROR cannot index a value of type `!` } diff --git a/src/test/compile-fail/issue-13847.rs b/src/test/compile-fail/issue-13847.rs index aa823d9a70e7c..0314f109a7c81 100644 --- a/src/test/compile-fail/issue-13847.rs +++ b/src/test/compile-fail/issue-13847.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - return.is_failure //~ ERROR the type of this value must be known in this context + return.is_failure //~ ERROR no field `is_failure` on type `!` } diff --git a/src/test/compile-fail/issue-15207.rs b/src/test/compile-fail/issue-15207.rs index 61877775269d1..70da8cf4169bd 100644 --- a/src/test/compile-fail/issue-15207.rs +++ b/src/test/compile-fail/issue-15207.rs @@ -10,7 +10,7 @@ fn main() { loop { - break.push(1) //~ ERROR the type of this value must be known in this context + break.push(1) //~ ERROR no method named `push` found for type `!` ; } } diff --git a/src/test/compile-fail/issue-15965.rs b/src/test/compile-fail/issue-15965.rs index 08b896f387bbe..d5d597c190ea0 100644 --- a/src/test/compile-fail/issue-15965.rs +++ b/src/test/compile-fail/issue-15965.rs @@ -11,7 +11,7 @@ fn main() { return { return () } -//~^ ERROR the type of this value must be known in this context +//~^ ERROR expected function, found `!` () ; } diff --git a/src/test/compile-fail/issue-17373.rs b/src/test/compile-fail/issue-17373.rs index 6895893adc4d0..f6e6a8a0852dd 100644 --- a/src/test/compile-fail/issue-17373.rs +++ b/src/test/compile-fail/issue-17373.rs @@ -9,6 +9,6 @@ // except according to those terms. fn main() { - *return //~ ERROR the type of this value must be known in this context + *return //~ ERROR type `!` cannot be dereferenced ; } diff --git a/src/test/compile-fail/issue-18532.rs b/src/test/compile-fail/issue-18532.rs index 94eab97c42a19..2be5fdcac4ede 100644 --- a/src/test/compile-fail/issue-18532.rs +++ b/src/test/compile-fail/issue-18532.rs @@ -13,6 +13,5 @@ // into it. fn main() { - (return)((),()); - //~^ ERROR the type of this value must be known + (return)((),()); //~ ERROR expected function, found `!` } From 16a71cce514f29cb8c532248cf749e26972afb01 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 11:49:53 -0400 Subject: [PATCH 15/36] rework how we handle the type of loops First, we keep a `CoerceMany` now to find the LUB of all the break expressions. Second, this `CoerceMany` is actually an `Option`, and we store `None` for loops where "break with an expression" is disallowed. This avoids silly duplicate errors about a type mismatch, since the loops pass already reports an error that the break cannot have an expression. Finally, since we now detect an invalid break target during HIR lowering, refactor `find_loop` to be infallible. Adjust tests as needed: - some spans from breaks are slightly different - break up a single loop into multiple since `CoerceMany` silences redundant and derived errors - add a ui test that we only give on error for loop-break-value --- src/librustc_typeck/check/mod.rs | 207 ++++++++++-------- src/test/compile-fail/issue-27042.rs | 8 +- src/test/compile-fail/loop-break-value.rs | 23 +- src/test/ui/loop-break-value-no-repeat.rs | 25 +++ src/test/ui/loop-break-value-no-repeat.stderr | 8 + 5 files changed, 161 insertions(+), 110 deletions(-) create mode 100644 src/test/ui/loop-break-value-no-repeat.rs create mode 100644 src/test/ui/loop-break-value-no-repeat.stderr diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index ae0ed81ac778b..59f3ac739e0a2 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -415,15 +415,16 @@ impl Diverges { } #[derive(Clone)] -pub struct BreakableCtxt<'gcx, 'tcx> { - unified: Ty<'tcx>, - coerce_to: Ty<'tcx>, - break_exprs: Vec<&'gcx hir::Expr>, +pub struct BreakableCtxt<'gcx: 'tcx, 'tcx> { may_break: bool, + + // this is `null` for loops where break with a value is illegal, + // such as `while`, `for`, and `while let` + coerce: Option>, } #[derive(Clone)] -pub struct EnclosingBreakables<'gcx, 'tcx> { +pub struct EnclosingBreakables<'gcx: 'tcx, 'tcx> { stack: Vec>, by_id: NodeMap, } @@ -3547,60 +3548,66 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { tcx.mk_nil() } hir::ExprBreak(destination, ref expr_opt) => { - if let Some(target_id) = destination.target_id.opt_id() { - let coerce_to = { - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - enclosing_breakables.find_breakable(target_id).coerce_to - }; - - let e_ty; - let cause; - if let Some(ref e) = *expr_opt { - // Recurse without `enclosing_loops` borrowed. - e_ty = self.check_expr_with_hint(e, coerce_to); - cause = self.misc(e.span); - // Notably, the recursive call may alter coerce_to - must not keep using it! - } else { - // `break` without argument acts like `break ()`. - e_ty = tcx.mk_nil(); - cause = self.misc(expr.span); - } - - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - let ctxt = enclosing_breakables.find_breakable(target_id); + if let Some(target_id) = destination.target_id.opt_id() { + let (e_ty, cause); + if let Some(ref e) = *expr_opt { + // If this is a break with a value, we need to type-check + // the expression. Get an expected type from the loop context. + let opt_coerce_to = { + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + enclosing_breakables.find_breakable(target_id) + .coerce + .as_ref() + .map(|coerce| coerce.expected_ty()) + }; + + // If the loop context is not a `loop { }`, then break with + // a value is illegal, and `opt_coerce_to` will be `None`. + // Just set expectation to error in that case. + let coerce_to = opt_coerce_to.unwrap_or(tcx.types.err); + + // Recurse without `enclosing_breakables` borrowed. + e_ty = self.check_expr_with_hint(e, coerce_to); + cause = self.misc(e.span); + } else { + // Otherwise, this is a break *without* a value. That's + // always legal, and is equivalent to `break ()`. + e_ty = tcx.mk_nil(); + cause = self.misc(expr.span); + } - let result = if let Some(ref e) = *expr_opt { - // Special-case the first element, as it has no "previous expressions". - let result = if !ctxt.may_break { - self.try_coerce(e, e_ty, ctxt.coerce_to) - } else { - self.try_find_coercion_lub(&cause, || ctxt.break_exprs.iter().cloned(), - ctxt.unified, e, e_ty) - }; + // Now that we have type-checked `expr_opt`, borrow + // the `enclosing_loops` field and let's coerce the + // type of `expr_opt` into what is expected. + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + let ctxt = enclosing_breakables.find_breakable(target_id); + if let Some(ref mut coerce) = ctxt.coerce { + if let Some(ref e) = *expr_opt { + coerce.coerce(self, &cause, e, e_ty); + } else { + assert!(e_ty.is_nil()); + coerce.coerce_forced_unit(self, &cause); + } + } else { + // If `ctxt.coerce` is `None`, we can just ignore + // the type of the expresison. This is because + // either this was a break *without* a value, in + // which case it is always a legal type (`()`), or + // else an error would have been flagged by the + // `loops` pass for using break with an expression + // where you are not supposed to. + assert!(expr_opt.is_none() || self.tcx.sess.err_count() > 0); + } - ctxt.break_exprs.push(e); - result - } else { - self.eq_types(true, &cause, e_ty, ctxt.unified) - .map(|InferOk { obligations, .. }| { - // FIXME(#32730) propagate obligations - assert!(obligations.is_empty()); - e_ty - }) - }; - match result { - Ok(ty) => ctxt.unified = ty, - Err(err) => { - self.report_mismatched_types(&cause, ctxt.unified, e_ty, err).emit(); - } - } + ctxt.may_break = true; + } else { + // Otherwise, we failed to find the enclosing loop; this can only happen if the + // `break` was not inside a loop at all, which is caught by the loop-checking pass. + assert!(self.tcx.sess.err_count() > 0); + } - ctxt.may_break = true; - } - // Otherwise, we failed to find the enclosing breakable; this can only happen if the - // `break` target was not found, which is caught in HIR lowering and reported by the - // loop-checking pass. - tcx.types.never + // the type of a `break` is always `!`, since it diverges + tcx.types.never } hir::ExprAgain(_) => { tcx.types.never } hir::ExprRet(ref expr_opt) => { @@ -3645,51 +3652,59 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { expr.span, expected) } hir::ExprWhile(ref cond, ref body, _) => { - let unified = self.tcx.mk_nil(); - let coerce_to = unified; - let ctxt = BreakableCtxt { - unified: unified, - coerce_to: coerce_to, - break_exprs: vec![], - may_break: true, - }; - self.with_breakable_ctxt(expr.id, ctxt, || { - self.check_expr_has_type(&cond, tcx.types.bool); - let cond_diverging = self.diverges.get(); - self.check_block_no_value(&body); + let ctxt = BreakableCtxt { + // cannot use break with a value from a while loop + coerce: None, + may_break: true, + }; - // We may never reach the body so it diverging means nothing. - self.diverges.set(cond_diverging); - }); + self.with_breakable_ctxt(expr.id, ctxt, || { + self.check_expr_has_type(&cond, tcx.types.bool); + let cond_diverging = self.diverges.get(); + self.check_block_no_value(&body); - if self.has_errors.get() { - tcx.types.err - } else { - tcx.mk_nil() - } + // We may never reach the body so it diverging means nothing. + self.diverges.set(cond_diverging); + }); + + self.tcx.mk_nil() } - hir::ExprLoop(ref body, _, _) => { - let unified = self.next_ty_var(TypeVariableOrigin::TypeInference(body.span)); - let coerce_to = expected.only_has_type(self).unwrap_or(unified); - let ctxt = BreakableCtxt { - unified: unified, - coerce_to: coerce_to, - break_exprs: vec![], - may_break: false, - }; + hir::ExprLoop(ref body, _, source) => { + let coerce = match source { + // you can only use break with a value from a normal `loop { }` + hir::LoopSource::Loop => { + let coerce_to = expected.only_has_type_or_fresh_var(self, body.span); + Some(CoerceMany::new(coerce_to)) + } - let (ctxt, ()) = self.with_breakable_ctxt(expr.id, ctxt, || { - self.check_block_no_value(&body); - }); - if ctxt.may_break { - // No way to know whether it's diverging because - // of a `break` or an outer `break` or `return. - self.diverges.set(Diverges::Maybe); + hir::LoopSource::WhileLet | + hir::LoopSource::ForLoop => { + None + } + }; - ctxt.unified - } else { - tcx.types.never - } + let ctxt = BreakableCtxt { + coerce: coerce, + may_break: false, // will get updated if/when we find a `break` + }; + + let (ctxt, ()) = self.with_breakable_ctxt(expr.id, ctxt, || { + self.check_block_no_value(&body); + }); + + if ctxt.may_break { + // No way to know whether it's diverging because + // of a `break` or an outer `break` or `return. + self.diverges.set(Diverges::Maybe); + } + + // If we permit break with a value, then result type is + // the LUB of the breaks (possibly ! if none); else, it + // is nil. This makes sense because infinite loops + // (which would have type !) are only possible iff we + // permit break with a value [1]. + assert!(ctxt.coerce.is_some() || ctxt.may_break); // [1] + ctxt.coerce.map(|c| c.complete(self)).unwrap_or(self.tcx.mk_nil()) } hir::ExprMatch(ref discrim, ref arms, match_src) => { self.check_match(expr, &discrim, arms, expected, match_src) diff --git a/src/test/compile-fail/issue-27042.rs b/src/test/compile-fail/issue-27042.rs index f31389f1337dd..23afa4b629636 100644 --- a/src/test/compile-fail/issue-27042.rs +++ b/src/test/compile-fail/issue-27042.rs @@ -12,14 +12,14 @@ fn main() { let _: i32 = - 'a: //~ ERROR mismatched types - loop { break }; + 'a: // in this case, the citation is just the `break`: + loop { break }; //~ ERROR mismatched types let _: i32 = 'b: //~ ERROR mismatched types - while true { break }; + while true { break }; // but here we cite the whole loop let _: i32 = 'c: //~ ERROR mismatched types - for _ in None { break }; + for _ in None { break }; // but here we cite the whole loop let _: i32 = 'd: //~ ERROR mismatched types while let Some(_) = None { break }; diff --git a/src/test/compile-fail/loop-break-value.rs b/src/test/compile-fail/loop-break-value.rs index d4f2959748698..a414321899203 100644 --- a/src/test/compile-fail/loop-break-value.rs +++ b/src/test/compile-fail/loop-break-value.rs @@ -40,37 +40,40 @@ fn main() { loop { break 'while_loop 123; //~^ ERROR `break` with value from a `while` loop - //~| ERROR mismatched types break 456; break 789; }; } - 'while_let_loop: while let Some(_) = Some(()) { + while let Some(_) = Some(()) { if break () { //~ ERROR `break` with value from a `while let` loop - break; - break None; - //~^ ERROR `break` with value from a `while let` loop - //~| ERROR mismatched types } + } + + while let Some(_) = Some(()) { + break None; + //~^ ERROR `break` with value from a `while let` loop + } + + 'while_let_loop: while let Some(_) = Some(()) { loop { break 'while_let_loop "nope"; //~^ ERROR `break` with value from a `while let` loop - //~| ERROR mismatched types break 33; }; } - 'for_loop: for _ in &[1,2,3] { + for _ in &[1,2,3] { break (); //~ ERROR `break` with value from a `for` loop break [()]; //~^ ERROR `break` with value from a `for` loop - //~| ERROR mismatched types + } + + 'for_loop: for _ in &[1,2,3] { loop { break Some(3); break 'for_loop Some(17); //~^ ERROR `break` with value from a `for` loop - //~| ERROR mismatched types }; } diff --git a/src/test/ui/loop-break-value-no-repeat.rs b/src/test/ui/loop-break-value-no-repeat.rs new file mode 100644 index 0000000000000..790f796fae07f --- /dev/null +++ b/src/test/ui/loop-break-value-no-repeat.rs @@ -0,0 +1,25 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(loop_break_value)] +#![allow(unused_variables)] + +use std::ptr; + +// Test that we only report **one** error here and that is that +// `break` with an expression is illegal in this context. In +// particular, we don't report any mismatched types error, which is +// besides the point. + +fn main() { + for _ in &[1,2,3] { + break 22 + } +} diff --git a/src/test/ui/loop-break-value-no-repeat.stderr b/src/test/ui/loop-break-value-no-repeat.stderr new file mode 100644 index 0000000000000..0d99abd3907d8 --- /dev/null +++ b/src/test/ui/loop-break-value-no-repeat.stderr @@ -0,0 +1,8 @@ +error[E0571]: `break` with value from a `for` loop + --> $DIR/loop-break-value-no-repeat.rs:23:9 + | +23 | break 22 + | ^^^^^^^^ can only break with a value inside `loop` + +error: aborting due to previous error + From 140165f22e5614a4f14e9f7175c5607dfaf12363 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 11:52:37 -0400 Subject: [PATCH 16/36] rewrite `ExprArray` processing to use `CoerceMany` --- src/librustc_typeck/check/mod.rs | 49 +++++++++++++------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 59f3ac739e0a2..73d4e7db1d842 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3752,36 +3752,27 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { typ } hir::ExprArray(ref args) => { - let uty = expected.to_option(self).and_then(|uty| { - match uty.sty { - ty::TyArray(ty, _) | ty::TySlice(ty) => Some(ty), - _ => None - } - }); - - let mut unified = self.next_ty_var(TypeVariableOrigin::TypeInference(expr.span)); - let coerce_to = uty.unwrap_or(unified); - - for (i, e) in args.iter().enumerate() { - let e_ty = self.check_expr_with_hint(e, coerce_to); - let cause = self.misc(e.span); - - // Special-case the first element, as it has no "previous expressions". - let result = if i == 0 { - self.try_coerce(e, e_ty, coerce_to) - } else { - let prev_elems = || args[..i].iter().map(|e| &*e); - self.try_find_coercion_lub(&cause, prev_elems, unified, e, e_ty) - }; + let uty = expected.to_option(self).and_then(|uty| { + match uty.sty { + ty::TyArray(ty, _) | ty::TySlice(ty) => Some(ty), + _ => None + } + }); - match result { - Ok(ty) => unified = ty, - Err(e) => { - self.report_mismatched_types(&cause, unified, e_ty, e).emit(); - } - } - } - tcx.mk_array(unified, args.len()) + let element_ty = if !args.is_empty() { + let coerce_to = uty.unwrap_or_else( + || self.next_ty_var(TypeVariableOrigin::TypeInference(expr.span))); + let mut coerce = CoerceMany::new(coerce_to); + for e in args { + let e_ty = self.check_expr_with_hint(e, coerce_to); + let cause = self.misc(e.span); + coerce.coerce(self, &cause, e, e_ty); + } + coerce.complete(self) + } else { + self.next_ty_var(TypeVariableOrigin::TypeInference(expr.span)) + }; + tcx.mk_array(element_ty, args.len()) } hir::ExprRepeat(ref element, count) => { let count = eval_length(self.tcx.global_tcx(), count, "repeat count") From cecccd9bef2ce56b91b7ac3b067f0afa2e7846db Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 11:53:02 -0400 Subject: [PATCH 17/36] whitespace changes, debug message --- src/librustc_typeck/check/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 73d4e7db1d842..21eaa0c2120d5 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1526,6 +1526,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if self.diverges.get() == Diverges::Always { self.diverges.set(Diverges::WarnedAlways); + debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); + self.tables.borrow_mut().lints.add_lint( lint::builtin::UNREACHABLE_CODE, id, span, @@ -2545,8 +2547,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Expectation::rvalue_hint(self, ty) }); - let checked_ty = self.check_expr_with_expectation(&arg, - expected.unwrap_or(ExpectHasType(formal_ty))); + let checked_ty = self.check_expr_with_expectation( + &arg, + expected.unwrap_or(ExpectHasType(formal_ty))); + // 2. Coerce to the most detailed type that could be coerced // to, which is `expected_ty` if `rvalue_hint` returns an // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. From 52e524a3578b7661d6430735c7cd51eeccdacb0b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 11:53:15 -0400 Subject: [PATCH 18/36] make `try_find_coercion_lub` private; we always use `CoerceMany` --- src/librustc_typeck/check/coercion.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index a08faf2610e33..c420449cea092 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -715,13 +715,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// Given some expressions, their known unified type and another expression, /// tries to unify the types, potentially inserting coercions on any of the /// provided expressions and returns their LUB (aka "common supertype"). - pub fn try_find_coercion_lub<'b, E, I>(&self, - cause: &ObligationCause<'tcx>, - exprs: E, - prev_ty: Ty<'tcx>, - new: &'b hir::Expr, - new_ty: Ty<'tcx>) - -> RelateResult<'tcx, Ty<'tcx>> + /// + /// This is really an internal helper. From outside the coercion + /// module, you should instantiate a `CoerceMany` instance. + fn try_find_coercion_lub<'b, E, I>(&self, + cause: &ObligationCause<'tcx>, + exprs: E, + prev_ty: Ty<'tcx>, + new: &'b hir::Expr, + new_ty: Ty<'tcx>) + -> RelateResult<'tcx, Ty<'tcx>> where E: Fn() -> I, I: IntoIterator { From 5cd99aa1677075baecbd5224861b36a7719e0f25 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 14:10:00 -0400 Subject: [PATCH 19/36] more detailed tests around diverging type variables --- .../compile-fail/defaulted-unit-warning.rs | 12 +- src/test/compile-fail/never-fallback.rs | 41 ------- .../diverging-fallback-control-flow.rs | 106 ++++++++++++++++++ ....rs => diverging-fallback-method-chain.rs} | 33 ++---- .../run-pass/diverging-fallback-option.rs | 22 ++++ 5 files changed, 140 insertions(+), 74 deletions(-) delete mode 100644 src/test/compile-fail/never-fallback.rs create mode 100644 src/test/run-pass/diverging-fallback-control-flow.rs rename src/test/run-pass/{unit-fallback.rs => diverging-fallback-method-chain.rs} (51%) create mode 100644 src/test/run-pass/diverging-fallback-option.rs diff --git a/src/test/compile-fail/defaulted-unit-warning.rs b/src/test/compile-fail/defaulted-unit-warning.rs index 5213a189714dd..ed6263d0fdbd1 100644 --- a/src/test/compile-fail/defaulted-unit-warning.rs +++ b/src/test/compile-fail/defaulted-unit-warning.rs @@ -22,16 +22,6 @@ impl Deserialize for () { } } -fn doit() -> Result<(), String> { - let _ = match Deserialize::deserialize() { - //~^ ERROR code relies on type - //~| WARNING previously accepted - Ok(x) => x, - Err(e) => return Err(e), - }; - Ok(()) -} - trait ImplementedForUnitButNotNever {} impl ImplementedForUnitButNotNever for () {} @@ -46,6 +36,6 @@ fn smeg() { } fn main() { - let _ = doit(); + smeg(); } diff --git a/src/test/compile-fail/never-fallback.rs b/src/test/compile-fail/never-fallback.rs deleted file mode 100644 index a43b1a45fe939..0000000000000 --- a/src/test/compile-fail/never-fallback.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Test that diverging types default to ! when feature(never_type) is enabled. This test is the -// same as run-pass/unit-fallback.rs except that ! is enabled. - -#![feature(never_type)] - -trait Balls: Sized { - fn smeg() -> Result; -} - -impl Balls for () { - fn smeg() -> Result<(), ()> { Ok(()) } -} - -struct Flah; - -impl Flah { - fn flah(&self) -> Result { - T::smeg() - } -} - -fn doit() -> Result<(), ()> { - // The type of _ is unconstrained here and should default to ! - let _ = try!(Flah.flah()); //~ ERROR the trait bound - Ok(()) -} - -fn main() { - let _ = doit(); -} - diff --git a/src/test/run-pass/diverging-fallback-control-flow.rs b/src/test/run-pass/diverging-fallback-control-flow.rs new file mode 100644 index 0000000000000..656e90d2d52d7 --- /dev/null +++ b/src/test/run-pass/diverging-fallback-control-flow.rs @@ -0,0 +1,106 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test various cases where we permit an unconstrained variable +// to fallback based on control-flow. +// +// These represent current behavior, but are pretty dubious. I would +// like to revisit these and potentially change them. --nmatsakis + +#![feature(never_type)] +#![feature(loop_break_value)] + +trait BadDefault { + fn default() -> Self; +} + +impl BadDefault for u32 { + fn default() -> Self { + 0 + } +} + +impl BadDefault for ! { + fn default() -> ! { + panic!() + } +} + +fn assignment() { + let x; + + if true { + x = BadDefault::default(); + } else { + x = return; + } +} + +fn assignment_rev() { + let x; + + if true { + x = return; + } else { + x = BadDefault::default(); + } +} + +fn if_then_else() { + let _x = if true { + BadDefault::default() + } else { + return; + }; +} + +fn if_then_else_rev() { + let _x = if true { + return; + } else { + BadDefault::default() + }; +} + +fn match_arm() { + let _x = match Ok(BadDefault::default()) { + Ok(v) => v, + Err(()) => return, + }; +} + +fn match_arm_rev() { + let _x = match Ok(BadDefault::default()) { + Err(()) => return, + Ok(v) => v, + }; +} + +fn loop_break() { + let _x = loop { + if false { + break return; + } else { + break BadDefault::default(); + } + }; +} + +fn loop_break_rev() { + let _x = loop { + if false { + break return; + } else { + break BadDefault::default(); + } + }; +} + +fn main() { } diff --git a/src/test/run-pass/unit-fallback.rs b/src/test/run-pass/diverging-fallback-method-chain.rs similarity index 51% rename from src/test/run-pass/unit-fallback.rs rename to src/test/run-pass/diverging-fallback-method-chain.rs index 2babc6348e107..664a329c228ae 100644 --- a/src/test/run-pass/unit-fallback.rs +++ b/src/test/run-pass/diverging-fallback-method-chain.rs @@ -8,31 +8,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Test that diverging types default to () (with feature(never_type) disabled). +// Test a regression found when building compiler. The `produce()` +// error type `T` winds up getting unified with result of `x.parse()`; +// the type of the closure given to `unwrap_or_else` needs to be +// inferred to `usize`. -trait Balls: Sized { - fn smeg() -> Result; -} - -impl Balls for () { - fn smeg() -> Result<(), ()> { Ok(()) } -} - -struct Flah; +use std::num::ParseIntError; -impl Flah { - fn flah(&self) -> Result { - T::smeg() - } -} - -fn doit() -> Result<(), ()> { - // The type of _ is unconstrained here and should default to () - let _ = try!(Flah.flah()); - Ok(()) +fn produce() -> Result<&'static str, T> { + Ok("22") } fn main() { - let _ = doit(); + let x: usize = produce() + .and_then(|x| x.parse()) + .unwrap_or_else(|_| panic!()); + println!("{}", x); } - diff --git a/src/test/run-pass/diverging-fallback-option.rs b/src/test/run-pass/diverging-fallback-option.rs new file mode 100644 index 0000000000000..49f90e7c91f34 --- /dev/null +++ b/src/test/run-pass/diverging-fallback-option.rs @@ -0,0 +1,22 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(warnings)] + +// Here the type of `c` is `Option`, where `?T` is unconstrained. +// Because there is data-flow from the `{ return; }` block, which +// diverges and hence has type `!`, into `c`, we will default `?T` to +// `!`, and hence this code compiles rather than failing and requiring +// a type annotation. + +fn main() { + let c = Some({ return; }); + c.unwrap(); +} From 2f526cc89773d3b14c3d909152697678bc8fa7a1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 11:54:23 -0400 Subject: [PATCH 20/36] we now get an extra unreachable code warning in this test --- src/test/compile-fail/never-assign-dead-code.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/compile-fail/never-assign-dead-code.rs b/src/test/compile-fail/never-assign-dead-code.rs index 57e0bca6a6d77..d8752e1c050fc 100644 --- a/src/test/compile-fail/never-assign-dead-code.rs +++ b/src/test/compile-fail/never-assign-dead-code.rs @@ -16,5 +16,6 @@ fn main() { let x: ! = panic!("aah"); //~ ERROR unused drop(x); //~ ERROR unreachable + //~^ ERROR unreachable } From f11b7d33bb71a78beb6a4671d53d2e89a2afc0ef Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 18 Mar 2017 15:22:48 -0400 Subject: [PATCH 21/36] add regression test for #39808 Fixes #39808 --- src/test/run-pass/issue-39808.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/test/run-pass/issue-39808.rs b/src/test/run-pass/issue-39808.rs index f83e9328e5879..00c2bdc8cc99d 100644 --- a/src/test/run-pass/issue-39808.rs +++ b/src/test/run-pass/issue-39808.rs @@ -8,14 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Regression test: even though `Ok` is dead-code, its type needs to -// be influenced by the result of `Err` or else we get a "type -// variable unconstrained" error. +#![allow(unreachable_code)] + +// Regression test for #39808. The type parameter of `Owned` was +// considered to be "unconstrained" because the type resulting from +// `format!` (`String`) was not being propagated upward, owing to the +// fact that the expression diverges. + +use std::borrow::Cow; fn main() { let _ = if false { - Ok(return) + Cow::Owned(format!("{:?}", panic!())) /* as Cow */ // uncomment to fix } else { - Err("") + Cow::Borrowed("") }; } From a60e27e25b88dd7febdb3f878a09bef08c03eb41 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 21 Mar 2017 09:19:49 -0400 Subject: [PATCH 22/36] pacify the mercilous tidy --- src/librustc_typeck/check/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 21eaa0c2120d5..65e7e240cc360 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3605,8 +3605,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ctxt.may_break = true; } else { - // Otherwise, we failed to find the enclosing loop; this can only happen if the - // `break` was not inside a loop at all, which is caught by the loop-checking pass. + // Otherwise, we failed to find the enclosing loop; + // this can only happen if the `break` was not + // inside a loop at all, which is caught by the + // loop-checking pass. assert!(self.tcx.sess.err_count() > 0); } From 609bfe82fd1c1207ab59f6c08bbadf45e587bd4a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 21 Mar 2017 09:41:41 -0400 Subject: [PATCH 23/36] cherry-pick over the tests I wrote for the reachability code For the most part, the current code performs similarly, although it differs in some particulars. I'll be nice to have these tests for judging future changes, as well. --- src/test/ui/reachable/README.md | 7 +++ src/test/ui/reachable/expr_add.rs | 28 ++++++++++++ src/test/ui/reachable/expr_add.stderr | 14 ++++++ src/test/ui/reachable/expr_again.rs | 20 +++++++++ src/test/ui/reachable/expr_again.stderr | 15 +++++++ src/test/ui/reachable/expr_andand.rs | 21 +++++++++ src/test/ui/reachable/expr_andand.stderr | 0 src/test/ui/reachable/expr_array.rs | 28 ++++++++++++ src/test/ui/reachable/expr_array.stderr | 20 +++++++++ src/test/ui/reachable/expr_assign.rs | 39 +++++++++++++++++ src/test/ui/reachable/expr_assign.stderr | 26 ++++++++++++ src/test/ui/reachable/expr_block.rs | 41 ++++++++++++++++++ src/test/ui/reachable/expr_block.stderr | 22 ++++++++++ src/test/ui/reachable/expr_box.rs | 18 ++++++++ src/test/ui/reachable/expr_box.stderr | 14 ++++++ src/test/ui/reachable/expr_call.rs | 31 ++++++++++++++ src/test/ui/reachable/expr_call.stderr | 20 +++++++++ src/test/ui/reachable/expr_cast.rs | 23 ++++++++++ src/test/ui/reachable/expr_cast.stderr | 14 ++++++ src/test/ui/reachable/expr_if.rs | 41 ++++++++++++++++++ src/test/ui/reachable/expr_if.stderr | 15 +++++++ src/test/ui/reachable/expr_loop.rs | 44 +++++++++++++++++++ src/test/ui/reachable/expr_loop.stderr | 31 ++++++++++++++ src/test/ui/reachable/expr_match.rs | 54 ++++++++++++++++++++++++ src/test/ui/reachable/expr_match.stderr | 30 +++++++++++++ src/test/ui/reachable/expr_method.rs | 34 +++++++++++++++ src/test/ui/reachable/expr_method.stderr | 20 +++++++++ src/test/ui/reachable/expr_oror.rs | 20 +++++++++ src/test/ui/reachable/expr_oror.stderr | 0 src/test/ui/reachable/expr_repeat.rs | 23 ++++++++++ src/test/ui/reachable/expr_repeat.stderr | 14 ++++++ src/test/ui/reachable/expr_return.rs | 24 +++++++++++ src/test/ui/reachable/expr_return.stderr | 14 ++++++ src/test/ui/reachable/expr_struct.rs | 43 +++++++++++++++++++ src/test/ui/reachable/expr_struct.stderr | 32 ++++++++++++++ src/test/ui/reachable/expr_tup.rs | 28 ++++++++++++ src/test/ui/reachable/expr_tup.stderr | 20 +++++++++ src/test/ui/reachable/expr_type.rs | 23 ++++++++++ src/test/ui/reachable/expr_type.stderr | 14 ++++++ src/test/ui/reachable/expr_unary.rs | 21 +++++++++ src/test/ui/reachable/expr_unary.stderr | 8 ++++ src/test/ui/reachable/expr_while.rs | 38 +++++++++++++++++ src/test/ui/reachable/expr_while.stderr | 31 ++++++++++++++ 43 files changed, 1023 insertions(+) create mode 100644 src/test/ui/reachable/README.md create mode 100644 src/test/ui/reachable/expr_add.rs create mode 100644 src/test/ui/reachable/expr_add.stderr create mode 100644 src/test/ui/reachable/expr_again.rs create mode 100644 src/test/ui/reachable/expr_again.stderr create mode 100644 src/test/ui/reachable/expr_andand.rs create mode 100644 src/test/ui/reachable/expr_andand.stderr create mode 100644 src/test/ui/reachable/expr_array.rs create mode 100644 src/test/ui/reachable/expr_array.stderr create mode 100644 src/test/ui/reachable/expr_assign.rs create mode 100644 src/test/ui/reachable/expr_assign.stderr create mode 100644 src/test/ui/reachable/expr_block.rs create mode 100644 src/test/ui/reachable/expr_block.stderr create mode 100644 src/test/ui/reachable/expr_box.rs create mode 100644 src/test/ui/reachable/expr_box.stderr create mode 100644 src/test/ui/reachable/expr_call.rs create mode 100644 src/test/ui/reachable/expr_call.stderr create mode 100644 src/test/ui/reachable/expr_cast.rs create mode 100644 src/test/ui/reachable/expr_cast.stderr create mode 100644 src/test/ui/reachable/expr_if.rs create mode 100644 src/test/ui/reachable/expr_if.stderr create mode 100644 src/test/ui/reachable/expr_loop.rs create mode 100644 src/test/ui/reachable/expr_loop.stderr create mode 100644 src/test/ui/reachable/expr_match.rs create mode 100644 src/test/ui/reachable/expr_match.stderr create mode 100644 src/test/ui/reachable/expr_method.rs create mode 100644 src/test/ui/reachable/expr_method.stderr create mode 100644 src/test/ui/reachable/expr_oror.rs create mode 100644 src/test/ui/reachable/expr_oror.stderr create mode 100644 src/test/ui/reachable/expr_repeat.rs create mode 100644 src/test/ui/reachable/expr_repeat.stderr create mode 100644 src/test/ui/reachable/expr_return.rs create mode 100644 src/test/ui/reachable/expr_return.stderr create mode 100644 src/test/ui/reachable/expr_struct.rs create mode 100644 src/test/ui/reachable/expr_struct.stderr create mode 100644 src/test/ui/reachable/expr_tup.rs create mode 100644 src/test/ui/reachable/expr_tup.stderr create mode 100644 src/test/ui/reachable/expr_type.rs create mode 100644 src/test/ui/reachable/expr_type.stderr create mode 100644 src/test/ui/reachable/expr_unary.rs create mode 100644 src/test/ui/reachable/expr_unary.stderr create mode 100644 src/test/ui/reachable/expr_while.rs create mode 100644 src/test/ui/reachable/expr_while.stderr diff --git a/src/test/ui/reachable/README.md b/src/test/ui/reachable/README.md new file mode 100644 index 0000000000000..8bed5fba7a2e8 --- /dev/null +++ b/src/test/ui/reachable/README.md @@ -0,0 +1,7 @@ +A variety of tests around reachability. These tests in general check +two things: + +- that we get unreachable code warnings in reasonable locations; +- that we permit coercions **into** `!` from expressions which + diverge, where an expression "diverges" if it must execute some + subexpression of type `!`, or it has type `!` itself. diff --git a/src/test/ui/reachable/expr_add.rs b/src/test/ui/reachable/expr_add.rs new file mode 100644 index 0000000000000..87d017adf6819 --- /dev/null +++ b/src/test/ui/reachable/expr_add.rs @@ -0,0 +1,28 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(never_type)] +#![allow(unused_variables)] +#![deny(unreachable_code)] + +use std::ops; + +struct Foo; + +impl ops::Add for Foo { + type Output = !; + fn add(self, rhs: !) -> ! { + unimplemented!() + } +} + +fn main() { + let x = Foo + return; +} diff --git a/src/test/ui/reachable/expr_add.stderr b/src/test/ui/reachable/expr_add.stderr new file mode 100644 index 0000000000000..1a2cc252051bf --- /dev/null +++ b/src/test/ui/reachable/expr_add.stderr @@ -0,0 +1,14 @@ +error: unreachable expression + --> $DIR/expr_add.rs:27:13 + | +27 | let x = Foo + return; + | ^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/expr_add.rs:13:9 + | +13 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/reachable/expr_again.rs b/src/test/ui/reachable/expr_again.rs new file mode 100644 index 0000000000000..cdbdb8dc0dbb3 --- /dev/null +++ b/src/test/ui/reachable/expr_again.rs @@ -0,0 +1,20 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(box_syntax)] +#![allow(unused_variables)] +#![deny(unreachable_code)] + +fn main() { + let x = loop { + continue; + println!("hi"); + }; +} diff --git a/src/test/ui/reachable/expr_again.stderr b/src/test/ui/reachable/expr_again.stderr new file mode 100644 index 0000000000000..bf4e4dc4711cb --- /dev/null +++ b/src/test/ui/reachable/expr_again.stderr @@ -0,0 +1,15 @@ +error: unreachable statement + --> $DIR/expr_again.rs:18:9 + | +18 | println!("hi"); + | ^^^^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/expr_again.rs:13:9 + | +13 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro outside of the current crate + +error: aborting due to previous error + diff --git a/src/test/ui/reachable/expr_andand.rs b/src/test/ui/reachable/expr_andand.rs new file mode 100644 index 0000000000000..af404d03097b6 --- /dev/null +++ b/src/test/ui/reachable/expr_andand.rs @@ -0,0 +1,21 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(dead_code)] +#![deny(unreachable_code)] + +fn foo() { + // No error here. + let x = false && (return); + println!("I am not dead."); +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_andand.stderr b/src/test/ui/reachable/expr_andand.stderr new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/test/ui/reachable/expr_array.rs b/src/test/ui/reachable/expr_array.rs new file mode 100644 index 0000000000000..00e8be0772547 --- /dev/null +++ b/src/test/ui/reachable/expr_array.rs @@ -0,0 +1,28 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(dead_code)] +#![deny(unreachable_code)] +#![feature(never_type)] +#![feature(type_ascription)] + +fn a() { + // the `22` is unreachable: + let x: [usize; 2] = [return, 22]; +} + +fn b() { + // the `array is unreachable: + let x: [usize; 2] = [22, return]; +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_array.stderr b/src/test/ui/reachable/expr_array.stderr new file mode 100644 index 0000000000000..f8dbdb5f8bb66 --- /dev/null +++ b/src/test/ui/reachable/expr_array.stderr @@ -0,0 +1,20 @@ +error: unreachable expression + --> $DIR/expr_array.rs:20:34 + | +20 | let x: [usize; 2] = [return, 22]; + | ^^ + | +note: lint level defined here + --> $DIR/expr_array.rs:14:9 + | +14 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: unreachable expression + --> $DIR/expr_array.rs:25:25 + | +25 | let x: [usize; 2] = [22, return]; + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/reachable/expr_assign.rs b/src/test/ui/reachable/expr_assign.rs new file mode 100644 index 0000000000000..1b9357013d270 --- /dev/null +++ b/src/test/ui/reachable/expr_assign.rs @@ -0,0 +1,39 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(dead_code)] +#![deny(unreachable_code)] +#![feature(never_type)] + +fn foo() { + // No error here. + let x; + x = return; +} + +fn bar() { + use std::ptr; + let p: *mut ! = ptr::null_mut::(); + unsafe { + // Here we consider the `return` unreachable because + // "evaluating" the `*p` has type `!`. This is somewhat + // dubious, I suppose. + *p = return; + } +} + +fn baz() { + let mut i = 0; + *{return; &mut i} = 22; +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_assign.stderr b/src/test/ui/reachable/expr_assign.stderr new file mode 100644 index 0000000000000..807f6a1c1d584 --- /dev/null +++ b/src/test/ui/reachable/expr_assign.stderr @@ -0,0 +1,26 @@ +error: unreachable expression + --> $DIR/expr_assign.rs:20:5 + | +20 | x = return; + | ^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/expr_assign.rs:14:9 + | +14 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: unreachable expression + --> $DIR/expr_assign.rs:30:14 + | +30 | *p = return; + | ^^^^^^ + +error: unreachable expression + --> $DIR/expr_assign.rs:36:15 + | +36 | *{return; &mut i} = 22; + | ^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/reachable/expr_block.rs b/src/test/ui/reachable/expr_block.rs new file mode 100644 index 0000000000000..093589b4dc839 --- /dev/null +++ b/src/test/ui/reachable/expr_block.rs @@ -0,0 +1,41 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(dead_code)] +#![deny(unreachable_code)] +#![feature(never_type)] + +fn a() { + // Here the tail expression is considered unreachable: + let x = { + return; + 22 + }; +} + +fn b() { + // Here the `x` assignment is considered unreachable, not the block: + let x = { + return; + }; +} + +fn c() { + // Here the `println!` is unreachable: + let x = { + return; + println!("foo"); + 22 + }; +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_block.stderr b/src/test/ui/reachable/expr_block.stderr new file mode 100644 index 0000000000000..542ce1c3fd9cb --- /dev/null +++ b/src/test/ui/reachable/expr_block.stderr @@ -0,0 +1,22 @@ +error: unreachable expression + --> $DIR/expr_block.rs:21:9 + | +21 | 22 + | ^^ + | +note: lint level defined here + --> $DIR/expr_block.rs:14:9 + | +14 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: unreachable statement + --> $DIR/expr_block.rs:36:9 + | +36 | println!("foo"); + | ^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro outside of the current crate + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/reachable/expr_box.rs b/src/test/ui/reachable/expr_box.rs new file mode 100644 index 0000000000000..6509b608335af --- /dev/null +++ b/src/test/ui/reachable/expr_box.rs @@ -0,0 +1,18 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(box_syntax)] +#![allow(unused_variables)] +#![deny(unreachable_code)] + +fn main() { + let x = box return; + println!("hi"); +} diff --git a/src/test/ui/reachable/expr_box.stderr b/src/test/ui/reachable/expr_box.stderr new file mode 100644 index 0000000000000..78ba231cef9fc --- /dev/null +++ b/src/test/ui/reachable/expr_box.stderr @@ -0,0 +1,14 @@ +error: unreachable expression + --> $DIR/expr_box.rs:16:13 + | +16 | let x = box return; + | ^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/expr_box.rs:13:9 + | +13 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/reachable/expr_call.rs b/src/test/ui/reachable/expr_call.rs new file mode 100644 index 0000000000000..8d9f303df7fdc --- /dev/null +++ b/src/test/ui/reachable/expr_call.rs @@ -0,0 +1,31 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(dead_code)] +#![deny(unreachable_code)] +#![feature(never_type)] + +fn foo(x: !, y: usize) { } + +fn bar(x: !) { } + +fn a() { + // the `22` is unreachable: + foo(return, 22); +} + +fn b() { + // the call is unreachable: + bar(return); +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_call.stderr b/src/test/ui/reachable/expr_call.stderr new file mode 100644 index 0000000000000..5526827f59fc9 --- /dev/null +++ b/src/test/ui/reachable/expr_call.stderr @@ -0,0 +1,20 @@ +error: unreachable expression + --> $DIR/expr_call.rs:23:17 + | +23 | foo(return, 22); + | ^^ + | +note: lint level defined here + --> $DIR/expr_call.rs:14:9 + | +14 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: unreachable expression + --> $DIR/expr_call.rs:28:5 + | +28 | bar(return); + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/reachable/expr_cast.rs b/src/test/ui/reachable/expr_cast.rs new file mode 100644 index 0000000000000..926ef864ebf21 --- /dev/null +++ b/src/test/ui/reachable/expr_cast.rs @@ -0,0 +1,23 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(dead_code)] +#![deny(unreachable_code)] +#![feature(never_type)] +#![feature(type_ascription)] + +fn a() { + // the cast is unreachable: + let x = {return} as !; +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_cast.stderr b/src/test/ui/reachable/expr_cast.stderr new file mode 100644 index 0000000000000..a22300dcc1398 --- /dev/null +++ b/src/test/ui/reachable/expr_cast.stderr @@ -0,0 +1,14 @@ +error: unreachable expression + --> $DIR/expr_cast.rs:20:13 + | +20 | let x = {return} as !; + | ^^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/expr_cast.rs:14:9 + | +14 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/reachable/expr_if.rs b/src/test/ui/reachable/expr_if.rs new file mode 100644 index 0000000000000..2a265e772f359 --- /dev/null +++ b/src/test/ui/reachable/expr_if.rs @@ -0,0 +1,41 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(dead_code)] +#![deny(unreachable_code)] +#![feature(never_type)] + +fn foo() { + if {return} { + println!("Hello, world!"); + } +} + +fn bar() { + if {true} { + return; + } + println!("I am not dead."); +} + +fn baz() { + if {true} { + return; + } else { + return; + } + // As the next action to be taken after the if arms, we should + // report the `println!` as unreachable: + println!("But I am."); +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_if.stderr b/src/test/ui/reachable/expr_if.stderr new file mode 100644 index 0000000000000..2cf17474f6e9d --- /dev/null +++ b/src/test/ui/reachable/expr_if.stderr @@ -0,0 +1,15 @@ +error: unreachable statement + --> $DIR/expr_if.rs:38:5 + | +38 | println!("But I am."); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/expr_if.rs:14:9 + | +14 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro outside of the current crate + +error: aborting due to previous error + diff --git a/src/test/ui/reachable/expr_loop.rs b/src/test/ui/reachable/expr_loop.rs new file mode 100644 index 0000000000000..3ed4b2dcf0cf8 --- /dev/null +++ b/src/test/ui/reachable/expr_loop.rs @@ -0,0 +1,44 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(dead_code)] +#![deny(unreachable_code)] +#![feature(never_type)] + +fn a() { + loop { return; } + println!("I am dead."); +} + +fn b() { + loop { + break; + } + println!("I am not dead."); +} + +fn c() { + loop { return; } + println!("I am dead."); +} + +fn d() { + 'outer: loop { loop { break 'outer; } } + println!("I am not dead."); +} + +fn e() { + loop { 'middle: loop { loop { break 'middle; } } } + println!("I am dead."); +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_loop.stderr b/src/test/ui/reachable/expr_loop.stderr new file mode 100644 index 0000000000000..6e98e754c54db --- /dev/null +++ b/src/test/ui/reachable/expr_loop.stderr @@ -0,0 +1,31 @@ +error: unreachable statement + --> $DIR/expr_loop.rs:19:5 + | +19 | println!("I am dead."); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/expr_loop.rs:14:9 + | +14 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro outside of the current crate + +error: unreachable statement + --> $DIR/expr_loop.rs:31:5 + | +31 | println!("I am dead."); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro outside of the current crate + +error: unreachable statement + --> $DIR/expr_loop.rs:41:5 + | +41 | println!("I am dead."); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro outside of the current crate + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/reachable/expr_match.rs b/src/test/ui/reachable/expr_match.rs new file mode 100644 index 0000000000000..23bdcc035b227 --- /dev/null +++ b/src/test/ui/reachable/expr_match.rs @@ -0,0 +1,54 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(dead_code)] +#![deny(unreachable_code)] +#![feature(never_type)] + +fn a() { + // The match is considered unreachable here, because the `return` + // diverges: + match {return} { } +} + +fn b() { + match () { () => return } + println!("I am dead"); +} + +fn c() { + match () { () if false => return, () => () } + println!("I am not dead"); +} + +fn d() { + match () { () if false => return, () => return } + println!("I am dead"); +} + +fn e() { + // Here the compiler fails to figure out that the `println` is dead. + match () { () if return => (), () => return } + println!("I am dead"); +} + +fn f() { + match Some(()) { None => (), Some(()) => return } + println!("I am not dead"); +} + +fn g() { + match Some(()) { None => return, Some(()) => () } + println!("I am not dead"); +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_match.stderr b/src/test/ui/reachable/expr_match.stderr new file mode 100644 index 0000000000000..f5857a5b345ec --- /dev/null +++ b/src/test/ui/reachable/expr_match.stderr @@ -0,0 +1,30 @@ +error: unreachable expression + --> $DIR/expr_match.rs:20:5 + | +20 | match {return} { } + | ^^^^^^^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/expr_match.rs:14:9 + | +14 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: unreachable statement + --> $DIR/expr_match.rs:25:5 + | +25 | println!("I am dead"); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro outside of the current crate + +error: unreachable statement + --> $DIR/expr_match.rs:35:5 + | +35 | println!("I am dead"); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro outside of the current crate + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/reachable/expr_method.rs b/src/test/ui/reachable/expr_method.rs new file mode 100644 index 0000000000000..f1d979d7df79d --- /dev/null +++ b/src/test/ui/reachable/expr_method.rs @@ -0,0 +1,34 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(dead_code)] +#![deny(unreachable_code)] +#![feature(never_type)] + +struct Foo; + +impl Foo { + fn foo(&self, x: !, y: usize) { } + fn bar(&self, x: !) { } +} + +fn a() { + // the `22` is unreachable: + Foo.foo(return, 22); +} + +fn b() { + // the call is unreachable: + Foo.bar(return); +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_method.stderr b/src/test/ui/reachable/expr_method.stderr new file mode 100644 index 0000000000000..177d4352a376d --- /dev/null +++ b/src/test/ui/reachable/expr_method.stderr @@ -0,0 +1,20 @@ +error: unreachable expression + --> $DIR/expr_method.rs:26:21 + | +26 | Foo.foo(return, 22); + | ^^ + | +note: lint level defined here + --> $DIR/expr_method.rs:14:9 + | +14 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: unreachable expression + --> $DIR/expr_method.rs:31:5 + | +31 | Foo.bar(return); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/reachable/expr_oror.rs b/src/test/ui/reachable/expr_oror.rs new file mode 100644 index 0000000000000..d01304d4034b5 --- /dev/null +++ b/src/test/ui/reachable/expr_oror.rs @@ -0,0 +1,20 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(dead_code)] +#![deny(unreachable_code)] + +fn foo() { + let x = false || (return); + println!("I am not dead."); +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_oror.stderr b/src/test/ui/reachable/expr_oror.stderr new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/test/ui/reachable/expr_repeat.rs b/src/test/ui/reachable/expr_repeat.rs new file mode 100644 index 0000000000000..6078d6d5bde46 --- /dev/null +++ b/src/test/ui/reachable/expr_repeat.rs @@ -0,0 +1,23 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(dead_code)] +#![deny(unreachable_code)] +#![feature(never_type)] +#![feature(type_ascription)] + +fn a() { + // the repeat is unreachable: + let x: [usize; 2] = [return; 2]; +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_repeat.stderr b/src/test/ui/reachable/expr_repeat.stderr new file mode 100644 index 0000000000000..19afc5dd7b5ee --- /dev/null +++ b/src/test/ui/reachable/expr_repeat.stderr @@ -0,0 +1,14 @@ +error: unreachable expression + --> $DIR/expr_repeat.rs:20:25 + | +20 | let x: [usize; 2] = [return; 2]; + | ^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/expr_repeat.rs:14:9 + | +14 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/reachable/expr_return.rs b/src/test/ui/reachable/expr_return.rs new file mode 100644 index 0000000000000..c640ca0663029 --- /dev/null +++ b/src/test/ui/reachable/expr_return.rs @@ -0,0 +1,24 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(dead_code)] +#![deny(unreachable_code)] +#![feature(never_type)] +#![feature(type_ascription)] + +fn a() { + // Here we issue that the "2nd-innermost" return is unreachable, + // but we stop there. + let x = {return {return {return;}}}; +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_return.stderr b/src/test/ui/reachable/expr_return.stderr new file mode 100644 index 0000000000000..3eb70a4dd7c84 --- /dev/null +++ b/src/test/ui/reachable/expr_return.stderr @@ -0,0 +1,14 @@ +error: unreachable expression + --> $DIR/expr_return.rs:21:22 + | +21 | let x = {return {return {return;}}}; + | ^^^^^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/expr_return.rs:14:9 + | +14 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/reachable/expr_struct.rs b/src/test/ui/reachable/expr_struct.rs new file mode 100644 index 0000000000000..09e31819279f2 --- /dev/null +++ b/src/test/ui/reachable/expr_struct.rs @@ -0,0 +1,43 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(dead_code)] +#![deny(unreachable_code)] +#![feature(never_type)] +#![feature(type_ascription)] + +struct Foo { + a: usize, + b: usize, +} + +fn a() { + // struct expr is unreachable: + let x = Foo { a: 22, b: 33, ..return }; +} + +fn b() { + // the `33` is unreachable: + let x = Foo { a: return, b: 33, ..return }; +} + +fn c() { + // the `..return` is unreachable: + let x = Foo { a: 22, b: return, ..return }; +} + +fn d() { + // the struct expr is unreachable: + let x = Foo { a: 22, b: return }; +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_struct.stderr b/src/test/ui/reachable/expr_struct.stderr new file mode 100644 index 0000000000000..4b7ac6604132c --- /dev/null +++ b/src/test/ui/reachable/expr_struct.stderr @@ -0,0 +1,32 @@ +error: unreachable expression + --> $DIR/expr_struct.rs:25:13 + | +25 | let x = Foo { a: 22, b: 33, ..return }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/expr_struct.rs:14:9 + | +14 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: unreachable expression + --> $DIR/expr_struct.rs:30:33 + | +30 | let x = Foo { a: return, b: 33, ..return }; + | ^^ + +error: unreachable expression + --> $DIR/expr_struct.rs:35:39 + | +35 | let x = Foo { a: 22, b: return, ..return }; + | ^^^^^^ + +error: unreachable expression + --> $DIR/expr_struct.rs:40:13 + | +40 | let x = Foo { a: 22, b: return }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/reachable/expr_tup.rs b/src/test/ui/reachable/expr_tup.rs new file mode 100644 index 0000000000000..7c75296de6c54 --- /dev/null +++ b/src/test/ui/reachable/expr_tup.rs @@ -0,0 +1,28 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(dead_code)] +#![deny(unreachable_code)] +#![feature(never_type)] +#![feature(type_ascription)] + +fn a() { + // the `2` is unreachable: + let x: (usize, usize) = (return, 2); +} + +fn b() { + // the tuple is unreachable: + let x: (usize, usize) = (2, return); +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_tup.stderr b/src/test/ui/reachable/expr_tup.stderr new file mode 100644 index 0000000000000..63f477fd0c373 --- /dev/null +++ b/src/test/ui/reachable/expr_tup.stderr @@ -0,0 +1,20 @@ +error: unreachable expression + --> $DIR/expr_tup.rs:20:38 + | +20 | let x: (usize, usize) = (return, 2); + | ^ + | +note: lint level defined here + --> $DIR/expr_tup.rs:14:9 + | +14 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: unreachable expression + --> $DIR/expr_tup.rs:25:29 + | +25 | let x: (usize, usize) = (2, return); + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/reachable/expr_type.rs b/src/test/ui/reachable/expr_type.rs new file mode 100644 index 0000000000000..2fa277c382e87 --- /dev/null +++ b/src/test/ui/reachable/expr_type.rs @@ -0,0 +1,23 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(dead_code)] +#![deny(unreachable_code)] +#![feature(never_type)] +#![feature(type_ascription)] + +fn a() { + // the cast is unreachable: + let x = {return}: !; +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_type.stderr b/src/test/ui/reachable/expr_type.stderr new file mode 100644 index 0000000000000..6ed79974ccb77 --- /dev/null +++ b/src/test/ui/reachable/expr_type.stderr @@ -0,0 +1,14 @@ +error: unreachable expression + --> $DIR/expr_type.rs:20:13 + | +20 | let x = {return}: !; + | ^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/expr_type.rs:14:9 + | +14 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/reachable/expr_unary.rs b/src/test/ui/reachable/expr_unary.rs new file mode 100644 index 0000000000000..57901fbaa7c44 --- /dev/null +++ b/src/test/ui/reachable/expr_unary.rs @@ -0,0 +1,21 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(dead_code)] +#![deny(unreachable_code)] +#![feature(never_type)] + +fn foo() { + let x: ! = ! { return; 22 }; +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_unary.stderr b/src/test/ui/reachable/expr_unary.stderr new file mode 100644 index 0000000000000..11172652d8445 --- /dev/null +++ b/src/test/ui/reachable/expr_unary.stderr @@ -0,0 +1,8 @@ +error: cannot apply unary operator `!` to type `!` + --> $DIR/expr_unary.rs:18:16 + | +18 | let x: ! = ! { return; 22 }; + | ^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/reachable/expr_while.rs b/src/test/ui/reachable/expr_while.rs new file mode 100644 index 0000000000000..7dcd609fbc8f4 --- /dev/null +++ b/src/test/ui/reachable/expr_while.rs @@ -0,0 +1,38 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(dead_code)] +#![deny(unreachable_code)] +#![feature(never_type)] + +fn foo() { + while {return} { + println!("Hello, world!"); + } +} + +fn bar() { + while {true} { + return; + } + println!("I am not dead."); +} + +fn baz() { + // Here, we cite the `while` loop as dead. + while {return} { + println!("I am dead."); + } + println!("I am, too."); +} + +fn main() { } diff --git a/src/test/ui/reachable/expr_while.stderr b/src/test/ui/reachable/expr_while.stderr new file mode 100644 index 0000000000000..066cfc86c6462 --- /dev/null +++ b/src/test/ui/reachable/expr_while.stderr @@ -0,0 +1,31 @@ +error: unreachable statement + --> $DIR/expr_while.rs:19:9 + | +19 | println!("Hello, world!"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/expr_while.rs:14:9 + | +14 | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro outside of the current crate + +error: unreachable statement + --> $DIR/expr_while.rs:33:9 + | +33 | println!("I am dead."); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro outside of the current crate + +error: unreachable statement + --> $DIR/expr_while.rs:35:5 + | +35 | println!("I am, too."); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro outside of the current crate + +error: aborting due to 3 previous errors + From bad79484fb99d40f48e3b84776699526a37513e0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 21 Mar 2017 15:37:57 -0400 Subject: [PATCH 24/36] avoid allocating a vector when the coercion sites are known upfront --- src/librustc_typeck/check/_match.rs | 2 +- src/librustc_typeck/check/autoderef.rs | 12 +- src/librustc_typeck/check/callee.rs | 2 +- src/librustc_typeck/check/coercion.rs | 174 +++++++++++++++----- src/librustc_typeck/check/method/confirm.rs | 4 +- src/librustc_typeck/check/mod.rs | 18 +- src/librustc_typeck/lib.rs | 1 + 7 files changed, 154 insertions(+), 59 deletions(-) diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index f0d2598a0fb2a..5ca8f2c01c589 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -474,7 +474,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Expectation::ExpectHasType(ety) if ety != self.tcx.mk_nil() => ety, _ => self.next_ty_var(TypeVariableOrigin::MiscVariable(expr.span)), }; - CoerceMany::new(coerce_first) + CoerceMany::with_coercion_sites(coerce_first, arms) }; for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() { diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs index 1aab4853a4f64..647adbbb82f2d 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -12,6 +12,7 @@ use astconv::AstConv; use super::FnCtxt; +use check::coercion::AsCoercionSite; use rustc::infer::InferOk; use rustc::traits; use rustc::ty::{self, Ty, TraitRef}; @@ -148,16 +149,16 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> { self.fcx.resolve_type_vars_if_possible(&self.cur_ty) } - pub fn finalize<'b, I>(self, pref: LvaluePreference, exprs: I) - where I: IntoIterator + pub fn finalize(self, pref: LvaluePreference, exprs: &[E]) + where E: AsCoercionSite { let fcx = self.fcx; fcx.register_infer_ok_obligations(self.finalize_as_infer_ok(pref, exprs)); } - pub fn finalize_as_infer_ok<'b, I>(self, pref: LvaluePreference, exprs: I) - -> InferOk<'tcx, ()> - where I: IntoIterator + pub fn finalize_as_infer_ok(self, pref: LvaluePreference, exprs: &[E]) + -> InferOk<'tcx, ()> + where E: AsCoercionSite { let methods: Vec<_> = self.steps .iter() @@ -176,6 +177,7 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> { self.obligations); for expr in exprs { + let expr = expr.as_coercion_site(); debug!("finalize - finalizing #{} - {:?}", expr.id, expr); for (n, method) in methods.iter().enumerate() { if let &Some(method) = method { diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 529ee107c46ce..f9bc947a97358 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -55,7 +55,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }) .next(); let callee_ty = autoderef.unambiguous_final_ty(); - autoderef.finalize(LvaluePreference::NoPreference, Some(callee_expr)); + autoderef.finalize(LvaluePreference::NoPreference, &[callee_expr]); let output = match result { None => { diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index c420449cea092..eba58df781f1b 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -76,6 +76,7 @@ use rustc::ty::relate::RelateResult; use rustc::ty::subst::Subst; use syntax::abi; use syntax::feature_gate; +use syntax::ptr::P; use std::collections::VecDeque; use std::ops::Deref; @@ -155,11 +156,9 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { }) } - fn coerce<'a, E, I>(&self, exprs: &E, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> - where E: Fn() -> I, - I: IntoIterator + fn coerce(&self, exprs: &[E], a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> + where E: AsCoercionSite { - let a = self.shallow_resolve(a); debug!("Coerce.tys({:?} => {:?})", a, b); @@ -239,15 +238,14 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`. /// To match `A` with `B`, autoderef will be performed, /// calling `deref`/`deref_mut` where necessary. - fn coerce_borrowed_pointer<'a, E, I>(&self, - exprs: &E, - a: Ty<'tcx>, - b: Ty<'tcx>, - r_b: &'tcx ty::Region, - mt_b: TypeAndMut<'tcx>) - -> CoerceResult<'tcx> - where E: Fn() -> I, - I: IntoIterator + fn coerce_borrowed_pointer(&self, + exprs: &[E], + a: Ty<'tcx>, + b: Ty<'tcx>, + r_b: &'tcx ty::Region, + mt_b: TypeAndMut<'tcx>) + -> CoerceResult<'tcx> + where E: AsCoercionSite { debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b); @@ -424,7 +422,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { autoref); let pref = LvaluePreference::from_mutbl(mt_b.mutbl); - obligations.extend(autoderef.finalize_as_infer_ok(pref, exprs()).obligations); + obligations.extend(autoderef.finalize_as_infer_ok(pref, exprs).obligations); success(Adjust::DerefRef { autoderefs: autoderefs, @@ -699,7 +697,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable); let coerce = Coerce::new(self, cause); self.commit_if_ok(|_| { - let ok = coerce.coerce(&|| Some(expr), source, target)?; + let ok = coerce.coerce(&[expr], source, target)?; let adjustment = self.register_infer_ok_obligations(ok); if !adjustment.is_identity() { debug!("Success, coerced with {:?}", adjustment); @@ -718,15 +716,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// /// This is really an internal helper. From outside the coercion /// module, you should instantiate a `CoerceMany` instance. - fn try_find_coercion_lub<'b, E, I>(&self, - cause: &ObligationCause<'tcx>, - exprs: E, - prev_ty: Ty<'tcx>, - new: &'b hir::Expr, - new_ty: Ty<'tcx>) - -> RelateResult<'tcx, Ty<'tcx>> - where E: Fn() -> I, - I: IntoIterator + fn try_find_coercion_lub(&self, + cause: &ObligationCause<'tcx>, + exprs: &[E], + prev_ty: Ty<'tcx>, + new: &hir::Expr, + new_ty: Ty<'tcx>) + -> RelateResult<'tcx, Ty<'tcx>> + where E: AsCoercionSite { let prev_ty = self.resolve_type_vars_with_obligations(prev_ty); @@ -758,7 +755,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Reify both sides and return the reified fn pointer type. let fn_ptr = self.tcx.mk_fn_ptr(fty); - for expr in exprs().into_iter().chain(Some(new)) { + for expr in exprs.iter().map(|e| e.as_coercion_site()).chain(Some(new)) { // No adjustments can produce a fn item, so this should never trip. assert!(!self.tables.borrow().adjustments.contains_key(&expr.id)); self.write_adjustment(expr.id, Adjustment { @@ -778,7 +775,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // but only if the new expression has no coercion already applied to it. let mut first_error = None; if !self.tables.borrow().adjustments.contains_key(&new.id) { - let result = self.commit_if_ok(|_| coerce.coerce(&|| Some(new), new_ty, prev_ty)); + let result = self.commit_if_ok(|_| coerce.coerce(&[new], new_ty, prev_ty)); match result { Ok(ok) => { let adjustment = self.register_infer_ok_obligations(ok); @@ -794,7 +791,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Then try to coerce the previous expressions to the type of the new one. // This requires ensuring there are no coercions applied to *any* of the // previous expressions, other than noop reborrows (ignoring lifetimes). - for expr in exprs() { + for expr in exprs { + let expr = expr.as_coercion_site(); let noop = match self.tables.borrow().adjustments.get(&expr.id).map(|adj| adj.kind) { Some(Adjust::DerefRef { autoderefs: 1, @@ -838,7 +836,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let adjustment = self.register_infer_ok_obligations(ok); if !adjustment.is_identity() { let mut tables = self.tables.borrow_mut(); - for expr in exprs() { + for expr in exprs { + let expr = expr.as_coercion_site(); if let Some(&mut Adjustment { kind: Adjust::NeverToAny, ref mut target @@ -897,25 +896,61 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// let final_ty = coerce.complete(fcx); /// ``` #[derive(Clone)] // (*) -pub struct CoerceMany<'gcx: 'tcx, 'tcx> { +pub struct CoerceMany<'gcx, 'tcx, 'exprs, E> + where 'gcx: 'tcx, E: 'exprs + AsCoercionSite, +{ expected_ty: Ty<'tcx>, final_ty: Option>, - expressions: Vec<&'gcx hir::Expr>, + expressions: Expressions<'gcx, 'exprs, E>, + pushed: usize, +} + +/// The type of a `CoerceMany` that is storing up the expressions into +/// a buffer. We use this in `check/mod.rs` for things like `break`. +pub type DynamicCoerceMany<'gcx, 'tcx> = CoerceMany<'gcx, 'tcx, 'static, hir::Expr>; + +#[derive(Clone)] // (*) +enum Expressions<'gcx, 'exprs, E> + where E: 'exprs + AsCoercionSite, +{ + Dynamic(Vec<&'gcx hir::Expr>), + UpFront(&'exprs [E]), } // (*) this is clone because `FnCtxt` is clone, but it seems dubious -- nmatsakis -impl<'gcx, 'tcx> CoerceMany<'gcx, 'tcx> { +impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> + where 'gcx: 'tcx, E: 'exprs + AsCoercionSite, +{ + /// The usual case; collect the set of expressions dynamically. + /// If the full set of coercion sites is known before hand, + /// consider `with_coercion_sites()` instead to avoid allocation. pub fn new(expected_ty: Ty<'tcx>) -> Self { + Self::make(expected_ty, Expressions::Dynamic(vec![])) + } + + /// As an optimization, you can create a `CoerceMany` with a + /// pre-existing slice of expressions. In this case, you are + /// expected to pass each element in the slice to `coerce(...)` in + /// order. This is used with arrays in particular to avoid + /// needlessly cloning the slice. + pub fn with_coercion_sites(expected_ty: Ty<'tcx>, + coercion_sites: &'exprs [E]) + -> Self { + Self::make(expected_ty, Expressions::UpFront(coercion_sites)) + } + + fn make(expected_ty: Ty<'tcx>, expressions: Expressions<'gcx, 'exprs, E>) -> Self { CoerceMany { expected_ty, final_ty: None, - expressions: vec![], + expressions, + pushed: 0, } } pub fn is_empty(&self) -> bool { - self.expressions.is_empty() + self.pushed == 0 } /// Return the "expected type" with which this coercion was @@ -997,16 +1032,25 @@ impl<'gcx, 'tcx> CoerceMany<'gcx, 'tcx> { // Handle the actual type unification etc. let result = if let Some(expression) = expression { - if self.expressions.is_empty() { + if self.pushed == 0 { // Special-case the first expression we are coercing. // To be honest, I'm not entirely sure why we do this. fcx.try_coerce(expression, expression_ty, self.expected_ty) } else { - fcx.try_find_coercion_lub(cause, - || self.expressions.iter().cloned(), - self.merged_ty(), - expression, - expression_ty) + match self.expressions { + Expressions::Dynamic(ref exprs) => + fcx.try_find_coercion_lub(cause, + exprs, + self.merged_ty(), + expression, + expression_ty), + Expressions::UpFront(ref coercion_sites) => + fcx.try_find_coercion_lub(cause, + &coercion_sites[0..self.pushed], + self.merged_ty(), + expression, + expression_ty), + } } } else { // this is a hack for cases where we default to `()` because @@ -1034,7 +1078,17 @@ impl<'gcx, 'tcx> CoerceMany<'gcx, 'tcx> { match result { Ok(v) => { self.final_ty = Some(v); - self.expressions.extend(expression); + if let Some(e) = expression { + match self.expressions { + Expressions::Dynamic(ref mut buffer) => buffer.push(e), + Expressions::UpFront(coercion_sites) => { + // if the user gave us an array to validate, check that we got + // the next expression in the list, as expected + assert_eq!(coercion_sites[self.pushed].as_coercion_site().id, e.id); + } + } + self.pushed += 1; + } } Err(err) => { let (expected, found) = if expression.is_none() { @@ -1076,8 +1130,46 @@ impl<'gcx, 'tcx> CoerceMany<'gcx, 'tcx> { } else { // If we only had inputs that were of type `!` (or no // inputs at all), then the final type is `!`. - assert!(self.expressions.is_empty()); + assert_eq!(self.pushed, 0); fcx.tcx.types.never } } } + +/// Something that can be converted into an expression to which we can +/// apply a coercion. +pub trait AsCoercionSite { + fn as_coercion_site(&self) -> &hir::Expr; +} + +impl AsCoercionSite for hir::Expr { + fn as_coercion_site(&self) -> &hir::Expr { + self + } +} + +impl AsCoercionSite for P { + fn as_coercion_site(&self) -> &hir::Expr { + self + } +} + +impl<'a, T> AsCoercionSite for &'a T + where T: AsCoercionSite +{ + fn as_coercion_site(&self) -> &hir::Expr { + (**self).as_coercion_site() + } +} + +impl AsCoercionSite for ! { + fn as_coercion_site(&self) -> &hir::Expr { + unreachable!() + } +} + +impl AsCoercionSite for hir::Arm { + fn as_coercion_site(&self) -> &hir::Expr { + &self.body + } +} diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index e6e4b577bd50d..73f6cd76290aa 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -137,7 +137,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { assert_eq!(n, pick.autoderefs); autoderef.unambiguous_final_ty(); - autoderef.finalize(LvaluePreference::NoPreference, Some(self.self_expr)); + autoderef.finalize(LvaluePreference::NoPreference, &[self.self_expr]); let target = pick.unsize.unwrap_or(autoderefd_ty); let target = target.adjust_for_autoref(self.tcx, autoref); @@ -444,7 +444,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { "expr was deref-able {} times but now isn't?", autoderefs); }); - autoderef.finalize(PreferMutLvalue, Some(expr)); + autoderef.finalize(PreferMutLvalue, &[expr]); } } Some(_) | None => {} diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 65e7e240cc360..4b7d9a0fd3aae 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -77,7 +77,7 @@ type parameter). */ pub use self::Expectation::*; -use self::coercion::CoerceMany; +use self::coercion::{CoerceMany, DynamicCoerceMany}; pub use self::compare_method::{compare_impl_method, compare_const_impl}; use self::TupleArgumentsFlag::*; @@ -420,7 +420,7 @@ pub struct BreakableCtxt<'gcx: 'tcx, 'tcx> { // this is `null` for loops where break with a value is illegal, // such as `while`, `for`, and `while let` - coerce: Option>, + coerce: Option>, } #[derive(Clone)] @@ -450,7 +450,7 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { // expects the types within the function to be consistent. err_count_on_creation: usize, - ret_coercion: Option>>, + ret_coercion: Option>>, ps: RefCell, @@ -2245,12 +2245,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { expr, base_expr, adj_ty, autoderefs, false, lvalue_pref, idx_ty) { - autoderef.finalize(lvalue_pref, Some(base_expr)); + autoderef.finalize(lvalue_pref, &[base_expr]); return Some(final_mt); } if let ty::TyArray(element_ty, _) = adj_ty.sty { - autoderef.finalize(lvalue_pref, Some(base_expr)); + autoderef.finalize(lvalue_pref, &[base_expr]); let adjusted_ty = self.tcx.mk_slice(element_ty); return self.try_index_step( MethodCall::expr(expr.id), expr, base_expr, @@ -2861,7 +2861,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // (`only_has_type`); otherwise, we just go with a // fresh type variable. let coerce_to_ty = expected.only_has_type_or_fresh_var(self, sp); - let mut coerce = CoerceMany::new(coerce_to_ty); + let mut coerce: DynamicCoerceMany = CoerceMany::new(coerce_to_ty); let if_cause = self.cause(sp, ObligationCauseCode::IfExpression); coerce.coerce(self, &if_cause, then_expr, then_ty); @@ -2908,7 +2908,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let Some(field) = base_def.struct_variant().find_field_named(field.node) { let field_ty = self.field_ty(expr.span, field, substs); if self.tcx.vis_is_accessible_from(field.vis, self.body_id) { - autoderef.finalize(lvalue_pref, Some(base)); + autoderef.finalize(lvalue_pref, &[base]); self.write_autoderef_adjustment(base.id, autoderefs, base_t); self.tcx.check_stability(field.did, expr.id, expr.span); @@ -3032,7 +3032,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; if let Some(field_ty) = field { - autoderef.finalize(lvalue_pref, Some(base)); + autoderef.finalize(lvalue_pref, &[base]); self.write_autoderef_adjustment(base.id, autoderefs, base_t); return field_ty; } @@ -3768,7 +3768,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let element_ty = if !args.is_empty() { let coerce_to = uty.unwrap_or_else( || self.next_ty_var(TypeVariableOrigin::TypeInference(expr.span))); - let mut coerce = CoerceMany::new(coerce_to); + let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args); for e in args { let e_ty = self.check_expr_with_hint(e, coerce_to); let cause = self.misc(e.span); diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index df1c94dc19b59..0bde9fefeba0c 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -79,6 +79,7 @@ This API is completely unstable and subject to change. #![feature(conservative_impl_trait)] #![cfg_attr(stage0,feature(field_init_shorthand))] #![feature(loop_break_value)] +#![feature(never_type)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] From f83706454fd00c2a3b7873b50223b68ca2cbd0d0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Mar 2017 11:48:44 -0400 Subject: [PATCH 25/36] coercion now depends on whether the expression diverges Before I was checking this in `demand_coerce` but that's not really the right place. The right place is to move that into the coercion routine itself. --- src/librustc_typeck/check/_match.rs | 2 +- src/librustc_typeck/check/cast.rs | 10 ++++-- src/librustc_typeck/check/coercion.rs | 46 ++++++++++++++++++++------- src/librustc_typeck/check/demand.rs | 16 +--------- src/librustc_typeck/check/mod.rs | 18 +++++++---- 5 files changed, 55 insertions(+), 37 deletions(-) diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 5ca8f2c01c589..e83b786b9a838 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -504,7 +504,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { arm_span: arm.body.span, source: match_src }); - coercion.coerce(self, &cause, &arm.body, arm_ty); + coercion.coerce(self, &cause, &arm.body, arm_ty, self.diverges.get()); } } diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 441d427fe499e..ea0aad007dd73 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -38,7 +38,7 @@ //! expression, `e as U2` is not necessarily so (in fact it will only be valid if //! `U1` coerces to `U2`). -use super::FnCtxt; +use super::{Diverges, FnCtxt}; use lint; use hir::def_id::DefId; @@ -376,7 +376,10 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { (None, Some(t_cast)) => { if let ty::TyFnDef(.., f) = self.expr_ty.sty { // Attempt a coercion to a fn pointer type. - let res = fcx.try_coerce(self.expr, self.expr_ty, fcx.tcx.mk_fn_ptr(f)); + let res = fcx.try_coerce(self.expr, + self.expr_ty, + Diverges::Maybe, // TODO + fcx.tcx.mk_fn_ptr(f)); if !res.is_ok() { return Err(CastError::NonScalar); } @@ -542,7 +545,8 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { } fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> bool { - fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty).is_ok() + // TODO + fcx.try_coerce(self.expr, self.expr_ty, Diverges::Maybe, self.cast_ty).is_ok() } } diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index eba58df781f1b..a737b82700e34 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -60,7 +60,7 @@ //! sort of a minor point so I've opted to leave it for later---after all //! we may want to adjust precisely when coercions occur. -use check::FnCtxt; +use check::{Diverges, FnCtxt}; use rustc::hir; use rustc::hir::def_id::DefId; @@ -156,7 +156,11 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { }) } - fn coerce(&self, exprs: &[E], a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> + fn coerce(&self, + exprs: &[E], + a: Ty<'tcx>, + b: Ty<'tcx>) + -> CoerceResult<'tcx> where E: AsCoercionSite { let a = self.shallow_resolve(a); @@ -689,11 +693,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn try_coerce(&self, expr: &hir::Expr, expr_ty: Ty<'tcx>, + expr_diverges: Diverges, target: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { let source = self.resolve_type_vars_with_obligations(expr_ty); debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target); + // Special-ish case: we can coerce any type `T` into the `!` + // type, but only if the source expression diverges. + if target.is_never() && expr_diverges.always() { + debug!("permit coercion to `!` because expr diverges"); + return Ok(target); + } + let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable); let coerce = Coerce::new(self, cause); self.commit_if_ok(|_| { @@ -721,15 +733,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { exprs: &[E], prev_ty: Ty<'tcx>, new: &hir::Expr, - new_ty: Ty<'tcx>) + new_ty: Ty<'tcx>, + new_diverges: Diverges) -> RelateResult<'tcx, Ty<'tcx>> where E: AsCoercionSite { - let prev_ty = self.resolve_type_vars_with_obligations(prev_ty); let new_ty = self.resolve_type_vars_with_obligations(new_ty); debug!("coercion::try_find_lub({:?}, {:?})", prev_ty, new_ty); + // Special-ish case: we can coerce any type `T` into the `!` + // type, but only if the source expression diverges. + if prev_ty.is_never() && new_diverges.always() { + debug!("permit coercion to `!` because expr diverges"); + return Ok(prev_ty); + } + let trace = TypeTrace::types(cause, true, prev_ty, new_ty); // Special-case that coercion alone cannot handle: @@ -982,9 +1001,10 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> fcx: &FnCtxt<'a, 'gcx, 'tcx>, cause: &ObligationCause<'tcx>, expression: &'gcx hir::Expr, - expression_ty: Ty<'tcx>) + expression_ty: Ty<'tcx>, + expression_diverges: Diverges) { - self.coerce_inner(fcx, cause, Some(expression), expression_ty) + self.coerce_inner(fcx, cause, Some(expression), expression_ty, expression_diverges) } /// Indicates that one of the inputs is a "forced unit". This @@ -1002,7 +1022,8 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> self.coerce_inner(fcx, cause, None, - fcx.tcx.mk_nil()) + fcx.tcx.mk_nil(), + Diverges::Maybe) } /// The inner coercion "engine". If `expression` is `None`, this @@ -1012,7 +1033,8 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> fcx: &FnCtxt<'a, 'gcx, 'tcx>, cause: &ObligationCause<'tcx>, expression: Option<&'gcx hir::Expr>, - mut expression_ty: Ty<'tcx>) + mut expression_ty: Ty<'tcx>, + expression_diverges: Diverges) { // Incorporate whatever type inference information we have // until now; in principle we might also want to process @@ -1035,7 +1057,7 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> if self.pushed == 0 { // Special-case the first expression we are coercing. // To be honest, I'm not entirely sure why we do this. - fcx.try_coerce(expression, expression_ty, self.expected_ty) + fcx.try_coerce(expression, expression_ty, expression_diverges, self.expected_ty) } else { match self.expressions { Expressions::Dynamic(ref exprs) => @@ -1043,13 +1065,15 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> exprs, self.merged_ty(), expression, - expression_ty), + expression_ty, + expression_diverges), Expressions::UpFront(ref coercion_sites) => fcx.try_find_coercion_lub(cause, &coercion_sites[0..self.pushed], self.merged_ty(), expression, - expression_ty), + expression_ty, + expression_diverges), } } } else { diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 25d689b3c2c45..e922c7447ff85 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -77,21 +77,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { expected: Ty<'tcx>) { let expected = self.resolve_type_vars_with_obligations(expected); - // If we are "assigning" to a `!` location, then we can permit - // any type to be assigned there, so long as we are in - // dead-code. This applies to e.g. `fn foo() -> ! { return; 22 - // }` but also `fn foo() { let x: ! = { return; 22 }; }`. - // - // You might imagine that we could just *always* bail if we - // are in dead-code, but we don't want to do that, because - // that leaves a lot of type variables unconstrained. See - // e.g. #39808 and #39984. - let in_dead_code = self.diverges.get().always(); - if expected.is_never() && in_dead_code { - return; - } - - if let Err(e) = self.try_coerce(expr, checked_ty, expected) { + if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) { let cause = self.misc(expr.span); let expr_ty = self.resolve_type_vars_with_obligations(checked_ty); let mode = probe::Mode::MethodCall; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 4b7d9a0fd3aae..539f16ec6709b 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -366,7 +366,7 @@ impl UnsafetyState { /// as diverging), with some manual adjustments for control-flow /// primitives (approximating a CFG). #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -enum Diverges { +pub enum Diverges { /// Potentially unknown, some cases converge, /// others require a CFG to determine them. Maybe, @@ -2833,7 +2833,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { .coerce(self, &self.misc(return_expr.span), return_expr, - return_expr_ty); + return_expr_ty, + self.diverges.get()); } @@ -2864,13 +2865,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mut coerce: DynamicCoerceMany = CoerceMany::new(coerce_to_ty); let if_cause = self.cause(sp, ObligationCauseCode::IfExpression); - coerce.coerce(self, &if_cause, then_expr, then_ty); + coerce.coerce(self, &if_cause, then_expr, then_ty, then_diverges); if let Some(else_expr) = opt_else_expr { let else_ty = self.check_expr_with_expectation(else_expr, expected); let else_diverges = self.diverges.get(); - coerce.coerce(self, &if_cause, else_expr, else_ty); + coerce.coerce(self, &if_cause, else_expr, else_ty, else_diverges); // We won't diverge unless both branches do (or the condition does). self.diverges.set(cond_diverges | then_diverges & else_diverges); @@ -3553,7 +3554,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } hir::ExprBreak(destination, ref expr_opt) => { if let Some(target_id) = destination.target_id.opt_id() { - let (e_ty, cause); + let (e_ty, e_diverges, cause); if let Some(ref e) = *expr_opt { // If this is a break with a value, we need to type-check // the expression. Get an expected type from the loop context. @@ -3572,11 +3573,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Recurse without `enclosing_breakables` borrowed. e_ty = self.check_expr_with_hint(e, coerce_to); + e_diverges = self.diverges.get(); cause = self.misc(e.span); } else { // Otherwise, this is a break *without* a value. That's // always legal, and is equivalent to `break ()`. e_ty = tcx.mk_nil(); + e_diverges = Diverges::Maybe; cause = self.misc(expr.span); } @@ -3587,7 +3590,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let ctxt = enclosing_breakables.find_breakable(target_id); if let Some(ref mut coerce) = ctxt.coerce { if let Some(ref e) = *expr_opt { - coerce.coerce(self, &cause, e, e_ty); + coerce.coerce(self, &cause, e, e_ty, e_diverges); } else { assert!(e_ty.is_nil()); coerce.coerce_forced_unit(self, &cause); @@ -3769,10 +3772,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let coerce_to = uty.unwrap_or_else( || self.next_ty_var(TypeVariableOrigin::TypeInference(expr.span))); let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args); + assert_eq!(self.diverges.get(), Diverges::Maybe); for e in args { let e_ty = self.check_expr_with_hint(e, coerce_to); let cause = self.misc(e.span); - coerce.coerce(self, &cause, e, e_ty); + coerce.coerce(self, &cause, e, e_ty, self.diverges.get()); } coerce.complete(self) } else { From d448329849bb2cef355cabfdc6cb4d8b5b76b80e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Mar 2017 11:50:36 -0400 Subject: [PATCH 26/36] fix handling of blocks with `CoerceMany` --- src/librustc_typeck/check/coercion.rs | 2 +- src/librustc_typeck/check/mod.rs | 137 ++++++++++---------------- 2 files changed, 51 insertions(+), 88 deletions(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index a737b82700e34..8a9db674c6ebc 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -926,7 +926,7 @@ pub struct CoerceMany<'gcx, 'tcx, 'exprs, E> /// The type of a `CoerceMany` that is storing up the expressions into /// a buffer. We use this in `check/mod.rs` for things like `break`. -pub type DynamicCoerceMany<'gcx, 'tcx> = CoerceMany<'gcx, 'tcx, 'static, hir::Expr>; +pub type DynamicCoerceMany<'gcx, 'tcx> = CoerceMany<'gcx, 'tcx, 'gcx, P>; #[derive(Clone)] // (*) enum Expressions<'gcx, 'exprs, E> diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 539f16ec6709b..413d09117af08 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -86,6 +86,7 @@ use dep_graph::DepNode; use fmt_macros::{Parser, Piece, Position}; use hir::def::{Def, CtorKind}; use hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_back::slice::ref_slice; use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin, TypeTrace}; use rustc::infer::type_variable::{self, TypeVariableOrigin}; use rustc::ty::subst::{Kind, Subst, Substs}; @@ -4108,102 +4109,64 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { replace(&mut *fcx_ps, unsafety_state) }; - let mut ty = if blk.targeted_by_break { - let unified = self.next_ty_var(TypeVariableOrigin::TypeInference(blk.span)); - let coerce_to = expected.only_has_type(self).unwrap_or(unified); - let ctxt = BreakableCtxt { - unified: unified, - coerce_to: coerce_to, - break_exprs: vec![], - may_break: false, + // In some cases, blocks have just one exit, but other blocks + // can be targeted by multiple breaks. This cannot happen in + // normal Rust syntax today, but it can happen when we desugar + // a `do catch { ... }` expression. + // + // Example 1: + // + // 'a: { if true { break 'a Err(()); } Ok(()) } + // + // Here we would wind up with two coercions, one from + // `Err(())` and the other from the tail expression + // `Ok(())`. If the tail expression is omitted, that's a + // "forced unit" -- unless the block diverges, in which + // case we can ignore the tail expression (e.g., `'a: { + // break 'a 22; }` would not force the type of the block + // to be `()`). + let tail_expr = blk.expr.as_ref(); + let coerce_to_ty = expected.coercion_target_type(self, blk.span); + let coerce = if blk.targeted_by_break { + CoerceMany::new(coerce_to_ty) + } else { + let tail_expr: &[P] = match tail_expr { + Some(e) => ref_slice(e), + None => &[], }; + CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr) + }; - let (mut ctxt, (e_ty, cause)) = self.with_breakable_ctxt(blk.id, ctxt, || { - for s in &blk.stmts { - self.check_stmt(s); - } - let coerce_to = { - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - enclosing_breakables.find_breakable(blk.id).coerce_to - }; - let e_ty; - let cause; - match blk.expr { - Some(ref e) => { - e_ty = self.check_expr_with_hint(e, coerce_to); - cause = self.misc(e.span); - }, - None => { - e_ty = if self.diverges.get().always() { - self.tcx.types.never - } else { - self.tcx.mk_nil() - }; - cause = self.misc(blk.span); - } - }; - - (e_ty, cause) - }); - - if let ExpectHasType(ety) = expected { - if let Some(ref e) = blk.expr { - let result = if !ctxt.may_break { - self.try_coerce(e, e_ty, ctxt.coerce_to) - } else { - self.try_find_coercion_lub(&cause, || ctxt.break_exprs.iter().cloned(), - ctxt.unified, e, e_ty) - }; - match result { - Ok(ty) => ctxt.unified = ty, - Err(err) => - self.report_mismatched_types(&cause, ctxt.unified, e_ty, err).emit(), - } - } else if self.diverges.get().always() { - // No tail expression and the body diverges; ignore - // the expected type, and keep `!` as the type of the - // block. - } else { - self.check_block_no_expr(blk, self.tcx.mk_nil(), e_ty); - }; + let ctxt = BreakableCtxt { + coerce: Some(coerce), + may_break: false, + }; - ctxt.unified - } else { + let (ctxt, ()) = self.with_breakable_ctxt(blk.id, ctxt, || { for s in &blk.stmts { self.check_stmt(s); } - let mut ty = match blk.expr { - Some(ref e) => self.check_expr_with_expectation(e, expected), - None => if self.diverges.get().always() { - self.tcx.types.never - } else { - self.tcx.mk_nil() - }, - }; + // check the tail expression **without** holding the + // `enclosing_breakables` lock below. + let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected)); - if let ExpectHasType(ety) = expected { - if let Some(ref e) = blk.expr { - // Coerce the tail expression to the right type. - self.demand_coerce(e, ty, ety); - - // We already applied the type (and potentially errored), - // use the expected type to avoid further errors out. - ty = ety; - } else if self.diverges.get().always() { - // No tail expression and the body diverges; ignore - // the expected type, and keep `!` as the type of the - // block. - } else { - self.check_block_no_expr(blk, ty, ety); - - // We already applied the type (and potentially errored), - // use the expected type to avoid further errors out. - ty = ety; - } + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + let mut ctxt = enclosing_breakables.find_breakable(blk.id); + let mut coerce = ctxt.coerce.as_mut().unwrap(); + if let Some(tail_expr_ty) = tail_expr_ty { + let tail_expr = tail_expr.unwrap(); + coerce.coerce(self, + &self.misc(tail_expr.span), + tail_expr, + tail_expr_ty, + self.diverges.get()); // TODO + } else if !self.diverges.get().always() { + coerce.coerce_forced_unit(self, &self.misc(blk.span)); } - ty - }; + }); + + let mut ty = ctxt.coerce.unwrap().complete(self); if self.has_errors.get() || ty.references_error() { ty = self.tcx.types.err From 127a7d643d70dea605ca479c12a32f450d985b09 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Mar 2017 11:51:02 -0400 Subject: [PATCH 27/36] rename `only_has_type_or_fresh_var` to `coercion_target_type` --- src/librustc_typeck/check/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 413d09117af08..0a2cb94c591d7 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -314,7 +314,7 @@ impl<'a, 'gcx, 'tcx> Expectation<'tcx> { /// Like `only_has_type`, but instead of returning `None` if no /// hard constraint exists, creates a fresh type variable. - fn only_has_type_or_fresh_var(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>, span: Span) -> Ty<'tcx> { + fn coercion_target_type(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>, span: Span) -> Ty<'tcx> { self.only_has_type(fcx) .unwrap_or_else(|| fcx.next_ty_var(TypeVariableOrigin::MiscVariable(span))) } @@ -2862,7 +2862,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // `expected` if it represents a *hard* constraint // (`only_has_type`); otherwise, we just go with a // fresh type variable. - let coerce_to_ty = expected.only_has_type_or_fresh_var(self, sp); + let coerce_to_ty = expected.coercion_target_type(self, sp); let mut coerce: DynamicCoerceMany = CoerceMany::new(coerce_to_ty); let if_cause = self.cause(sp, ObligationCauseCode::IfExpression); @@ -3683,7 +3683,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let coerce = match source { // you can only use break with a value from a normal `loop { }` hir::LoopSource::Loop => { - let coerce_to = expected.only_has_type_or_fresh_var(self, body.span); + let coerce_to = expected.coercion_target_type(self, body.span); Some(CoerceMany::new(coerce_to)) } From d08a6da1f05dc6f8de4201efe949f1c26704ced0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 21 Mar 2017 21:21:24 -0400 Subject: [PATCH 28/36] remove `Clone` from `FnCtxt` --- src/librustc_typeck/check/coercion.rs | 4 ---- src/librustc_typeck/check/mod.rs | 3 --- 2 files changed, 7 deletions(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 8a9db674c6ebc..37d442f1d059e 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -914,7 +914,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// } /// let final_ty = coerce.complete(fcx); /// ``` -#[derive(Clone)] // (*) pub struct CoerceMany<'gcx, 'tcx, 'exprs, E> where 'gcx: 'tcx, E: 'exprs + AsCoercionSite, { @@ -928,7 +927,6 @@ pub struct CoerceMany<'gcx, 'tcx, 'exprs, E> /// a buffer. We use this in `check/mod.rs` for things like `break`. pub type DynamicCoerceMany<'gcx, 'tcx> = CoerceMany<'gcx, 'tcx, 'gcx, P>; -#[derive(Clone)] // (*) enum Expressions<'gcx, 'exprs, E> where E: 'exprs + AsCoercionSite, { @@ -936,8 +934,6 @@ enum Expressions<'gcx, 'exprs, E> UpFront(&'exprs [E]), } -// (*) this is clone because `FnCtxt` is clone, but it seems dubious -- nmatsakis - impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> where 'gcx: 'tcx, E: 'exprs + AsCoercionSite, { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 0a2cb94c591d7..9eaa8404bded9 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -415,7 +415,6 @@ impl Diverges { } } -#[derive(Clone)] pub struct BreakableCtxt<'gcx: 'tcx, 'tcx> { may_break: bool, @@ -424,7 +423,6 @@ pub struct BreakableCtxt<'gcx: 'tcx, 'tcx> { coerce: Option>, } -#[derive(Clone)] pub struct EnclosingBreakables<'gcx: 'tcx, 'tcx> { stack: Vec>, by_id: NodeMap, @@ -439,7 +437,6 @@ impl<'gcx, 'tcx> EnclosingBreakables<'gcx, 'tcx> { } } -#[derive(Clone)] pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { ast_ty_to_ty_cache: RefCell>>, From 8c6156e1d1f7e46c59fd21c878dccbc4526d7e0c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 22 Mar 2017 21:07:02 -0400 Subject: [PATCH 29/36] have coercion supply back the target type The `try_coerce` method coerces from a source to a target type, possibly inserting adjustments. It should guarantee that the post-adjustment type is a subtype of the target type (or else that some side-constraint has been registered which will lead to an error). However, it used to return the (possibly adjusted) source as the type of the expression rather than the target. This led to less good downstream errors. To work around this, the code around blocks -- and particular tail expressions in blocks -- had some special case manipulation. However, since that code is now using the more general `CoerceMany` construct (to account for breaks), it can no longer take advantage of that. This lead to some regressions in compile-fail tests were errors were reported at "less good" locations than before. This change modifies coercions to return the target type when successful rather the source type. This extends the behavior from blocks to all coercions. Typically this has limited effect but on a few tests yielded better errors results (and avoided regressions, of course). This change also restores the hint about removing semicolons which went missing (by giving 'force-unit' coercions a chance to add notes etc). --- src/librustc_typeck/check/_match.rs | 2 +- src/librustc_typeck/check/coercion.rs | 40 +++++--- src/librustc_typeck/check/mod.rs | 99 +++++++++++-------- src/test/compile-fail/issue-15965.rs | 2 +- src/test/compile-fail/issue-2149.rs | 2 +- ...region-invariant-static-error-reporting.rs | 3 - src/test/compile-fail/regions-bounds.rs | 4 +- 7 files changed, 91 insertions(+), 61 deletions(-) diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index e83b786b9a838..4a04464244442 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -498,7 +498,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if is_if_let_fallback { let cause = self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse); assert!(arm_ty.is_nil()); - coercion.coerce_forced_unit(self, &cause); + coercion.coerce_forced_unit(self, &cause, &mut |_| ()); } else { let cause = self.cause(expr.span, ObligationCauseCode::MatchExpressionArm { arm_span: arm.body.span, diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 37d442f1d059e..a5acd0c7e5300 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -74,6 +74,7 @@ use rustc::ty::fold::TypeFoldable; use rustc::ty::error::TypeError; use rustc::ty::relate::RelateResult; use rustc::ty::subst::Subst; +use errors::DiagnosticBuilder; use syntax::abi; use syntax::feature_gate; use syntax::ptr::P; @@ -718,7 +719,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } self.write_adjustment(expr.id, adjustment); } - Ok(adjustment.target) + + // We should now have added sufficient adjustments etc to + // ensure that the type of expression, post-adjustment, is + // a subtype of target. + Ok(target) }) } @@ -1000,7 +1005,7 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> expression_ty: Ty<'tcx>, expression_diverges: Diverges) { - self.coerce_inner(fcx, cause, Some(expression), expression_ty, expression_diverges) + self.coerce_inner(fcx, cause, Some(expression), expression_ty, expression_diverges, None) } /// Indicates that one of the inputs is a "forced unit". This @@ -1011,15 +1016,21 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> /// purposes. Note that these tend to correspond to cases where /// the `()` expression is implicit in the source, and hence we do /// not take an expression argument. + /// + /// The `augment_error` gives you a chance to extend the error + /// message, in case any results (e.g., we use this to suggest + /// removing a `;`). pub fn coerce_forced_unit<'a>(&mut self, fcx: &FnCtxt<'a, 'gcx, 'tcx>, - cause: &ObligationCause<'tcx>) + cause: &ObligationCause<'tcx>, + augment_error: &mut FnMut(&mut DiagnosticBuilder)) { self.coerce_inner(fcx, cause, None, fcx.tcx.mk_nil(), - Diverges::Maybe) + Diverges::Maybe, + Some(augment_error)) } /// The inner coercion "engine". If `expression` is `None`, this @@ -1030,7 +1041,8 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> cause: &ObligationCause<'tcx>, expression: Option<&'gcx hir::Expr>, mut expression_ty: Ty<'tcx>, - expression_diverges: Diverges) + expression_diverges: Diverges, + augment_error: Option<&mut FnMut(&mut DiagnosticBuilder)>) { // Incorporate whatever type inference information we have // until now; in principle we might also want to process @@ -1126,19 +1138,25 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> (self.final_ty.unwrap_or(self.expected_ty), expression_ty) }; + let mut db; match cause.code { ObligationCauseCode::ReturnNoExpression => { - struct_span_err!(fcx.tcx.sess, cause.span, E0069, - "`return;` in a function whose return type is not `()`") - .span_label(cause.span, &format!("return type is not ()")) - .emit(); + db = struct_span_err!( + fcx.tcx.sess, cause.span, E0069, + "`return;` in a function whose return type is not `()`"); + db.span_label(cause.span, &format!("return type is not ()")); } _ => { - fcx.report_mismatched_types(cause, expected, found, err) - .emit(); + db = fcx.report_mismatched_types(cause, expected, found, err); } } + if let Some(mut augment_error) = augment_error { + augment_error(&mut db); + } + + db.emit(); + self.final_ty = Some(fcx.tcx.types.err); } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9eaa8404bded9..29b83523aed6b 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -87,7 +87,7 @@ use fmt_macros::{Parser, Piece, Position}; use hir::def::{Def, CtorKind}; use hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_back::slice::ref_slice; -use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin, TypeTrace}; +use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin}; use rustc::infer::type_variable::{self, TypeVariableOrigin}; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::traits::{self, ObligationCause, ObligationCauseCode, Reveal}; @@ -99,6 +99,7 @@ use rustc::ty::adjustment; use rustc::ty::fold::{BottomUpFolder, TypeFoldable}; use rustc::ty::maps::Providers; use rustc::ty::util::{Representability, IntTypeExt}; +use errors::DiagnosticBuilder; use require_c_abi_if_variadic; use session::{Session, CompileResult}; use TypeAndSubsts; @@ -2875,7 +2876,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.diverges.set(cond_diverges | then_diverges & else_diverges); } else { let else_cause = self.cause(sp, ObligationCauseCode::IfExpressionWithNoElse); - coerce.coerce_forced_unit(self, &else_cause); + coerce.coerce_forced_unit(self, &else_cause, &mut |_| ()); // If the condition is false we can't diverge. self.diverges.set(cond_diverges); @@ -3591,7 +3592,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { coerce.coerce(self, &cause, e, e_ty, e_diverges); } else { assert!(e_ty.is_nil()); - coerce.coerce_forced_unit(self, &cause); + coerce.coerce_forced_unit(self, &cause, &mut |_| ()); } } else { // If `ctxt.coerce` is `None`, we can just ignore @@ -3626,7 +3627,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } else { let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut(); let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression); - coercion.coerce_forced_unit(self, &cause); + coercion.coerce_forced_unit(self, &cause, &mut |_| ()); } tcx.types.never } @@ -4158,8 +4159,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { tail_expr, tail_expr_ty, self.diverges.get()); // TODO - } else if !self.diverges.get().always() { - coerce.coerce_forced_unit(self, &self.misc(blk.span)); + } else { + // Subtle: if there is no explicit tail expression, + // that is typically equivalent to a tail expression + // of `()` -- except if the block diverges. In that + // case, there is no value supplied from the tail + // expression (assuming there are no other breaks, + // this implies that the type of the block will be + // `!`). + if !self.diverges.get().always() { + coerce.coerce_forced_unit(self, &self.misc(blk.span), &mut |err| { + if let Some(expected_ty) = expected.only_has_type(self) { + self.consider_hint_about_removing_semicolon(blk, + expected_ty, + err); + } + }); + } } }); @@ -4175,43 +4191,42 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ty } - pub fn check_block_no_expr(&self, blk: &'gcx hir::Block, ty: Ty<'tcx>, ety: Ty<'tcx>) { - // We're not diverging and there's an expected type, which, - // in case it's not `()`, could result in an error higher-up. - // We have a chance to error here early and be more helpful. - let cause = self.misc(blk.span); - let trace = TypeTrace::types(&cause, false, ty, ety); - match self.sub_types(false, &cause, ty, ety) { - Ok(InferOk { obligations, .. }) => { - // FIXME(#32730) propagate obligations - assert!(obligations.is_empty()); - }, - Err(err) => { - let mut err = self.report_and_explain_type_error(trace, &err); - - // Be helpful when the user wrote `{... expr;}` and - // taking the `;` off is enough to fix the error. - let mut extra_semi = None; - if let Some(stmt) = blk.stmts.last() { - if let hir::StmtSemi(ref e, _) = stmt.node { - if self.can_sub_types(self.node_ty(e.id), ety).is_ok() { - extra_semi = Some(stmt); - } - } - } - if let Some(last_stmt) = extra_semi { - let original_span = original_sp(last_stmt.span, blk.span); - let span_semi = Span { - lo: original_span.hi - BytePos(1), - hi: original_span.hi, - ctxt: original_span.ctxt, - }; - err.span_help(span_semi, "consider removing this semicolon:"); - } - - err.emit(); - } + /// A common error is to add an extra semicolon: + /// + /// ``` + /// fn foo() -> usize { + /// 22; + /// } + /// ``` + /// + /// This routine checks if the final statement in a block is an + /// expression with an explicit semicolon whose type is compatible + /// with `expected_ty`. If so, it suggests removing the semicolon. + fn consider_hint_about_removing_semicolon(&self, + blk: &'gcx hir::Block, + expected_ty: Ty<'tcx>, + err: &mut DiagnosticBuilder) { + // Be helpful when the user wrote `{... expr;}` and + // taking the `;` off is enough to fix the error. + let last_stmt = match blk.stmts.last() { + Some(s) => s, + None => return, + }; + let last_expr = match last_stmt.node { + hir::StmtSemi(ref e, _) => e, + _ => return, + }; + let last_expr_ty = self.expr_ty(last_expr); + if self.can_sub_types(last_expr_ty, expected_ty).is_err() { + return; } + let original_span = original_sp(last_stmt.span, blk.span); + let span_semi = Span { + lo: original_span.hi - BytePos(1), + hi: original_span.hi, + ctxt: original_span.ctxt, + }; + err.span_help(span_semi, "consider removing this semicolon:"); } // Instantiates the given path, which must refer to an item with the given diff --git a/src/test/compile-fail/issue-15965.rs b/src/test/compile-fail/issue-15965.rs index d5d597c190ea0..08b896f387bbe 100644 --- a/src/test/compile-fail/issue-15965.rs +++ b/src/test/compile-fail/issue-15965.rs @@ -11,7 +11,7 @@ fn main() { return { return () } -//~^ ERROR expected function, found `!` +//~^ ERROR the type of this value must be known in this context () ; } diff --git a/src/test/compile-fail/issue-2149.rs b/src/test/compile-fail/issue-2149.rs index 9143a226a2483..256c5d8e6f72c 100644 --- a/src/test/compile-fail/issue-2149.rs +++ b/src/test/compile-fail/issue-2149.rs @@ -21,5 +21,5 @@ impl vec_monad for Vec { } fn main() { ["hi"].bind(|x| [x] ); - //~^ ERROR no method named `bind` found for type `[&'static str; 1]` in the current scope + //~^ ERROR no method named `bind` found for type `[&str; 1]` in the current scope } diff --git a/src/test/compile-fail/region-invariant-static-error-reporting.rs b/src/test/compile-fail/region-invariant-static-error-reporting.rs index ac0167e08bdd6..d25674a74b1d3 100644 --- a/src/test/compile-fail/region-invariant-static-error-reporting.rs +++ b/src/test/compile-fail/region-invariant-static-error-reporting.rs @@ -13,9 +13,6 @@ // over time, but this test used to exhibit some pretty bogus messages // that were not remotely helpful. -// error-pattern:cannot infer -// error-pattern:cannot outlive the lifetime 'a -// error-pattern:must be valid for the static lifetime // error-pattern:cannot infer // error-pattern:cannot outlive the lifetime 'a // error-pattern:must be valid for the static lifetime diff --git a/src/test/compile-fail/regions-bounds.rs b/src/test/compile-fail/regions-bounds.rs index 810a8671c536b..5ce80be98d974 100644 --- a/src/test/compile-fail/regions-bounds.rs +++ b/src/test/compile-fail/regions-bounds.rs @@ -16,11 +16,11 @@ struct an_enum<'a>(&'a isize); struct a_class<'a> { x:&'a isize } fn a_fn1<'a,'b>(e: an_enum<'a>) -> an_enum<'b> { - return e; //~^ ERROR mismatched types + return e; //~ ERROR mismatched types } fn a_fn3<'a,'b>(e: a_class<'a>) -> a_class<'b> { - return e; //~^ ERROR mismatched types + return e; //~ ERROR mismatched types } fn main() { } From 7eeddb409364fdeecbec8611f08a6ccea134971f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Mar 2017 13:21:50 -0400 Subject: [PATCH 30/36] add test illustrating current "coerce to `!`" behavior --- src/test/compile-fail/coerce-to-bang.rs | 90 +++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/test/compile-fail/coerce-to-bang.rs diff --git a/src/test/compile-fail/coerce-to-bang.rs b/src/test/compile-fail/coerce-to-bang.rs new file mode 100644 index 0000000000000..870665bb49ee6 --- /dev/null +++ b/src/test/compile-fail/coerce-to-bang.rs @@ -0,0 +1,90 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(never_type)] + +fn foo(x: usize, y: !, z: usize) { } + +fn call_foo_a() { + // FIXME(#40800) -- accepted beacuse divergence happens **before** + // the coercion to `!`, but within same expression. Not clear that + // these are the rules we want. + foo(return, 22, 44); +} + +fn call_foo_b() { + // Divergence happens in the argument itself, definitely ok. + foo(22, return, 44); +} + +fn call_foo_c() { + // This test fails because the divergence happens **after** the + // coercion to `!`: + foo(22, 44, return); //~ ERROR mismatched types +} + +fn call_foo_d() { + // This test passes because `a` has type `!`: + let a: ! = return; + let b = 22; + let c = 44; + foo(a, b, c); // ... and hence a reference to `a` is expected to diverge. +} + +fn call_foo_e() { + // This test probably could pass but we don't *know* that `a` + // has type `!` so we don't let it work. + let a = return; + let b = 22; + let c = 44; + foo(a, b, c); //~ ERROR mismatched types +} + +fn call_foo_f() { + // This fn fails because `a` has type `usize`, and hence a + // reference to is it **not** considered to diverge. + let a: usize = return; + let b = 22; + let c = 44; + foo(a, b, c); //~ ERROR mismatched types +} + +fn array_a() { + // Accepted: return is coerced to `!` just fine, and then `22` can be + // because we already diverged. + let x: [!; 2] = [return, 22]; +} + +fn array_b() { + // Error: divergence has not yet occurred. + let x: [!; 2] = [22, return]; //~ ERROR mismatched types +} + +fn tuple_a() { + // No divergence at all. + let x: (usize, !, usize) = (22, 44, 66); //~ ERROR mismatched types +} + +fn tuple_b() { + // Divergence happens before coercion: OK + let x: (usize, !, usize) = (return, 44, 66); +} + +fn tuple_c() { + // Divergence happens before coercion: OK + let x: (usize, !, usize) = (22, return, 66); +} + +fn tuple_d() { + // Error: divergence happens too late + let x: (usize, !, usize) = (22, 44, return); //~ ERROR mismatched types +} + +fn main() { } From 8450add8bede7bc5119457e37bbbcc95a5e8b121 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Mar 2017 13:35:49 -0400 Subject: [PATCH 31/36] fix `X as !` behavior --- src/librustc_typeck/check/cast.rs | 8 ++++--- src/librustc_typeck/check/mod.rs | 5 +++-- src/test/compile-fail/coerce-to-bang-cast.rs | 23 ++++++++++++++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 src/test/compile-fail/coerce-to-bang-cast.rs diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index ea0aad007dd73..32b363ed755f4 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -56,6 +56,7 @@ use util::common::ErrorReported; pub struct CastCheck<'tcx> { expr: &'tcx hir::Expr, expr_ty: Ty<'tcx>, + expr_diverges: Diverges, cast_ty: Ty<'tcx>, cast_span: Span, span: Span, @@ -115,6 +116,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { pub fn new(fcx: &FnCtxt<'a, 'gcx, 'tcx>, expr: &'tcx hir::Expr, expr_ty: Ty<'tcx>, + expr_diverges: Diverges, cast_ty: Ty<'tcx>, cast_span: Span, span: Span) @@ -122,6 +124,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { let check = CastCheck { expr: expr, expr_ty: expr_ty, + expr_diverges: expr_diverges, cast_ty: cast_ty, cast_span: cast_span, span: span, @@ -378,7 +381,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { // Attempt a coercion to a fn pointer type. let res = fcx.try_coerce(self.expr, self.expr_ty, - Diverges::Maybe, // TODO + self.expr_diverges, fcx.tcx.mk_fn_ptr(f)); if !res.is_ok() { return Err(CastError::NonScalar); @@ -545,8 +548,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { } fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> bool { - // TODO - fcx.try_coerce(self.expr, self.expr_ty, Diverges::Maybe, self.cast_ty).is_ok() + fcx.try_coerce(self.expr, self.expr_ty, self.expr_diverges, self.cast_ty).is_ok() } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 29b83523aed6b..da15c8f766e37 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3736,6 +3736,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let t_cast = self.resolve_type_vars_if_possible(&t_cast); let t_expr = self.check_expr_with_expectation(e, ExpectCastableToType(t_cast)); let t_cast = self.resolve_type_vars_if_possible(&t_cast); + let diverges = self.diverges.get(); // Eagerly check for some obvious errors. if t_expr.references_error() || t_cast.references_error() { @@ -3743,7 +3744,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } else { // Defer other checks until we're done type checking. let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); - match cast::CastCheck::new(self, e, t_expr, t_cast, t.span, expr.span) { + match cast::CastCheck::new(self, e, t_expr, diverges, t_cast, t.span, expr.span) { Ok(cast_check) => { deferred_cast_checks.push(cast_check); t_cast @@ -4158,7 +4159,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { &self.misc(tail_expr.span), tail_expr, tail_expr_ty, - self.diverges.get()); // TODO + self.diverges.get()); } else { // Subtle: if there is no explicit tail expression, // that is typically equivalent to a tail expression diff --git a/src/test/compile-fail/coerce-to-bang-cast.rs b/src/test/compile-fail/coerce-to-bang-cast.rs new file mode 100644 index 0000000000000..57d2192e6356b --- /dev/null +++ b/src/test/compile-fail/coerce-to-bang-cast.rs @@ -0,0 +1,23 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(never_type)] + +fn foo(x: usize, y: !, z: usize) { } + +fn cast_a() { + let y = {return; 22} as !; +} + +fn cast_b() { + let y = 22 as !; //~ ERROR non-scalar cast +} + +fn main() { } From 1b5768df4e70e4a05e15271ee58c32cb00d7fefd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Mar 2017 13:36:01 -0400 Subject: [PATCH 32/36] document `diverges` more correctly --- src/librustc_typeck/check/mod.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index da15c8f766e37..aaa3cf0f29e74 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -454,11 +454,19 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { ps: RefCell, /// Whether the last checked node generates a divergence (e.g., - /// `return` will set this to Always). In general, this is - /// typically set to *Maybe* on the way **down** the tree, and - /// then values are propagated **up** the tree. In a block, we - /// combine the results from statements and propagate the - /// end-result up. + /// `return` will set this to Always). In general, when entering + /// an expression or other node in the tree, the initial value + /// indicates whether prior parts of the containing expression may + /// have diverged. It is then typically set to `Maybe` (and the + /// old value remembered) for processing the subparts of the + /// current expression. As each subpart is processed, they may set + /// the flag to `Always` etc. Finally, at the end, we take the + /// result and "union" it with the original value, so that when we + /// return the flag indicates if any subpart of the the parent + /// expression (up to and including this part) has diverged. So, + /// if you read it after evaluating a subexpression `X`, the value + /// you get indicates whether any subexpression that was + /// evaluating up to and including `X` diverged. /// /// We use this flag for two purposes: /// From e97fc5247a1d3e3e223febbbcda15e3439b61c30 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Mar 2017 18:47:58 -0400 Subject: [PATCH 33/36] kill the graphviz-flowgraph tests They are so annoying to update, and haven't caught any bugs afaik. --- src/test/run-make/graphviz-flowgraph/Makefile | 38 ----- .../graphviz-flowgraph/f00.dot-expected.dot | 9 - src/test/run-make/graphviz-flowgraph/f00.rs | 13 -- .../graphviz-flowgraph/f01.dot-expected.dot | 13 -- src/test/run-make/graphviz-flowgraph/f01.rs | 13 -- .../graphviz-flowgraph/f02.dot-expected.dot | 13 -- src/test/run-make/graphviz-flowgraph/f02.rs | 13 -- .../graphviz-flowgraph/f03.dot-expected.dot | 17 -- src/test/run-make/graphviz-flowgraph/f03.rs | 13 -- .../graphviz-flowgraph/f04.dot-expected.dot | 15 -- src/test/run-make/graphviz-flowgraph/f04.rs | 13 -- .../graphviz-flowgraph/f05.dot-expected.dot | 23 --- src/test/run-make/graphviz-flowgraph/f05.rs | 13 -- .../graphviz-flowgraph/f06.dot-expected.dot | 19 --- src/test/run-make/graphviz-flowgraph/f06.rs | 14 -- .../graphviz-flowgraph/f07.dot-expected.dot | 39 ----- src/test/run-make/graphviz-flowgraph/f07.rs | 17 -- .../graphviz-flowgraph/f08.dot-expected.dot | 38 ----- src/test/run-make/graphviz-flowgraph/f08.rs | 16 -- .../graphviz-flowgraph/f09.dot-expected.dot | 54 ------ src/test/run-make/graphviz-flowgraph/f09.rs | 18 -- .../graphviz-flowgraph/f10.dot-expected.dot | 36 ---- src/test/run-make/graphviz-flowgraph/f10.rs | 16 -- .../graphviz-flowgraph/f11.dot-expected.dot | 35 ---- src/test/run-make/graphviz-flowgraph/f11.rs | 18 -- .../graphviz-flowgraph/f12.dot-expected.dot | 50 ------ src/test/run-make/graphviz-flowgraph/f12.rs | 18 -- .../graphviz-flowgraph/f13.dot-expected.dot | 54 ------ src/test/run-make/graphviz-flowgraph/f13.rs | 18 -- .../graphviz-flowgraph/f14.dot-expected.dot | 36 ---- src/test/run-make/graphviz-flowgraph/f14.rs | 18 -- .../graphviz-flowgraph/f15.dot-expected.dot | 105 ------------ src/test/run-make/graphviz-flowgraph/f15.rs | 30 ---- .../graphviz-flowgraph/f16.dot-expected.dot | 111 ------------ src/test/run-make/graphviz-flowgraph/f16.rs | 31 ---- .../graphviz-flowgraph/f17.dot-expected.dot | 21 --- src/test/run-make/graphviz-flowgraph/f17.rs | 13 -- .../graphviz-flowgraph/f18.dot-expected.dot | 23 --- src/test/run-make/graphviz-flowgraph/f18.rs | 14 -- .../graphviz-flowgraph/f19.dot-expected.dot | 29 ---- src/test/run-make/graphviz-flowgraph/f19.rs | 16 -- .../graphviz-flowgraph/f20.dot-expected.dot | 29 ---- src/test/run-make/graphviz-flowgraph/f20.rs | 14 -- .../graphviz-flowgraph/f21.dot-expected.dot | 101 ----------- src/test/run-make/graphviz-flowgraph/f21.rs | 30 ---- .../graphviz-flowgraph/f22.dot-expected.dot | 107 ------------ src/test/run-make/graphviz-flowgraph/f22.rs | 31 ---- .../graphviz-flowgraph/f23.dot-expected.dot | 113 ------------ src/test/run-make/graphviz-flowgraph/f23.rs | 31 ---- .../graphviz-flowgraph/f24.dot-expected.dot | 161 ------------------ src/test/run-make/graphviz-flowgraph/f24.rs | 36 ---- .../graphviz-flowgraph/f25.dot-expected.dot | 161 ------------------ src/test/run-make/graphviz-flowgraph/f25.rs | 36 ---- 53 files changed, 1963 deletions(-) delete mode 100644 src/test/run-make/graphviz-flowgraph/Makefile delete mode 100644 src/test/run-make/graphviz-flowgraph/f00.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f00.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f01.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f01.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f02.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f02.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f03.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f03.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f04.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f04.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f05.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f05.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f06.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f06.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f07.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f08.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f08.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f09.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f09.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f10.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f10.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f11.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f11.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f12.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f12.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f13.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f14.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f14.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f15.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f15.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f16.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f16.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f17.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f17.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f18.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f18.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f19.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f19.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f20.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f20.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f21.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f21.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f22.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f22.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f23.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f23.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f24.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f24.rs delete mode 100644 src/test/run-make/graphviz-flowgraph/f25.dot-expected.dot delete mode 100644 src/test/run-make/graphviz-flowgraph/f25.rs diff --git a/src/test/run-make/graphviz-flowgraph/Makefile b/src/test/run-make/graphviz-flowgraph/Makefile deleted file mode 100644 index 5740a36359c9c..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/Makefile +++ /dev/null @@ -1,38 +0,0 @@ --include ../tools.mk - -FILES=f00.rs f01.rs f02.rs f03.rs f04.rs f05.rs f06.rs f07.rs \ - f08.rs f09.rs f10.rs f11.rs f12.rs f13.rs f14.rs f15.rs \ - f16.rs f17.rs f18.rs f19.rs f20.rs f21.rs f22.rs f23.rs \ - f24.rs f25.rs - - -# all: $(patsubst %.rs,$(TMPDIR)/%.dot,$(FILES)) $(patsubst %.rs,$(TMPDIR)/%.pp,$(FILES)) -all: $(patsubst %.rs,$(TMPDIR)/%.check,$(FILES)) - - -RUSTC_LIB=$(RUSTC) --crate-type=lib - -define FIND_LAST_BLOCK -LASTBLOCKNUM_$(1) := $(shell $(RUSTC_LIB) -Z unstable-options --pretty=expanded,identified $(1) \ - | grep block - | tail -1 - | sed -e 's@.*/\* block \([0-9]*\) \*/.*@\1@') -endef - -ifeq ($(findstring rustc,$(RUSTC)),) -$(error Must set RUSTC) -endif - -$(TMPDIR)/%.pp: %.rs - $(RUSTC_LIB) --pretty=expanded,identified $< -o $@ - -$(TMPDIR)/%.dot: %.rs - $(eval $(call FIND_LAST_BLOCK,$<)) - $(RUSTC_LIB) -Z unstable-options --unpretty flowgraph,unlabelled=$(LASTBLOCKNUM_$<) $< -o $@.tmp - cat $@.tmp | sed -e 's@ (id=[0-9]*)@@g' \ - -e 's@\[label=""\]@@' \ - -e 's@digraph [a-zA-Z0-9_]* @digraph block @' \ - > $@ - -$(TMPDIR)/%.check: %.rs $(TMPDIR)/%.dot - diff -u $(patsubst %.rs,$(TMPDIR)/%.dot,$<) $(patsubst %.rs,%.dot-expected.dot,$<) diff --git a/src/test/run-make/graphviz-flowgraph/f00.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f00.dot-expected.dot deleted file mode 100644 index 8ea8370ab235d..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f00.dot-expected.dot +++ /dev/null @@ -1,9 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="block { }"]; - N3[label="expr { }"]; - N0 -> N2; - N2 -> N3; - N3 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f00.rs b/src/test/run-make/graphviz-flowgraph/f00.rs deleted file mode 100644 index 4e7fc7ea9b084..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f00.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn empty_0() { - -} diff --git a/src/test/run-make/graphviz-flowgraph/f01.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f01.dot-expected.dot deleted file mode 100644 index 5982fbea76902..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f01.dot-expected.dot +++ /dev/null @@ -1,13 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 1"]; - N3[label="stmt 1;"]; - N4[label="block { 1; }"]; - N5[label="expr { 1; }"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f01.rs b/src/test/run-make/graphviz-flowgraph/f01.rs deleted file mode 100644 index 231aab69e50d9..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f01.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn lit_1() { - 1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f02.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f02.dot-expected.dot deleted file mode 100644 index 1639785bd68c0..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f02.dot-expected.dot +++ /dev/null @@ -1,13 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="local _x"]; - N3[label="stmt let _x: isize;"]; - N4[label="block { let _x: isize; }"]; - N5[label="expr { let _x: isize; }"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f02.rs b/src/test/run-make/graphviz-flowgraph/f02.rs deleted file mode 100644 index f7fe126619853..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f02.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn decl_x_2() { - let _x : isize; -} diff --git a/src/test/run-make/graphviz-flowgraph/f03.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f03.dot-expected.dot deleted file mode 100644 index b0ae00d81675a..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f03.dot-expected.dot +++ /dev/null @@ -1,17 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 3"]; - N3[label="expr 4"]; - N4[label="expr 3 + 4"]; - N5[label="stmt 3 + 4;"]; - N6[label="block { 3 + 4; }"]; - N7[label="expr { 3 + 4; }"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f03.rs b/src/test/run-make/graphviz-flowgraph/f03.rs deleted file mode 100644 index 2dd71b623c24d..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f03.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn expr_add_3() { - 3 + 4; -} diff --git a/src/test/run-make/graphviz-flowgraph/f04.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f04.dot-expected.dot deleted file mode 100644 index 41ace15a4c680..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f04.dot-expected.dot +++ /dev/null @@ -1,15 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 4"]; - N3[label="local _x"]; - N4[label="stmt let _x = 4;"]; - N5[label="block { let _x = 4; }"]; - N6[label="expr { let _x = 4; }"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f04.rs b/src/test/run-make/graphviz-flowgraph/f04.rs deleted file mode 100644 index 2a0ac8ac9e570..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f04.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn pat_id_4() { - let _x = 4; -} diff --git a/src/test/run-make/graphviz-flowgraph/f05.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f05.dot-expected.dot deleted file mode 100644 index 72b8ae71751c2..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f05.dot-expected.dot +++ /dev/null @@ -1,23 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 5"]; - N3[label="expr 55"]; - N4[label="expr (5, 55)"]; - N5[label="local _x"]; - N6[label="local _y"]; - N7[label="pat (_x, _y)"]; - N8[label="stmt let (_x, _y) = (5, 55);"]; - N9[label="block { let (_x, _y) = (5, 55); }"]; - N10[label="expr { let (_x, _y) = (5, 55); }"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N9; - N9 -> N10; - N10 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f05.rs b/src/test/run-make/graphviz-flowgraph/f05.rs deleted file mode 100644 index 616d822bed07b..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f05.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn pat_tup_5() { - let (_x, _y) = (5, 55); -} diff --git a/src/test/run-make/graphviz-flowgraph/f06.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f06.dot-expected.dot deleted file mode 100644 index acba71ef625ff..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f06.dot-expected.dot +++ /dev/null @@ -1,19 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 6"]; - N3[label="expr S6{val: 6,}"]; - N4[label="local _x"]; - N5[label="pat S6 { val: _x }"]; - N6[label="stmt let S6 { val: _x } = S6{val: 6,};"]; - N7[label="block { let S6 { val: _x } = S6{val: 6,}; }"]; - N8[label="expr { let S6 { val: _x } = S6{val: 6,}; }"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f06.rs b/src/test/run-make/graphviz-flowgraph/f06.rs deleted file mode 100644 index 538ef2af89896..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f06.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -struct S6 { val: isize } -pub fn pat_struct_6() { - let S6 { val: _x } = S6{ val: 6 }; -} diff --git a/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot deleted file mode 100644 index 251e2b39f14c8..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot +++ /dev/null @@ -1,39 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 7"]; - N3[label="expr 77"]; - N4[label="expr 777"]; - N5[label="expr 7777"]; - N6[label="expr [7, 77, 777, 7777]"]; - N7[label="expr match [7, 77, 777, 7777] { [x, y, ..] => x + y, }"]; - N8[label="(dummy_node)"]; - N9[label="local x"]; - N10[label="local y"]; - N11[label="pat _"]; - N12[label="pat [x, y, ..]"]; - N13[label="expr x"]; - N14[label="expr y"]; - N15[label="expr x + y"]; - N16[label="stmt match [7, 77, 777, 7777] { [x, y, ..] => x + y, };"]; - N17[label="block { match [7, 77, 777, 7777] { [x, y, ..] => x + y, }; }"]; - N18[label="expr { match [7, 77, 777, 7777] { [x, y, ..] => x + y, }; }"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N9; - N9 -> N10; - N10 -> N11; - N11 -> N12; - N12 -> N8; - N8 -> N13; - N13 -> N14; - N14 -> N15; - N15 -> N7; - N7 -> N16; - N16 -> N17; - N17 -> N18; - N18 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f07.rs b/src/test/run-make/graphviz-flowgraph/f07.rs deleted file mode 100644 index f36b8d0abc7e3..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f07.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(slice_patterns)] - -pub fn pat_vec_7() { - match [7, 77, 777, 7777] { - [x, y, ..] => x + y - }; -} diff --git a/src/test/run-make/graphviz-flowgraph/f08.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f08.dot-expected.dot deleted file mode 100644 index e2779c9414a9e..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f08.dot-expected.dot +++ /dev/null @@ -1,38 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 8"]; - N3[label="local x"]; - N4[label="stmt let x = 8;"]; - N5[label="local _y"]; - N6[label="stmt let _y;"]; - N7[label="expr x"]; - N8[label="expr 88"]; - N9[label="expr x > 88"]; - N10[label="expr 888"]; - N11[label="expr _y"]; - N12[label="expr _y = 888"]; - N13[label="stmt _y = 888;"]; - N14[label="block { _y = 888; }"]; - N15[label="expr if x > 88 { _y = 888; }"]; - N16[label="block { let x = 8; let _y; if x > 88 { _y = 888; } }"]; - N17[label="expr { let x = 8; let _y; if x > 88 { _y = 888; } }"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N9; - N9 -> N10; - N10 -> N11; - N11 -> N12; - N12 -> N13; - N13 -> N14; - N9 -> N15; - N14 -> N15; - N15 -> N16; - N16 -> N17; - N17 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f08.rs b/src/test/run-make/graphviz-flowgraph/f08.rs deleted file mode 100644 index 6ba7b03d54da5..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f08.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn expr_if_onearm_8() { - let x = 8; let _y; - if x > 88 { - _y = 888; - } -} diff --git a/src/test/run-make/graphviz-flowgraph/f09.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f09.dot-expected.dot deleted file mode 100644 index 536abde91e81a..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f09.dot-expected.dot +++ /dev/null @@ -1,54 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 91"]; - N3[label="local x"]; - N4[label="stmt let x = 91;"]; - N5[label="local _y"]; - N6[label="stmt let _y;"]; - N7[label="expr x"]; - N8[label="expr 92"]; - N9[label="expr x > 92"]; - N10[label="expr 93"]; - N11[label="expr _y"]; - N12[label="expr _y = 93"]; - N13[label="stmt _y = 93;"]; - N14[label="block { _y = 93; }"]; - N15[label="expr 94"]; - N16[label="expr 95"]; - N17[label="expr 94 + 95"]; - N18[label="expr _y"]; - N19[label="expr _y = 94 + 95"]; - N20[label="stmt _y = 94 + 95;"]; - N21[label="block { _y = 94 + 95; }"]; - N22[label="expr { _y = 94 + 95; }"]; - N23[label="expr if x > 92 { _y = 93; } else { _y = 94 + 95; }"]; - N24[label="block { let x = 91; let _y; if x > 92 { _y = 93; } else { _y = 94 + 95; } }"]; - N25[label="expr { let x = 91; let _y; if x > 92 { _y = 93; } else { _y = 94 + 95; } }"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N9; - N9 -> N10; - N10 -> N11; - N11 -> N12; - N12 -> N13; - N13 -> N14; - N9 -> N15; - N15 -> N16; - N16 -> N17; - N17 -> N18; - N18 -> N19; - N19 -> N20; - N20 -> N21; - N21 -> N22; - N14 -> N23; - N22 -> N23; - N23 -> N24; - N24 -> N25; - N25 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f09.rs b/src/test/run-make/graphviz-flowgraph/f09.rs deleted file mode 100644 index a78ccb8a93741..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f09.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn expr_if_twoarm_9() { - let x = 91; let _y; - if x > 92 { - _y = 93; - } else { - _y = 94+95; - } -} diff --git a/src/test/run-make/graphviz-flowgraph/f10.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f10.dot-expected.dot deleted file mode 100644 index 07b9c744a7171..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f10.dot-expected.dot +++ /dev/null @@ -1,36 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 10"]; - N3[label="local mut x"]; - N4[label="stmt let mut x = 10;"]; - N5[label="(dummy_node)"]; - N6[label="expr while x > 0 { x -= 1; }"]; - N7[label="expr x"]; - N8[label="expr 0"]; - N9[label="expr x > 0"]; - N10[label="expr 1"]; - N11[label="expr x"]; - N12[label="expr x -= 1"]; - N13[label="stmt x -= 1;"]; - N14[label="block { x -= 1; }"]; - N15[label="block { let mut x = 10; while x > 0 { x -= 1; } }"]; - N16[label="expr { let mut x = 10; while x > 0 { x -= 1; } }"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N7; - N7 -> N8; - N8 -> N9; - N9 -> N6; - N9 -> N10; - N10 -> N11; - N11 -> N12; - N12 -> N13; - N13 -> N14; - N14 -> N5; - N6 -> N15; - N15 -> N16; - N16 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f10.rs b/src/test/run-make/graphviz-flowgraph/f10.rs deleted file mode 100644 index 0ca7cc5ee86bd..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f10.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn expr_while_10() { - let mut x = 10; - while x > 0 { - x -= 1; - } -} diff --git a/src/test/run-make/graphviz-flowgraph/f11.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f11.dot-expected.dot deleted file mode 100644 index 70034d299ba95..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f11.dot-expected.dot +++ /dev/null @@ -1,35 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 11"]; - N3[label="local mut _x"]; - N4[label="stmt let mut _x = 11;"]; - N5[label="(dummy_node)"]; - N6[label="expr loop { _x -= 1; }"]; - N7[label="expr 1"]; - N8[label="expr _x"]; - N9[label="expr _x -= 1"]; - N10[label="stmt _x -= 1;"]; - N11[label="block { _x -= 1; }"]; - N12[label="stmt loop { _x -= 1; }"]; - N13[label="expr \"unreachable\""]; - N14[label="stmt \"unreachable\";"]; - N15[label="block { let mut _x = 11; loop { _x -= 1; } \"unreachable\"; }"]; - N16[label="expr { let mut _x = 11; loop { _x -= 1; } \"unreachable\"; }"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N7; - N7 -> N8; - N8 -> N9; - N9 -> N10; - N10 -> N11; - N11 -> N5; - N6 -> N12; - N12 -> N13; - N13 -> N14; - N14 -> N15; - N15 -> N16; - N16 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f11.rs b/src/test/run-make/graphviz-flowgraph/f11.rs deleted file mode 100644 index d0f3452119e16..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f11.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[allow(unreachable_code)] -pub fn expr_loop_11() { - let mut _x = 11; - loop { - _x -= 1; - } - "unreachable"; -} diff --git a/src/test/run-make/graphviz-flowgraph/f12.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f12.dot-expected.dot deleted file mode 100644 index 245afc43504c4..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f12.dot-expected.dot +++ /dev/null @@ -1,50 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 12"]; - N3[label="local mut x"]; - N4[label="stmt let mut x = 12;"]; - N5[label="(dummy_node)"]; - N6[label="expr loop { x -= 1; if x == 2 { break ; \"unreachable\"; } }"]; - N7[label="expr 1"]; - N8[label="expr x"]; - N9[label="expr x -= 1"]; - N10[label="stmt x -= 1;"]; - N11[label="expr x"]; - N12[label="expr 2"]; - N13[label="expr x == 2"]; - N14[label="expr break"]; - N15[label="(dummy_node)"]; - N16[label="stmt break ;"]; - N17[label="expr \"unreachable\""]; - N18[label="stmt \"unreachable\";"]; - N19[label="block { break ; \"unreachable\"; }"]; - N20[label="expr if x == 2 { break ; \"unreachable\"; }"]; - N21[label="block { x -= 1; if x == 2 { break ; \"unreachable\"; } }"]; - N22[label="block { let mut x = 12; loop { x -= 1; if x == 2 { break ; \"unreachable\"; } } }"]; - N23[label="expr { let mut x = 12; loop { x -= 1; if x == 2 { break ; \"unreachable\"; } } }"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N7; - N7 -> N8; - N8 -> N9; - N9 -> N10; - N10 -> N11; - N11 -> N12; - N12 -> N13; - N13 -> N14; - N14 -> N6; - N15 -> N16; - N16 -> N17; - N17 -> N18; - N18 -> N19; - N13 -> N20; - N19 -> N20; - N20 -> N21; - N21 -> N5; - N6 -> N22; - N22 -> N23; - N23 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f12.rs b/src/test/run-make/graphviz-flowgraph/f12.rs deleted file mode 100644 index 90b146340b6f5..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f12.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[allow(unreachable_code)] -pub fn expr_loop_12() { - let mut x = 12; - loop { - x -= 1; - if x == 2 { break; "unreachable"; } - } -} diff --git a/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot deleted file mode 100644 index 0f268bd0f2aeb..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot +++ /dev/null @@ -1,54 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr E13::E13b"]; - N3[label="expr 13"]; - N4[label="expr E13::E13b(13)"]; - N5[label="local x"]; - N6[label="stmt let x = E13::E13b(13);"]; - N7[label="local _y"]; - N8[label="stmt let _y;"]; - N9[label="expr x"]; - N10[label="expr match x { E13::E13a => _y = 1, E13::E13b(v) => _y = v + 1, }"]; - N11[label="(dummy_node)"]; - N12[label="pat E13::E13a"]; - N13[label="expr 1"]; - N14[label="expr _y"]; - N15[label="expr _y = 1"]; - N16[label="(dummy_node)"]; - N17[label="local v"]; - N18[label="pat E13::E13b(v)"]; - N19[label="expr v"]; - N20[label="expr 1"]; - N21[label="expr v + 1"]; - N22[label="expr _y"]; - N23[label="expr _y = v + 1"]; - N24[label="block {\l let x = E13::E13b(13);\l let _y;\l match x { E13::E13a => _y = 1, E13::E13b(v) => _y = v + 1, }\l}\l"]; - N25[label="expr {\l let x = E13::E13b(13);\l let _y;\l match x { E13::E13a => _y = 1, E13::E13b(v) => _y = v + 1, }\l}\l"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N9; - N9 -> N12; - N12 -> N11; - N11 -> N13; - N13 -> N14; - N14 -> N15; - N15 -> N10; - N9 -> N17; - N17 -> N18; - N18 -> N16; - N16 -> N19; - N19 -> N20; - N20 -> N21; - N21 -> N22; - N22 -> N23; - N23 -> N10; - N10 -> N24; - N24 -> N25; - N25 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f13.rs b/src/test/run-make/graphviz-flowgraph/f13.rs deleted file mode 100644 index babb283c7342c..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f13.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -enum E13 { E13a, E13b(isize) } -pub fn expr_match_13() { - let x = E13::E13b(13); let _y; - match x { - E13::E13a => _y = 1, - E13::E13b(v) => _y = v + 1, - } -} diff --git a/src/test/run-make/graphviz-flowgraph/f14.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f14.dot-expected.dot deleted file mode 100644 index 719a6cf2619d3..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f14.dot-expected.dot +++ /dev/null @@ -1,36 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 14"]; - N3[label="local x"]; - N4[label="stmt let x = 14;"]; - N5[label="expr x"]; - N6[label="expr 1"]; - N7[label="expr x > 1"]; - N8[label="expr return"]; - N9[label="(dummy_node)"]; - N10[label="stmt return;"]; - N11[label="expr \"unreachable\""]; - N12[label="stmt \"unreachable\";"]; - N13[label="block { return; \"unreachable\"; }"]; - N14[label="expr if x > 1 { return; \"unreachable\"; }"]; - N15[label="block { let x = 14; if x > 1 { return; \"unreachable\"; } }"]; - N16[label="expr { let x = 14; if x > 1 { return; \"unreachable\"; } }"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N1; - N9 -> N10; - N10 -> N11; - N11 -> N12; - N12 -> N13; - N7 -> N14; - N13 -> N14; - N14 -> N15; - N15 -> N16; - N16 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f14.rs b/src/test/run-make/graphviz-flowgraph/f14.rs deleted file mode 100644 index 98ff095c8317c..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f14.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[allow(unreachable_code)] -pub fn expr_ret_14() { - let x = 14; - if x > 1 { - return; - "unreachable"; - } -} diff --git a/src/test/run-make/graphviz-flowgraph/f15.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f15.dot-expected.dot deleted file mode 100644 index d8cbd8411e209..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f15.dot-expected.dot +++ /dev/null @@ -1,105 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 15"]; - N3[label="local mut x"]; - N4[label="stmt let mut x = 15;"]; - N5[label="expr 151"]; - N6[label="local mut y"]; - N7[label="stmt let mut y = 151;"]; - N8[label="(dummy_node)"]; - N9[label="expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { break ; \"unreachable\"; }\l y -= 3;\l }\l y -= 4;\l x -= 5;\l }\l"]; - N10[label="(dummy_node)"]; - N11[label="expr \'inner:\l loop {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { break ; \"unreachable\"; }\l y -= 3;\l }\l"]; - N12[label="expr x"]; - N13[label="expr 1"]; - N14[label="expr x == 1"]; - N15[label="expr break \'outer"]; - N16[label="(dummy_node)"]; - N17[label="stmt break \'outer ;"]; - N18[label="expr \"unreachable\""]; - N19[label="stmt \"unreachable\";"]; - N20[label="block { break \'outer ; \"unreachable\"; }"]; - N21[label="expr if x == 1 { break \'outer ; \"unreachable\"; }"]; - N22[label="stmt if x == 1 { break \'outer ; \"unreachable\"; }"]; - N23[label="expr y"]; - N24[label="expr 2"]; - N25[label="expr y >= 2"]; - N26[label="expr break"]; - N27[label="(dummy_node)"]; - N28[label="stmt break ;"]; - N29[label="expr \"unreachable\""]; - N30[label="stmt \"unreachable\";"]; - N31[label="block { break ; \"unreachable\"; }"]; - N32[label="expr if y >= 2 { break ; \"unreachable\"; }"]; - N33[label="stmt if y >= 2 { break ; \"unreachable\"; }"]; - N34[label="expr 3"]; - N35[label="expr y"]; - N36[label="expr y -= 3"]; - N37[label="stmt y -= 3;"]; - N38[label="block {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { break ; \"unreachable\"; }\l y -= 3;\l}\l"]; - N39[label="stmt \'inner:\l loop {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { break ; \"unreachable\"; }\l y -= 3;\l }\l"]; - N40[label="expr 4"]; - N41[label="expr y"]; - N42[label="expr y -= 4"]; - N43[label="stmt y -= 4;"]; - N44[label="expr 5"]; - N45[label="expr x"]; - N46[label="expr x -= 5"]; - N47[label="stmt x -= 5;"]; - N48[label="block {\l \'inner:\l loop {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { break ; \"unreachable\"; }\l y -= 3;\l }\l y -= 4;\l x -= 5;\l}\l"]; - N49[label="block {\l let mut x = 15;\l let mut y = 151;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { break ; \"unreachable\"; }\l y -= 3;\l }\l y -= 4;\l x -= 5;\l }\l}\l"]; - N50[label="expr {\l let mut x = 15;\l let mut y = 151;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { break ; \"unreachable\"; }\l y -= 3;\l }\l y -= 4;\l x -= 5;\l }\l}\l"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N10; - N10 -> N12; - N12 -> N13; - N13 -> N14; - N14 -> N15; - N15 -> N9; - N16 -> N17; - N17 -> N18; - N18 -> N19; - N19 -> N20; - N14 -> N21; - N20 -> N21; - N21 -> N22; - N22 -> N23; - N23 -> N24; - N24 -> N25; - N25 -> N26; - N26 -> N11; - N27 -> N28; - N28 -> N29; - N29 -> N30; - N30 -> N31; - N25 -> N32; - N31 -> N32; - N32 -> N33; - N33 -> N34; - N34 -> N35; - N35 -> N36; - N36 -> N37; - N37 -> N38; - N38 -> N10; - N11 -> N39; - N39 -> N40; - N40 -> N41; - N41 -> N42; - N42 -> N43; - N43 -> N44; - N44 -> N45; - N45 -> N46; - N46 -> N47; - N47 -> N48; - N48 -> N8; - N9 -> N49; - N49 -> N50; - N50 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f15.rs b/src/test/run-make/graphviz-flowgraph/f15.rs deleted file mode 100644 index 056458e5558de..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f15.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[allow(unreachable_code)] -pub fn expr_break_label_15() { - let mut x = 15; - let mut y = 151; - 'outer: loop { - 'inner: loop { - if x == 1 { - break 'outer; - "unreachable"; - } - if y >= 2 { - break; - "unreachable"; - } - y -= 3; - } - y -= 4; - x -= 5; - } -} diff --git a/src/test/run-make/graphviz-flowgraph/f16.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f16.dot-expected.dot deleted file mode 100644 index b11881247fb6a..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f16.dot-expected.dot +++ /dev/null @@ -1,111 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 16"]; - N3[label="local mut x"]; - N4[label="stmt let mut x = 16;"]; - N5[label="expr 16"]; - N6[label="local mut y"]; - N7[label="stmt let mut y = 16;"]; - N8[label="(dummy_node)"]; - N9[label="expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 1 { break ; \"unreachable\"; }\l y -= 1;\l }\l y -= 1;\l x -= 1;\l }\l"]; - N10[label="(dummy_node)"]; - N11[label="expr \'inner:\l loop {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 1 { break ; \"unreachable\"; }\l y -= 1;\l }\l"]; - N12[label="expr x"]; - N13[label="expr 1"]; - N14[label="expr x == 1"]; - N15[label="expr continue \'outer"]; - N16[label="(dummy_node)"]; - N17[label="stmt continue \'outer ;"]; - N18[label="expr \"unreachable\""]; - N19[label="stmt \"unreachable\";"]; - N20[label="block { continue \'outer ; \"unreachable\"; }"]; - N21[label="expr if x == 1 { continue \'outer ; \"unreachable\"; }"]; - N22[label="stmt if x == 1 { continue \'outer ; \"unreachable\"; }"]; - N23[label="expr y"]; - N24[label="expr 1"]; - N25[label="expr y >= 1"]; - N26[label="expr break"]; - N27[label="(dummy_node)"]; - N28[label="stmt break ;"]; - N29[label="expr \"unreachable\""]; - N30[label="stmt \"unreachable\";"]; - N31[label="block { break ; \"unreachable\"; }"]; - N32[label="expr if y >= 1 { break ; \"unreachable\"; }"]; - N33[label="stmt if y >= 1 { break ; \"unreachable\"; }"]; - N34[label="expr 1"]; - N35[label="expr y"]; - N36[label="expr y -= 1"]; - N37[label="stmt y -= 1;"]; - N38[label="block {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 1 { break ; \"unreachable\"; }\l y -= 1;\l}\l"]; - N39[label="stmt \'inner:\l loop {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 1 { break ; \"unreachable\"; }\l y -= 1;\l }\l"]; - N40[label="expr 1"]; - N41[label="expr y"]; - N42[label="expr y -= 1"]; - N43[label="stmt y -= 1;"]; - N44[label="expr 1"]; - N45[label="expr x"]; - N46[label="expr x -= 1"]; - N47[label="stmt x -= 1;"]; - N48[label="block {\l \'inner:\l loop {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 1 { break ; \"unreachable\"; }\l y -= 1;\l }\l y -= 1;\l x -= 1;\l}\l"]; - N49[label="stmt \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 1 { break ; \"unreachable\"; }\l y -= 1;\l }\l y -= 1;\l x -= 1;\l }\l"]; - N50[label="expr \"unreachable\""]; - N51[label="stmt \"unreachable\";"]; - N52[label="block {\l let mut x = 16;\l let mut y = 16;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 1 { break ; \"unreachable\"; }\l y -= 1;\l }\l y -= 1;\l x -= 1;\l }\l \"unreachable\";\l}\l"]; - N53[label="expr {\l let mut x = 16;\l let mut y = 16;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 1 { break ; \"unreachable\"; }\l y -= 1;\l }\l y -= 1;\l x -= 1;\l }\l \"unreachable\";\l}\l"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N10; - N10 -> N12; - N12 -> N13; - N13 -> N14; - N14 -> N15; - N15 -> N8; - N16 -> N17; - N17 -> N18; - N18 -> N19; - N19 -> N20; - N14 -> N21; - N20 -> N21; - N21 -> N22; - N22 -> N23; - N23 -> N24; - N24 -> N25; - N25 -> N26; - N26 -> N11; - N27 -> N28; - N28 -> N29; - N29 -> N30; - N30 -> N31; - N25 -> N32; - N31 -> N32; - N32 -> N33; - N33 -> N34; - N34 -> N35; - N35 -> N36; - N36 -> N37; - N37 -> N38; - N38 -> N10; - N11 -> N39; - N39 -> N40; - N40 -> N41; - N41 -> N42; - N42 -> N43; - N43 -> N44; - N44 -> N45; - N45 -> N46; - N46 -> N47; - N47 -> N48; - N48 -> N8; - N9 -> N49; - N49 -> N50; - N50 -> N51; - N51 -> N52; - N52 -> N53; - N53 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f16.rs b/src/test/run-make/graphviz-flowgraph/f16.rs deleted file mode 100644 index e225b0080e59a..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f16.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[allow(unreachable_code)] -pub fn expr_continue_label_16() { - let mut x = 16; - let mut y = 16; - 'outer: loop { - 'inner: loop { - if x == 1 { - continue 'outer; - "unreachable"; - } - if y >= 1 { - break; - "unreachable"; - } - y -= 1; - } - y -= 1; - x -= 1; - } - "unreachable"; -} diff --git a/src/test/run-make/graphviz-flowgraph/f17.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f17.dot-expected.dot deleted file mode 100644 index 705eece77558d..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f17.dot-expected.dot +++ /dev/null @@ -1,21 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 1"]; - N3[label="expr 7"]; - N4[label="expr 17"]; - N5[label="expr [1, 7, 17]"]; - N6[label="local _v"]; - N7[label="stmt let _v = [1, 7, 17];"]; - N8[label="block { let _v = [1, 7, 17]; }"]; - N9[label="expr { let _v = [1, 7, 17]; }"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N9; - N9 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f17.rs b/src/test/run-make/graphviz-flowgraph/f17.rs deleted file mode 100644 index 23f5bb8a1eb17..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f17.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn expr_vec_17() { - let _v = [1, 7, 17]; -} diff --git a/src/test/run-make/graphviz-flowgraph/f18.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f18.dot-expected.dot deleted file mode 100644 index b0491fe6e27fd..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f18.dot-expected.dot +++ /dev/null @@ -1,23 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="stmt fn inner(x: isize) -> isize { x + x }"]; - N3[label="expr inner"]; - N4[label="expr inner"]; - N5[label="expr 18"]; - N6[label="expr inner(18)"]; - N7[label="expr inner(inner(18))"]; - N8[label="stmt inner(inner(18));"]; - N9[label="block {\l fn inner(x: isize) -> isize { x + x }\l inner(inner(18));\l}\l"]; - N10[label="expr {\l fn inner(x: isize) -> isize { x + x }\l inner(inner(18));\l}\l"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N9; - N9 -> N10; - N10 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f18.rs b/src/test/run-make/graphviz-flowgraph/f18.rs deleted file mode 100644 index cbf8aa5db4391..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f18.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn expr_call_18() { - fn inner(x:isize) -> isize { x + x } - inner(inner(18)); -} diff --git a/src/test/run-make/graphviz-flowgraph/f19.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f19.dot-expected.dot deleted file mode 100644 index 223978c3d7634..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f19.dot-expected.dot +++ /dev/null @@ -1,29 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="stmt struct S19 {\l x: isize,\l}\l"]; - N3[label="stmt impl S19 {\l fn inner(self: Self) -> S19 { S19{x: self.x + self.x,} }\l}\l"]; - N4[label="expr 19"]; - N5[label="expr S19{x: 19,}"]; - N6[label="local s"]; - N7[label="stmt let s = S19{x: 19,};"]; - N8[label="expr s"]; - N9[label="expr s.inner()"]; - N10[label="expr s.inner().inner()"]; - N11[label="stmt s.inner().inner();"]; - N12[label="block {\l struct S19 {\l x: isize,\l }\l impl S19 {\l fn inner(self: Self) -> S19 { S19{x: self.x + self.x,} }\l }\l let s = S19{x: 19,};\l s.inner().inner();\l}\l"]; - N13[label="expr {\l struct S19 {\l x: isize,\l }\l impl S19 {\l fn inner(self: Self) -> S19 { S19{x: self.x + self.x,} }\l }\l let s = S19{x: 19,};\l s.inner().inner();\l}\l"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N9; - N9 -> N10; - N10 -> N11; - N11 -> N12; - N12 -> N13; - N13 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f19.rs b/src/test/run-make/graphviz-flowgraph/f19.rs deleted file mode 100644 index 78c15dd64adc4..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f19.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn expr_method_call_19() { - struct S19 { x: isize } - impl S19 { fn inner(self) -> S19 { S19 { x: self.x + self.x } } } - let s = S19 { x: 19 }; - s.inner().inner(); -} diff --git a/src/test/run-make/graphviz-flowgraph/f20.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f20.dot-expected.dot deleted file mode 100644 index 120eab4dac909..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f20.dot-expected.dot +++ /dev/null @@ -1,29 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 2"]; - N3[label="expr 0"]; - N4[label="expr 20"]; - N5[label="expr [2, 0, 20]"]; - N6[label="local v"]; - N7[label="stmt let v = [2, 0, 20];"]; - N8[label="expr v"]; - N9[label="expr 20"]; - N10[label="expr v[20]"]; - N11[label="stmt v[20];"]; - N12[label="block { let v = [2, 0, 20]; v[20]; }"]; - N13[label="expr { let v = [2, 0, 20]; v[20]; }"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N9; - N9 -> N10; - N10 -> N11; - N11 -> N12; - N12 -> N13; - N13 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f20.rs b/src/test/run-make/graphviz-flowgraph/f20.rs deleted file mode 100644 index d7349932355b1..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f20.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn expr_index_20() { - let v = [2, 0, 20]; - v[20]; -} diff --git a/src/test/run-make/graphviz-flowgraph/f21.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f21.dot-expected.dot deleted file mode 100644 index 370dcdd8554da..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f21.dot-expected.dot +++ /dev/null @@ -1,101 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 15"]; - N3[label="local mut x"]; - N4[label="stmt let mut x = 15;"]; - N5[label="expr 151"]; - N6[label="local mut y"]; - N7[label="stmt let mut y = 151;"]; - N8[label="(dummy_node)"]; - N9[label="expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { return; \"unreachable\"; }\l y -= 3;\l x -= 5;\l }\l \"unreachable\";\l }\l"]; - N10[label="(dummy_node)"]; - N11[label="expr \'inner:\l loop {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { return; \"unreachable\"; }\l y -= 3;\l x -= 5;\l }\l"]; - N12[label="expr x"]; - N13[label="expr 1"]; - N14[label="expr x == 1"]; - N15[label="expr break \'outer"]; - N16[label="(dummy_node)"]; - N17[label="stmt break \'outer ;"]; - N18[label="expr \"unreachable\""]; - N19[label="stmt \"unreachable\";"]; - N20[label="block { break \'outer ; \"unreachable\"; }"]; - N21[label="expr if x == 1 { break \'outer ; \"unreachable\"; }"]; - N22[label="stmt if x == 1 { break \'outer ; \"unreachable\"; }"]; - N23[label="expr y"]; - N24[label="expr 2"]; - N25[label="expr y >= 2"]; - N26[label="expr return"]; - N27[label="(dummy_node)"]; - N28[label="stmt return;"]; - N29[label="expr \"unreachable\""]; - N30[label="stmt \"unreachable\";"]; - N31[label="block { return; \"unreachable\"; }"]; - N32[label="expr if y >= 2 { return; \"unreachable\"; }"]; - N33[label="stmt if y >= 2 { return; \"unreachable\"; }"]; - N34[label="expr 3"]; - N35[label="expr y"]; - N36[label="expr y -= 3"]; - N37[label="stmt y -= 3;"]; - N38[label="expr 5"]; - N39[label="expr x"]; - N40[label="expr x -= 5"]; - N41[label="stmt x -= 5;"]; - N42[label="block {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { return; \"unreachable\"; }\l y -= 3;\l x -= 5;\l}\l"]; - N43[label="stmt \'inner:\l loop {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { return; \"unreachable\"; }\l y -= 3;\l x -= 5;\l }\l"]; - N44[label="expr \"unreachable\""]; - N45[label="stmt \"unreachable\";"]; - N46[label="block {\l \'inner:\l loop {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { return; \"unreachable\"; }\l y -= 3;\l x -= 5;\l }\l \"unreachable\";\l}\l"]; - N47[label="block {\l let mut x = 15;\l let mut y = 151;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { return; \"unreachable\"; }\l y -= 3;\l x -= 5;\l }\l \"unreachable\";\l }\l}\l"]; - N48[label="expr {\l let mut x = 15;\l let mut y = 151;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { return; \"unreachable\"; }\l y -= 3;\l x -= 5;\l }\l \"unreachable\";\l }\l}\l"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N10; - N10 -> N12; - N12 -> N13; - N13 -> N14; - N14 -> N15; - N15 -> N9; - N16 -> N17; - N17 -> N18; - N18 -> N19; - N19 -> N20; - N14 -> N21; - N20 -> N21; - N21 -> N22; - N22 -> N23; - N23 -> N24; - N24 -> N25; - N25 -> N26; - N26 -> N1; - N27 -> N28; - N28 -> N29; - N29 -> N30; - N30 -> N31; - N25 -> N32; - N31 -> N32; - N32 -> N33; - N33 -> N34; - N34 -> N35; - N35 -> N36; - N36 -> N37; - N37 -> N38; - N38 -> N39; - N39 -> N40; - N40 -> N41; - N41 -> N42; - N42 -> N10; - N11 -> N43; - N43 -> N44; - N44 -> N45; - N45 -> N46; - N46 -> N8; - N9 -> N47; - N47 -> N48; - N48 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f21.rs b/src/test/run-make/graphviz-flowgraph/f21.rs deleted file mode 100644 index 70083ed8312cb..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f21.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[allow(unreachable_code)] -pub fn expr_break_label_21() { - let mut x = 15; - let mut y = 151; - 'outer: loop { - 'inner: loop { - if x == 1 { - break 'outer; - "unreachable"; - } - if y >= 2 { - return; - "unreachable"; - } - y -= 3; - x -= 5; - } - "unreachable"; - } -} diff --git a/src/test/run-make/graphviz-flowgraph/f22.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f22.dot-expected.dot deleted file mode 100644 index 9d3bc22831a13..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f22.dot-expected.dot +++ /dev/null @@ -1,107 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 15"]; - N3[label="local mut x"]; - N4[label="stmt let mut x = 15;"]; - N5[label="expr 151"]; - N6[label="local mut y"]; - N7[label="stmt let mut y = 151;"]; - N8[label="(dummy_node)"]; - N9[label="expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 2 { return; \"unreachable\"; }\l x -= 1;\l y -= 3;\l }\l \"unreachable\";\l }\l"]; - N10[label="(dummy_node)"]; - N11[label="expr \'inner:\l loop {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 2 { return; \"unreachable\"; }\l x -= 1;\l y -= 3;\l }\l"]; - N12[label="expr x"]; - N13[label="expr 1"]; - N14[label="expr x == 1"]; - N15[label="expr continue \'outer"]; - N16[label="(dummy_node)"]; - N17[label="stmt continue \'outer ;"]; - N18[label="expr \"unreachable\""]; - N19[label="stmt \"unreachable\";"]; - N20[label="block { continue \'outer ; \"unreachable\"; }"]; - N21[label="expr if x == 1 { continue \'outer ; \"unreachable\"; }"]; - N22[label="stmt if x == 1 { continue \'outer ; \"unreachable\"; }"]; - N23[label="expr y"]; - N24[label="expr 2"]; - N25[label="expr y >= 2"]; - N26[label="expr return"]; - N27[label="(dummy_node)"]; - N28[label="stmt return;"]; - N29[label="expr \"unreachable\""]; - N30[label="stmt \"unreachable\";"]; - N31[label="block { return; \"unreachable\"; }"]; - N32[label="expr if y >= 2 { return; \"unreachable\"; }"]; - N33[label="stmt if y >= 2 { return; \"unreachable\"; }"]; - N34[label="expr 1"]; - N35[label="expr x"]; - N36[label="expr x -= 1"]; - N37[label="stmt x -= 1;"]; - N38[label="expr 3"]; - N39[label="expr y"]; - N40[label="expr y -= 3"]; - N41[label="stmt y -= 3;"]; - N42[label="block {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 2 { return; \"unreachable\"; }\l x -= 1;\l y -= 3;\l}\l"]; - N43[label="stmt \'inner:\l loop {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 2 { return; \"unreachable\"; }\l x -= 1;\l y -= 3;\l }\l"]; - N44[label="expr \"unreachable\""]; - N45[label="stmt \"unreachable\";"]; - N46[label="block {\l \'inner:\l loop {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 2 { return; \"unreachable\"; }\l x -= 1;\l y -= 3;\l }\l \"unreachable\";\l}\l"]; - N47[label="stmt \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 2 { return; \"unreachable\"; }\l x -= 1;\l y -= 3;\l }\l \"unreachable\";\l }\l"]; - N48[label="expr \"unreachable\""]; - N49[label="stmt \"unreachable\";"]; - N50[label="block {\l let mut x = 15;\l let mut y = 151;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 2 { return; \"unreachable\"; }\l x -= 1;\l y -= 3;\l }\l \"unreachable\";\l }\l \"unreachable\";\l}\l"]; - N51[label="expr {\l let mut x = 15;\l let mut y = 151;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 2 { return; \"unreachable\"; }\l x -= 1;\l y -= 3;\l }\l \"unreachable\";\l }\l \"unreachable\";\l}\l"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N10; - N10 -> N12; - N12 -> N13; - N13 -> N14; - N14 -> N15; - N15 -> N8; - N16 -> N17; - N17 -> N18; - N18 -> N19; - N19 -> N20; - N14 -> N21; - N20 -> N21; - N21 -> N22; - N22 -> N23; - N23 -> N24; - N24 -> N25; - N25 -> N26; - N26 -> N1; - N27 -> N28; - N28 -> N29; - N29 -> N30; - N30 -> N31; - N25 -> N32; - N31 -> N32; - N32 -> N33; - N33 -> N34; - N34 -> N35; - N35 -> N36; - N36 -> N37; - N37 -> N38; - N38 -> N39; - N39 -> N40; - N40 -> N41; - N41 -> N42; - N42 -> N10; - N11 -> N43; - N43 -> N44; - N44 -> N45; - N45 -> N46; - N46 -> N8; - N9 -> N47; - N47 -> N48; - N48 -> N49; - N49 -> N50; - N50 -> N51; - N51 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f22.rs b/src/test/run-make/graphviz-flowgraph/f22.rs deleted file mode 100644 index b35aac9ec422e..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f22.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[allow(unreachable_code)] -pub fn expr_break_label_21() { - let mut x = 15; - let mut y = 151; - 'outer: loop { - 'inner: loop { - if x == 1 { - continue 'outer; - "unreachable"; - } - if y >= 2 { - return; - "unreachable"; - } - x -= 1; - y -= 3; - } - "unreachable"; - } - "unreachable"; -} diff --git a/src/test/run-make/graphviz-flowgraph/f23.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f23.dot-expected.dot deleted file mode 100644 index c8bfcd6510b30..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f23.dot-expected.dot +++ /dev/null @@ -1,113 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 23"]; - N3[label="local mut x"]; - N4[label="stmt let mut x = 23;"]; - N5[label="expr 23"]; - N6[label="local mut y"]; - N7[label="stmt let mut y = 23;"]; - N8[label="expr 23"]; - N9[label="local mut z"]; - N10[label="stmt let mut z = 23;"]; - N11[label="(dummy_node)"]; - N12[label="expr while x > 0 {\l x -= 1;\l while y > 0 {\l y -= 1;\l while z > 0 { z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l}\l"]; - N13[label="expr x"]; - N14[label="expr 0"]; - N15[label="expr x > 0"]; - N16[label="expr 1"]; - N17[label="expr x"]; - N18[label="expr x -= 1"]; - N19[label="stmt x -= 1;"]; - N20[label="(dummy_node)"]; - N21[label="expr while y > 0 {\l y -= 1;\l while z > 0 { z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l}\l"]; - N22[label="expr y"]; - N23[label="expr 0"]; - N24[label="expr y > 0"]; - N25[label="expr 1"]; - N26[label="expr y"]; - N27[label="expr y -= 1"]; - N28[label="stmt y -= 1;"]; - N29[label="(dummy_node)"]; - N30[label="expr while z > 0 { z -= 1; }"]; - N31[label="expr z"]; - N32[label="expr 0"]; - N33[label="expr z > 0"]; - N34[label="expr 1"]; - N35[label="expr z"]; - N36[label="expr z -= 1"]; - N37[label="stmt z -= 1;"]; - N38[label="block { z -= 1; }"]; - N39[label="stmt while z > 0 { z -= 1; }"]; - N40[label="expr x"]; - N41[label="expr 10"]; - N42[label="expr x > 10"]; - N43[label="expr return"]; - N44[label="(dummy_node)"]; - N45[label="stmt return;"]; - N46[label="expr \"unreachable\""]; - N47[label="stmt \"unreachable\";"]; - N48[label="block { return; \"unreachable\"; }"]; - N49[label="expr if x > 10 { return; \"unreachable\"; }"]; - N50[label="block { y -= 1; while z > 0 { z -= 1; } if x > 10 { return; \"unreachable\"; } }"]; - N51[label="block {\l x -= 1;\l while y > 0 {\l y -= 1;\l while z > 0 { z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l}\l"]; - N52[label="block {\l let mut x = 23;\l let mut y = 23;\l let mut z = 23;\l while x > 0 {\l x -= 1;\l while y > 0 {\l y -= 1;\l while z > 0 { z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l }\l}\l"]; - N53[label="expr {\l let mut x = 23;\l let mut y = 23;\l let mut z = 23;\l while x > 0 {\l x -= 1;\l while y > 0 {\l y -= 1;\l while z > 0 { z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l }\l}\l"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N9; - N9 -> N10; - N10 -> N11; - N11 -> N13; - N13 -> N14; - N14 -> N15; - N15 -> N12; - N15 -> N16; - N16 -> N17; - N17 -> N18; - N18 -> N19; - N19 -> N20; - N20 -> N22; - N22 -> N23; - N23 -> N24; - N24 -> N21; - N24 -> N25; - N25 -> N26; - N26 -> N27; - N27 -> N28; - N28 -> N29; - N29 -> N31; - N31 -> N32; - N32 -> N33; - N33 -> N30; - N33 -> N34; - N34 -> N35; - N35 -> N36; - N36 -> N37; - N37 -> N38; - N38 -> N29; - N30 -> N39; - N39 -> N40; - N40 -> N41; - N41 -> N42; - N42 -> N43; - N43 -> N1; - N44 -> N45; - N45 -> N46; - N46 -> N47; - N47 -> N48; - N42 -> N49; - N48 -> N49; - N49 -> N50; - N50 -> N20; - N21 -> N51; - N51 -> N11; - N12 -> N52; - N52 -> N53; - N53 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f23.rs b/src/test/run-make/graphviz-flowgraph/f23.rs deleted file mode 100644 index 52341a3fbd408..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f23.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[allow(unreachable_code)] -pub fn expr_while_23() { - let mut x = 23; - let mut y = 23; - let mut z = 23; - - while x > 0 { - x -= 1; - - while y > 0 { - y -= 1; - - while z > 0 { z -= 1; } - - if x > 10 { - return; - "unreachable"; - } - } - } -} diff --git a/src/test/run-make/graphviz-flowgraph/f24.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f24.dot-expected.dot deleted file mode 100644 index e40dd014f0a4d..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f24.dot-expected.dot +++ /dev/null @@ -1,161 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 24"]; - N3[label="local mut x"]; - N4[label="stmt let mut x = 24;"]; - N5[label="expr 24"]; - N6[label="local mut y"]; - N7[label="stmt let mut y = 24;"]; - N8[label="expr 24"]; - N9[label="local mut z"]; - N10[label="stmt let mut z = 24;"]; - N11[label="(dummy_node)"]; - N12[label="expr loop {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l}\l"]; - N13[label="expr x"]; - N14[label="expr 0"]; - N15[label="expr x == 0"]; - N16[label="expr break"]; - N17[label="(dummy_node)"]; - N18[label="stmt break ;"]; - N19[label="expr \"unreachable\""]; - N20[label="stmt \"unreachable\";"]; - N21[label="block { break ; \"unreachable\"; }"]; - N22[label="expr if x == 0 { break ; \"unreachable\"; }"]; - N23[label="stmt if x == 0 { break ; \"unreachable\"; }"]; - N24[label="expr 1"]; - N25[label="expr x"]; - N26[label="expr x -= 1"]; - N27[label="stmt x -= 1;"]; - N28[label="(dummy_node)"]; - N29[label="expr loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l}\l"]; - N30[label="expr y"]; - N31[label="expr 0"]; - N32[label="expr y == 0"]; - N33[label="expr break"]; - N34[label="(dummy_node)"]; - N35[label="stmt break ;"]; - N36[label="expr \"unreachable\""]; - N37[label="stmt \"unreachable\";"]; - N38[label="block { break ; \"unreachable\"; }"]; - N39[label="expr if y == 0 { break ; \"unreachable\"; }"]; - N40[label="stmt if y == 0 { break ; \"unreachable\"; }"]; - N41[label="expr 1"]; - N42[label="expr y"]; - N43[label="expr y -= 1"]; - N44[label="stmt y -= 1;"]; - N45[label="(dummy_node)"]; - N46[label="expr loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }"]; - N47[label="expr z"]; - N48[label="expr 0"]; - N49[label="expr z == 0"]; - N50[label="expr break"]; - N51[label="(dummy_node)"]; - N52[label="stmt break ;"]; - N53[label="expr \"unreachable\""]; - N54[label="stmt \"unreachable\";"]; - N55[label="block { break ; \"unreachable\"; }"]; - N56[label="expr if z == 0 { break ; \"unreachable\"; }"]; - N57[label="stmt if z == 0 { break ; \"unreachable\"; }"]; - N58[label="expr 1"]; - N59[label="expr z"]; - N60[label="expr z -= 1"]; - N61[label="stmt z -= 1;"]; - N62[label="block { if z == 0 { break ; \"unreachable\"; } z -= 1; }"]; - N63[label="stmt loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }"]; - N64[label="expr x"]; - N65[label="expr 10"]; - N66[label="expr x > 10"]; - N67[label="expr return"]; - N68[label="(dummy_node)"]; - N69[label="stmt return;"]; - N70[label="expr \"unreachable\""]; - N71[label="stmt \"unreachable\";"]; - N72[label="block { return; \"unreachable\"; }"]; - N73[label="expr if x > 10 { return; \"unreachable\"; }"]; - N74[label="block {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l}\l"]; - N75[label="block {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l}\l"]; - N76[label="block {\l let mut x = 24;\l let mut y = 24;\l let mut z = 24;\l loop {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l }\l}\l"]; - N77[label="expr {\l let mut x = 24;\l let mut y = 24;\l let mut z = 24;\l loop {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l }\l}\l"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N9; - N9 -> N10; - N10 -> N11; - N11 -> N13; - N13 -> N14; - N14 -> N15; - N15 -> N16; - N16 -> N12; - N17 -> N18; - N18 -> N19; - N19 -> N20; - N20 -> N21; - N15 -> N22; - N21 -> N22; - N22 -> N23; - N23 -> N24; - N24 -> N25; - N25 -> N26; - N26 -> N27; - N27 -> N28; - N28 -> N30; - N30 -> N31; - N31 -> N32; - N32 -> N33; - N33 -> N29; - N34 -> N35; - N35 -> N36; - N36 -> N37; - N37 -> N38; - N32 -> N39; - N38 -> N39; - N39 -> N40; - N40 -> N41; - N41 -> N42; - N42 -> N43; - N43 -> N44; - N44 -> N45; - N45 -> N47; - N47 -> N48; - N48 -> N49; - N49 -> N50; - N50 -> N46; - N51 -> N52; - N52 -> N53; - N53 -> N54; - N54 -> N55; - N49 -> N56; - N55 -> N56; - N56 -> N57; - N57 -> N58; - N58 -> N59; - N59 -> N60; - N60 -> N61; - N61 -> N62; - N62 -> N45; - N46 -> N63; - N63 -> N64; - N64 -> N65; - N65 -> N66; - N66 -> N67; - N67 -> N1; - N68 -> N69; - N69 -> N70; - N70 -> N71; - N71 -> N72; - N66 -> N73; - N72 -> N73; - N73 -> N74; - N74 -> N28; - N29 -> N75; - N75 -> N11; - N12 -> N76; - N76 -> N77; - N77 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f24.rs b/src/test/run-make/graphviz-flowgraph/f24.rs deleted file mode 100644 index f796d660a1856..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f24.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[allow(unreachable_code)] -pub fn expr_while_24() { - let mut x = 24; - let mut y = 24; - let mut z = 24; - - loop { - if x == 0 { break; "unreachable"; } - x -= 1; - - loop { - if y == 0 { break; "unreachable"; } - y -= 1; - - loop { - if z == 0 { break; "unreachable"; } - z -= 1; - } - - if x > 10 { - return; - "unreachable"; - } - } - } -} diff --git a/src/test/run-make/graphviz-flowgraph/f25.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f25.dot-expected.dot deleted file mode 100644 index 1e2df1ab5e7b7..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f25.dot-expected.dot +++ /dev/null @@ -1,161 +0,0 @@ -digraph block { - N0[label="entry"]; - N1[label="exit"]; - N2[label="expr 25"]; - N3[label="local mut x"]; - N4[label="stmt let mut x = 25;"]; - N5[label="expr 25"]; - N6[label="local mut y"]; - N7[label="stmt let mut y = 25;"]; - N8[label="expr 25"]; - N9[label="local mut z"]; - N10[label="stmt let mut z = 25;"]; - N11[label="(dummy_node)"]; - N12[label="expr \'a:\l loop {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l \'a:\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { continue \'a ; \"unreachable\"; }\l }\l }\l"]; - N13[label="expr x"]; - N14[label="expr 0"]; - N15[label="expr x == 0"]; - N16[label="expr break"]; - N17[label="(dummy_node)"]; - N18[label="stmt break ;"]; - N19[label="expr \"unreachable\""]; - N20[label="stmt \"unreachable\";"]; - N21[label="block { break ; \"unreachable\"; }"]; - N22[label="expr if x == 0 { break ; \"unreachable\"; }"]; - N23[label="stmt if x == 0 { break ; \"unreachable\"; }"]; - N24[label="expr 1"]; - N25[label="expr x"]; - N26[label="expr x -= 1"]; - N27[label="stmt x -= 1;"]; - N28[label="(dummy_node)"]; - N29[label="expr \'a:\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { continue \'a ; \"unreachable\"; }\l }\l"]; - N30[label="expr y"]; - N31[label="expr 0"]; - N32[label="expr y == 0"]; - N33[label="expr break"]; - N34[label="(dummy_node)"]; - N35[label="stmt break ;"]; - N36[label="expr \"unreachable\""]; - N37[label="stmt \"unreachable\";"]; - N38[label="block { break ; \"unreachable\"; }"]; - N39[label="expr if y == 0 { break ; \"unreachable\"; }"]; - N40[label="stmt if y == 0 { break ; \"unreachable\"; }"]; - N41[label="expr 1"]; - N42[label="expr y"]; - N43[label="expr y -= 1"]; - N44[label="stmt y -= 1;"]; - N45[label="(dummy_node)"]; - N46[label="expr \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }"]; - N47[label="expr z"]; - N48[label="expr 0"]; - N49[label="expr z == 0"]; - N50[label="expr break"]; - N51[label="(dummy_node)"]; - N52[label="stmt break ;"]; - N53[label="expr \"unreachable\""]; - N54[label="stmt \"unreachable\";"]; - N55[label="block { break ; \"unreachable\"; }"]; - N56[label="expr if z == 0 { break ; \"unreachable\"; }"]; - N57[label="stmt if z == 0 { break ; \"unreachable\"; }"]; - N58[label="expr 1"]; - N59[label="expr z"]; - N60[label="expr z -= 1"]; - N61[label="stmt z -= 1;"]; - N62[label="block { if z == 0 { break ; \"unreachable\"; } z -= 1; }"]; - N63[label="stmt \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }"]; - N64[label="expr x"]; - N65[label="expr 10"]; - N66[label="expr x > 10"]; - N67[label="expr continue \'a"]; - N68[label="(dummy_node)"]; - N69[label="stmt continue \'a ;"]; - N70[label="expr \"unreachable\""]; - N71[label="stmt \"unreachable\";"]; - N72[label="block { continue \'a ; \"unreachable\"; }"]; - N73[label="expr if x > 10 { continue \'a ; \"unreachable\"; }"]; - N74[label="block {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { continue \'a ; \"unreachable\"; }\l}\l"]; - N75[label="block {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l \'a:\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { continue \'a ; \"unreachable\"; }\l }\l}\l"]; - N76[label="block {\l let mut x = 25;\l let mut y = 25;\l let mut z = 25;\l \'a:\l loop {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l \'a:\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { continue \'a ; \"unreachable\"; }\l }\l }\l}\l"]; - N77[label="expr {\l let mut x = 25;\l let mut y = 25;\l let mut z = 25;\l \'a:\l loop {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l \'a:\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { continue \'a ; \"unreachable\"; }\l }\l }\l}\l"]; - N0 -> N2; - N2 -> N3; - N3 -> N4; - N4 -> N5; - N5 -> N6; - N6 -> N7; - N7 -> N8; - N8 -> N9; - N9 -> N10; - N10 -> N11; - N11 -> N13; - N13 -> N14; - N14 -> N15; - N15 -> N16; - N16 -> N12; - N17 -> N18; - N18 -> N19; - N19 -> N20; - N20 -> N21; - N15 -> N22; - N21 -> N22; - N22 -> N23; - N23 -> N24; - N24 -> N25; - N25 -> N26; - N26 -> N27; - N27 -> N28; - N28 -> N30; - N30 -> N31; - N31 -> N32; - N32 -> N33; - N33 -> N29; - N34 -> N35; - N35 -> N36; - N36 -> N37; - N37 -> N38; - N32 -> N39; - N38 -> N39; - N39 -> N40; - N40 -> N41; - N41 -> N42; - N42 -> N43; - N43 -> N44; - N44 -> N45; - N45 -> N47; - N47 -> N48; - N48 -> N49; - N49 -> N50; - N50 -> N46; - N51 -> N52; - N52 -> N53; - N53 -> N54; - N54 -> N55; - N49 -> N56; - N55 -> N56; - N56 -> N57; - N57 -> N58; - N58 -> N59; - N59 -> N60; - N60 -> N61; - N61 -> N62; - N62 -> N45; - N46 -> N63; - N63 -> N64; - N64 -> N65; - N65 -> N66; - N66 -> N67; - N67 -> N28; - N68 -> N69; - N69 -> N70; - N70 -> N71; - N71 -> N72; - N66 -> N73; - N72 -> N73; - N73 -> N74; - N74 -> N28; - N29 -> N75; - N75 -> N11; - N12 -> N76; - N76 -> N77; - N77 -> N1; -} diff --git a/src/test/run-make/graphviz-flowgraph/f25.rs b/src/test/run-make/graphviz-flowgraph/f25.rs deleted file mode 100644 index 2ee2e48fd10e0..0000000000000 --- a/src/test/run-make/graphviz-flowgraph/f25.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[allow(unreachable_code)] -pub fn expr_while_25() { - let mut x = 25; - let mut y = 25; - let mut z = 25; - - 'a: loop { - if x == 0 { break; "unreachable"; } - x -= 1; - - 'a: loop { - if y == 0 { break; "unreachable"; } - y -= 1; - - 'a: loop { - if z == 0 { break; "unreachable"; } - z -= 1; - } - - if x > 10 { - continue 'a; - "unreachable"; - } - } - } -} From fb99d87c460c8f35ae8ab9e3694aa34d983dbd9c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 25 Mar 2017 05:30:39 -0400 Subject: [PATCH 34/36] update UI test We no longer give suggestions; this is presumably related to the changes I made in coercion. However, those suggestions appear to be wrong anyhow! --- src/test/ui/resolve/token-error-correct-3.stderr | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/test/ui/resolve/token-error-correct-3.stderr b/src/test/ui/resolve/token-error-correct-3.stderr index 56e3688957502..5be23d8ca48c8 100644 --- a/src/test/ui/resolve/token-error-correct-3.stderr +++ b/src/test/ui/resolve/token-error-correct-3.stderr @@ -36,10 +36,6 @@ error[E0308]: mismatched types | = note: expected type `()` found type `std::result::Result` - = help: here are some functions which might fulfill your needs: - - .unwrap() - - .unwrap_err() - - .unwrap_or_default() error: aborting due to previous error From 744f666445348d505f42b3fb77217e31d8eb786b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 26 Mar 2017 07:02:39 -0400 Subject: [PATCH 35/36] fix error message for issue-10176.rs --- src/test/compile-fail/issue-10176.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/compile-fail/issue-10176.rs b/src/test/compile-fail/issue-10176.rs index 434b795ff31f5..c968844ae21ac 100644 --- a/src/test/compile-fail/issue-10176.rs +++ b/src/test/compile-fail/issue-10176.rs @@ -12,7 +12,7 @@ fn f() -> isize { (return 1, return 2) //~^ ERROR mismatched types //~| expected type `isize` -//~| found type `(_, _)` +//~| found type `(!, !)` //~| expected isize, found tuple } From 2414222b17f45e33d68582dd5ebe0083ed27b8cc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 27 Mar 2017 19:48:55 -0400 Subject: [PATCH 36/36] remove comments that were tripping up pretty printer --- src/test/run-pass/issue-39808.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/run-pass/issue-39808.rs b/src/test/run-pass/issue-39808.rs index 00c2bdc8cc99d..91c70d76eefbc 100644 --- a/src/test/run-pass/issue-39808.rs +++ b/src/test/run-pass/issue-39808.rs @@ -19,7 +19,7 @@ use std::borrow::Cow; fn main() { let _ = if false { - Cow::Owned(format!("{:?}", panic!())) /* as Cow */ // uncomment to fix + Cow::Owned(format!("{:?}", panic!())) } else { Cow::Borrowed("") };