From 67867c663593e60108d09310752e5758b291e6ce Mon Sep 17 00:00:00 2001 From: Micah Tigley Date: Mon, 20 Mar 2017 13:11:58 -0600 Subject: [PATCH 01/61] Update docs for std::str --- src/libcollections/str.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/libcollections/str.rs b/src/libcollections/str.rs index 90e54a383d623..40abc8a96f066 100644 --- a/src/libcollections/str.rs +++ b/src/libcollections/str.rs @@ -59,6 +59,27 @@ pub use std_unicode::str::SplitWhitespace; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::pattern; +/// Unicode string slices. +/// +/// The `&str` type is one of the two main string types, the other being `String`. Unlike its `String` counterpart, its contents +/// are borrowed and therefore cannot be moved someplace else. +/// +/// # Basic Usage +/// A basic string declaration of `&str` type: +/// +/// ``` +/// let hello_world = "Hello, World!"; +/// ``` +/// Here we have declared a string literal, also known as a string slice. +/// String literals have a static lifetime, which means the string `hello_world` +/// is guaranteed to be valid for the duration of the entire program. We can explicitly specify +/// `hello_world`'s lifetime as well: +/// +/// ``` +/// let hello_world:&'static str = "Hello, world!"; +/// ``` +/// + #[unstable(feature = "slice_concat_ext", reason = "trait should not have to exist", issue = "27747")] From 63652726c39a0c018b7897f7c3e92690c3599207 Mon Sep 17 00:00:00 2001 From: Micah Tigley Date: Mon, 20 Mar 2017 15:21:28 -0600 Subject: [PATCH 02/61] Move str docs to proper place in file. --- src/libcollections/str.rs | 41 ++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/libcollections/str.rs b/src/libcollections/str.rs index 40abc8a96f066..c87e2b086adda 100644 --- a/src/libcollections/str.rs +++ b/src/libcollections/str.rs @@ -10,9 +10,27 @@ //! Unicode string slices. //! +//! The `&str` type is one of the two main string types, the other being `String`. Unlike its `String` counterpart, its contents +//! are borrowed and therefore cannot be moved someplace else. +//! +//! # Basic Usage +//! A basic string declaration of `&str` type: +//! +//! ``` +//! let hello_world = "Hello, World!"; +//! ``` +//! +//! Here we have declared a string literal, also known as a string slice. +//! String literals have a static lifetime, which means the string `hello_world` +//! is guaranteed to be valid for the duration of the entire program. We can explicitly specify +//! `hello_world`'s lifetime as well: +//! +//! ``` +//! let hello_world:&'static str = "Hello, world!"; +//! ``` +//! //! *[See also the `str` primitive type](../../std/primitive.str.html).* - #![stable(feature = "rust1", since = "1.0.0")] // Many of the usings in this module are only used in the test configuration. @@ -59,27 +77,6 @@ pub use std_unicode::str::SplitWhitespace; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::pattern; -/// Unicode string slices. -/// -/// The `&str` type is one of the two main string types, the other being `String`. Unlike its `String` counterpart, its contents -/// are borrowed and therefore cannot be moved someplace else. -/// -/// # Basic Usage -/// A basic string declaration of `&str` type: -/// -/// ``` -/// let hello_world = "Hello, World!"; -/// ``` -/// Here we have declared a string literal, also known as a string slice. -/// String literals have a static lifetime, which means the string `hello_world` -/// is guaranteed to be valid for the duration of the entire program. We can explicitly specify -/// `hello_world`'s lifetime as well: -/// -/// ``` -/// let hello_world:&'static str = "Hello, world!"; -/// ``` -/// - #[unstable(feature = "slice_concat_ext", reason = "trait should not have to exist", issue = "27747")] From f628117529dfb117d87d62803d0746bedd6e0e83 Mon Sep 17 00:00:00 2001 From: Micah Tigley Date: Mon, 20 Mar 2017 20:20:12 -0600 Subject: [PATCH 03/61] Fix Rust linting error --- src/libcollections/str.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libcollections/str.rs b/src/libcollections/str.rs index c87e2b086adda..d4480ce77d646 100644 --- a/src/libcollections/str.rs +++ b/src/libcollections/str.rs @@ -10,8 +10,9 @@ //! Unicode string slices. //! -//! The `&str` type is one of the two main string types, the other being `String`. Unlike its `String` counterpart, its contents -//! are borrowed and therefore cannot be moved someplace else. +//! The `&str` type is one of the two main string types, the other being `String`. +//! Unlike its `String` counterpart, its contents are borrowed and therefore +//! cannot be moved someplace else. //! //! # Basic Usage //! A basic string declaration of `&str` type: @@ -22,8 +23,8 @@ //! //! Here we have declared a string literal, also known as a string slice. //! String literals have a static lifetime, which means the string `hello_world` -//! is guaranteed to be valid for the duration of the entire program. We can explicitly specify -//! `hello_world`'s lifetime as well: +//! is guaranteed to be valid for the duration of the entire program. +//! We can explicitly specify `hello_world`'s lifetime as well: //! //! ``` //! let hello_world:&'static str = "Hello, world!"; From dae66e000a974dd3bea7ae10b8827a5ece2b941e Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 17 Mar 2017 22:36:21 -0700 Subject: [PATCH 04/61] Specialize Vec::from_iter for vec::IntoIter It's fairly common to expose an API which takes an `IntoIterator` and immediately collects that into a vector. It's also common to buffer a bunch of items into a vector and then pass that into one of these APIs. If the iterator hasn't been advanced, we can make this `from_iter` simply reassemble the original `Vec` with no actual iteration or reallocation. --- src/libcollections/vec.rs | 29 +++++++++++++++++++++++++---- src/libcollectionstest/vec.rs | 16 ++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index 7b408af13aa2f..56b60a3e00341 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -1563,7 +1563,7 @@ impl ops::DerefMut for Vec { impl FromIterator for Vec { #[inline] fn from_iter>(iter: I) -> Vec { - >::from_iter(iter.into_iter()) + >::from_iter(iter.into_iter()) } } @@ -1631,7 +1631,7 @@ impl<'a, T> IntoIterator for &'a mut Vec { impl Extend for Vec { #[inline] fn extend>(&mut self, iter: I) { - self.spec_extend(iter.into_iter()) + >::spec_extend(self, iter.into_iter()) } } @@ -1662,7 +1662,7 @@ impl SpecExtend for Vec vector } }; - vector.spec_extend(iterator); + as SpecExtend>::spec_extend(&mut vector, iterator); vector } @@ -1674,7 +1674,7 @@ impl SpecExtend for Vec impl SpecExtend for Vec where I: TrustedLen, { - fn from_iter(iterator: I) -> Self { + default fn from_iter(iterator: I) -> Self { let mut vector = Vec::new(); vector.spec_extend(iterator); vector @@ -1706,6 +1706,27 @@ impl SpecExtend for Vec } } +impl SpecExtend> for Vec { + fn from_iter(iterator: IntoIter) -> Self { + // A common case is passing a vector into a function which immediately + // re-collects into a vector. We can short circuit this if the IntoIter + // has not been advanced at all. + if *iterator.buf == iterator.ptr as *mut T { + unsafe { + let vec = Vec::from_raw_parts(*iterator.buf as *mut T, + iterator.len(), + iterator.cap); + mem::forget(iterator); + vec + } + } else { + let mut vector = Vec::new(); + vector.spec_extend(iterator); + vector + } + } +} + impl<'a, T: 'a, I> SpecExtend<&'a T, I> for Vec where I: Iterator, T: Clone, diff --git a/src/libcollectionstest/vec.rs b/src/libcollectionstest/vec.rs index 06d70800d3925..63df0eb730509 100644 --- a/src/libcollectionstest/vec.rs +++ b/src/libcollectionstest/vec.rs @@ -680,3 +680,19 @@ fn test_placement_panic() { let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { vec.place_back() <- mkpanic(); })); assert_eq!(vec.len(), 3); } + +#[test] +fn from_into_inner() { + let vec = vec![1, 2, 3]; + let ptr = vec.as_ptr(); + let vec = vec.into_iter().collect::>(); + assert_eq!(vec, [1, 2, 3]); + assert_eq!(vec.as_ptr(), ptr); + + let ptr = &vec[1] as *const _; + let mut it = vec.into_iter(); + it.next().unwrap(); + let vec = it.collect::>(); + assert_eq!(vec, [2, 3]); + assert!(ptr != vec.as_ptr()); +} From 8a91e4d1238b40200724826cc7ef3f7893c6152b Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Fri, 24 Mar 2017 05:05:34 +0300 Subject: [PATCH 05/61] Document Cursor::new position is 0 ... even if contained `Vec` is not empty. E. g. for ``` let v = vec![10u8, 20]; let mut c = io::Cursor::new(v); c.write_all(b"aaaa").unwrap(); println!("{:?}", c.into_inner()); ``` result is ``` [97, 97, 97, 97] ``` and not ``` [10, 20, 97, 97, 97, 97] ``` --- src/libstd/io/cursor.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs index 60767ea478661..53347eb14db0d 100644 --- a/src/libstd/io/cursor.rs +++ b/src/libstd/io/cursor.rs @@ -89,6 +89,10 @@ pub struct Cursor { impl Cursor { /// Creates a new cursor wrapping the provided underlying I/O object. /// + /// Cursor initial position is `0` even if underlying object (e. + /// g. `Vec`) is not empty. So writing to cursor starts with + /// overwriting `Vec` content, not with appending to it. + /// /// # Examples /// /// ``` From b442b5e3f5b16761c99ab3ce956b2852ac1e01a0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 9 Mar 2017 09:53:00 -0500 Subject: [PATCH 06/61] 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 2ac1a036f99e1..b48ab6d41c086 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -1846,7 +1846,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 1c79a02d3da0e..a2d850082ce92 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -989,7 +989,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 9c62fd486d45a..3dd7ce1fb38ba 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2737,7 +2737,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> { @@ -2746,7 +2746,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); @@ -2761,26 +2761,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). @@ -3585,9 +3572,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 0dcf7c43cd33501755b8ecb7a5af7e687aad35f3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 9 Mar 2017 09:57:26 -0500 Subject: [PATCH 07/61] 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 a2d850082ce92..a6e7f740e8af0 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -988,7 +988,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 0960d7f91e9425187b4bdb61701016473a0bceed Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 22 Mar 2017 11:40:29 -0400 Subject: [PATCH 08/61] 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 b48ab6d41c086..dbe1fe03b3cc5 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -1146,7 +1146,7 @@ impl<'a> LoweringContext<'a> { bounds.iter().map(|bound| self.lower_ty_param_bound(bound)).collect() } - fn lower_block(&mut self, b: &Block, break_to: Option) -> P { + fn lower_block(&mut self, b: &Block, targeted_by_break: bool) -> P { let mut expr = None; let mut stmts = vec![]; @@ -1169,7 +1169,7 @@ impl<'a> LoweringContext<'a> { expr: expr, rules: self.lower_block_check_mode(&b.rules), span: b.span, - break_to_expr_id: break_to, + targeted_by_break: targeted_by_break, }) } @@ -1264,7 +1264,7 @@ impl<'a> LoweringContext<'a> { } ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => { self.with_new_scopes(|this| { - let body = this.lower_block(body, None); + let body = this.lower_block(body, false); let body = this.expr_block(body, ThinVec::new()); let body_id = this.record_body(body, Some(decl)); hir::ItemFn(this.lower_fn_decl(decl), @@ -1358,7 +1358,7 @@ impl<'a> LoweringContext<'a> { hir::TraitMethod::Required(names)) } TraitItemKind::Method(ref sig, Some(ref body)) => { - let body = this.lower_block(body, None); + let body = this.lower_block(body, false); let expr = this.expr_block(body, ThinVec::new()); let body_id = this.record_body(expr, Some(&sig.decl)); hir::TraitItemKind::Method(this.lower_method_sig(sig), @@ -1414,7 +1414,7 @@ impl<'a> LoweringContext<'a> { hir::ImplItemKind::Const(this.lower_ty(ty), body_id) } ImplItemKind::Method(ref sig, ref body) => { - let body = this.lower_block(body, None); + let body = this.lower_block(body, false); let expr = this.expr_block(body, ThinVec::new()); let body_id = this.record_body(expr, Some(&sig.decl)); hir::ImplItemKind::Method(this.lower_method_sig(sig), body_id) @@ -1838,7 +1838,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())) } @@ -1846,7 +1846,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) @@ -1855,18 +1855,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)), @@ -1884,7 +1884,7 @@ impl<'a> LoweringContext<'a> { }) }) } - ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk, None)), + ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk, false)), ExprKind::Assign(ref el, ref er) => { hir::ExprAssign(P(self.lower_expr(el)), P(self.lower_expr(er))) } @@ -2042,7 +2042,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)); @@ -2114,7 +2114,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 { @@ -2164,7 +2164,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))), )); @@ -2225,7 +2225,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); @@ -2668,7 +2668,7 @@ impl<'a> LoweringContext<'a> { id: self.next_id(), rules: hir::DefaultBlock, span: span, - break_to_expr_id: None, + targeted_by_break: false, } } @@ -2776,7 +2776,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 a6e7f740e8af0..139f98921edce 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -544,9 +544,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 3dd7ce1fb38ba..4ad09b8b78c6f 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] } } @@ -3470,12 +3466,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 { @@ -3490,7 +3486,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". @@ -4022,7 +4018,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 { @@ -4032,15 +4028,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 21b2cdf4a140316857956c72f20bf9eac7fa2274 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 2 Mar 2017 21:15:26 -0500 Subject: [PATCH 09/61] 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 4ad09b8b78c6f..6cbbda8074fa8 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1531,18 +1531,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>) { @@ -3280,6 +3275,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. @@ -3965,7 +3965,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) => { @@ -3991,9 +3991,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); } @@ -4044,7 +4041,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); } }; @@ -4052,6 +4053,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 64643c5596a2c8ef30494fb21949de55f7df95af Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 09:35:50 -0400 Subject: [PATCH 10/61] 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 b51c6c0ed230588a47c9a427a1f36226110af24a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 09:36:44 -0400 Subject: [PATCH 11/61] 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 339c20eb36bf8449e97f7490f2d3533bd6baf97c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 09:37:18 -0400 Subject: [PATCH 12/61] 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 6b64b868d9215914646b152082775bff3dff7b25 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 09:39:13 -0400 Subject: [PATCH 13/61] 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 0e29ef5ae4ee9922c8f5ffaad9c6b0f1ff31d4ac Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 09:51:31 -0400 Subject: [PATCH 14/61] 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 e5b66467de57f8e279871a102d3220e63be7ead9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 09:52:38 -0400 Subject: [PATCH 15/61] 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 6cbbda8074fa8..48e93f789e078 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)] @@ -2741,54 +2753,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 4544296037350da6a6863c2677be008384fc2047 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 09:54:26 -0400 Subject: [PATCH 16/61] 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 48e93f789e078..58956b864d801 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, @@ -677,7 +677,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); @@ -798,15 +798,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 @@ -831,7 +832,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 } @@ -1427,14 +1459,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), @@ -2736,6 +2767,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, @@ -3520,24 +3568,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 bdb5400cb45eb7f8a5d7708b4a508b82429c2684 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 11:10:12 -0400 Subject: [PATCH 17/61] 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 58956b864d801..a79b8aef57046 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 9e24b9115bfe78a624ff53a8c776688c8b7f1ab7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 11:48:18 -0400 Subject: [PATCH 18/61] 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 a79b8aef57046..d46941bef84a7 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2662,7 +2662,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 } @@ -3368,18 +3383,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 } @@ -4070,7 +4073,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, @@ -4109,7 +4117,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() }; @@ -4133,6 +4141,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); }; @@ -4145,33 +4157,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 a1678392711e05d2f4b17ebddd4fcedddde15bd1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 11:49:53 -0400 Subject: [PATCH 19/61] 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 d46941bef84a7..83903bcf5dd12 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, } @@ -3545,60 +3546,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) => { @@ -3643,51 +3650,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 3816675aaa9f7d8f71784bac0f703ef1719f93dd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 11:52:37 -0400 Subject: [PATCH 20/61] 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 83903bcf5dd12..eff7322b52a69 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3750,36 +3750,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 79ce5894ee82085bd85c0a4543a10f22d524b435 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 11:53:02 -0400 Subject: [PATCH 21/61] 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 eff7322b52a69..3ff0aa3b54d1a 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1524,6 +1524,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, @@ -2543,8 +2545,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 2b1faeadb6519af9becbbd5e58943f8898b618d5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 11:53:15 -0400 Subject: [PATCH 22/61] 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 f38abfc6032fe0d951eaa6748fa2f9ae85571e1f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 14:10:00 -0400 Subject: [PATCH 23/61] 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 410e4bbb609785fe4cebf40fd0cae02d12ba5a9c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Mar 2017 11:54:23 -0400 Subject: [PATCH 24/61] 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 692156f9caece89a50371b2928024a49877bdf57 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 18 Mar 2017 15:22:48 -0400 Subject: [PATCH 25/61] 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 25903b6d973c76c076b5e7d5aab0f4d3d95ddb8d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 21 Mar 2017 09:19:49 -0400 Subject: [PATCH 26/61] 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 3ff0aa3b54d1a..d0dd0cf5bcf06 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3603,8 +3603,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 169facfc288586b7635652abace56d56da2a6f99 Mon Sep 17 00:00:00 2001 From: lukaramu Date: Sat, 25 Mar 2017 17:11:08 +0100 Subject: [PATCH 27/61] added missing links in std::net TCP docs part of #29363 --- src/libstd/net/tcp.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index a07972468e68a..8a21f17dc49fb 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -83,11 +83,15 @@ impl TcpStream { /// Opens a TCP connection to a remote host. /// /// `addr` is an address of the remote host. Anything which implements - /// `ToSocketAddrs` trait can be supplied for the address; see this trait + /// [`ToSocketAddrs`] trait can be supplied for the address; see this trait /// documentation for concrete examples. - /// In case `ToSocketAddrs::to_socket_addrs()` returns more than one entry, + /// In case [`ToSocketAddrs::to_socket_addrs()`] returns more than one entry, /// then the first valid and reachable address is used. /// + /// [`ToSocketAddrs`]: ../../std/net/trait.ToSocketAddrs.html + /// [`ToSocketAddrs::to_socket_addrs()`]: + /// ../../std/net/trait.ToSocketAddrs.html#tymethod.to_socket_addrs + /// /// # Examples /// /// ```no_run @@ -494,11 +498,14 @@ impl TcpListener { /// /// Binding with a port number of 0 will request that the OS assigns a port /// to this listener. The port allocated can be queried via the - /// `local_addr` method. + /// [`local_addr`] method. /// - /// The address type can be any implementor of `ToSocketAddrs` trait. See + /// The address type can be any implementor of [`ToSocketAddrs`] trait. See /// its documentation for concrete examples. /// + /// [`local_addr`]: #method.local_addr + /// [`ToSocketAddrs`]: ../../std/net/trait.ToSocketAddrs.html + /// /// # Examples /// /// ```no_run @@ -529,10 +536,12 @@ impl TcpListener { /// Creates a new independently owned handle to the underlying socket. /// - /// The returned `TcpListener` is a reference to the same socket that this + /// The returned [`TcpListener`] is a reference to the same socket that this /// object references. Both handles can be used to accept incoming /// connections and options set on one listener will affect the other. /// + /// [`TcpListener`]: ../../std/net/struct.TcpListener.html + /// /// # Examples /// /// ```no_run @@ -549,9 +558,11 @@ impl TcpListener { /// Accept a new incoming connection from this listener. /// /// This function will block the calling thread until a new TCP connection - /// is established. When established, the corresponding `TcpStream` and the + /// is established. When established, the corresponding [`TcpStream`] and the /// remote peer's address will be returned. /// + /// [`TcpStream`]: ../../std/net/struct.TcpStream.html + /// /// # Examples /// /// ```no_run From 76d08eda7d228ed475bf190253b8fd7ebfe667db Mon Sep 17 00:00:00 2001 From: lukaramu Date: Sun, 26 Mar 2017 14:30:03 +0200 Subject: [PATCH 28/61] Update std::net:Incoming's docs to use standard iterator boilerplate Part of #29363 --- src/libstd/net/tcp.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index 8a21f17dc49fb..c6cf748d981c7 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -65,16 +65,14 @@ pub struct TcpStream(net_imp::TcpStream); #[stable(feature = "rust1", since = "1.0.0")] pub struct TcpListener(net_imp::TcpListener); -/// An infinite iterator over the connections from a `TcpListener`. -/// -/// This iterator will infinitely yield [`Some`] of the accepted connections. It -/// is equivalent to calling `accept` in a loop. +/// An iterator that infinitely [`accept`]s connections on a [`TcpListener`]. /// /// This `struct` is created by the [`incoming`] method on [`TcpListener`]. +/// See its documentation for more. /// -/// [`Some`]: ../../std/option/enum.Option.html#variant.Some -/// [`incoming`]: struct.TcpListener.html#method.incoming -/// [`TcpListener`]: struct.TcpListener.html +/// [`accept`]: ../../std/net/struct.TcpListener.html#method.accept +/// [`incoming`]: ../../std/net/struct.TcpListener.html#method.incoming +/// [`TcpListener`]: ../../std/net/struct.TcpListener.html #[stable(feature = "rust1", since = "1.0.0")] #[derive(Debug)] pub struct Incoming<'a> { listener: &'a TcpListener } @@ -583,10 +581,12 @@ impl TcpListener { /// listener. /// /// The returned iterator will never return [`None`] and will also not yield - /// the peer's [`SocketAddr`] structure. + /// the peer's [`SocketAddr`] structure. Iterating over it is equivalent to + /// calling [`accept`] in a loop. /// /// [`None`]: ../../std/option/enum.Option.html#variant.None /// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html + /// [`accept`]: #method.accept /// /// # Examples /// From 0df7398558cb7d8fbf5d191d7e07a601a4a30702 Mon Sep 17 00:00:00 2001 From: lukaramu Date: Sat, 25 Mar 2017 17:27:00 +0100 Subject: [PATCH 29/61] std::net docs: changed occurences of "RFC" to say "IETF RFC" part of #29363 --- src/libstd/net/ip.rs | 48 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index 24e0e6f3fa659..44dc689a30f4c 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -296,9 +296,9 @@ impl Ipv4Addr { /// Returns true if this is a loopback address (127.0.0.0/8). /// - /// This property is defined by [RFC 1122]. + /// This property is defined by [IETF RFC 1122]. /// - /// [RFC 1122]: https://tools.ietf.org/html/rfc1122 + /// [IETF RFC 1122]: https://tools.ietf.org/html/rfc1122 /// /// # Examples /// @@ -315,13 +315,13 @@ impl Ipv4Addr { /// Returns true if this is a private address. /// - /// The private address ranges are defined in [RFC 1918] and include: + /// The private address ranges are defined in [IETF RFC 1918] and include: /// /// - 10.0.0.0/8 /// - 172.16.0.0/12 /// - 192.168.0.0/16 /// - /// [RFC 1918]: https://tools.ietf.org/html/rfc1918 + /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 /// /// # Examples /// @@ -348,9 +348,9 @@ impl Ipv4Addr { /// Returns true if the address is link-local (169.254.0.0/16). /// - /// This property is defined by [RFC 3927]. + /// This property is defined by [IETF RFC 3927]. /// - /// [RFC 3927]: https://tools.ietf.org/html/rfc3927 + /// [IETF RFC 3927]: https://tools.ietf.org/html/rfc3927 /// /// # Examples /// @@ -403,9 +403,9 @@ impl Ipv4Addr { /// Returns true if this is a multicast address (224.0.0.0/4). /// /// Multicast addresses have a most significant octet between 224 and 239, - /// and is defined by [RFC 5771]. + /// and is defined by [IETF RFC 5771]. /// - /// [RFC 5771]: https://tools.ietf.org/html/rfc5771 + /// [IETF RFC 5771]: https://tools.ietf.org/html/rfc5771 /// /// # Examples /// @@ -423,9 +423,9 @@ impl Ipv4Addr { /// Returns true if this is a broadcast address (255.255.255.255). /// - /// A broadcast address has all octets set to 255 as defined in [RFC 919]. + /// A broadcast address has all octets set to 255 as defined in [IETF RFC 919]. /// - /// [RFC 919]: https://tools.ietf.org/html/rfc919 + /// [IETF RFC 919]: https://tools.ietf.org/html/rfc919 /// /// # Examples /// @@ -443,13 +443,13 @@ impl Ipv4Addr { /// Returns true if this address is in a range designated for documentation. /// - /// This is defined in [RFC 5737]: + /// This is defined in [IETF RFC 5737]: /// /// - 192.0.2.0/24 (TEST-NET-1) /// - 198.51.100.0/24 (TEST-NET-2) /// - 203.0.113.0/24 (TEST-NET-3) /// - /// [RFC 5737]: https://tools.ietf.org/html/rfc5737 + /// [IETF RFC 5737]: https://tools.ietf.org/html/rfc5737 /// /// # Examples /// @@ -719,9 +719,9 @@ impl Ipv6Addr { /// Returns true for the special 'unspecified' address (::). /// - /// This property is defined in [RFC 4291]. + /// This property is defined in [IETF RFC 4291]. /// - /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 /// /// # Examples /// @@ -738,9 +738,9 @@ impl Ipv6Addr { /// Returns true if this is a loopback address (::1). /// - /// This property is defined in [RFC 4291]. + /// This property is defined in [IETF RFC 4291]. /// - /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 /// /// # Examples /// @@ -786,9 +786,9 @@ impl Ipv6Addr { /// Returns true if this is a unique local address (fc00::/7). /// - /// This property is defined in [RFC 4193]. + /// This property is defined in [IETF RFC 4193]. /// - /// [RFC 4193]: https://tools.ietf.org/html/rfc4193 + /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 /// /// # Examples /// @@ -809,9 +809,9 @@ impl Ipv6Addr { /// Returns true if the address is unicast and link-local (fe80::/10). /// - /// This property is defined in [RFC 4291]. + /// This property is defined in [IETF RFC 4291]. /// - /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 /// /// # Examples /// @@ -853,9 +853,9 @@ impl Ipv6Addr { /// Returns true if this is an address reserved for documentation /// (2001:db8::/32). /// - /// This property is defined in [RFC 3849]. + /// This property is defined in [IETF RFC 3849]. /// - /// [RFC 3849]: https://tools.ietf.org/html/rfc3849 + /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 /// /// # Examples /// @@ -939,9 +939,9 @@ impl Ipv6Addr { /// Returns true if this is a multicast address (ff00::/8). /// - /// This property is defined by [RFC 4291]. + /// This property is defined by [IETF RFC 4291]. /// - /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 /// # Examples /// /// ``` From df5830a4ece5eae8451e7b3d4f77b8af92a9f9fc Mon Sep 17 00:00:00 2001 From: lukaramu Date: Sat, 25 Mar 2017 18:28:12 +0100 Subject: [PATCH 30/61] Added links throughout std::net::ToSocketAddrs' documentation Part of #29363 In the section about the default implementations of ToSocketAddrs, I moved the bulletpoint of SocketAddrV4 & SocketAddrV6 to the one stating that SocketAddr is constructed trivially, as this is what's actually the case --- src/libstd/net/addr.rs | 52 ++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs index 84c4acb8d9247..ea78843aa8c0c 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -559,37 +559,51 @@ impl hash::Hash for SocketAddrV6 { } /// A trait for objects which can be converted or resolved to one or more -/// `SocketAddr` values. +/// [`SocketAddr`] values. /// /// This trait is used for generic address resolution when constructing network /// objects. By default it is implemented for the following types: /// -/// * `SocketAddr`, `SocketAddrV4`, `SocketAddrV6` - `to_socket_addrs` is -/// identity function. +/// * [`SocketAddr`]: [`to_socket_addrs`] is the identity function. /// -/// * `(IpvNAddr, u16)` - `to_socket_addrs` constructs `SocketAddr` trivially. +/// * [`SocketAddrV4`], [`SocketAddrV6`], `(`[`IpAddr`]`, `[`u16`]`)`, +/// `(`[`Ipv4Addr`]`, `[`u16`]`)`, `(`[`Ipv6Addr`]`, `[`u16`]`)`: +/// [`to_socket_addrs`] constructs a [`SocketAddr`] trivially. /// -/// * `(&str, u16)` - the string should be either a string representation of an -/// IP address expected by `FromStr` implementation for `IpvNAddr` or a host +/// * `(`[`&str`]`, `[`u16`]`)`: the string should be either a string representation +/// of an [`IpAddr`] address as expected by [`FromStr`] implementation or a host /// name. /// -/// * `&str` - the string should be either a string representation of a -/// `SocketAddr` as expected by its `FromStr` implementation or a string like -/// `:` pair where `` is a `u16` value. +/// * [`&str`]: the string should be either a string representation of a +/// [`SocketAddr`] as expected by its [`FromStr`] implementation or a string like +/// `:` pair where `` is a [`u16`] value. /// -/// This trait allows constructing network objects like `TcpStream` or -/// `UdpSocket` easily with values of various types for the bind/connection +/// This trait allows constructing network objects like [`TcpStream`] or +/// [`UdpSocket`] easily with values of various types for the bind/connection /// address. It is needed because sometimes one type is more appropriate than /// the other: for simple uses a string like `"localhost:12345"` is much nicer -/// than manual construction of the corresponding `SocketAddr`, but sometimes -/// `SocketAddr` value is *the* main source of the address, and converting it to +/// than manual construction of the corresponding [`SocketAddr`], but sometimes +/// [`SocketAddr`] value is *the* main source of the address, and converting it to /// some other type (e.g. a string) just for it to be converted back to -/// `SocketAddr` in constructor methods is pointless. +/// [`SocketAddr`] in constructor methods is pointless. /// /// Addresses returned by the operating system that are not IP addresses are /// silently ignored. /// -/// Some examples: +/// [`FromStr`]: ../../std/str/trait.FromStr.html +/// [`IpAddr`]: ../../std/net/enum.IpAddr.html +/// [`Ipv4Addr`]: ../../std/net/struct.Ipv4Addr.html +/// [`Ipv6Addr`]: ../../std/net/struct.Ipv6Addr.html +/// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html +/// [`SocketAddrV4`]: ../../std/net/struct.SocketAddrV4.html +/// [`SocketAddrV6`]: ../../std/net/struct.SocketAddrV6.html +/// [`&str`]: ../../std/primitive.str.html +/// [`TcpStream`]: ../../std/net/struct.TcpStream.html +/// [`to_socket_addrs`]: #tymethod.to_socket_addrs +/// [`UdpSocket`]: ../../std/net/struct.UdpSocket.html +/// [`u16`]: ../../std/primitive.u16.html +/// +/// # Examples /// /// ```no_run /// use std::net::{SocketAddrV4, TcpStream, UdpSocket, TcpListener, Ipv4Addr}; @@ -622,7 +636,7 @@ pub trait ToSocketAddrs { #[stable(feature = "rust1", since = "1.0.0")] type Iter: Iterator; - /// Converts this object to an iterator of resolved `SocketAddr`s. + /// Converts this object to an iterator of resolved [`SocketAddr`]s. /// /// The returned iterator may not actually yield any values depending on the /// outcome of any resolution performed. @@ -630,9 +644,13 @@ pub trait ToSocketAddrs { /// Note that this function may block the current thread while resolution is /// performed. /// + /// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html + /// /// # Errors /// - /// Any errors encountered during resolution will be returned as an `Err`. + /// Any errors encountered during resolution will be returned as an [`Err`]. + /// + /// [`Err`]: ../../std/result/enum.Result.html#variant.Err #[stable(feature = "rust1", since = "1.0.0")] fn to_socket_addrs(&self) -> io::Result; } From 0d5baba70d3adbb2a8ea436c6c89f21c1c6927b8 Mon Sep 17 00:00:00 2001 From: lukaramu Date: Sat, 25 Mar 2017 19:23:13 +0100 Subject: [PATCH 31/61] Added links to std::net::AddrParseError's documentation Additionally changed the summary sentence to be more consistent with most of the other FromStr implementations' error types. --- src/libstd/net/parser.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libstd/net/parser.rs b/src/libstd/net/parser.rs index d86711c10ac79..7d7c67ff3f9f7 100644 --- a/src/libstd/net/parser.rs +++ b/src/libstd/net/parser.rs @@ -368,7 +368,19 @@ impl FromStr for SocketAddr { } } -/// An error returned when parsing an IP address or a socket address. +/// An error which can be returned when parsing an IP address or a socket address. +/// +/// This error is used as the error type for the [`FromStr`] implementation for +/// [`IpAddr`], [`Ipv4Addr`], [`Ipv6Addr`], [`SocketAddr`], [`SocketAddrV4`], and +/// [`SocketAddrV6`]. +/// +/// [`FromStr`]: ../../std/str/trait.FromStr.html +/// [`IpAddr`]: ../../std/net/enum.IpAddr.html +/// [`Ipv4Addr`]: ../../std/net/struct.Ipv4Addr.html +/// [`Ipv6Addr`]: ../../std/net/struct.Ipv6Addr.html +/// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html +/// [`SocketAddrV4`]: ../../std/net/struct.SocketAddrV4.html +/// [`SocketAddrV6`]: ../../std/net/struct.SocketAddrV6.html #[stable(feature = "rust1", since = "1.0.0")] #[derive(Debug, Clone, PartialEq, Eq)] pub struct AddrParseError(()); From 347b70901cdabcd78d2b0229c13c31b334b00617 Mon Sep 17 00:00:00 2001 From: lukaramu Date: Sun, 26 Mar 2017 14:25:31 +0200 Subject: [PATCH 32/61] Expanded and added links to std::net::{IpAddr,Ipv4Addr,Ipv6Addr} docs Part of #29363 Expanded top-level documentation & linked to relevant IETF RFCs. Added a bunch of links (to true/false/Ipv4Addr/etc.) throughout the docs. --- src/libstd/net/ip.rs | 202 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 160 insertions(+), 42 deletions(-) diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index 44dc689a30f4c..3e58a335470ae 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -21,44 +21,99 @@ use net::{hton, ntoh}; use sys::net::netc as c; use sys_common::{AsInner, FromInner}; -/// An IP address, either an IPv4 or IPv6 address. +/// Either an IPv4 address or an IPv6 address. /// -/// # Examples +/// This enum can contain either an [`Ipv4Addr`] or an [`Ipv6Addr`], see their +/// respective documentation for more details. /// -/// Constructing an IPv4 address: +/// [`Ipv4Addr`]: ../../std/net/struct.Ipv4Addr.html +/// [`Ipv6Addr`]: ../../std/net/struct.Ipv6Addr.html /// -/// ``` -/// use std::net::{IpAddr, Ipv4Addr}; +/// # Examples /// -/// IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); /// ``` +/// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// -/// Constructing an IPv6 address: +/// let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); +/// let localhost_v6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); /// -/// ``` -/// use std::net::{IpAddr, Ipv6Addr}; +/// assert_eq!("127.0.0.1".parse(), Ok(localhost_v4)); +/// assert_eq!("::1".parse(), Ok(localhost_v6)); /// -/// IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); +/// assert_eq!(localhost_v4.is_ipv6(), false); +/// assert_eq!(localhost_v4.is_ipv4(), true); /// ``` #[stable(feature = "ip_addr", since = "1.7.0")] #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, PartialOrd, Ord)] pub enum IpAddr { - /// Representation of an IPv4 address. + /// An IPv4 address. #[stable(feature = "ip_addr", since = "1.7.0")] V4(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv4Addr), - /// Representation of an IPv6 address. + /// An IPv6 address. #[stable(feature = "ip_addr", since = "1.7.0")] V6(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv6Addr), } -/// Representation of an IPv4 address. +/// An IPv4 address. +/// +/// IPv4 addresses are defined as 32-bit integers in [IETF RFC 791]. +/// They are usually represented as four octets. +/// +/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses. +/// +/// [IETF RFC 791]: https://tools.ietf.org/html/rfc791 +/// [`IpAddr`]: ../../std/net/enum.IpAddr.html +/// +/// # Textual representation +/// +/// `Ipv4Addr` provides a [`FromStr`] implementation. The four octets are in decimal +/// notation, divided by `.` (this is called "dot-decimal notation"). +/// +/// [`FromStr`]: ../../std/str/trait.FromStr.html +/// +/// # Examples +/// +/// ``` +/// use std::net::Ipv4Addr; +/// +/// let localhost = Ipv4Addr::new(127, 0, 0, 1); +/// assert_eq!("127.0.0.1".parse(), Ok(localhost)); +/// assert_eq!(localhost.is_loopback(), true); +/// ``` #[derive(Copy)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Ipv4Addr { inner: c::in_addr, } -/// Representation of an IPv6 address. +/// An IPv6 address. +/// +/// IPv6 addresses are defined as 128-bit integers in [IETF RFC 4291]. +/// They are usually represented as eight 16-bit segments. +/// +/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses. +/// +/// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 +/// [`IpAddr`]: ../../std/net/enum.IpAddr.html +/// +/// # Textual representation +/// +/// `Ipv6Addr` provides a [`FromStr`] implementation. There are many ways to represent +/// an IPv6 address in text, but in general, each segments is written in hexadecimal +/// notation, and segments are separated by `:`. For more information, see +/// [IETF RFC 5952]. +/// +/// [`FromStr`]: ../../std/str/trait.FromStr.html +/// [IETF RFC 5952]: https://tools.ietf.org/html/rfc5952 +/// +/// # Examples +/// +/// ``` +/// use std::net::Ipv6Addr; +/// +/// let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); +/// assert_eq!("::1".parse(), Ok(localhost)); +/// assert_eq!(localhost.is_loopback(), true); #[derive(Copy)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Ipv6Addr { @@ -78,10 +133,14 @@ pub enum Ipv6MulticastScope { } impl IpAddr { - /// Returns true for the special 'unspecified' address ([IPv4], [IPv6]). + /// Returns [`true`] for the special 'unspecified' address. + /// + /// See the documentation for [`Ipv4Addr::is_unspecified`][IPv4] and + /// [`Ipv6Addr::is_unspecified`][IPv6] for more details. /// /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_unspecified /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_unspecified + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -99,10 +158,14 @@ impl IpAddr { } } - /// Returns true if this is a loopback address ([IPv4], [IPv6]). + /// Returns [`true`] if this is a loopback address. + /// + /// See the documentation for [`Ipv4Addr::is_loopback`][IPv4] and + /// [`Ipv6Addr::is_loopback`][IPv6] for more details. /// /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_loopback /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_loopback + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -120,10 +183,14 @@ impl IpAddr { } } - /// Returns true if the address appears to be globally routable ([IPv4], [IPv6]). + /// Returns [`true`] if the address appears to be globally routable. + /// + /// See the documentation for [`Ipv4Addr::is_global`][IPv4] and + /// [`Ipv6Addr::is_global`][IPv6] for more details. /// /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_global /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_global + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -145,10 +212,14 @@ impl IpAddr { } } - /// Returns true if this is a multicast address ([IPv4], [IPv6]). + /// Returns [`true`] if this is a multicast address. + /// + /// See the documentation for [`Ipv4Addr::is_multicast`][IPv4] and + /// [`Ipv6Addr::is_multicast`][IPv6] for more details. /// /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_multicast /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_multicast + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -166,10 +237,14 @@ impl IpAddr { } } - /// Returns true if this address is in a range designated for documentation ([IPv4], [IPv6]). + /// Returns [`true`] if this address is in a range designated for documentation. + /// + /// See the documentation for [`Ipv4Addr::is_documentation`][IPv4] and + /// [`Ipv6Addr::is_documentation`][IPv6] for more details. /// /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_documentation /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_documentation + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -191,7 +266,13 @@ impl IpAddr { } } - /// Returns true if this address is a valid IPv4 address, false if it's a valid IPv6 address. + /// Returns [`true`] if this address is an [IPv4 address] and [`false`] if it's an + /// [IPv6 address]. + /// + /// [`true`]: ../../std/primitive.bool.html + /// [`false`]: ../../std/primitive.bool.html + /// [IPv4 address]: #variant.V4 + /// [IPv6 address]: #variant.V6 /// /// # Examples /// @@ -212,7 +293,13 @@ impl IpAddr { } } - /// Returns true if this address is a valid IPv6 address, false if it's a valid IPv4 address. + /// Returns [`true`] if this address is an [IPv6 address] and [`false`] if it's an + /// [IPv4 address]. + /// + /// [`true`]: ../../std/primitive.bool.html + /// [`false`]: ../../std/primitive.bool.html + /// [IPv4 address]: #variant.V4 + /// [IPv6 address]: #variant.V6 /// /// # Examples /// @@ -274,12 +361,13 @@ impl Ipv4Addr { [(bits >> 24) as u8, (bits >> 16) as u8, (bits >> 8) as u8, bits as u8] } - /// Returns true for the special 'unspecified' address (0.0.0.0). + /// Returns [`true`] for the special 'unspecified' address (0.0.0.0). /// /// This property is defined in _UNIX Network Programming, Second Edition_, /// W. Richard Stevens, p. 891; see also [ip7]. /// /// [ip7]: http://man7.org/linux/man-pages/man7/ip.7.html + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -294,11 +382,12 @@ impl Ipv4Addr { self.inner.s_addr == 0 } - /// Returns true if this is a loopback address (127.0.0.0/8). + /// Returns [`true`] if this is a loopback address (127.0.0.0/8). /// /// This property is defined by [IETF RFC 1122]. /// /// [IETF RFC 1122]: https://tools.ietf.org/html/rfc1122 + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -313,7 +402,7 @@ impl Ipv4Addr { self.octets()[0] == 127 } - /// Returns true if this is a private address. + /// Returns [`true`] if this is a private address. /// /// The private address ranges are defined in [IETF RFC 1918] and include: /// @@ -322,6 +411,7 @@ impl Ipv4Addr { /// - 192.168.0.0/16 /// /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -346,11 +436,12 @@ impl Ipv4Addr { } } - /// Returns true if the address is link-local (169.254.0.0/16). + /// Returns [`true`] if the address is link-local (169.254.0.0/16). /// /// This property is defined by [IETF RFC 3927]. /// /// [IETF RFC 3927]: https://tools.ietf.org/html/rfc3927 + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -366,7 +457,7 @@ impl Ipv4Addr { self.octets()[0] == 169 && self.octets()[1] == 254 } - /// Returns true if the address appears to be globally routable. + /// Returns [`true`] if the address appears to be globally routable. /// See [iana-ipv4-special-registry][ipv4-sr]. /// /// The following return false: @@ -379,6 +470,7 @@ impl Ipv4Addr { /// - the unspecified address (0.0.0.0) /// /// [ipv4-sr]: http://goo.gl/RaZ7lg + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -400,12 +492,13 @@ impl Ipv4Addr { !self.is_broadcast() && !self.is_documentation() && !self.is_unspecified() } - /// Returns true if this is a multicast address (224.0.0.0/4). + /// Returns [`true`] if this is a multicast address (224.0.0.0/4). /// /// Multicast addresses have a most significant octet between 224 and 239, /// and is defined by [IETF RFC 5771]. /// /// [IETF RFC 5771]: https://tools.ietf.org/html/rfc5771 + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -421,11 +514,12 @@ impl Ipv4Addr { self.octets()[0] >= 224 && self.octets()[0] <= 239 } - /// Returns true if this is a broadcast address (255.255.255.255). + /// Returns [`true`] if this is a broadcast address (255.255.255.255). /// /// A broadcast address has all octets set to 255 as defined in [IETF RFC 919]. /// /// [IETF RFC 919]: https://tools.ietf.org/html/rfc919 + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -441,7 +535,7 @@ impl Ipv4Addr { self.octets()[2] == 255 && self.octets()[3] == 255 } - /// Returns true if this address is in a range designated for documentation. + /// Returns [`true`] if this address is in a range designated for documentation. /// /// This is defined in [IETF RFC 5737]: /// @@ -450,6 +544,7 @@ impl Ipv4Addr { /// - 203.0.113.0/24 (TEST-NET-3) /// /// [IETF RFC 5737]: https://tools.ietf.org/html/rfc5737 + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -471,10 +566,12 @@ impl Ipv4Addr { } } - /// Converts this address to an IPv4-compatible IPv6 address. + /// Converts this address to an IPv4-compatible [IPv6 address]. /// /// a.b.c.d becomes ::a.b.c.d /// + /// [IPv6 address]: ../../std/net/struct.Ipv6Addr.html + /// /// # Examples /// /// ``` @@ -490,10 +587,12 @@ impl Ipv4Addr { ((self.octets()[2] as u16) << 8) | self.octets()[3] as u16) } - /// Converts this address to an IPv4-mapped IPv6 address. + /// Converts this address to an IPv4-mapped [IPv6 address]. /// /// a.b.c.d becomes ::ffff:a.b.c.d /// + /// [IPv6 address]: ../../std/net/struct.Ipv6Addr.html + /// /// # Examples /// /// ``` @@ -717,11 +816,12 @@ impl Ipv6Addr { ] } - /// Returns true for the special 'unspecified' address (::). + /// Returns [`true`] for the special 'unspecified' address (::). /// /// This property is defined in [IETF RFC 4291]. /// /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -736,11 +836,12 @@ impl Ipv6Addr { self.segments() == [0, 0, 0, 0, 0, 0, 0, 0] } - /// Returns true if this is a loopback address (::1). + /// Returns [`true`] if this is a loopback address (::1). /// /// This property is defined in [IETF RFC 4291]. /// /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -755,14 +856,17 @@ impl Ipv6Addr { self.segments() == [0, 0, 0, 0, 0, 0, 0, 1] } - /// Returns true if the address appears to be globally routable. + /// Returns [`true`] if the address appears to be globally routable. /// - /// The following return false: + /// The following return [`false`]: /// /// - the loopback address /// - link-local, site-local, and unique local unicast addresses /// - interface-, link-, realm-, admin- and site-local multicast addresses /// + /// [`true`]: ../../std/primitive.bool.html + /// [`false`]: ../../std/primitive.bool.html + /// /// # Examples /// /// ``` @@ -784,11 +888,12 @@ impl Ipv6Addr { } } - /// Returns true if this is a unique local address (fc00::/7). + /// Returns [`true`] if this is a unique local address (fc00::/7). /// /// This property is defined in [IETF RFC 4193]. /// /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -807,11 +912,12 @@ impl Ipv6Addr { (self.segments()[0] & 0xfe00) == 0xfc00 } - /// Returns true if the address is unicast and link-local (fe80::/10). + /// Returns [`true`] if the address is unicast and link-local (fe80::/10). /// /// This property is defined in [IETF RFC 4291]. /// /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -830,9 +936,11 @@ impl Ipv6Addr { (self.segments()[0] & 0xffc0) == 0xfe80 } - /// Returns true if this is a deprecated unicast site-local address + /// Returns [`true`] if this is a deprecated unicast site-local address /// (fec0::/10). /// + /// [`true`]: ../../std/primitive.bool.html + /// /// # Examples /// /// ``` @@ -850,12 +958,13 @@ impl Ipv6Addr { (self.segments()[0] & 0xffc0) == 0xfec0 } - /// Returns true if this is an address reserved for documentation + /// Returns [`true`] if this is an address reserved for documentation /// (2001:db8::/32). /// /// This property is defined in [IETF RFC 3849]. /// /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 + /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// @@ -874,7 +983,7 @@ impl Ipv6Addr { (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) } - /// Returns true if the address is a globally routable unicast address. + /// Returns [`true`] if the address is a globally routable unicast address. /// /// The following return false: /// @@ -885,6 +994,8 @@ impl Ipv6Addr { /// - the unspecified address /// - the address range reserved for documentation /// + /// [`true`]: ../../std/primitive.bool.html + /// /// # Examples /// /// ``` @@ -937,11 +1048,13 @@ impl Ipv6Addr { } } - /// Returns true if this is a multicast address (ff00::/8). + /// Returns [`true`] if this is a multicast address (ff00::/8). /// /// This property is defined by [IETF RFC 4291]. /// /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [`true`]: ../../std/primitive.bool.html + /// /// # Examples /// /// ``` @@ -955,11 +1068,16 @@ impl Ipv6Addr { (self.segments()[0] & 0xff00) == 0xff00 } - /// Converts this address to an IPv4 address. Returns None if this address is + /// Converts this address to an [IPv4 address]. Returns [`None`] if this address is /// neither IPv4-compatible or IPv4-mapped. /// /// ::a.b.c.d and ::ffff:a.b.c.d become a.b.c.d /// + /// [IPv4 address]: ../../std/net/struct.Ipv4Addr.html + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// + /// # Examples + /// /// ``` /// use std::net::{Ipv4Addr, Ipv6Addr}; /// From be713fa4bf12cf87a799dddfd29ab095a548fd01 Mon Sep 17 00:00:00 2001 From: lukaramu Date: Sun, 26 Mar 2017 01:01:32 +0100 Subject: [PATCH 33/61] Removed link in std::net::ToSocketAddr's summary sentence Relative links in trait methods don't resolve in e.g. std/primitive.tuple.html :( --- src/libstd/net/addr.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs index ea78843aa8c0c..0b63a5a5c1431 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -636,7 +636,7 @@ pub trait ToSocketAddrs { #[stable(feature = "rust1", since = "1.0.0")] type Iter: Iterator; - /// Converts this object to an iterator of resolved [`SocketAddr`]s. + /// Converts this object to an iterator of resolved `SocketAddr`s. /// /// The returned iterator may not actually yield any values depending on the /// outcome of any resolution performed. @@ -644,8 +644,6 @@ pub trait ToSocketAddrs { /// Note that this function may block the current thread while resolution is /// performed. /// - /// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html - /// /// # Errors /// /// Any errors encountered during resolution will be returned as an [`Err`]. From 6f0c742b001e7861099e1048c4f0cd08f0528774 Mon Sep 17 00:00:00 2001 From: lukaramu Date: Sun, 26 Mar 2017 14:13:37 +0200 Subject: [PATCH 34/61] Expanded and added links to std::net::{SocketAddr,SocketAddrV4,SocketAddrV6} docs Part of #29363 Changed summary sentences of SocketAddr and IpAddr for consistency Linked to SocketAddrV4 and SocketAddrV6 from SocketAddr, moving explaination there Expanded top-level docs for SocketAddrV4 and SocketAddrV6, linking to some relevant IETF RFCs, and linking back to SocketAddr Changed some of the method summaries to third person as per RFC 1574; added links to IETF RFCs where appropriate --- src/libstd/net/addr.rs | 114 ++++++++++++++++++++++++++++++++--------- src/libstd/net/ip.rs | 2 +- 2 files changed, 90 insertions(+), 26 deletions(-) diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs index 0b63a5a5c1431..6584531a92a2a 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -20,15 +20,17 @@ use vec; use iter; use slice; -/// Representation of a socket address for networking applications. +/// An internet socket address, either IPv4 or IPv6. /// -/// A socket address can either represent the IPv4 or IPv6 protocol and is -/// paired with at least a port number as well. Each protocol may have more -/// specific information about the address available to it as well. +/// This enum can contain either an [`SocketAddrV4`] or an [`SocketAddrV6`]. see their +/// respective documentation for more details. +/// +/// [`SocketAddrV4`]: ../../std/net/struct.SocketAddrV4.html +/// [`SocketAddrV6`]: ../../std/net/struct.SocketAddrV6.html #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub enum SocketAddr { - /// An IPv4 socket address which is a (ip, port) combination. + /// An IPv4 socket address. #[stable(feature = "rust1", since = "1.0.0")] V4(#[stable(feature = "rust1", since = "1.0.0")] SocketAddrV4), /// An IPv6 socket address. @@ -36,18 +38,39 @@ pub enum SocketAddr { V6(#[stable(feature = "rust1", since = "1.0.0")] SocketAddrV6), } -/// An IPv4 socket address which is a (ip, port) combination. +/// An IPv4 socket address. +/// +/// IPv4 socket addresses consist of an [IPv4 address] and a 16-bit port number, as +/// stated in [IETF RFC 793]. +/// +/// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses. +/// +/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 +/// [IPv4 address]: ../../std/net/struct.Ipv4Addr.html +/// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html #[derive(Copy)] #[stable(feature = "rust1", since = "1.0.0")] pub struct SocketAddrV4 { inner: c::sockaddr_in } /// An IPv6 socket address. +/// +/// IPv6 socket addresses consist of an [Ipv6 address], a 16-bit port number, as well +/// as fields containing the traffic class, the flow label, and a scope identifier +/// (see [IETF RFC 2553, Section 3.3] for more details). +/// +/// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses. +/// +/// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 +/// [IPv6 address]: ../../std/net/struct.Ipv6Addr.html +/// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html #[derive(Copy)] #[stable(feature = "rust1", since = "1.0.0")] pub struct SocketAddrV6 { inner: c::sockaddr_in6 } impl SocketAddr { - /// Creates a new socket address from the (ip, port) pair. + /// Creates a new socket address from an [IP address] and a port number. + /// + /// [IP address]: ../../std/net/enum.IpAddr.html /// /// # Examples /// @@ -84,7 +107,7 @@ impl SocketAddr { } } - /// Change the IP address associated with this socket address. + /// Changes the IP address associated with this socket address. /// /// # Examples /// @@ -123,7 +146,7 @@ impl SocketAddr { } } - /// Change the port number associated with this socket address. + /// Changes the port number associated with this socket address. /// /// # Examples /// @@ -142,8 +165,14 @@ impl SocketAddr { } } - /// Returns true if the IP in this `SocketAddr` is a valid IPv4 address, - /// false if it's a valid IPv6 address. + /// Returns [`true`] if the [IP address] in this `SocketAddr` is an + /// [IPv4 address] and [`false`] if it's an [IPv6 address]. + /// + /// [`true`]: ../../std/primitive.bool.html + /// [`false`]: ../../std/primitive.bool.html + /// [IP address]: ../../std/net/enum.IpAddr.html + /// [IPv4 address]: ../../std/net/enum.IpAddr.html#variant.V4 + /// [IPv6 address]: ../../std/net/enum.IpAddr.html#variant.V6 /// /// # Examples /// @@ -164,8 +193,14 @@ impl SocketAddr { } } - /// Returns true if the IP in this `SocketAddr` is a valid IPv6 address, - /// false if it's a valid IPv4 address. + /// Returns [`true`] if the [IP address] in this `SocketAddr` is an + /// [IPv6 address] and [`false`] if it's an [IPv4 address]. + /// + /// [`true`]: ../../std/primitive.bool.html + /// [`false`]: ../../std/primitive.bool.html + /// [IP address]: ../../std/net/enum.IpAddr.html + /// [IPv4 address]: ../../std/net/enum.IpAddr.html#variant.V4 + /// [IPv6 address]: ../../std/net/enum.IpAddr.html#variant.V6 /// /// # Examples /// @@ -189,7 +224,9 @@ impl SocketAddr { } impl SocketAddrV4 { - /// Creates a new socket address from the (ip, port) pair. + /// Creates a new socket address from an [IPv4 address] and a port number. + /// + /// [IPv4 address]: ../../std/net/struct.Ipv4Addr.html /// /// # Examples /// @@ -227,7 +264,7 @@ impl SocketAddrV4 { } } - /// Change the IP address associated with this socket address. + /// Changes the IP address associated with this socket address. /// /// # Examples /// @@ -258,7 +295,7 @@ impl SocketAddrV4 { ntoh(self.inner.sin_port) } - /// Change the port number associated with this socket address. + /// Changes the port number associated with this socket address. /// /// # Examples /// @@ -276,8 +313,14 @@ impl SocketAddrV4 { } impl SocketAddrV6 { - /// Creates a new socket address from the ip/port/flowinfo/scope_id - /// components. + /// Creates a new socket address from an [IPv6 address], a 16-bit port number, + /// and the `flowinfo` and `scope_id` fields. + /// + /// For more information on the meaning and layout of the `flowinfo` and `scope_id` + /// parameters, see [IETF RFC 2553, Section 3.3]. + /// + /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 + /// [IPv6 address]: ../../std/net/struct.Ipv6Addr.html /// /// # Examples /// @@ -318,7 +361,7 @@ impl SocketAddrV6 { } } - /// Change the IP address associated with this socket address. + /// Changes the IP address associated with this socket address. /// /// # Examples /// @@ -349,7 +392,7 @@ impl SocketAddrV6 { ntoh(self.inner.sin6_port) } - /// Change the port number associated with this socket address. + /// Changes the port number associated with this socket address. /// /// # Examples /// @@ -365,8 +408,17 @@ impl SocketAddrV6 { self.inner.sin6_port = hton(new_port); } - /// Returns the flow information associated with this address, - /// corresponding to the `sin6_flowinfo` field in C. + /// Returns the flow information associated with this address. + /// + /// This information corresponds to the `sin6_flowinfo` field in C, as specified in + /// [IETF RFC 2553, Section 3.3]. It combines information about the flow label and + /// the traffic class as specified in [IETF RFC 2460], respectively [Section 6] and + /// [Section 7]. + /// + /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 + /// [IETF RFC 2460]: https://tools.ietf.org/html/rfc2460 + /// [Section 6]: https://tools.ietf.org/html/rfc2460#section-6 + /// [Section 7]: https://tools.ietf.org/html/rfc2460#section-7 /// /// # Examples /// @@ -381,7 +433,11 @@ impl SocketAddrV6 { self.inner.sin6_flowinfo } - /// Change the flow information associated with this socket address. + /// Changes the flow information associated with this socket address. + /// + /// See the [`flowinfo`] method's documentation for more details. + /// + /// [`flowinfo`]: #method.flowinfo /// /// # Examples /// @@ -397,8 +453,12 @@ impl SocketAddrV6 { self.inner.sin6_flowinfo = new_flowinfo; } - /// Returns the scope ID associated with this address, - /// corresponding to the `sin6_scope_id` field in C. + /// Returns the scope ID associated with this address. + /// + /// This information corresponds to the `sin6_scope_id` field in C, as specified in + /// [IETF RFC 2553, Section 3.3]. + /// + /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 /// /// # Examples /// @@ -415,6 +475,10 @@ impl SocketAddrV6 { /// Change the scope ID associated with this socket address. /// + /// See the [`scope_id`] method's documentation for more details. + /// + /// [`scope_id`]: #method.scope_id + /// /// # Examples /// /// ``` diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index 3e58a335470ae..85803ff0501d4 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -21,7 +21,7 @@ use net::{hton, ntoh}; use sys::net::netc as c; use sys_common::{AsInner, FromInner}; -/// Either an IPv4 address or an IPv6 address. +/// An IP address, either IPv4 or IPv6. /// /// This enum can contain either an [`Ipv4Addr`] or an [`Ipv6Addr`], see their /// respective documentation for more details. From 1a9c8baed55547593610eb935c9cceccec40fdb4 Mon Sep 17 00:00:00 2001 From: lukaramu Date: Sun, 26 Mar 2017 15:43:25 +0200 Subject: [PATCH 35/61] Added examples to std::net::{SocketAddr, SocketAddrV4, SocketAddrV6} docs --- src/libstd/net/addr.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs index 6584531a92a2a..ccfd2a1dfb153 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -27,6 +27,18 @@ use slice; /// /// [`SocketAddrV4`]: ../../std/net/struct.SocketAddrV4.html /// [`SocketAddrV6`]: ../../std/net/struct.SocketAddrV6.html +/// +/// # Examples +/// +/// ``` +/// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +/// +/// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); +/// +/// assert_eq!("127.0.0.1:8080".parse(), Ok(socket)); +/// assert_eq!(socket.port(), 8080); +/// assert_eq!(socket.is_ipv4(), true); +/// ``` #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub enum SocketAddr { @@ -48,6 +60,18 @@ pub enum SocketAddr { /// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 /// [IPv4 address]: ../../std/net/struct.Ipv4Addr.html /// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html +/// +/// # Examples +/// +/// ``` +/// use std::net::{Ipv4Addr, SocketAddrV4}; +/// +/// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); +/// +/// assert_eq!("127.0.0.1:8080".parse(), Ok(socket)); +/// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1)); +/// assert_eq!(socket.port(), 8080); +/// ``` #[derive(Copy)] #[stable(feature = "rust1", since = "1.0.0")] pub struct SocketAddrV4 { inner: c::sockaddr_in } @@ -63,6 +87,18 @@ pub struct SocketAddrV4 { inner: c::sockaddr_in } /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 /// [IPv6 address]: ../../std/net/struct.Ipv6Addr.html /// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html +/// +/// # Examples +/// +/// ``` +/// use std::net::{Ipv6Addr, SocketAddrV6}; +/// +/// let socket = SocketAddrV6::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 0, 0); +/// +/// assert_eq!("[2001:db8::1]:8080".parse(), Ok(socket)); +/// assert_eq!(socket.ip(), &Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)); +/// assert_eq!(socket.port(), 8080); +/// ``` #[derive(Copy)] #[stable(feature = "rust1", since = "1.0.0")] pub struct SocketAddrV6 { inner: c::sockaddr_in6 } From ad816f81748e6314b428a91352b0eb536f7fffc5 Mon Sep 17 00:00:00 2001 From: lukaramu Date: Sun, 26 Mar 2017 16:12:27 +0200 Subject: [PATCH 36/61] Added links to std::net::Shutdown docs and made them more consistent Part of #29363 --- src/libstd/net/mod.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/libstd/net/mod.rs b/src/libstd/net/mod.rs index b0d2e3e4687b4..e4593cf381772 100644 --- a/src/libstd/net/mod.rs +++ b/src/libstd/net/mod.rs @@ -43,17 +43,30 @@ mod test; #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub enum Shutdown { - /// Indicates that the reading portion of this stream/socket should be shut - /// down. All currently blocked and future reads will return `Ok(0)`. + /// The reading portion of the [`TcpStream`] should be shut down. + /// + /// All currently blocked and future [reads] will return [`Ok(0)`]. + /// + /// [`TcpStream`]: ../../std/net/struct.TcpStream.html + /// [reads]: ../../std/io/trait.Read.html + /// [`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok #[stable(feature = "rust1", since = "1.0.0")] Read, - /// Indicates that the writing portion of this stream/socket should be shut - /// down. All currently blocked and future writes will return an error. + /// The writing portion of the [`TcpStream`] should be shut down. + /// + /// All currently blocked and future [writes] will return an error. + /// + /// [`TcpStream`]: ../../std/net/struct.TcpStream.html + /// [writes]: ../../std/io/trait.Write.html #[stable(feature = "rust1", since = "1.0.0")] Write, - /// Shut down both the reading and writing portions of this stream. + /// Both the reading and the writing portions of the [`TcpStream`] should be shut down. + /// + /// See [`Shutdown::Read`] and [`Shutdown::Write`] for more information. /// - /// See `Shutdown::Read` and `Shutdown::Write` for more information. + /// [`TcpStream`]: ../../std/net/struct.TcpStream.html + /// [`Shutdown::Read`]: #variant.Read + /// [`Shutdown::Write`]: #variant.Write #[stable(feature = "rust1", since = "1.0.0")] Both, } From 597bcec379bec40641eb6a4637ebbec69e184582 Mon Sep 17 00:00:00 2001 From: lukaramu Date: Sun, 26 Mar 2017 17:06:39 +0200 Subject: [PATCH 37/61] Expanded top-level docs for std::net{TcpListener,TcpStream,UdpSocket} Part of #29363 --- src/libstd/net/tcp.rs | 37 +++++++++++++++++++++++++++++++++---- src/libstd/net/udp.rs | 30 +++++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index c6cf748d981c7..cf119720e5a17 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -17,10 +17,25 @@ use sys_common::net as net_imp; use sys_common::{AsInner, FromInner, IntoInner}; use time::Duration; -/// A structure which represents a TCP stream between a local socket and a -/// remote socket. +/// A TCP stream between a local and a remote socket. /// -/// The socket will be closed when the value is dropped. +/// After creating a `TcpStream` by either [`connect`]ing to a remote host or +/// [`accept`]ing a connection on a [`TcpListener`], data can be transmitted +/// by [reading] and [writing] to it. +/// +/// The connection will be closed when the value is dropped. The reading and writing +/// portions of the connection can also be shut down individually with the [`shutdown`] +/// method. +/// +/// The Transmission Control Protocol is specified in [IETF RFC 793]. +/// +/// [`accept`]: ../../std/net/struct.TcpListener.html#method.accept +/// [`connect`]: #method.connect +/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 +/// [reading]: ../../std/io/trait.Read.html +/// [`shutdown`]: #method.shutdown +/// [`TcpListener`]: ../../std/net/struct.TcpListener.html +/// [writing]: ../../std/io/trait.Write.html /// /// # Examples /// @@ -39,7 +54,21 @@ use time::Duration; #[stable(feature = "rust1", since = "1.0.0")] pub struct TcpStream(net_imp::TcpStream); -/// A structure representing a socket server. +/// A TCP socket server, listening for connections. +/// +/// After creating a `TcpListener` by [`bind`]ing it to a socket address, it listens +/// for incoming TCP connections. These can be accepted by calling [`accept`] or by +/// iterating over the [`Incoming`] iterator returned by [`incoming`]. +/// +/// The socket will be closed when the value is dropped. +/// +/// The Transmission Control Protocol is specified in [IETF RFC 793]. +/// +/// [`accept`]: #method.accept +/// [`bind`]: #method.bind +/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 +/// [`Incoming`]: ../../std/net/struct.Incoming.html +/// [`incoming`]: #method.incoming /// /// # Examples /// diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index 1ebce93934840..cdf04f7f1a484 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -15,11 +15,29 @@ use sys_common::net as net_imp; use sys_common::{AsInner, FromInner, IntoInner}; use time::Duration; -/// A User Datagram Protocol socket. +/// A UDP socket. /// -/// This is an implementation of a bound UDP socket. This supports both IPv4 and -/// IPv6 addresses, and there is no corresponding notion of a server because UDP -/// is a datagram protocol. +/// After creating a `UdpSocket` by [`bind`]ing it to a socket address, data can be +/// [sent to] and [received from] any other socket address. +/// +/// Although UDP is a connectionless protocol, this implementation provides an interface +/// to set an address where data should be sent and received from. After setting a remote +/// address with [`connect`], data can be sent to and received from that address with +/// [`send`] and [`recv`]. +/// +/// As stated in the User Datagram Protocol's specification in [IETF RFC 768], UDP is +/// an unordered, unreliable protocol; refer to [`TcpListener`] and [`TcpStream`] for TCP +/// primitives. +/// +/// [`bind`]: #method.bind +/// [`connect`]: #method.connect +/// [IETF RFC 768]: https://tools.ietf.org/html/rfc768 +/// [`recv`]: #method.recv +/// [received from]: #method.recv_from +/// [`send`]: #method.send +/// [sent to]: #method.send_to +/// [`TcpListener`]: ../../std/net/struct.TcpListener.html +/// [`TcpStream`]: ../../std/net/struct.TcpStream.html /// /// # Examples /// @@ -582,9 +600,11 @@ impl UdpSocket { /// Receives data on the socket from the remote address to which it is /// connected. /// - /// The `connect` method will connect this socket to a remote address. This + /// The [`connect`] method will connect this socket to a remote address. This /// method will fail if the socket is not connected. /// + /// [`connect`]: #method.connect + /// /// # Examples /// /// ```no_run From 577677d55d7ca793b3845577939d99a97e112691 Mon Sep 17 00:00:00 2001 From: lukaramu Date: Sun, 26 Mar 2017 17:27:40 +0200 Subject: [PATCH 38/61] Expanded std::net module docs Part of #29363 --- src/libstd/net/mod.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/libstd/net/mod.rs b/src/libstd/net/mod.rs index e4593cf381772..3b4808fee2515 100644 --- a/src/libstd/net/mod.rs +++ b/src/libstd/net/mod.rs @@ -9,6 +9,32 @@ // except according to those terms. //! Networking primitives for TCP/UDP communication. +//! +//! This module provides networking functionality for the Transmission Control and User +//! Datagram Protocols, as well as types for IP and socket addresses. +//! +//! # Organization +//! +//! * [`TcpListener`] and [`TcpStream`] provide functionality for communication over TCP +//! * [`UdpSocket`] provides functionality for communication over UDP +//! * [`IpAddr`] represents IP addresses of either IPv4 or IPv6; [`Ipv4Addr`] and +//! [`Ipv6Addr`] are respectively IPv4 and IPv6 addresses +//! * [`SocketAddr`] represents socket addresses of either IPv4 or IPv6; [`SocketAddrV4`] +//! and [`SocketAddrV6`] are respectively IPv4 and IPv6 socket addresses +//! * [`ToSocketAddr`] is a trait that used for generic address resolution interacting +//! with networking objects like [`TcpListener`], [`TcpStream`] or [`UdpSocket`] +//! * Other types are return or parameter types for various methods in this module +//! +//! [`IpAddr`]: ../../std/net/enum.IpAddr.html +//! [`Ipv4Addr`]: ../../std/net/struct.Ipv4Addr.html +//! [`Ipv6Addr`]: ../../std/net/struct.Ipv6Addr.html +//! [`SocketAddr`]: ../../std/net/enum.SocketAddr.html +//! [`SocketAddrV4`]: ../../std/net/struct.SocketAddrV4.html +//! [`SocketAddrV6`]: ../../std/net/struct.SocketAddrV6.html +//! [`TcpListener`]: ../../std/net/struct.TcpListener.html +//! [`TcpStream`]: ../../std/net/struct.TcpStream.html +//! [`ToSocketAddr`]: ../../std/net/trait.ToSocketAddr.html +//! [`UdpSocket`]: ../../std/net/struct.UdpSocket.html #![stable(feature = "rust1", since = "1.0.0")] From c2601fd358769cbecae479e8d32521f6e5d5c633 Mon Sep 17 00:00:00 2001 From: lukaramu Date: Sun, 26 Mar 2017 18:06:22 +0200 Subject: [PATCH 39/61] fix trailing whitespace --- src/libstd/net/ip.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index 85803ff0501d4..2a1f959b35d03 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -99,7 +99,7 @@ pub struct Ipv4Addr { /// # Textual representation /// /// `Ipv6Addr` provides a [`FromStr`] implementation. There are many ways to represent -/// an IPv6 address in text, but in general, each segments is written in hexadecimal +/// an IPv6 address in text, but in general, each segments is written in hexadecimal /// notation, and segments are separated by `:`. For more information, see /// [IETF RFC 5952]. /// From 62036e3879e9e47f9dfba66f8906e89d4884bee5 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Wed, 8 Mar 2017 16:30:31 +1300 Subject: [PATCH 40/61] save-analysis: only index path references once --- src/librustc_save_analysis/dump_visitor.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index f2aa89ba4b66e..dd5c1690ce8bb 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -1377,15 +1377,6 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, debug!("visit_expr {:?}", ex.node); self.process_macro_use(ex.span, ex.id); match ex.node { - ast::ExprKind::Call(ref _f, ref _args) => { - // Don't need to do anything for function calls, - // because just walking the callee path does what we want. - visit::walk_expr(self, ex); - } - ast::ExprKind::Path(_, ref path) => { - self.process_path(ex.id, path, None); - visit::walk_expr(self, ex); - } ast::ExprKind::Struct(ref path, ref fields, ref base) => { let hir_expr = self.save_ctxt.tcx.hir.expect_expr(ex.id); let adt = match self.save_ctxt.tables.expr_ty_opt(&hir_expr) { @@ -1483,6 +1474,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, self.visit_expr(element); self.nest_tables(count.id, |v| v.visit_expr(count)); } + // In particular, we take this branch for call and path expressions, + // where we'll index the idents involved just by continuing to walk. _ => { visit::walk_expr(self, ex) } From 28173d31150965f7b7b18248405f706d5bf7b6ac Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Wed, 8 Mar 2017 18:04:30 +1300 Subject: [PATCH 41/61] save-analysis: index extern blocks --- src/librustc_save_analysis/dump_visitor.rs | 35 +++++++++++++ src/librustc_save_analysis/lib.rs | 55 +++++++++++++++++++++ src/test/run-make/save-analysis-fail/foo.rs | 5 ++ 3 files changed, 95 insertions(+) diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index dd5c1690ce8bb..c711ecffea0a8 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -1575,4 +1575,39 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, walk_list!(self, visit_ty, &l.ty); walk_list!(self, visit_expr, &l.init); } + + fn visit_foreign_item(&mut self, item: &'l ast::ForeignItem) { + match item.node { + ast::ForeignItemKind::Fn(ref decl, ref generics) => { + if let Some(fn_data) = self.save_ctxt.get_extern_item_data(item) { + down_cast_data!(fn_data, FunctionData, item.span); + if !self.span.filter_generated(Some(fn_data.span), item.span) { + self.dumper.function(fn_data.clone().lower(self.tcx)); + } + + self.nest_tables(item.id, |v| v.process_formals(&decl.inputs, + &fn_data.qualname)); + self.process_generic_params(generics, item.span, &fn_data.qualname, item.id); + } + + for arg in &decl.inputs { + self.visit_ty(&arg.ty); + } + + if let ast::FunctionRetTy::Ty(ref ret_ty) = decl.output { + self.visit_ty(&ret_ty); + } + } + ast::ForeignItemKind::Static(ref ty, _) => { + if let Some(var_data) = self.save_ctxt.get_extern_item_data(item) { + down_cast_data!(var_data, VariableData, item.span); + if !self.span.filter_generated(Some(var_data.span), item.span) { + self.dumper.variable(var_data.lower(self.tcx)); + } + } + + self.visit_ty(ty); + } + } + } } diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 5e2b1df9d34f8..b36939fb5c49d 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -119,6 +119,46 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { result } + pub fn get_extern_item_data(&self, item: &ast::ForeignItem) -> Option { + let qualname = format!("::{}", self.tcx.node_path_str(item.id)); + match item.node { + ast::ForeignItemKind::Fn(ref decl, ref generics) => { + let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn); + Some(Data::FunctionData(FunctionData { + id: item.id, + name: item.ident.to_string(), + qualname: qualname, + declaration: None, + span: sub_span.unwrap(), + scope: self.enclosing_scope(item.id), + value: make_signature(decl, generics), + visibility: From::from(&item.vis), + parent: None, + docs: docs_for_attrs(&item.attrs), + sig: self.sig_base_extern(item), + })) + } + ast::ForeignItemKind::Static(ref ty, m) => { + let keyword = if m { keywords::Mut } else { keywords::Static }; + let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword); + Some(Data::VariableData(VariableData { + id: item.id, + kind: VariableKind::Static, + name: item.ident.to_string(), + qualname: qualname, + span: sub_span.unwrap(), + scope: self.enclosing_scope(item.id), + parent: None, + value: String::new(), + type_value: ty_to_string(ty), + visibility: From::from(&item.vis), + docs: docs_for_attrs(&item.attrs), + sig: Some(self.sig_base_extern(item)), + })) + } + } + } + pub fn get_item_data(&self, item: &ast::Item) -> Option { match item.node { ast::ItemKind::Fn(ref decl, .., ref generics, _) => { @@ -751,6 +791,21 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } } + fn sig_base_extern(&self, item: &ast::ForeignItem) -> Signature { + let text = self.span_utils.signature_string_for_span(item.span); + let name = item.ident.to_string(); + let ident_start = text.find(&name).expect("Name not in signature?"); + let ident_end = ident_start + name.len(); + Signature { + span: mk_sp(item.span.lo, item.span.lo + BytePos(text.len() as u32)), + text: text, + ident_start: ident_start, + ident_end: ident_end, + defs: vec![], + refs: vec![], + } + } + #[inline] pub fn enclosing_scope(&self, id: NodeId) -> NodeId { self.tcx.hir.get_enclosing_scope(id).unwrap_or(CRATE_NODE_ID) diff --git a/src/test/run-make/save-analysis-fail/foo.rs b/src/test/run-make/save-analysis-fail/foo.rs index e331f65abb7b3..a996aa4fad5a7 100644 --- a/src/test/run-make/save-analysis-fail/foo.rs +++ b/src/test/run-make/save-analysis-fail/foo.rs @@ -448,3 +448,8 @@ fn test_format_args() { print!("{0} + {} = {}", x, y); print!("x is {}, y is {1}, name is {n}", x, y, n = name); } + +extern { + static EXTERN_FOO: u8; + fn extern_foo(a: u8, b: i32) -> String; +} From b8cbc5d46af4b15bfeca324aa37d8c2ca054e58e Mon Sep 17 00:00:00 2001 From: lukaramu Date: Mon, 27 Mar 2017 16:38:17 +0200 Subject: [PATCH 42/61] Addressed requested changes for PR #40838 * Fixed spelling ToSocketAddr -> ToSocketAddrs in module docs (which also fixes a link) * Added missing "when" before "interacting" in module docs * Changed SocketAddr's top-level docs to explicitly state what socket addresses consist of, making them more consistent with SocketAddrV4's and SocketAddrV6's docs * Changed "in C" -> "in C's `netinet/in.h`" * Changed wording in is_ipv4/is_ipv6 methods to ", `false` otherwise" * Add missing closing ` ``` ` in Ipv6Addr's examples * Removed "Errors" section in ToSocketAddrs' to_socket_addrs method as it was rather redundant --- src/libstd/net/addr.rs | 30 ++++++++++++------------------ src/libstd/net/ip.rs | 9 +++------ src/libstd/net/mod.rs | 4 ++-- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs index ccfd2a1dfb153..36c06dc0b58d0 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -22,9 +22,11 @@ use slice; /// An internet socket address, either IPv4 or IPv6. /// -/// This enum can contain either an [`SocketAddrV4`] or an [`SocketAddrV6`]. see their -/// respective documentation for more details. +/// Internet socket addresses consist of an [IP address], a 16-bit port number, as well +/// as possibly some version-dependent additional information. See [`SocketAddrV4`]'s and +/// [`SocketAddrV6`]'s respective documentation for more details. /// +/// [IP address]: ../../std/net/enum.IpAddr.html /// [`SocketAddrV4`]: ../../std/net/struct.SocketAddrV4.html /// [`SocketAddrV6`]: ../../std/net/struct.SocketAddrV6.html /// @@ -202,13 +204,12 @@ impl SocketAddr { } /// Returns [`true`] if the [IP address] in this `SocketAddr` is an - /// [IPv4 address] and [`false`] if it's an [IPv6 address]. + /// [IPv4 address], and [`false`] otherwise. /// /// [`true`]: ../../std/primitive.bool.html /// [`false`]: ../../std/primitive.bool.html /// [IP address]: ../../std/net/enum.IpAddr.html /// [IPv4 address]: ../../std/net/enum.IpAddr.html#variant.V4 - /// [IPv6 address]: ../../std/net/enum.IpAddr.html#variant.V6 /// /// # Examples /// @@ -230,12 +231,11 @@ impl SocketAddr { } /// Returns [`true`] if the [IP address] in this `SocketAddr` is an - /// [IPv6 address] and [`false`] if it's an [IPv4 address]. + /// [IPv6 address], and [`false`] otherwise. /// /// [`true`]: ../../std/primitive.bool.html /// [`false`]: ../../std/primitive.bool.html /// [IP address]: ../../std/net/enum.IpAddr.html - /// [IPv4 address]: ../../std/net/enum.IpAddr.html#variant.V4 /// [IPv6 address]: ../../std/net/enum.IpAddr.html#variant.V6 /// /// # Examples @@ -446,10 +446,10 @@ impl SocketAddrV6 { /// Returns the flow information associated with this address. /// - /// This information corresponds to the `sin6_flowinfo` field in C, as specified in - /// [IETF RFC 2553, Section 3.3]. It combines information about the flow label and - /// the traffic class as specified in [IETF RFC 2460], respectively [Section 6] and - /// [Section 7]. + /// This information corresponds to the `sin6_flowinfo` field in C's `netinet/in.h`, + /// as specified in [IETF RFC 2553, Section 3.3]. + /// It combines information about the flow label and the traffic class as specified + /// in [IETF RFC 2460], respectively [Section 6] and [Section 7]. /// /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 /// [IETF RFC 2460]: https://tools.ietf.org/html/rfc2460 @@ -491,8 +491,8 @@ impl SocketAddrV6 { /// Returns the scope ID associated with this address. /// - /// This information corresponds to the `sin6_scope_id` field in C, as specified in - /// [IETF RFC 2553, Section 3.3]. + /// This information corresponds to the `sin6_scope_id` field in C's `netinet/in.h`, + /// as specified in [IETF RFC 2553, Section 3.3]. /// /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 /// @@ -743,12 +743,6 @@ pub trait ToSocketAddrs { /// /// Note that this function may block the current thread while resolution is /// performed. - /// - /// # Errors - /// - /// Any errors encountered during resolution will be returned as an [`Err`]. - /// - /// [`Err`]: ../../std/result/enum.Result.html#variant.Err #[stable(feature = "rust1", since = "1.0.0")] fn to_socket_addrs(&self) -> io::Result; } diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index 2a1f959b35d03..c46fe4a58c7e2 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -114,6 +114,7 @@ pub struct Ipv4Addr { /// let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); /// assert_eq!("::1".parse(), Ok(localhost)); /// assert_eq!(localhost.is_loopback(), true); +/// ``` #[derive(Copy)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Ipv6Addr { @@ -266,13 +267,11 @@ impl IpAddr { } } - /// Returns [`true`] if this address is an [IPv4 address] and [`false`] if it's an - /// [IPv6 address]. + /// Returns [`true`] if this address is an [IPv4 address], and [`false`] otherwise. /// /// [`true`]: ../../std/primitive.bool.html /// [`false`]: ../../std/primitive.bool.html /// [IPv4 address]: #variant.V4 - /// [IPv6 address]: #variant.V6 /// /// # Examples /// @@ -293,12 +292,10 @@ impl IpAddr { } } - /// Returns [`true`] if this address is an [IPv6 address] and [`false`] if it's an - /// [IPv4 address]. + /// Returns [`true`] if this address is an [IPv6 address], and [`false`] otherwise. /// /// [`true`]: ../../std/primitive.bool.html /// [`false`]: ../../std/primitive.bool.html - /// [IPv4 address]: #variant.V4 /// [IPv6 address]: #variant.V6 /// /// # Examples diff --git a/src/libstd/net/mod.rs b/src/libstd/net/mod.rs index 3b4808fee2515..9fcb93e2032b3 100644 --- a/src/libstd/net/mod.rs +++ b/src/libstd/net/mod.rs @@ -21,7 +21,7 @@ //! [`Ipv6Addr`] are respectively IPv4 and IPv6 addresses //! * [`SocketAddr`] represents socket addresses of either IPv4 or IPv6; [`SocketAddrV4`] //! and [`SocketAddrV6`] are respectively IPv4 and IPv6 socket addresses -//! * [`ToSocketAddr`] is a trait that used for generic address resolution interacting +//! * [`ToSocketAddrs`] is a trait that used for generic address resolution when interacting //! with networking objects like [`TcpListener`], [`TcpStream`] or [`UdpSocket`] //! * Other types are return or parameter types for various methods in this module //! @@ -33,7 +33,7 @@ //! [`SocketAddrV6`]: ../../std/net/struct.SocketAddrV6.html //! [`TcpListener`]: ../../std/net/struct.TcpListener.html //! [`TcpStream`]: ../../std/net/struct.TcpStream.html -//! [`ToSocketAddr`]: ../../std/net/trait.ToSocketAddr.html +//! [`ToSocketAddrs`]: ../../std/net/trait.ToSocketAddrs.html //! [`UdpSocket`]: ../../std/net/struct.UdpSocket.html #![stable(feature = "rust1", since = "1.0.0")] From ff0a976761d76396430ed3ed41c71416d6130131 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 21 Mar 2017 09:41:41 -0400 Subject: [PATCH 43/61] 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 ceba72af34b8bcc375ae7d6f6e24a6a506854dd6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 21 Mar 2017 15:37:57 -0400 Subject: [PATCH 44/61] 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 d0dd0cf5bcf06..15ab004c3717f 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, @@ -2243,12 +2243,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, @@ -2859,7 +2859,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); @@ -2906,7 +2906,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); @@ -3030,7 +3030,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; } @@ -3766,7 +3766,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 f734cee36b4747a15984afeb30dcefa888e13f2c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Mar 2017 11:48:44 -0400 Subject: [PATCH 45/61] 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 15ab004c3717f..8844092a62614 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, @@ -2831,7 +2831,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()); } @@ -2862,13 +2863,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); @@ -3551,7 +3552,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. @@ -3570,11 +3571,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); } @@ -3585,7 +3588,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); @@ -3767,10 +3770,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 dbbe7fc52e6c37e1a2ee1facc20c9f35cf71cf72 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Mar 2017 11:50:36 -0400 Subject: [PATCH 46/61] 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 8844092a62614..453fa0094d0ac 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::{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}; @@ -4106,102 +4107,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 1f97c3977634ff964d410961f736f692c584a4e0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Mar 2017 11:51:02 -0400 Subject: [PATCH 47/61] 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 453fa0094d0ac..2adc8b0404ce5 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))) } @@ -2860,7 +2860,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); @@ -3681,7 +3681,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 84002a5306daf6212bfd202ee604d3d67689441e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 21 Mar 2017 21:21:24 -0400 Subject: [PATCH 48/61] 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 2adc8b0404ce5..f35f3615d77c7 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 c54346703acb724efe67a39c7c3f895441229407 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 22 Mar 2017 21:07:02 -0400 Subject: [PATCH 49/61] 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 | 100 ++++++++++-------- 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(+), 62 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 f35f3615d77c7..eb5f6be525c27 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::{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; @@ -2873,7 +2874,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); @@ -3589,7 +3590,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 @@ -3624,7 +3625,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 } @@ -4156,8 +4157,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); + } + }); + } } }); @@ -4173,44 +4189,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(self.tcx.sess.codemap(), - last_stmt.span, blk.span); - let span_semi = Span { - lo: original_span.hi - BytePos(1), - hi: original_span.hi, - expn_id: original_span.expn_id - }; - 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(self.tcx.sess.codemap(), last_stmt.span, blk.span); + let span_semi = Span { + lo: original_span.hi - BytePos(1), + hi: original_span.hi, + expn_id: original_span.expn_id + }; + 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 c85d5684dc2c7574fb4248582862a01d20919ede Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Mar 2017 13:21:50 -0400 Subject: [PATCH 50/61] 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 2892a1ed48330c762f8e35c16e2872bc93704ce5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Mar 2017 13:35:49 -0400 Subject: [PATCH 51/61] 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 eb5f6be525c27..bf88fb3cb53b0 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3734,6 +3734,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() { @@ -3741,7 +3742,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 @@ -4156,7 +4157,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 f5179b6c2afa0ed38692e7e91b0383945e4dece2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Mar 2017 13:36:01 -0400 Subject: [PATCH 52/61] 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 bf88fb3cb53b0..082e79b46c911 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 0d2ca4f21e1978c1225fdc79763661e52485c5f0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Mar 2017 18:47:58 -0400 Subject: [PATCH 53/61] 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 faf8b42f1271d3cb150f29c9dee589eb28b46370 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 25 Mar 2017 05:30:39 -0400 Subject: [PATCH 54/61] 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 4885f9ed4fdafbdf02cdab7272c63ada743118e1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 26 Mar 2017 07:02:39 -0400 Subject: [PATCH 55/61] 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 434e601446cfb8fd97731f94aa690528ebef4da0 Mon Sep 17 00:00:00 2001 From: steveklabnik Date: Mon, 27 Mar 2017 16:04:56 -0400 Subject: [PATCH 56/61] Update various book modules This includes an important fix for rustc contributors in https://github.com/rust-lang/book/pull/571 I'm going to update the other ones as well here while we're at it; no need to spam PRs. --- src/doc/book | 2 +- src/doc/nomicon | 2 +- src/doc/reference | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/book b/src/doc/book index 9bd223ca406b1..a2c56870d4dc5 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 9bd223ca406b1170a24942d6474f9e8a56f4a420 +Subproject commit a2c56870d4dc589237102cc5e0fe7b9ebd0d14a1 diff --git a/src/doc/nomicon b/src/doc/nomicon index d08fe97d12b41..616b98444ff4e 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit d08fe97d12b41c1ed8cc7701e545864132783941 +Subproject commit 616b98444ff4eb5260deee95ee3e090dfd98b947 diff --git a/src/doc/reference b/src/doc/reference index 516549972d61c..acedc32cacae8 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 516549972d61c8946542d1a34afeae97167ff77b +Subproject commit acedc32cacae80cf2f4925753a4ce7f7ffd7c86a From da74e865b5a2400fb44660f989008208eb25d538 Mon Sep 17 00:00:00 2001 From: Micah Tigley Date: Mon, 27 Mar 2017 16:14:13 -0600 Subject: [PATCH 57/61] Review request changes --- src/libcollections/str.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libcollections/str.rs b/src/libcollections/str.rs index d4480ce77d646..34563e310df3b 100644 --- a/src/libcollections/str.rs +++ b/src/libcollections/str.rs @@ -11,10 +11,10 @@ //! Unicode string slices. //! //! The `&str` type is one of the two main string types, the other being `String`. -//! Unlike its `String` counterpart, its contents are borrowed and therefore -//! cannot be moved someplace else. +//! Unlike its `String` counterpart, its contents are borrowed. //! //! # Basic Usage +//! //! A basic string declaration of `&str` type: //! //! ``` @@ -27,7 +27,7 @@ //! We can explicitly specify `hello_world`'s lifetime as well: //! //! ``` -//! let hello_world:&'static str = "Hello, world!"; +//! let hello_world: &'static str = "Hello, world!"; //! ``` //! //! *[See also the `str` primitive type](../../std/primitive.str.html).* From 93a0f543ae4faf22eb5d4f04ca450ec96f8f5b80 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 27 Mar 2017 19:48:55 -0400 Subject: [PATCH 58/61] 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("") }; From 8f6b68cb8a3828d2f5b025178733f70b36293ec7 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Thu, 16 Mar 2017 10:43:11 +1300 Subject: [PATCH 59/61] rebased --- src/librustc_save_analysis/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index b36939fb5c49d..89c7632e8cca6 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -124,6 +124,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { match item.node { ast::ForeignItemKind::Fn(ref decl, ref generics) => { let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn); + filter!(self.span_utils, sub_span, item.span, None); Some(Data::FunctionData(FunctionData { id: item.id, name: item.ident.to_string(), @@ -136,11 +137,13 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { parent: None, docs: docs_for_attrs(&item.attrs), sig: self.sig_base_extern(item), + attributes: item.attrs.clone(), })) } ast::ForeignItemKind::Static(ref ty, m) => { let keyword = if m { keywords::Mut } else { keywords::Static }; let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword); + filter!(self.span_utils, sub_span, item.span, None); Some(Data::VariableData(VariableData { id: item.id, kind: VariableKind::Static, @@ -154,6 +157,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { visibility: From::from(&item.vis), docs: docs_for_attrs(&item.attrs), sig: Some(self.sig_base_extern(item)), + attributes: item.attrs.clone(), })) } } From 1fea03548cc9ecba3010c9e75eea0ef533ea44c9 Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Fri, 24 Mar 2017 03:50:32 -0700 Subject: [PATCH 60/61] Rustdoc: memoize `pub use`-reexported macros so they don't appear twice in docs --- src/librustdoc/visit_ast.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 2793307697852..c89ec5bbe15bd 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -21,7 +21,7 @@ use syntax_pos::Span; use rustc::hir::map as hir_map; use rustc::hir::def::Def; -use rustc::hir::def_id::LOCAL_CRATE; +use rustc::hir::def_id::{DefId, LOCAL_CRATE}; use rustc::middle::cstore::LoadedMacro; use rustc::middle::privacy::AccessLevel; use rustc::util::nodemap::FxHashSet; @@ -48,6 +48,7 @@ pub struct RustdocVisitor<'a, 'tcx: 'a> { inlining: bool, /// Is the current module and all of its parents public? inside_public_path: bool, + reexported_macros: FxHashSet, } impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { @@ -62,6 +63,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { view_item_stack: stack, inlining: false, inside_public_path: true, + reexported_macros: FxHashSet(), } } @@ -201,9 +203,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { if let Some(exports) = self.cx.tcx.export_map.get(&id) { for export in exports { if let Def::Macro(def_id, ..) = export.def { - if def_id.krate == LOCAL_CRATE { + if def_id.krate == LOCAL_CRATE || self.reexported_macros.contains(&def_id) { continue // These are `krate.exported_macros`, handled in `self.visit()`. } + let imported_from = self.cx.sess().cstore.original_crate_name(def_id.krate); let def = match self.cx.sess().cstore.load_macro(def_id, self.cx.sess()) { LoadedMacro::MacroDef(macro_def) => macro_def, @@ -217,6 +220,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } else { unreachable!() }; + om.macros.push(Macro { def_id: def_id, attrs: def.attrs.clone().into(), @@ -263,6 +267,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { false } + debug!("maybe_inline_local def: {:?}", def); + let tcx = self.cx.tcx; if def == Def::Err { return false; @@ -274,6 +280,17 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let is_no_inline = use_attrs.lists("doc").has_word("no_inline") || use_attrs.lists("doc").has_word("hidden"); + // Memoize the non-inlined `pub use`'d macros so we don't push an extra + // declaration in `visit_mod_contents()` + if !def_did.is_local() { + if let Def::Macro(did, _) = def { + if please_inline { return true } + debug!("memoizing non-inlined macro export: {:?}", def); + self.reexported_macros.insert(did); + return false; + } + } + // For cross-crate impl inlining we need to know whether items are // reachable in documentation - a previously nonreachable item can be // made reachable by cross-crate inlining which we're checking here. @@ -294,6 +311,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { }, _ => {}, } + return false } From d8fc5b80b61f662dd0d63d236875ade5a3f1129c Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Tue, 28 Mar 2017 00:02:19 -0700 Subject: [PATCH 61/61] Rustdoc: test proper representation for `pub use` macros --- .../auxiliary/pub-use-extern-macros.rs | 30 ++++++++++++++++++ src/test/rustdoc/pub-use-extern-macros.rs | 31 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/test/rustdoc/auxiliary/pub-use-extern-macros.rs create mode 100644 src/test/rustdoc/pub-use-extern-macros.rs diff --git a/src/test/rustdoc/auxiliary/pub-use-extern-macros.rs b/src/test/rustdoc/auxiliary/pub-use-extern-macros.rs new file mode 100644 index 0000000000000..70d174a149daa --- /dev/null +++ b/src/test/rustdoc/auxiliary/pub-use-extern-macros.rs @@ -0,0 +1,30 @@ +// Copyright 2017 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. +#![crate_name="macros"] + +#[macro_export] +macro_rules! foo { + () => {}; +} + +#[macro_export] +macro_rules! bar { + () => {}; +} + +#[macro_export] +macro_rules! baz { + () => {}; +} + +#[macro_export] +macro_rules! quux { + () => {}; +} diff --git a/src/test/rustdoc/pub-use-extern-macros.rs b/src/test/rustdoc/pub-use-extern-macros.rs new file mode 100644 index 0000000000000..3f8f6f9544e80 --- /dev/null +++ b/src/test/rustdoc/pub-use-extern-macros.rs @@ -0,0 +1,31 @@ +// Copyright 2017 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. + +// aux-build:pub-use-extern-macros.rs + +#![feature(use_extern_macros, macro_reexport)] + +// @has pub_use_extern_macros/macro.foo.html +// @!has pub_use_extern_macros/index.html 'pub use macros::foo;' +#[macro_reexport(foo)] extern crate macros; + +// @has pub_use_extern_macros/index.html 'pub use macros::bar;' +// @!has pub_use_extern_macros/macro.bar.html +pub use macros::bar; + +// @has pub_use_extern_macros/macro.baz.html +// @!has pub_use_extern_macros/index.html 'pub use macros::baz;' +#[doc(inline)] +pub use macros::baz; + +// @!has pub_use_extern_macros/macro.quux.html +// @!has pub_use_extern_macros/index.html 'pub use macros::quux;' +#[doc(hidden)] +pub use macros::quux;