From 41244661979cda16bf2b15bd802337a0ee9764dd Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 14 Mar 2016 22:46:42 -0700 Subject: [PATCH 01/25] std: Fix inheriting stdin on status() This regression was accidentally introduced in #31618, and it's just flipping a boolean! Closes #32254 --- src/libstd/process.rs | 2 +- .../run-pass/process-status-inherits-stdin.rs | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/test/run-pass/process-status-inherits-stdin.rs diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 5813d82a315a6..9594c71267b35 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -339,7 +339,7 @@ impl Command { /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn status(&mut self) -> io::Result { - self.inner.spawn(imp::Stdio::Inherit, false).map(Child::from_inner) + self.inner.spawn(imp::Stdio::Inherit, true).map(Child::from_inner) .and_then(|mut p| p.wait()) } } diff --git a/src/test/run-pass/process-status-inherits-stdin.rs b/src/test/run-pass/process-status-inherits-stdin.rs new file mode 100644 index 0000000000000..2ad47c4f116ae --- /dev/null +++ b/src/test/run-pass/process-status-inherits-stdin.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. + +use std::env; +use std::io; +use std::io::Write; +use std::process::{Command, Stdio}; + +fn main() { + let mut args = env::args(); + let me = args.next().unwrap(); + let arg = args.next(); + match arg.as_ref().map(|s| &s[..]) { + None => { + let mut s = Command::new(&me) + .arg("a1") + .stdin(Stdio::piped()) + .spawn() + .unwrap(); + s.stdin.take().unwrap().write_all(b"foo\n").unwrap(); + let s = s.wait().unwrap(); + assert!(s.success()); + } + Some("a1") => { + let s = Command::new(&me).arg("a2").status().unwrap(); + assert!(s.success()); + } + Some(..) => { + let mut s = String::new(); + io::stdin().read_line(&mut s).unwrap(); + assert_eq!(s, "foo\n"); + } + } +} From 3ee841c3351326a7bea83b689f54d9fee27e6e85 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Fri, 18 Mar 2016 15:49:12 +1300 Subject: [PATCH 02/25] Don't loop forever on error recovery with EOF closes #31804 --- src/libsyntax/parse/parser.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 6839f11cd709d..66912abb6f5a6 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3824,7 +3824,9 @@ impl<'a> Parser<'a> { fn recover_stmt_(&mut self, break_on_semi: SemiColonMode) { let mut brace_depth = 0; let mut bracket_depth = 0; + debug!("recover_stmt_ enter loop"); loop { + debug!("recover_stmt_ loop {:?}", self.token); match self.token { token::OpenDelim(token::DelimToken::Brace) => { brace_depth += 1; @@ -3836,6 +3838,7 @@ impl<'a> Parser<'a> { } token::CloseDelim(token::DelimToken::Brace) => { if brace_depth == 0 { + debug!("recover_stmt_ return - close delim {:?}", self.token); return; } brace_depth -= 1; @@ -3848,12 +3851,16 @@ impl<'a> Parser<'a> { } self.bump(); } - token::Eof => return, + token::Eof => { + debug!("recover_stmt_ return - Eof"); + return; + } token::Semi => { self.bump(); if break_on_semi == SemiColonMode::Break && brace_depth == 0 && bracket_depth == 0 { + debug!("recover_stmt_ return - Semi"); return; } } @@ -4042,6 +4049,8 @@ impl<'a> Parser<'a> { while !self.eat(&token::CloseDelim(token::Brace)) { let Spanned {node, span} = if let Some(s) = self.parse_stmt_() { s + } else if self.token == token::Eof { + break; } else { // Found only `;` or `}`. continue; From 2731dc169c3e35707049575829cb106e2bdc9801 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Tue, 22 Mar 2016 16:02:09 +1300 Subject: [PATCH 03/25] Error recovery in the tokeniser Closes #31994 --- src/libsyntax/parse/parser.rs | 83 ++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 25 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 66912abb6f5a6..3010c040914df 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -268,8 +268,8 @@ pub struct Parser<'a> { /// Used to determine the path to externally loaded source files pub filename: Option, pub mod_path_stack: Vec, - /// Stack of spans of open delimiters. Used for error message. - pub open_braces: Vec, + /// Stack of open delimiters and their spans. Used for error message. + pub open_braces: Vec<(token::DelimToken, Span)>, /// Flag if this parser "owns" the directory that it is currently parsing /// in. This will affect how nested files are looked up. pub owns_directory: bool, @@ -895,7 +895,7 @@ impl<'a> Parser<'a> { sep: SeqSep, f: F) -> Vec - where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, + where F: FnMut(&mut Parser<'a>) -> PResult<'a, T> { self.parse_seq_to_before_tokens(&[ket], sep, f, |mut e| e.emit()) } @@ -2755,8 +2755,8 @@ impl<'a> Parser<'a> { let mut err: DiagnosticBuilder<'a> = self.diagnostic().struct_span_err(self.span, "this file contains an un-closed delimiter"); - for sp in &self.open_braces { - err.span_help(*sp, "did you mean to close this delimiter?"); + for &(_, sp) in &self.open_braces { + err.span_help(sp, "did you mean to close this delimiter?"); } Err(err) @@ -2766,23 +2766,66 @@ impl<'a> Parser<'a> { let pre_span = self.span; // Parse the open delimiter. - self.open_braces.push(self.span); + self.open_braces.push((delim, self.span)); let open_span = self.span; self.bump(); - // Parse the token trees within the delimiters - let tts = self.parse_seq_to_before_end(&token::CloseDelim(delim), - SeqSep::none(), - |p| p.parse_token_tree()); + // Parse the token trees within the delimiters. + // We stop at any delimiter so we can try to recover if the user + // uses an incorrect delimiter. + let tts = self.parse_seq_to_before_tokens(&[&token::CloseDelim(token::Brace), + &token::CloseDelim(token::Paren), + &token::CloseDelim(token::Bracket)], + SeqSep::none(), + |p| p.parse_token_tree(), + |mut e| e.emit()); - // Parse the close delimiter. let close_span = self.span; - self.bump(); - self.open_braces.pop().unwrap(); - // Expand to cover the entire delimited token tree let span = Span { hi: close_span.hi, ..pre_span }; + match self.token { + // Correct delmiter. + token::CloseDelim(d) if d == delim => { + self.open_braces.pop().unwrap(); + + // Parse the close delimiter. + self.bump(); + } + // Incorect delimiter. + token::CloseDelim(other) => { + let token_str = self.this_token_to_string(); + let mut err = self.diagnostic().struct_span_err(self.span, + &format!("incorrect close delimiter: `{}`", token_str)); + // This is a conservative error: only report the last unclosed delimiter. + // The previous unclosed delimiters could actually be closed! The parser + // just hasn't gotten to them yet. + if let Some(&(_, sp)) = self.open_braces.last() { + err.span_note(sp, "unclosed delimiter"); + }; + err.emit(); + + self.open_braces.pop().unwrap(); + + // If the incorrect delimter matches an earlier opening + // delimiter, then don't consume it (it can be used to + // close the earlier one)Otherwise, consume it. + // E.g., we try to recover from: + // fn foo() { + // bar(baz( + // } // Incorrect delimiter but matches the earlier `{` + if !self.open_braces.iter().any(|&(b, _)| b == other) { + self.bump(); + } + } + token::Eof => { + // Silently recover, the EOF token will be seen again + // and an error emitted then. Thus we don't pop from + // self.open_braces here. + }, + _ => unreachable!(), + } + Ok(TokenTree::Delimited(span, Rc::new(Delimited { delim: delim, open_span: open_span, @@ -2798,17 +2841,7 @@ impl<'a> Parser<'a> { maybe_whole!(deref self, NtTT); match self.token { token::CloseDelim(_) => { - let token_str = self.this_token_to_string(); - let mut err = self.diagnostic().struct_span_err(self.span, - &format!("incorrect close delimiter: `{}`", token_str)); - // This is a conservative error: only report the last unclosed delimiter. - // The previous unclosed delimiters could actually be closed! The parser - // just hasn't gotten to them yet. - if let Some(&sp) = self.open_braces.last() { - err.span_note(sp, "unclosed delimiter"); - }; - - Err(err) + panic!("should have been caught above"); }, /* we ought to allow different depths of unquotation */ token::Dollar | token::SubstNt(..) if self.quote_depth > 0 => { From 0950dc3d86c281f32439754a4cd8d616b574527f Mon Sep 17 00:00:00 2001 From: "NODA, Kai" Date: Wed, 23 Mar 2016 13:53:15 +0800 Subject: [PATCH 04/25] Remove ungrammatical dots from the error index. They were probably meant as a shorthand for omitted code. Part of #32446 but there should be a separate fix for the issue. Signed-off-by: NODA, Kai --- src/librustc/diagnostics.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index f474f7d4585f9..9348c05d44461 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1261,7 +1261,7 @@ compiled: fn foo>(x: T){} #[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] -trait Index { ... } +trait Index { /* ... */ } foo(true); // `bool` does not implement `Index` ``` @@ -1291,7 +1291,7 @@ compiled: fn foo>(x: T){} #[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] -trait Index { ... } +trait Index { /* ... */ } foo(true); // `bool` does not implement `Index` ``` @@ -1319,7 +1319,7 @@ compiled: fn foo>(x: T){} #[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] -trait Index { ... } +trait Index { /* ... */ } foo(true); // `bool` does not implement `Index` ``` From be87650f6310e31347a173787090dc89635fb57e Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Tue, 22 Mar 2016 23:28:22 -0700 Subject: [PATCH 05/25] Add augmented assignment operator impls for time types --- src/libstd/time/duration.rs | 30 +++++++++++++++++++++++++++++- src/libstd/time/mod.rs | 30 +++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index 7c3240b4a40c4..945eb6a42e5a7 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ops::{Add, Sub, Mul, Div}; +use ops::{Add, Sub, Mul, Div, AddAssign, SubAssign, MulAssign, DivAssign}; const NANOS_PER_SEC: u32 = 1_000_000_000; const NANOS_PER_MILLI: u32 = 1_000_000; @@ -105,6 +105,13 @@ impl Add for Duration { } } +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl AddAssign for Duration { + fn add_assign(&mut self, rhs: Duration) { + *self = *self + rhs; + } +} + #[stable(feature = "duration", since = "1.3.0")] impl Sub for Duration { type Output = Duration; @@ -124,6 +131,13 @@ impl Sub for Duration { } } +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl SubAssign for Duration { + fn sub_assign(&mut self, rhs: Duration) { + *self = *self - rhs; + } +} + #[stable(feature = "duration", since = "1.3.0")] impl Mul for Duration { type Output = Duration; @@ -141,6 +155,13 @@ impl Mul for Duration { } } +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl MulAssign for Duration { + fn mul_assign(&mut self, rhs: u32) { + *self = *self * rhs; + } +} + #[stable(feature = "duration", since = "1.3.0")] impl Div for Duration { type Output = Duration; @@ -155,6 +176,13 @@ impl Div for Duration { } } +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl DivAssign for Duration { + fn div_assign(&mut self, rhs: u32) { + *self = *self / rhs; + } +} + #[cfg(test)] mod tests { use super::Duration; diff --git a/src/libstd/time/mod.rs b/src/libstd/time/mod.rs index aa0a843dc9a54..414aeac2afe45 100644 --- a/src/libstd/time/mod.rs +++ b/src/libstd/time/mod.rs @@ -14,7 +14,7 @@ use error::Error; use fmt; -use ops::{Add, Sub}; +use ops::{Add, Sub, AddAssign, SubAssign}; use sys::time; use sys_common::FromInner; @@ -122,6 +122,13 @@ impl Add for Instant { } } +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl AddAssign for Instant { + fn add_assign(&mut self, other: Duration) { + *self = *self + other; + } +} + #[stable(feature = "time2", since = "1.8.0")] impl Sub for Instant { type Output = Instant; @@ -131,6 +138,13 @@ impl Sub for Instant { } } +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl SubAssign for Instant { + fn sub_assign(&mut self, other: Duration) { + *self = *self - other; + } +} + #[stable(feature = "time2", since = "1.8.0")] impl Sub for Instant { type Output = Duration; @@ -204,6 +218,13 @@ impl Add for SystemTime { } } +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl AddAssign for SystemTime { + fn add_assign(&mut self, other: Duration) { + *self = *self + other; + } +} + #[stable(feature = "time2", since = "1.8.0")] impl Sub for SystemTime { type Output = SystemTime; @@ -213,6 +234,13 @@ impl Sub for SystemTime { } } +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl SubAssign for SystemTime { + fn sub_assign(&mut self, other: Duration) { + *self = *self - other; + } +} + #[stable(feature = "time2", since = "1.8.0")] impl fmt::Debug for SystemTime { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { From 80e7a1be359fc0de4eb17056230ae2fc130b7090 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 23 Mar 2016 22:02:36 +0100 Subject: [PATCH 06/25] Mark str::split_at inline --- src/libcore/str/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index f9d1902bea7a7..4584c26bebcc8 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1965,6 +1965,7 @@ impl StrExt for str { self.find(pat) } + #[inline] fn split_at(&self, mid: usize) -> (&str, &str) { // is_char_boundary checks that the index is in [0, .len()] if self.is_char_boundary(mid) { From f621193e5ea7ac54fcd37f0e730e955fd9f61200 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 23 Mar 2016 21:57:44 +0100 Subject: [PATCH 07/25] Accept 0 as a valid str char boundary Index 0 must be a valid char boundary (invariant of str that it contains valid UTF-8 data). If we check explicitly for index == 0, that removes the need to read the byte at index 0, so it avoids a trip to the string's memory, and it optimizes out the slicing index' bounds check whenever it is zero. With this change, the following examples all change from having a read of the byte at 0 and a branch to possibly panicing, to having the bounds checking optimized away. ```rust pub fn split(s: &str) -> (&str, &str) { s.split_at(0) } pub fn both(s: &str) -> &str { &s[0..s.len()] } pub fn first(s: &str) -> &str { &s[..0] } pub fn last(s: &str) -> &str { &s[0..] } ``` --- src/libcore/str/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 4584c26bebcc8..f033f50e59e5c 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1892,7 +1892,10 @@ impl StrExt for str { #[inline] fn is_char_boundary(&self, index: usize) -> bool { - if index == self.len() { return true; } + // 0 and len are always ok. + // Test for 0 explicitly so that it can optimize out the check + // easily and skip reading string data for that case. + if index == 0 || index == self.len() { return true; } match self.as_bytes().get(index) { None => false, Some(&b) => b < 128 || b >= 192, From 180d6b55ca19c63347664f0622b6ccc37fb101f5 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Wed, 23 Mar 2016 09:24:54 +1300 Subject: [PATCH 08/25] Tests --- src/libsyntax/parse/parser.rs | 7 +++- .../issue-10636-2.rs | 8 ++--- src/test/compile-fail/issue-31804.rs | 16 +++++++++ .../compile-fail/token-error-correct-2.rs | 17 ++++++++++ .../compile-fail/token-error-correct-3.rs | 33 +++++++++++++++++++ src/test/compile-fail/token-error-correct.rs | 20 +++++++++++ src/test/parse-fail/issue-2354-1.rs | 2 +- .../macro-mismatched-delim-paren-brace.rs | 2 +- 8 files changed, 97 insertions(+), 8 deletions(-) rename src/test/{parse-fail => compile-fail}/issue-10636-2.rs (75%) create mode 100644 src/test/compile-fail/issue-31804.rs create mode 100644 src/test/compile-fail/token-error-correct-2.rs create mode 100644 src/test/compile-fail/token-error-correct-3.rs create mode 100644 src/test/compile-fail/token-error-correct.rs diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 3010c040914df..29ef105eccb72 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2841,7 +2841,12 @@ impl<'a> Parser<'a> { maybe_whole!(deref self, NtTT); match self.token { token::CloseDelim(_) => { - panic!("should have been caught above"); + // An unexpected closing delimiter (i.e., there is no + // matching opening delimiter). + let token_str = self.this_token_to_string(); + let err = self.diagnostic().struct_span_err(self.span, + &format!("unexpected close delimiter: `{}`", token_str)); + Err(err) }, /* we ought to allow different depths of unquotation */ token::Dollar | token::SubstNt(..) if self.quote_depth > 0 => { diff --git a/src/test/parse-fail/issue-10636-2.rs b/src/test/compile-fail/issue-10636-2.rs similarity index 75% rename from src/test/parse-fail/issue-10636-2.rs rename to src/test/compile-fail/issue-10636-2.rs index 41a3b06e6556e..eaccaf3cdbd27 100644 --- a/src/test/parse-fail/issue-10636-2.rs +++ b/src/test/compile-fail/issue-10636-2.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2013-2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -11,11 +11,9 @@ // FIXME(31528) we emit a bunch of silly errors here due to continuing past the // first one. This would be easy-ish to address by better recovery in tokenisation. -// compile-flags: -Z parse-only - -pub fn trace_option(option: Option) { //~ HELP did you mean to close this delimiter? +pub fn trace_option(option: Option) { option.map(|some| 42; //~ NOTE: unclosed delimiter //~^ ERROR: expected one of + //~^^ ERROR: mismatched types } //~ ERROR: incorrect close delimiter //~^ ERROR: expected one of -//~ ERROR: this file contains an un-closed delimiter diff --git a/src/test/compile-fail/issue-31804.rs b/src/test/compile-fail/issue-31804.rs new file mode 100644 index 0000000000000..b6a04bee85d4f --- /dev/null +++ b/src/test/compile-fail/issue-31804.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. + +// Test that error recovery in the parser to an EOF does not give an infinite +// spew of errors. + +fn main() { + let +} //~ ERROR unexpected token: `}` diff --git a/src/test/compile-fail/token-error-correct-2.rs b/src/test/compile-fail/token-error-correct-2.rs new file mode 100644 index 0000000000000..ab429ab878073 --- /dev/null +++ b/src/test/compile-fail/token-error-correct-2.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. + +// Test that we do some basic error correcton in the tokeniser (and don't ICE). + +fn main() { + if foo { //~ NOTE: unclosed delimiter + //~^ ERROR: unresolved name `foo` + ) //~ ERROR: incorrect close delimiter: `)` +} diff --git a/src/test/compile-fail/token-error-correct-3.rs b/src/test/compile-fail/token-error-correct-3.rs new file mode 100644 index 0000000000000..fe8c9f690139f --- /dev/null +++ b/src/test/compile-fail/token-error-correct-3.rs @@ -0,0 +1,33 @@ +// 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. + +// Test that we do some basic error correcton in the tokeniser (and don't spew +// too many bogus errors). + +pub mod raw { + use std::{io, fs}; + use std::path::Path; + + pub fn ensure_dir_exists, F: FnOnce(&Path)>(path: P, + callback: F) + -> io::Result { + if !is_directory(path.as_ref()) { //~ ERROR: unresolved name `is_directory` + callback(path.as_ref(); //~ NOTE: unclosed delimiter + //~^ ERROR: expected one of + fs::create_dir_all(path.as_ref()).map(|()| true) //~ ERROR: expected one of + } else { //~ ERROR: incorrect close delimiter: `}` + Ok(false); + } + + panic!(); + } +} + +fn main() {} diff --git a/src/test/compile-fail/token-error-correct.rs b/src/test/compile-fail/token-error-correct.rs new file mode 100644 index 0000000000000..6c54acd7bdbf6 --- /dev/null +++ b/src/test/compile-fail/token-error-correct.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. + +// Test that we do some basic error correcton in the tokeniser. + +fn main() { + foo(bar(; //~ NOTE: unclosed delimiter + //~^ NOTE: unclosed delimiter + //~^^ ERROR: unexpected token: `;` + //~^^^ ERROR: unresolved name `bar` + //~^^^^ ERROR: unresolved name `foo` +} //~ ERROR: incorrect close delimiter: `}` +//~^ ERROR: incorrect close delimiter: `}` diff --git a/src/test/parse-fail/issue-2354-1.rs b/src/test/parse-fail/issue-2354-1.rs index e65f95780bb96..f24c544073578 100644 --- a/src/test/parse-fail/issue-2354-1.rs +++ b/src/test/parse-fail/issue-2354-1.rs @@ -10,4 +10,4 @@ // compile-flags: -Z parse-only -static foo: isize = 2; } //~ ERROR incorrect close delimiter: +static foo: isize = 2; } //~ ERROR unexpected close delimiter: diff --git a/src/test/parse-fail/macro-mismatched-delim-paren-brace.rs b/src/test/parse-fail/macro-mismatched-delim-paren-brace.rs index 84094ab6ca89c..cbc0ed0ccdb84 100644 --- a/src/test/parse-fail/macro-mismatched-delim-paren-brace.rs +++ b/src/test/parse-fail/macro-mismatched-delim-paren-brace.rs @@ -14,4 +14,4 @@ fn main() { foo! ( bar, "baz", 1, 2.0 } //~ ERROR incorrect close delimiter -} +} //~ ERROR unexpected close delimiter: `}` From 13877ac4428cc9cbf6b07ad98e0f0b152381fac7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 24 Mar 2016 14:56:02 -0400 Subject: [PATCH 09/25] make available monomorphizations shared by CGU The current setup means that all generics are local to a codegen-unit, which means massive duplication. --- src/librustc_trans/trans/context.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 8f5572f5c4e0e..f232f1afea723 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -79,6 +79,7 @@ pub struct SharedCrateContext<'a, 'tcx: 'a> { mir_map: &'a MirMap<'tcx>, mir_cache: RefCell>>>, + available_monomorphizations: RefCell>, available_drop_glues: RefCell, String>>, use_dll_storage_attrs: bool, @@ -104,7 +105,6 @@ pub struct LocalCrateContext<'tcx> { /// Cache instances of monomorphic and polymorphic items instances: RefCell, ValueRef>>, monomorphizing: RefCell>, - available_monomorphizations: RefCell>, /// Cache generated vtables vtables: RefCell, ValueRef>>, /// Cache of constant strings, @@ -356,6 +356,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { }, check_overflow: check_overflow, check_drop_flag_for_sanity: check_drop_flag_for_sanity, + available_monomorphizations: RefCell::new(FnvHashSet()), available_drop_glues: RefCell::new(FnvHashMap()), use_dll_storage_attrs: use_dll_storage_attrs, translation_items: RefCell::new(FnvHashMap()), @@ -473,7 +474,6 @@ impl<'tcx> LocalCrateContext<'tcx> { external_srcs: RefCell::new(NodeMap()), instances: RefCell::new(FnvHashMap()), monomorphizing: RefCell::new(DefIdMap()), - available_monomorphizations: RefCell::new(FnvHashSet()), vtables: RefCell::new(FnvHashMap()), const_cstr_cache: RefCell::new(FnvHashMap()), const_unsized: RefCell::new(FnvHashMap()), @@ -723,7 +723,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { } pub fn available_monomorphizations<'a>(&'a self) -> &'a RefCell> { - &self.local.available_monomorphizations + &self.shared.available_monomorphizations } pub fn available_drop_glues(&self) -> &RefCell, String>> { From 8d4b1d1cf38a51b92b91e020ecb22a5d442297be Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 8 Mar 2016 21:40:13 +0300 Subject: [PATCH 10/25] Introduce name resolution fallback for primitive types --- src/librustc_resolve/lib.rs | 49 +++++++++++++++++++++------- src/test/compile-fail/issue-20427.rs | 1 + 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index af8c9d8168742..8efac52158ece 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -2654,15 +2654,27 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // Try to find a path to an item in a module. let last_ident = segments.last().unwrap().identifier; - if segments.len() <= 1 { - let unqualified_def = self.resolve_identifier(last_ident, namespace, true); + if segments.len() == 1 { + // In `a(::assoc_item)*` `a` cannot be a module. If `a` does resolve to a module we + // don't report an error right away, but try to fallback to a primitive type. + // So, we are still able to successfully resolve something like + // + // use std::u8; // bring module u8 in scope + // fn f() -> u8 { // OK, resolves to primitive u8, not to std::u8 + // u8::MAX // OK, resolves to associated constant ::MAX, + // // not to non-existent std::u8::MAX + // } + // + // Such behavior is required for backward compatibility. + // The same fallback is used when `a` resolves to nothing. + let unqualified_def = self.resolve_identifier_with_fallback(last_ident, namespace, true); return unqualified_def.and_then(|def| self.adjust_local_def(def, span)) .map(|def| { PathResolution::new(def, path_depth) }); } - let unqualified_def = self.resolve_identifier(last_ident, namespace, false); + let unqualified_def = self.resolve_identifier_with_fallback(last_ident, namespace, false); let def = self.resolve_module_relative_path(span, segments, namespace); match (def, unqualified_def) { (Some(d), Some(ref ud)) if d == ud.def => { @@ -2678,6 +2690,28 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { def.map(mk_res) } + // Resolve a single identifier with fallback to primitive types + fn resolve_identifier_with_fallback(&mut self, + identifier: hir::Ident, + namespace: Namespace, + check_ribs: bool, + record_used: bool) + -> Option { + let def = self.resolve_identifier(identifier, namespace, check_ribs, record_used); + match def { + None | Some(LocalDef{def: Def::Mod(..), ..}) => { + if let Some(&prim_ty) = self.primitive_type_table + .primitive_types + .get(&identifier.unhygienic_name) { + Some(LocalDef::from_def(Def::PrimTy(prim_ty))) + } else { + def + } + } + _ => def + } + } + // Resolve a single identifier fn resolve_identifier(&mut self, identifier: hir::Ident, @@ -2688,15 +2722,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { return Some(LocalDef::from_def(Def::Err)); } - // First, check to see whether the name is a primitive type. - if namespace == TypeNS { - if let Some(&prim_ty) = self.primitive_type_table - .primitive_types - .get(&identifier.unhygienic_name) { - return Some(LocalDef::from_def(Def::PrimTy(prim_ty))); - } - } - self.resolve_identifier_in_local_ribs(identifier, namespace, record_used) } diff --git a/src/test/compile-fail/issue-20427.rs b/src/test/compile-fail/issue-20427.rs index 99dd22a888cb5..7dedb9593e13c 100644 --- a/src/test/compile-fail/issue-20427.rs +++ b/src/test/compile-fail/issue-20427.rs @@ -17,6 +17,7 @@ fn u8(f32: f32) {} fn f(f64: f64) {} //~^ ERROR user-defined types or type parameters cannot shadow the primitive types type u16 = u16; //~ ERROR user-defined types or type parameters cannot shadow the primitive types +//~^ ERROR unsupported cyclic reference between types/traits detected enum u32 {} //~ ERROR user-defined types or type parameters cannot shadow the primitive types struct u64; //~ ERROR user-defined types or type parameters cannot shadow the primitive types trait bool {} //~ ERROR user-defined types or type parameters cannot shadow the primitive types From 77f033bac17d2767e9605f3f44d43500662c4ec7 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 9 Mar 2016 00:08:29 +0300 Subject: [PATCH 11/25] Lift the restriction on reusing names of primitive types --- src/librustc_resolve/diagnostics.rs | 45 ------------------- src/librustc_resolve/lib.rs | 40 +---------------- .../{compile-fail => run-pass}/issue-20427.rs | 36 +++++++++------ 3 files changed, 23 insertions(+), 98 deletions(-) rename src/test/{compile-fail => run-pass}/issue-20427.rs (53%) diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index bfd8a6f1f61f0..8a196768ae516 100644 --- a/src/librustc_resolve/diagnostics.rs +++ b/src/librustc_resolve/diagnostics.rs @@ -205,51 +205,6 @@ about what constitutes an Item declaration and what does not: https://doc.rust-lang.org/reference.html#statements "##, -E0317: r##" -User-defined types or type parameters cannot shadow the primitive types. -This error indicates you tried to define a type, struct or enum with the same -name as an existing primitive type: - -```compile_fail -struct u8 { - // ... -} -``` - -To fix this, simply name it something else. - -Such an error may also occur if you define a type parameter which shadows a -primitive type. An example would be something like: - -```compile_fail -impl MyTrait for Option { - // ... -} -``` - -In such a case, if you meant for `u8` to be a generic type parameter (i.e. any -type can be used in its place), use something like `T` instead: - -```ignore -impl MyTrait for Option { - // ... -} -``` - -On the other hand, if you wished to refer to the specific type `u8`, remove it -from the type parameter list: - -```ignore -impl MyTrait for Option { - // ... -} - -See the Types section of the reference for more information about the primitive -types: - -https://doc.rust-lang.org/reference.html#types -"##, - E0364: r##" Private items cannot be publicly re-exported. This error indicates that you attempted to `pub use` a type or value that was not itself public. diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 8efac52158ece..a54338b636078 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1615,15 +1615,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { intravisit::walk_crate(self, krate); } - fn check_if_primitive_type_name(&self, name: Name, span: Span) { - if let Some(_) = self.primitive_type_table.primitive_types.get(&name) { - span_err!(self.session, - span, - E0317, - "user-defined types or type parameters cannot shadow the primitive types"); - } - } - fn resolve_item(&mut self, item: &Item) { let name = item.name; @@ -1633,8 +1624,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ItemEnum(_, ref generics) | ItemTy(_, ref generics) | ItemStruct(_, ref generics) => { - self.check_if_primitive_type_name(name, item.span); - self.with_type_parameter_rib(HasTypeParameters(generics, TypeSpace, ItemRibKind), |this| intravisit::walk_item(this, item)); } @@ -1655,8 +1644,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } ItemTrait(_, ref generics, ref bounds, ref trait_items) => { - self.check_if_primitive_type_name(name, item.span); - // Create a new rib for the trait-wide type parameters. self.with_type_parameter_rib(HasTypeParameters(generics, TypeSpace, @@ -1691,8 +1678,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { }); } hir::TypeTraitItem(..) => { - this.check_if_primitive_type_name(trait_item.name, - trait_item.span); this.with_type_parameter_rib(NoTypeParameters, |this| { intravisit::walk_trait_item(this, trait_item) }); @@ -1716,28 +1701,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } ItemUse(ref view_path) => { - // check for imports shadowing primitive types - let check_rename = |this: &Self, id, name| { - match this.def_map.borrow().get(&id).map(|d| d.full_def()) { - Some(Def::Enum(..)) | Some(Def::TyAlias(..)) | Some(Def::Struct(..)) | - Some(Def::Trait(..)) | None => { - this.check_if_primitive_type_name(name, item.span); - } - _ => {} - } - }; - match view_path.node { - hir::ViewPathSimple(name, _) => { - check_rename(self, item.id, name); - } hir::ViewPathList(ref prefix, ref items) => { - for item in items { - if let Some(name) = item.node.rename() { - check_rename(self, item.node.id(), name); - } - } - // Resolve prefix of an import with empty braces (issue #28388) if items.is_empty() && !prefix.segments.is_empty() { match self.resolve_crate_relative_path(prefix.span, @@ -1918,9 +1883,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } fn resolve_generics(&mut self, generics: &Generics) { - for type_parameter in generics.ty_params.iter() { - self.check_if_primitive_type_name(type_parameter.name, type_parameter.span); - } for predicate in &generics.where_clause.predicates { match predicate { &hir::WherePredicate::BoundPredicate(_) | @@ -2699,7 +2661,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { -> Option { let def = self.resolve_identifier(identifier, namespace, check_ribs, record_used); match def { - None | Some(LocalDef{def: Def::Mod(..), ..}) => { + None | Some(LocalDef{def: Def::Mod(..), ..}) if namespace == TypeNS => { if let Some(&prim_ty) = self.primitive_type_table .primitive_types .get(&identifier.unhygienic_name) { diff --git a/src/test/compile-fail/issue-20427.rs b/src/test/run-pass/issue-20427.rs similarity index 53% rename from src/test/compile-fail/issue-20427.rs rename to src/test/run-pass/issue-20427.rs index 7dedb9593e13c..43674ccf54352 100644 --- a/src/test/compile-fail/issue-20427.rs +++ b/src/test/run-pass/issue-20427.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// 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. // @@ -15,12 +15,9 @@ static i32: i32 = 0; const i64: i64 = 0; fn u8(f32: f32) {} fn f(f64: f64) {} -//~^ ERROR user-defined types or type parameters cannot shadow the primitive types -type u16 = u16; //~ ERROR user-defined types or type parameters cannot shadow the primitive types -//~^ ERROR unsupported cyclic reference between types/traits detected -enum u32 {} //~ ERROR user-defined types or type parameters cannot shadow the primitive types -struct u64; //~ ERROR user-defined types or type parameters cannot shadow the primitive types -trait bool {} //~ ERROR user-defined types or type parameters cannot shadow the primitive types +enum u32 {} +struct u64; +trait bool {} mod char { extern crate i8; @@ -41,29 +38,40 @@ mod char { use super::u8_ as u8; use super::f_ as f64; use super::u16_ as u16; - //~^ ERROR user-defined types or type parameters cannot shadow the primitive types use super::u32_ as u32; - //~^ ERROR user-defined types or type parameters cannot shadow the primitive types use super::u64_ as u64; - //~^ ERROR user-defined types or type parameters cannot shadow the primitive types use super::bool_ as bool; - //~^ ERROR user-defined types or type parameters cannot shadow the primitive types use super::{bool_ as str}; - //~^ ERROR user-defined types or type parameters cannot shadow the primitive types use super::char_ as char; } } trait isize_ { - type isize; //~ ERROR user-defined types or type parameters cannot shadow the primitive types + type isize; } fn usize<'usize>(usize: &'usize usize) -> &'usize usize { usize } +mod reuse { + use std::mem::size_of; + + type u8 = u64; + use std::string::String as i16; + + pub fn check() { + assert_eq!(size_of::(), 8); + assert_eq!(size_of::<::u64>(), 0); + assert_eq!(size_of::(), 3 * size_of::<*const ()>()); + assert_eq!(size_of::(), 0); + } +} + fn main() { let bool = true; - match bool { + let _ = match bool { str @ true => if str { i32 as i64 } else { i64 }, false => i64, }; + + reuse::check::(); } From b418cd23068b1074e78d8631aec923b865bcc583 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 10 Mar 2016 22:01:38 +0300 Subject: [PATCH 12/25] Cleanup + Fix a comment and add a test based on it --- src/librustc_resolve/lib.rs | 48 ++++++++++++-------------------- src/test/run-pass/issue-20427.rs | 14 ++++++++++ 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index a54338b636078..231bf666deafc 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -2616,6 +2616,19 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // Try to find a path to an item in a module. let last_ident = segments.last().unwrap().identifier; + // Resolve a single identifier with fallback to primitive types + let resolve_identifier_with_fallback = |this: &mut Self, record_used| { + let def = this.resolve_identifier(last_ident, namespace, record_used); + match def { + None | Some(LocalDef{def: Def::Mod(..), ..}) if namespace == TypeNS => + this.primitive_type_table + .primitive_types + .get(&last_ident.unhygienic_name) + .map_or(def, |prim_ty| Some(LocalDef::from_def(Def::PrimTy(*prim_ty)))), + _ => def + } + }; + if segments.len() == 1 { // In `a(::assoc_item)*` `a` cannot be a module. If `a` does resolve to a module we // don't report an error right away, but try to fallback to a primitive type. @@ -2623,20 +2636,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // // use std::u8; // bring module u8 in scope // fn f() -> u8 { // OK, resolves to primitive u8, not to std::u8 - // u8::MAX // OK, resolves to associated constant ::MAX, - // // not to non-existent std::u8::MAX + // u8::max_value() // OK, resolves to associated function ::max_value, + // // not to non-existent std::u8::max_value // } // // Such behavior is required for backward compatibility. // The same fallback is used when `a` resolves to nothing. - let unqualified_def = self.resolve_identifier_with_fallback(last_ident, namespace, true); - return unqualified_def.and_then(|def| self.adjust_local_def(def, span)) - .map(|def| { - PathResolution::new(def, path_depth) - }); + let unqualified_def = resolve_identifier_with_fallback(self, true); + return unqualified_def.and_then(|def| self.adjust_local_def(def, span)).map(mk_res); } - let unqualified_def = self.resolve_identifier_with_fallback(last_ident, namespace, false); + let unqualified_def = resolve_identifier_with_fallback(self, false); let def = self.resolve_module_relative_path(span, segments, namespace); match (def, unqualified_def) { (Some(d), Some(ref ud)) if d == ud.def => { @@ -2652,28 +2662,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { def.map(mk_res) } - // Resolve a single identifier with fallback to primitive types - fn resolve_identifier_with_fallback(&mut self, - identifier: hir::Ident, - namespace: Namespace, - check_ribs: bool, - record_used: bool) - -> Option { - let def = self.resolve_identifier(identifier, namespace, check_ribs, record_used); - match def { - None | Some(LocalDef{def: Def::Mod(..), ..}) if namespace == TypeNS => { - if let Some(&prim_ty) = self.primitive_type_table - .primitive_types - .get(&identifier.unhygienic_name) { - Some(LocalDef::from_def(Def::PrimTy(prim_ty))) - } else { - def - } - } - _ => def - } - } - // Resolve a single identifier fn resolve_identifier(&mut self, identifier: hir::Ident, diff --git a/src/test/run-pass/issue-20427.rs b/src/test/run-pass/issue-20427.rs index 43674ccf54352..dd3d952224c05 100644 --- a/src/test/run-pass/issue-20427.rs +++ b/src/test/run-pass/issue-20427.rs @@ -9,6 +9,8 @@ // except according to those terms. // aux-build:i8.rs +// ignore-pretty (#23623) + extern crate i8; use std::string as i16; static i32: i32 = 0; @@ -66,6 +68,17 @@ mod reuse { } } +mod guard { + pub fn check() { + use std::u8; // bring module u8 in scope + fn f() -> u8 { // OK, resolves to primitive u8, not to std::u8 + u8::max_value() // OK, resolves to associated function ::max_value, + // not to non-existent std::u8::max_value + } + assert_eq!(f(), u8::MAX); // OK, resolves to std::u8::MAX + } +} + fn main() { let bool = true; let _ = match bool { @@ -74,4 +87,5 @@ fn main() { }; reuse::check::(); + guard::check(); } From 78495d5082f51a2737619824548c9f2407b12a2b Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 25 Mar 2016 05:46:45 +0100 Subject: [PATCH 13/25] Fix unsound behaviour with null characters in thread names (issue #32475) Previously, the thread name (&str) was converted to a CString in the new thread, but outside unwind::try, causing a panic to continue into FFI. This patch changes that behaviour, so that the panic instead happens in the parent thread (where panic infrastructure is properly set up), not the new thread. This could potentially be a breaking change for architectures who don't support thread names. Signed-off-by: David Henningsson --- src/libstd/sys/unix/thread.rs | 27 +++++++++++---------------- src/libstd/sys/windows/thread.rs | 2 +- src/libstd/thread/mod.rs | 21 ++++++++++++++++++--- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index 793a2ecae89f1..6d966a0f6944c 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -13,7 +13,7 @@ use prelude::v1::*; use alloc::boxed::FnBox; use cmp; #[cfg(not(any(target_env = "newlib", target_os = "solaris")))] -use ffi::CString; +use ffi::CStr; use io; use libc; use mem; @@ -84,15 +84,12 @@ impl Thread { #[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))] - pub fn set_name(name: &str) { + pub fn set_name(name: &CStr) { const PR_SET_NAME: libc::c_int = 15; - let cname = CString::new(name).unwrap_or_else(|_| { - panic!("thread name may not contain interior null bytes") - }); // pthread wrapper only appeared in glibc 2.12, so we use syscall // directly. unsafe { - libc::prctl(PR_SET_NAME, cname.as_ptr() as libc::c_ulong, 0, 0, 0); + libc::prctl(PR_SET_NAME, name.as_ptr() as libc::c_ulong, 0, 0, 0); } } @@ -100,32 +97,30 @@ impl Thread { target_os = "dragonfly", target_os = "bitrig", target_os = "openbsd"))] - pub fn set_name(name: &str) { - let cname = CString::new(name).unwrap(); + pub fn set_name(name: &CStr) { unsafe { - libc::pthread_set_name_np(libc::pthread_self(), cname.as_ptr()); + libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); } } #[cfg(any(target_os = "macos", target_os = "ios"))] - pub fn set_name(name: &str) { - let cname = CString::new(name).unwrap(); + pub fn set_name(name: &CStr) { unsafe { - libc::pthread_setname_np(cname.as_ptr()); + libc::pthread_setname_np(name.as_ptr()); } } #[cfg(target_os = "netbsd")] - pub fn set_name(name: &str) { + pub fn set_name(name: &CStr) { + use ffi::CString; let cname = CString::new(&b"%s"[..]).unwrap(); - let carg = CString::new(name).unwrap(); unsafe { libc::pthread_setname_np(libc::pthread_self(), cname.as_ptr(), - carg.as_ptr() as *mut libc::c_void); + name.as_ptr() as *mut libc::c_void); } } #[cfg(any(target_env = "newlib", target_os = "solaris"))] - pub fn set_name(_name: &str) { + pub fn set_name(_name: &CStr) { // Newlib and Illumos has no way to set a thread name. } diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs index b18772c0c2438..6908775e86fc1 100644 --- a/src/libstd/sys/windows/thread.rs +++ b/src/libstd/sys/windows/thread.rs @@ -54,7 +54,7 @@ impl Thread { } } - pub fn set_name(_name: &str) { + pub fn set_name(_name: &CStr) { // Windows threads are nameless // The names in MSVC debugger are obtained using a "magic" exception, // which requires a use of MS C++ extensions. diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index f3139aaf98d83..b3549dc12645a 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -166,6 +166,8 @@ use any::Any; use cell::UnsafeCell; use fmt; use io; +use str; +use ffi::{CStr, CString}; use sync::{Mutex, Condvar, Arc}; use sys::thread as imp; use sys_common::thread_info; @@ -267,7 +269,7 @@ impl Builder { let their_packet = my_packet.clone(); let main = move || { - if let Some(name) = their_thread.name() { + if let Some(name) = their_thread.cname() { imp::Thread::set_name(name); } unsafe { @@ -450,7 +452,7 @@ pub fn park_timeout(dur: Duration) { /// The internal representation of a `Thread` handle struct Inner { - name: Option, + name: Option, // Guaranteed to be UTF-8 lock: Mutex, // true when there is a buffered unpark cvar: Condvar, } @@ -465,9 +467,12 @@ pub struct Thread { impl Thread { // Used only internally to construct a thread object without spawning fn new(name: Option) -> Thread { + let cname = name.map(|n| CString::new(n).unwrap_or_else(|_| { + panic!("thread name may not contain interior null bytes") + })); Thread { inner: Arc::new(Inner { - name: name, + name: cname, lock: Mutex::new(false), cvar: Condvar::new(), }) @@ -489,6 +494,10 @@ impl Thread { /// Gets the thread's name. #[stable(feature = "rust1", since = "1.0.0")] pub fn name(&self) -> Option<&str> { + self.cname().map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) } ) + } + + fn cname(&self) -> Option<&CStr> { self.inner.name.as_ref().map(|s| &**s) } } @@ -622,6 +631,12 @@ mod tests { }).unwrap().join().unwrap(); } + #[test] + #[should_panic] + fn test_invalid_named_thread() { + let _ = Builder::new().name("ada l\0velace".to_string()).spawn(|| {}); + } + #[test] fn test_run_basic() { let (tx, rx) = channel(); From 5bc286806023ef4d63bceec4ba703399ba9ee2f7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 3 Feb 2016 16:38:48 -0500 Subject: [PATCH 14/25] make `const_expr_to_pat` fallible (but never have it actually fail) --- src/librustc/middle/check_match.rs | 26 +++++++++++++------ src/librustc/middle/const_eval.rs | 40 ++++++++++++++++++----------- src/librustc_mir/hair/cx/pattern.rs | 12 ++++++--- 3 files changed, 52 insertions(+), 26 deletions(-) diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index b5e1d5899967f..77b0995827808 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -478,15 +478,25 @@ impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> { Some(Def::Const(did)) => { let substs = Some(self.tcx.node_id_item_substs(pat.id).substs); if let Some((const_expr, _)) = lookup_const_by_id(self.tcx, did, substs) { - const_expr_to_pat(self.tcx, const_expr, pat.span).map(|new_pat| { - - if let Some(ref mut renaming_map) = self.renaming_map { - // Record any renamings we do here - record_renamings(const_expr, &pat, renaming_map); + match const_expr_to_pat(self.tcx, const_expr, pat.span) { + Ok(new_pat) => { + if let Some(ref mut map) = self.renaming_map { + // Record any renamings we do here + record_renamings(const_expr, &pat, map); + } + new_pat } - - new_pat - }) + Err(def_id) => { + // TODO back-compat + self.failed = true; + self.tcx.sess.span_err( + pat.span, + &format!("constants of the type `{}` \ + cannot be used in patterns", + self.tcx.item_path_str(def_id))); + pat + } + } } else { self.failed = true; span_err!(self.tcx.sess, pat.span, E0158, diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index a8c2a73e72f51..af1e9d60be441 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -323,10 +323,13 @@ impl ConstVal { } } -pub fn const_expr_to_pat(tcx: &TyCtxt, expr: &Expr, span: Span) -> P { +pub fn const_expr_to_pat(tcx: &ty::TyCtxt, expr: &Expr, span: Span) + -> Result, DefId> { let pat = match expr.node { hir::ExprTup(ref exprs) => - PatKind::Tup(exprs.iter().map(|expr| const_expr_to_pat(tcx, &expr, span)).collect()), + PatKind::Tup(try!(exprs.iter() + .map(|expr| const_expr_to_pat(tcx, &expr, span)) + .collect())), hir::ExprCall(ref callee, ref args) => { let def = *tcx.def_map.borrow().get(&callee.id).unwrap(); @@ -336,31 +339,38 @@ pub fn const_expr_to_pat(tcx: &TyCtxt, expr: &Expr, span: Span) -> P { let path = match def.full_def() { Def::Struct(def_id) => def_to_path(tcx, def_id), Def::Variant(_, variant_did) => def_to_path(tcx, variant_did), - Def::Fn(..) => return P(hir::Pat { + Def::Fn(..) => return Ok(P(hir::Pat { id: expr.id, node: PatKind::Lit(P(expr.clone())), span: span, - }), + })), _ => unreachable!() }; - let pats = args.iter().map(|expr| const_expr_to_pat(tcx, &expr, span)).collect(); + let pats = try!(args.iter() + .map(|expr| const_expr_to_pat(tcx, &**expr, span)) + .collect()); PatKind::TupleStruct(path, Some(pats)) } hir::ExprStruct(ref path, ref fields, None) => { - let field_pats = fields.iter().map(|field| codemap::Spanned { - span: codemap::DUMMY_SP, - node: hir::FieldPat { - name: field.name.node, - pat: const_expr_to_pat(tcx, &field.expr, span), - is_shorthand: false, - }, - }).collect(); + let field_pats = + try!(fields.iter() + .map(|field| Ok(codemap::Spanned { + span: codemap::DUMMY_SP, + node: hir::FieldPat { + name: field.name.node, + pat: try!(const_expr_to_pat(tcx, &field.expr, span)), + is_shorthand: false, + }, + })) + .collect()); PatKind::Struct(path.clone(), field_pats, false) } hir::ExprVec(ref exprs) => { - let pats = exprs.iter().map(|expr| const_expr_to_pat(tcx, &expr, span)).collect(); + let pats = try!(exprs.iter() + .map(|expr| const_expr_to_pat(tcx, &expr, span)) + .collect()); PatKind::Vec(pats, None, hir::HirVec::new()) } @@ -381,7 +391,7 @@ pub fn const_expr_to_pat(tcx: &TyCtxt, expr: &Expr, span: Span) -> P { _ => PatKind::Lit(P(expr.clone())) }; - P(hir::Pat { id: expr.id, node: pat, span: span }) + Ok(P(hir::Pat { id: expr.id, node: pat, span: span })) } pub fn eval_const_expr(tcx: &TyCtxt, e: &Expr) -> ConstVal { diff --git a/src/librustc_mir/hair/cx/pattern.rs b/src/librustc_mir/hair/cx/pattern.rs index a987377837417..bfb8d1c401aaa 100644 --- a/src/librustc_mir/hair/cx/pattern.rs +++ b/src/librustc_mir/hair/cx/pattern.rs @@ -90,9 +90,15 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> { let substs = Some(self.cx.tcx.node_id_item_substs(pat.id).substs); match const_eval::lookup_const_by_id(self.cx.tcx, def_id, substs) { Some((const_expr, _const_ty)) => { - let pat = const_eval::const_expr_to_pat(self.cx.tcx, const_expr, - pat.span); - return self.to_pattern(&pat); + match const_eval::const_expr_to_pat(self.cx.tcx, + const_expr, + pat.span) { + Ok(pat) => + return self.to_pattern(&pat), + Err(_) => + self.cx.tcx.sess.span_bug( + pat.span, "illegal constant"), + } } None => { self.cx.tcx.sess.span_bug( From 99c2a6b335d953eca64e631f3e9946b1cc6643e1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 11 Mar 2016 13:27:42 -0500 Subject: [PATCH 15/25] modify #[deriving(Eq)] to emit #[structural_match] to careful use of the span from deriving, we can permit it in stable code if it derives from deriving (not-even-a-pun intended) --- src/libsyntax/feature_gate.rs | 9 +++++- src/libsyntax_ext/deriving/mod.rs | 48 +++++++++++++++++++++++++++++-- src/libsyntax_ext/lib.rs | 1 + 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 299b7d8b9ba07..8e7f4876ee2ab 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -109,6 +109,8 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status // to bootstrap fix for #5723. ("issue_5723_bootstrap", "1.0.0", None, Accepted), + ("structural_match", "1.8.0", Some(31434), Active), + // A way to temporarily opt out of opt in copy. This will *never* be accepted. ("opt_out_copy", "1.0.0", None, Removed), @@ -304,6 +306,11 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat ("link_args", Normal, Ungated), ("macro_escape", Normal, Ungated), + // RFC #1445. + ("structural_match", Whitelisted, Gated("structural_match", + "the semantics of constant patterns is \ + not yet settled")), + // Not used any more, but we can't feature gate it ("no_stack_check", Normal, Ungated), @@ -676,7 +683,7 @@ impl<'a> Context<'a> { fn gate_feature(&self, feature: &str, span: Span, explain: &str) { let has_feature = self.has_feature(feature); debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", feature, span, has_feature); - if !has_feature { + if !has_feature && !self.cm.span_allows_unstable(span) { emit_feature_err(self.span_handler, feature, span, GateIssue::Language, explain); } } diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index 75de5c56ea139..dbd3cbd4fbaa3 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -78,7 +78,10 @@ fn expand_derive(cx: &mut ExtCtxt, mitem: &MetaItem, annotatable: Annotatable) -> Annotatable { - annotatable.map_item_or(|item| { + debug!("expand_derive: span = {:?}", span); + debug!("expand_derive: mitem = {:?}", mitem); + debug!("expand_derive: annotatable input = {:?}", annotatable); + let annot = annotatable.map_item_or(|item| { item.map(|mut item| { if mitem.value_str().is_some() { cx.span_err(mitem.span, "unexpected value in `derive`"); @@ -107,6 +110,45 @@ fn expand_derive(cx: &mut ExtCtxt, continue; } + // RFC #1445. `#[derive(Eq)]` adds a (trusted) + // `#[structural_match]` attribute. + if &tname[..] == "Eq" { + // This span is **very** sensitive and crucial to + // getting the stability behavior we want. What we + // are doing is marking `#[structural_match]` with + // the span of the `#[deriving(Eq)]` attribute + // (the entire attribute, not just the `Eq` part), + // but with the current backtrace. The current + // backtrace will contain a topmost entry that IS + // this `#[deriving(Eq)]` attribute and with the + // "allow-unstable" flag set to true. + // + // Note that we do NOT use the span of the `Eq` + // text itself. You might think this is + // equivalent, because the `Eq` appears within the + // `#[deriving(Eq)]` attribute, and hence we would + // inherit the "allows unstable" from the + // backtrace. But in fact this is not always the + // case. The actual source text that led to + // deriving can be `#[$attr]`, for example, where + // `$attr == deriving(Eq)`. In that case, the + // "#[structural_match]" would be considered to + // originate not from the deriving call but from + // text outside the deriving call, and hence would + // be forbidden from using unstable + // content. + // + // See tests src/run-pass/rfc1445 for + // examples. --nmatsakis + let span = Span { expn_id: cx.backtrace(), .. span }; + assert!(cx.parse_sess.codemap().span_allows_unstable(span)); + debug!("inserting structural_match with span {:?}", span); + let structural_match = intern_and_get_ident("structural_match"); + item.attrs.push(cx.attribute(span, + cx.meta_word(span, + structural_match))); + } + // #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar] item.attrs.push(cx.attribute(titem.span, cx.meta_word(titem.span, intern_and_get_ident(&format!("derive_{}", tname))))); @@ -117,7 +159,9 @@ fn expand_derive(cx: &mut ExtCtxt, }, |a| { cx.span_err(span, "`derive` can only be applied to items"); a - }) + }); + debug!("expand_derive: annotatable output = {:?}", annot); + annot } macro_rules! derive_traits { diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index 97531d4279d4b..f214ecdc3368d 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -24,6 +24,7 @@ #![feature(str_char)] extern crate fmt_macros; +#[macro_use] extern crate log; #[macro_use] extern crate syntax; From 05baf645e47a0ed3893f2413696e56be180249ff Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 11 Mar 2016 13:29:06 -0500 Subject: [PATCH 16/25] do not overwrite spans as eagerly this was required to preserve the span from the #[structural_match] attribute -- but honestly I am not 100% sure if it makes sense. --- src/libsyntax/codemap.rs | 25 ++++++++++++++++++++++ src/libsyntax/ext/expand.rs | 42 +++++++++++++++++++++++++++++++------ 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 804ca6705ecbb..f771ee95bd121 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -1304,6 +1304,31 @@ impl CodeMap { return a; } + /// Check if the backtrace `subtrace` contains `suptrace` as a prefix. + pub fn more_specific_trace(&self, + mut subtrace: ExpnId, + suptrace: ExpnId) + -> bool { + loop { + if subtrace == suptrace { + return true; + } + + let stop = self.with_expn_info(subtrace, |opt_expn_info| { + if let Some(expn_info) = opt_expn_info { + subtrace = expn_info.call_site.expn_id; + false + } else { + true + } + }); + + if stop { + return false; + } + } + } + pub fn record_expansion(&self, expn_info: ExpnInfo) -> ExpnId { let mut expansions = self.expansions.borrow_mut(); expansions.push(expn_info); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 5bfdab791d638..8550617560df3 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -33,7 +33,7 @@ use visit::Visitor; use std_inject; use std::collections::HashSet; - +use std::env; pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { let expr_span = e.span; @@ -1275,11 +1275,41 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } fn new_span(cx: &ExtCtxt, sp: Span) -> Span { - /* this discards information in the case of macro-defining macros */ - Span { - lo: sp.lo, - hi: sp.hi, - expn_id: cx.backtrace(), + debug!("new_span(sp={:?})", sp); + + if cx.codemap().more_specific_trace(sp.expn_id, cx.backtrace()) { + // If the span we are looking at has a backtrace that has more + // detail than our current backtrace, then we keep that + // backtrace. Honestly, I have no idea if this makes sense, + // because I have no idea why we are stripping the backtrace + // below. But the reason I made this change is because, in + // deriving, we were generating attributes with a specific + // backtrace, which was essential for `#[structural_match]` to + // be properly supported, but these backtraces were being + // stripped and replaced with a null backtrace. Sort of + // unclear why this is the case. --nmatsakis + debug!("new_span: keeping trace from {:?} because it is more specific", + sp.expn_id); + sp + } else { + // This discards information in the case of macro-defining macros. + // + // The comment above was originally added in + // b7ec2488ff2f29681fe28691d20fd2c260a9e454 in Feb 2012. I + // *THINK* the reason we are doing this is because we want to + // replace the backtrace of the macro contents with the + // backtrace that contains the macro use. But it's pretty + // unclear to me. --nmatsakis + let sp1 = Span { + lo: sp.lo, + hi: sp.hi, + expn_id: cx.backtrace(), + }; + debug!("new_span({:?}) = {:?}", sp, sp1); + if sp.expn_id.into_u32() == 0 && env::var_os("NDM").is_some() { + panic!("NDM"); + } + sp1 } } From f69eb8efbe5dbc373426bf0ff021b49f37db41cb Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 11 Mar 2016 13:30:32 -0500 Subject: [PATCH 17/25] issue a future-compat lint for constants of invalid type This is a [breaking-change]: according to RFC #1445, constants used as patterns must be of a type that *derives* `Eq`. If you encounter a problem, you are most likely using a constant in an expression where the type of the constant is some struct that does not currently implement `Eq`. Something like the following: ```rust struct SomeType { ... } const SOME_CONST: SomeType = ...; match foo { SOME_CONST => ... } ``` The easiest and most future compatible fix is to annotate the type in question with `#[derive(Eq)]` (note that merely *implementing* `Eq` is not enough, it must be *derived*): ```rust struct SomeType { ... } const SOME_CONST: SomeType = ...; match foo { SOME_CONST => ... } ``` Another good option is to rewrite the match arm to use an `if` condition (this is also particularly good for floating point types, which implement `PartialEq` but not `Eq`): ```rust match foo { c if c == SOME_CONST => ... } ``` Finally, a third alternative is to tag the type with `#[structural_match]`; but this is not recommended, as the attribute is never expected to be stabilized. Please see RFC #1445 for more details. --- src/librustc/lint/builtin.rs | 15 ++++++++++ src/librustc/middle/check_match.rs | 3 +- src/librustc/middle/const_eval.rs | 46 ++++++++++++++++++++++++----- src/librustc_lint/lib.rs | 8 +++++ src/librustc_mir/hair/cx/pattern.rs | 1 + 5 files changed, 64 insertions(+), 9 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index f2371c1819fc4..5d257fc7b2f22 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -136,6 +136,19 @@ declare_lint! { "type parameter default erroneously allowed in invalid location" } +declare_lint! { + pub ILLEGAL_FLOATING_POINT_CONSTANT_PATTERN, + Warn, + "floating-point constants cannot be used in patterns" +} + +declare_lint! { + pub ILLEGAL_STRUCT_OR_ENUM_CONSTANT_PATTERN, + Deny, + "constants of struct or enum type can only be used in a pattern if \ + the struct or enum has `#[derive(Eq)]`" +} + declare_lint! { pub MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT, Deny, @@ -193,6 +206,8 @@ impl LintPass for HardwiredLints { PRIVATE_IN_PUBLIC, INACCESSIBLE_EXTERN_CRATE, INVALID_TYPE_PARAM_DEFAULT, + ILLEGAL_FLOATING_POINT_CONSTANT_PATTERN, + ILLEGAL_STRUCT_OR_ENUM_CONSTANT_PATTERN, MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT, CONST_ERR, RAW_POINTER_DERIVE, diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 77b0995827808..3414d509d953f 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -478,7 +478,7 @@ impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> { Some(Def::Const(did)) => { let substs = Some(self.tcx.node_id_item_substs(pat.id).substs); if let Some((const_expr, _)) = lookup_const_by_id(self.tcx, did, substs) { - match const_expr_to_pat(self.tcx, const_expr, pat.span) { + match const_expr_to_pat(self.tcx, const_expr, pat.id, pat.span) { Ok(new_pat) => { if let Some(ref mut map) = self.renaming_map { // Record any renamings we do here @@ -487,7 +487,6 @@ impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> { new_pat } Err(def_id) => { - // TODO back-compat self.failed = true; self.tcx.sess.span_err( pat.span, diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index af1e9d60be441..dfeb5a5e3f1ab 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -16,6 +16,7 @@ use self::EvalHint::*; use front::map as ast_map; use front::map::blocks::FnLikeNode; +use lint; use middle::cstore::{self, CrateStore, InlinedItem}; use middle::{infer, subst, traits}; use middle::def::Def; @@ -323,13 +324,41 @@ impl ConstVal { } } -pub fn const_expr_to_pat(tcx: &ty::TyCtxt, expr: &Expr, span: Span) +pub fn const_expr_to_pat(tcx: &ty::TyCtxt, expr: &Expr, pat_id: ast::NodeId, span: Span) -> Result, DefId> { + let pat_ty = tcx.expr_ty(expr); + debug!("expr={:?} pat_ty={:?} pat_id={}", expr, pat_ty, pat_id); + match pat_ty.sty { + ty::TyFloat(_) => { + tcx.sess.add_lint( + lint::builtin::ILLEGAL_FLOATING_POINT_CONSTANT_PATTERN, + pat_id, + span, + format!("floating point constants cannot be used in patterns")); + } + ty::TyEnum(adt_def, _) | + ty::TyStruct(adt_def, _) => { + if !tcx.has_attr(adt_def.did, "structural_match") { + tcx.sess.add_lint( + lint::builtin::ILLEGAL_STRUCT_OR_ENUM_CONSTANT_PATTERN, + pat_id, + span, + format!("to use a constant of type `{}` \ + in a pattern, \ + `{}` must be annotated with `#[derive(Eq)]`", + tcx.item_path_str(adt_def.did), + tcx.item_path_str(adt_def.did))); + } + } + _ => { } + } + let pat = match expr.node { hir::ExprTup(ref exprs) => PatKind::Tup(try!(exprs.iter() - .map(|expr| const_expr_to_pat(tcx, &expr, span)) - .collect())), + .map(|expr| const_expr_to_pat(tcx, &expr, + pat_id, span)) + .collect())), hir::ExprCall(ref callee, ref args) => { let def = *tcx.def_map.borrow().get(&callee.id).unwrap(); @@ -347,7 +376,8 @@ pub fn const_expr_to_pat(tcx: &ty::TyCtxt, expr: &Expr, span: Span) _ => unreachable!() }; let pats = try!(args.iter() - .map(|expr| const_expr_to_pat(tcx, &**expr, span)) + .map(|expr| const_expr_to_pat(tcx, &**expr, + pat_id, span)) .collect()); PatKind::TupleStruct(path, Some(pats)) } @@ -359,7 +389,8 @@ pub fn const_expr_to_pat(tcx: &ty::TyCtxt, expr: &Expr, span: Span) span: codemap::DUMMY_SP, node: hir::FieldPat { name: field.name.node, - pat: try!(const_expr_to_pat(tcx, &field.expr, span)), + pat: try!(const_expr_to_pat(tcx, &field.expr, + pat_id, span)), is_shorthand: false, }, })) @@ -369,7 +400,8 @@ pub fn const_expr_to_pat(tcx: &ty::TyCtxt, expr: &Expr, span: Span) hir::ExprVec(ref exprs) => { let pats = try!(exprs.iter() - .map(|expr| const_expr_to_pat(tcx, &expr, span)) + .map(|expr| const_expr_to_pat(tcx, &expr, + pat_id, span)) .collect()); PatKind::Vec(pats, None, hir::HirVec::new()) } @@ -383,7 +415,7 @@ pub fn const_expr_to_pat(tcx: &ty::TyCtxt, expr: &Expr, span: Span) Some(Def::AssociatedConst(def_id)) => { let substs = Some(tcx.node_id_item_substs(expr.id).substs); let (expr, _ty) = lookup_const_by_id(tcx, def_id, substs).unwrap(); - return const_expr_to_pat(tcx, expr, span); + return const_expr_to_pat(tcx, expr, pat_id, span); }, _ => unreachable!(), } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 4288f6258ae0d..9ed21117cebca 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -179,6 +179,14 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { id: LintId::of(OVERLAPPING_INHERENT_IMPLS), reference: "issue #22889 ", }, + FutureIncompatibleInfo { + id: LintId::of(ILLEGAL_FLOATING_POINT_CONSTANT_PATTERN), + reference: "RFC 1445 ", + }, + FutureIncompatibleInfo { + id: LintId::of(ILLEGAL_STRUCT_OR_ENUM_CONSTANT_PATTERN), + reference: "RFC 1445 ", + }, ]); // We have one lint pass defined specially diff --git a/src/librustc_mir/hair/cx/pattern.rs b/src/librustc_mir/hair/cx/pattern.rs index bfb8d1c401aaa..a582a4622a6d0 100644 --- a/src/librustc_mir/hair/cx/pattern.rs +++ b/src/librustc_mir/hair/cx/pattern.rs @@ -92,6 +92,7 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> { Some((const_expr, _const_ty)) => { match const_eval::const_expr_to_pat(self.cx.tcx, const_expr, + pat.id, pat.span) { Ok(pat) => return self.to_pattern(&pat), From 73b4f06b83fd7a7ab4bcc9bf2ac97844f3b27df5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 11 Mar 2016 15:36:24 -0500 Subject: [PATCH 18/25] suppress duplicate lints --- src/librustc/session/mod.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index b198eda181208..e0982656c69bc 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -246,7 +246,13 @@ impl Session { let lint_id = lint::LintId::of(lint); let mut lints = self.lints.borrow_mut(); match lints.get_mut(&id) { - Some(arr) => { arr.push((lint_id, sp, msg)); return; } + Some(arr) => { + let tuple = (lint_id, sp, msg); + if !arr.contains(&tuple) { + arr.push(tuple); + } + return; + } None => {} } lints.insert(id, vec!((lint_id, sp, msg))); From 56ebf2b046fd4f0b9d05a90ff1e8a38b62be0325 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 11 Mar 2016 13:36:46 -0500 Subject: [PATCH 19/25] fallout in existing tests --- src/libstd/num/f32.rs | 7 ++++--- src/libstd/num/f64.rs | 7 ++++--- src/test/compile-fail/issue-6804.rs | 8 ++++++++ src/test/debuginfo/constant-in-match-pattern.rs | 3 +++ src/test/run-pass/associated-const-match-patterns.rs | 1 + src/test/run-pass/empty-struct-braces.rs | 3 +++ src/test/run-pass/issue-12860.rs | 2 -- src/test/run-pass/match-arm-statics.rs | 11 ++++++++++- 8 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index e78d46b22e940..6fc26bb7eed71 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -1152,9 +1152,10 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn asinh(self) -> f32 { - match self { - NEG_INFINITY => NEG_INFINITY, - x => (x + ((x * x) + 1.0).sqrt()).ln(), + if self == NEG_INFINITY { + NEG_INFINITY + } else { + (self + ((self * self) + 1.0).sqrt()).ln() } } diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index cea5a9edd680b..93e5969a275c3 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -1023,9 +1023,10 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn asinh(self) -> f64 { - match self { - NEG_INFINITY => NEG_INFINITY, - x => (x + ((x * x) + 1.0).sqrt()).ln(), + if self == NEG_INFINITY { + NEG_INFINITY + } else { + (self + ((self * self) + 1.0).sqrt()).ln() } } diff --git a/src/test/compile-fail/issue-6804.rs b/src/test/compile-fail/issue-6804.rs index ffab194149e12..1cb5dbccf2105 100644 --- a/src/test/compile-fail/issue-6804.rs +++ b/src/test/compile-fail/issue-6804.rs @@ -24,9 +24,17 @@ fn main() { //~ ERROR compilation successful _ => {}, }; //~^^^ WARNING unmatchable NaN in pattern, use the is_nan method in a guard instead + //~| WARNING floating point constants cannot be used + //~| WARNING this was previously accepted + //~| WARNING floating point constants cannot be used + //~| WARNING this was previously accepted match [x, 1.0] { [NAN, _] => {}, _ => {}, }; //~^^^ WARNING unmatchable NaN in pattern, use the is_nan method in a guard instead + //~| WARNING floating point constants cannot be used + //~| WARNING this was previously accepted + //~| WARNING floating point constants cannot be used + //~| WARNING this was previously accepted } diff --git a/src/test/debuginfo/constant-in-match-pattern.rs b/src/test/debuginfo/constant-in-match-pattern.rs index fb40400a4429c..6974238ac72f2 100644 --- a/src/test/debuginfo/constant-in-match-pattern.rs +++ b/src/test/debuginfo/constant-in-match-pattern.rs @@ -21,15 +21,18 @@ const CONSTANT: u64 = 3; +#[derive(PartialEq, Eq)] struct Struct { a: isize, b: usize, } const STRUCT: Struct = Struct { a: 1, b: 2 }; +#[derive(PartialEq, Eq)] struct TupleStruct(u32); const TUPLE_STRUCT: TupleStruct = TupleStruct(4); +#[derive(PartialEq, Eq)] enum Enum { Variant1(char), Variant2 { a: u8 }, diff --git a/src/test/run-pass/associated-const-match-patterns.rs b/src/test/run-pass/associated-const-match-patterns.rs index 605ca6b65e2cf..01d1b27bfc99f 100644 --- a/src/test/run-pass/associated-const-match-patterns.rs +++ b/src/test/run-pass/associated-const-match-patterns.rs @@ -17,6 +17,7 @@ use empty_struct::XEmpty2 as XFoo; struct Foo; +#[derive(PartialEq, Eq)] enum Bar { Var1, Var2, diff --git a/src/test/run-pass/empty-struct-braces.rs b/src/test/run-pass/empty-struct-braces.rs index 85ae77f20f155..0060150fbece0 100644 --- a/src/test/run-pass/empty-struct-braces.rs +++ b/src/test/run-pass/empty-struct-braces.rs @@ -18,7 +18,10 @@ use empty_struct::*; struct Empty1 {} struct Empty2; + +#[derive(PartialEq, Eq)] struct Empty3 {} + const Empty3: Empty3 = Empty3 {}; enum E { diff --git a/src/test/run-pass/issue-12860.rs b/src/test/run-pass/issue-12860.rs index c854747bcf7bd..5c9ee74472b12 100644 --- a/src/test/run-pass/issue-12860.rs +++ b/src/test/run-pass/issue-12860.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// pretty-expanded FIXME #23616 - #![feature(collections)] extern crate collections; diff --git a/src/test/run-pass/match-arm-statics.rs b/src/test/run-pass/match-arm-statics.rs index 43ff69fe75e6c..9700ed247959b 100644 --- a/src/test/run-pass/match-arm-statics.rs +++ b/src/test/run-pass/match-arm-statics.rs @@ -9,18 +9,24 @@ // except according to those terms. +#[derive(PartialEq, Eq)] struct NewBool(bool); +#[derive(PartialEq, Eq)] enum Direction { North, East, South, West } + +#[derive(PartialEq, Eq)] struct Foo { bar: Option, baz: NewBool } + +#[derive(PartialEq, Eq)] enum EnumWithStructVariants { Variant1(bool), Variant2 { @@ -37,7 +43,7 @@ const VARIANT2_NORTH: EnumWithStructVariants = EnumWithStructVariants::Variant2 dir: Direction::North }; pub mod glfw { - #[derive(Copy, Clone)] + #[derive(Copy, Clone, PartialEq, Eq)] pub struct InputState(usize); pub const RELEASE : InputState = InputState(0); @@ -82,6 +88,7 @@ fn issue_14576() { _ => unreachable!() } + #[derive(PartialEq, Eq)] enum C { D = 3, E = 4 } const F : C = C::D; @@ -89,6 +96,7 @@ fn issue_14576() { } fn issue_13731() { + #[derive(PartialEq, Eq)] enum A { AA(()) } const B: A = A::AA(()); @@ -99,6 +107,7 @@ fn issue_13731() { fn issue_15393() { #![allow(dead_code)] + #[derive(PartialEq, Eq)] struct Flags { bits: usize } From 7f661ec417616ea7036f0cc4aefc7034592a3647 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 11 Mar 2016 13:36:55 -0500 Subject: [PATCH 20/25] new tests for RFC #1445 --- src/test/compile-fail/issue-6804.rs | 4 -- src/test/compile-fail/rfc1445/feature-gate.rs | 36 +++++++++++++++++ .../rfc1445/match-forbidden-without-eq.rs | 39 +++++++++++++++++++ .../rfc1445/eq-allows-match-on-ty-in-macro.rs | 32 +++++++++++++++ src/test/run-pass/rfc1445/eq-allows-match.rs | 26 +++++++++++++ 5 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 src/test/compile-fail/rfc1445/feature-gate.rs create mode 100644 src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs create mode 100644 src/test/run-pass/rfc1445/eq-allows-match-on-ty-in-macro.rs create mode 100644 src/test/run-pass/rfc1445/eq-allows-match.rs diff --git a/src/test/compile-fail/issue-6804.rs b/src/test/compile-fail/issue-6804.rs index 1cb5dbccf2105..f6b7e13c4f5e9 100644 --- a/src/test/compile-fail/issue-6804.rs +++ b/src/test/compile-fail/issue-6804.rs @@ -26,8 +26,6 @@ fn main() { //~ ERROR compilation successful //~^^^ WARNING unmatchable NaN in pattern, use the is_nan method in a guard instead //~| WARNING floating point constants cannot be used //~| WARNING this was previously accepted - //~| WARNING floating point constants cannot be used - //~| WARNING this was previously accepted match [x, 1.0] { [NAN, _] => {}, _ => {}, @@ -35,6 +33,4 @@ fn main() { //~ ERROR compilation successful //~^^^ WARNING unmatchable NaN in pattern, use the is_nan method in a guard instead //~| WARNING floating point constants cannot be used //~| WARNING this was previously accepted - //~| WARNING floating point constants cannot be used - //~| WARNING this was previously accepted } diff --git a/src/test/compile-fail/rfc1445/feature-gate.rs b/src/test/compile-fail/rfc1445/feature-gate.rs new file mode 100644 index 0000000000000..1f2d7819e26d8 --- /dev/null +++ b/src/test/compile-fail/rfc1445/feature-gate.rs @@ -0,0 +1,36 @@ +// 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. + +// Test that structural match is only permitted with a feature gate, +// and that if a feature gate is supplied, it permits the type to be +// used in a match. + +// revisions: with_gate no_gate + +#![allow(dead_code)] +#![deny(future_incompatible)] +#![feature(rustc_attrs)] +#![cfg_attr(with_gate, feature(structural_match))] + +#[structural_match] //[no_gate]~ ERROR semantics of constant patterns is not yet settled +struct Foo { + x: u32 +} + +const FOO: Foo = Foo { x: 0 }; + +#[rustc_error] +fn main() { //[with_gate]~ ERROR compilation successful + let y = Foo { x: 1 }; + match y { + FOO => { } + _ => { } + } +} diff --git a/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs b/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs new file mode 100644 index 0000000000000..b3e465688fdd4 --- /dev/null +++ b/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs @@ -0,0 +1,39 @@ +// 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(dead_code)] +#![deny(future_incompatible)] + +use std::f32; + +#[derive(PartialEq)] +struct Foo { + x: u32 +} + +const FOO: Foo = Foo { x: 0 }; + +fn main() { + let y = Foo { x: 1 }; + match y { + FOO => { } + //~^ ERROR must be annotated with `#[derive(Eq)]` + //~| WARNING will become a hard error + _ => { } + } + + let x = 0.0; + match x { + f32::INFINITY => { } + //~^ ERROR floating point constants cannot be used in patterns + //~| WARNING will become a hard error + _ => { } + } +} diff --git a/src/test/run-pass/rfc1445/eq-allows-match-on-ty-in-macro.rs b/src/test/run-pass/rfc1445/eq-allows-match-on-ty-in-macro.rs new file mode 100644 index 0000000000000..241fe6c6ab1e9 --- /dev/null +++ b/src/test/run-pass/rfc1445/eq-allows-match-on-ty-in-macro.rs @@ -0,0 +1,32 @@ +// 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(dead_code)] + +macro_rules! foo { + (#[$attr:meta] $x:ident) => { + #[$attr] + struct $x { + x: u32 + } + } +} + +foo! { #[derive(PartialEq, Eq)] Foo } + +const FOO: Foo = Foo { x: 0 }; + +fn main() { + let y = Foo { x: 1 }; + match y { + FOO => { } + _ => { } + } +} diff --git a/src/test/run-pass/rfc1445/eq-allows-match.rs b/src/test/run-pass/rfc1445/eq-allows-match.rs new file mode 100644 index 0000000000000..f02a45625c9f6 --- /dev/null +++ b/src/test/run-pass/rfc1445/eq-allows-match.rs @@ -0,0 +1,26 @@ +// 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(dead_code)] + +#[derive(PartialEq, Eq)] +struct Foo { + x: u32 +} + +const FOO: Foo = Foo { x: 0 }; + +fn main() { + let y = Foo { x: 1 }; + match y { + FOO => { } + _ => { } + } +} From 93e44432e1055a529c4923709a6533114bb7820f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 25 Mar 2016 10:02:56 -0400 Subject: [PATCH 21/25] check for both partialeq and eq --- src/librustc/lint/builtin.rs | 2 +- src/librustc/middle/const_eval.rs | 2 +- src/libsyntax_ext/deriving/mod.rs | 81 ++++++++++--------- .../match-requires-both-partialeq-and-eq.rs | 35 ++++++++ 4 files changed, 82 insertions(+), 38 deletions(-) create mode 100644 src/test/compile-fail/rfc1445/match-requires-both-partialeq-and-eq.rs diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 5d257fc7b2f22..879b894562092 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -146,7 +146,7 @@ declare_lint! { pub ILLEGAL_STRUCT_OR_ENUM_CONSTANT_PATTERN, Deny, "constants of struct or enum type can only be used in a pattern if \ - the struct or enum has `#[derive(Eq)]`" + the struct or enum has `#[derive(PartialEq, Eq)]`" } declare_lint! { diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index dfeb5a5e3f1ab..c102822e8c126 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -345,7 +345,7 @@ pub fn const_expr_to_pat(tcx: &ty::TyCtxt, expr: &Expr, pat_id: ast::NodeId, spa span, format!("to use a constant of type `{}` \ in a pattern, \ - `{}` must be annotated with `#[derive(Eq)]`", + `{}` must be annotated with `#[derive(PartialEq, Eq)]`", tcx.item_path_str(adt_def.did), tcx.item_path_str(adt_def.did))); } diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index dbd3cbd4fbaa3..1774167e83000 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -92,6 +92,9 @@ fn expand_derive(cx: &mut ExtCtxt, cx.span_warn(mitem.span, "empty trait list in `derive`"); } + let mut found_partial_eq = false; + let mut found_eq = false; + for titem in traits.iter().rev() { let tname = match titem.node { MetaItemKind::Word(ref tname) => tname, @@ -110,43 +113,10 @@ fn expand_derive(cx: &mut ExtCtxt, continue; } - // RFC #1445. `#[derive(Eq)]` adds a (trusted) - // `#[structural_match]` attribute. if &tname[..] == "Eq" { - // This span is **very** sensitive and crucial to - // getting the stability behavior we want. What we - // are doing is marking `#[structural_match]` with - // the span of the `#[deriving(Eq)]` attribute - // (the entire attribute, not just the `Eq` part), - // but with the current backtrace. The current - // backtrace will contain a topmost entry that IS - // this `#[deriving(Eq)]` attribute and with the - // "allow-unstable" flag set to true. - // - // Note that we do NOT use the span of the `Eq` - // text itself. You might think this is - // equivalent, because the `Eq` appears within the - // `#[deriving(Eq)]` attribute, and hence we would - // inherit the "allows unstable" from the - // backtrace. But in fact this is not always the - // case. The actual source text that led to - // deriving can be `#[$attr]`, for example, where - // `$attr == deriving(Eq)`. In that case, the - // "#[structural_match]" would be considered to - // originate not from the deriving call but from - // text outside the deriving call, and hence would - // be forbidden from using unstable - // content. - // - // See tests src/run-pass/rfc1445 for - // examples. --nmatsakis - let span = Span { expn_id: cx.backtrace(), .. span }; - assert!(cx.parse_sess.codemap().span_allows_unstable(span)); - debug!("inserting structural_match with span {:?}", span); - let structural_match = intern_and_get_ident("structural_match"); - item.attrs.push(cx.attribute(span, - cx.meta_word(span, - structural_match))); + found_eq = true; + } else if &tname[..] == "PartialEq" { + found_partial_eq = true; } // #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar] @@ -154,6 +124,45 @@ fn expand_derive(cx: &mut ExtCtxt, intern_and_get_ident(&format!("derive_{}", tname))))); } + // RFC #1445. `#[derive(PartialEq, Eq)]` adds a (trusted) + // `#[structural_match]` attribute. + if found_partial_eq && found_eq { + // This span is **very** sensitive and crucial to + // getting the stability behavior we want. What we are + // doing is marking `#[structural_match]` with the + // span of the `#[deriving(...)]` attribute (the + // entire attribute, not just the `PartialEq` or `Eq` + // part), but with the current backtrace. The current + // backtrace will contain a topmost entry that IS this + // `#[deriving(...)]` attribute and with the + // "allow-unstable" flag set to true. + // + // Note that we do NOT use the span of the `Eq` + // text itself. You might think this is + // equivalent, because the `Eq` appears within the + // `#[deriving(Eq)]` attribute, and hence we would + // inherit the "allows unstable" from the + // backtrace. But in fact this is not always the + // case. The actual source text that led to + // deriving can be `#[$attr]`, for example, where + // `$attr == deriving(Eq)`. In that case, the + // "#[structural_match]" would be considered to + // originate not from the deriving call but from + // text outside the deriving call, and hence would + // be forbidden from using unstable + // content. + // + // See tests src/run-pass/rfc1445 for + // examples. --nmatsakis + let span = Span { expn_id: cx.backtrace(), .. span }; + assert!(cx.parse_sess.codemap().span_allows_unstable(span)); + debug!("inserting structural_match with span {:?}", span); + let structural_match = intern_and_get_ident("structural_match"); + item.attrs.push(cx.attribute(span, + cx.meta_word(span, + structural_match))); + } + item }) }, |a| { diff --git a/src/test/compile-fail/rfc1445/match-requires-both-partialeq-and-eq.rs b/src/test/compile-fail/rfc1445/match-requires-both-partialeq-and-eq.rs new file mode 100644 index 0000000000000..029df08ebc37a --- /dev/null +++ b/src/test/compile-fail/rfc1445/match-requires-both-partialeq-and-eq.rs @@ -0,0 +1,35 @@ +// 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(dead_code)] +#![deny(future_incompatible)] + +#[derive(Eq)] +struct Foo { + x: u32 +} + +impl PartialEq for Foo { + fn eq(&self, _: &Foo) -> bool { + false // ha ha sucker! + } +} + +const FOO: Foo = Foo { x: 0 }; + +fn main() { + let y = Foo { x: 1 }; + match y { + FOO => { } + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARNING will become a hard error + _ => { } + } +} From e539b74f5438651c5d46e5c707d1dcf480faf0db Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 25 Mar 2016 13:10:32 -0400 Subject: [PATCH 22/25] use new visitor to erase regions --- src/librustc/mir/visit.rs | 13 ++- src/librustc_mir/transform/erase_regions.rs | 91 ++------------------- 2 files changed, 17 insertions(+), 87 deletions(-) diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index bc0056b0af02f..8c180ecb233b6 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -614,9 +614,15 @@ macro_rules! make_mir_visitor { fn super_constant(&mut self, constant: & $($mutability)* Constant<'tcx>) { - self.visit_span(& $($mutability)* constant.span); - self.visit_ty(& $($mutability)* constant.ty); - self.visit_literal(& $($mutability)* constant.literal); + let Constant { + ref $($mutability)* span, + ref $($mutability)* ty, + ref $($mutability)* literal, + } = *constant; + + self.visit_span(span); + self.visit_ty(ty); + self.visit_literal(literal); } fn super_typed_const_val(&mut self, @@ -626,6 +632,7 @@ macro_rules! make_mir_visitor { ref $($mutability)* ty, ref $($mutability)* value, } = *constant; + self.visit_span(span); self.visit_ty(ty); self.visit_const_usize(value); diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index d8aa0d9b72503..6ea684c2f5c68 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -12,7 +12,8 @@ //! We want to do this once just before trans, so trans does not have to take //! care erasing regions all over the place. -use rustc::middle::ty::{self, TyCtxt}; +use rustc::middle::subst::Substs; +use rustc::middle::ty::{Ty, TyCtxt}; use rustc::mir::repr::*; use rustc::mir::visit::MutVisitor; use rustc::mir::transform::{MirPass, Pass}; @@ -28,94 +29,16 @@ impl<'a, 'tcx> EraseRegionsVisitor<'a, 'tcx> { tcx: tcx } } - - fn erase_regions_return_ty(&mut self, fn_output: &mut ty::FnOutput<'tcx>) { - match *fn_output { - ty::FnConverging(ref mut ty) => { - *ty = self.tcx.erase_regions(ty); - }, - ty::FnDiverging => {} - } - } - - fn erase_regions_tys<'b, T>(&mut self, tys: T) - where T: Iterator>, - 'tcx: 'b - { - for ty in tys { - *ty = self.tcx.erase_regions(ty); - } - } } impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> { - fn visit_mir(&mut self, mir: &mut Mir<'tcx>) { - self.erase_regions_return_ty(&mut mir.return_ty); - self.erase_regions_tys(mir.var_decls.iter_mut().map(|d| &mut d.ty)); - self.erase_regions_tys(mir.arg_decls.iter_mut().map(|d| &mut d.ty)); - self.erase_regions_tys(mir.temp_decls.iter_mut().map(|d| &mut d.ty)); - self.super_mir(mir); - } - - fn visit_terminator(&mut self, bb: BasicBlock, terminator: &mut Terminator<'tcx>) { - match terminator.kind { - TerminatorKind::Goto { .. } | - TerminatorKind::Resume | - TerminatorKind::Return | - TerminatorKind::If { .. } | - TerminatorKind::Switch { .. } | - TerminatorKind::Drop { .. } | - TerminatorKind::Call { .. } => { - /* nothing to do */ - }, - TerminatorKind::SwitchInt { ref mut switch_ty, .. } => { - *switch_ty = self.tcx.erase_regions(switch_ty); - }, - } - self.super_terminator(bb, terminator); - } - - fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>) { - match *rvalue { - Rvalue::Use(_) | - Rvalue::Len(_) | - Rvalue::BinaryOp(_, _, _) | - Rvalue::UnaryOp(_, _) | - Rvalue::Slice { input: _, from_start: _, from_end: _ } | - Rvalue::InlineAsm {..} => {}, - - Rvalue::Repeat(_, ref mut value) => value.ty = self.tcx.erase_regions(&value.ty), - Rvalue::Ref(ref mut region, _, _) => *region = ty::ReStatic, - Rvalue::Cast(_, _, ref mut ty) => *ty = self.tcx.erase_regions(ty), - Rvalue::Box(ref mut ty) => *ty = self.tcx.erase_regions(ty), - - - Rvalue::Aggregate(AggregateKind::Vec, _) | - Rvalue::Aggregate(AggregateKind::Tuple, _) => {}, - Rvalue::Aggregate(AggregateKind::Adt(_, _, ref mut substs), _) => - *substs = self.tcx.mk_substs(self.tcx.erase_regions(*substs)), - Rvalue::Aggregate(AggregateKind::Closure(def_id, ref mut closure_substs), _) => { - let cloned = Box::new(closure_substs.clone()); - let ty = self.tcx.mk_closure_from_closure_substs(def_id, cloned); - let erased = self.tcx.erase_regions(&ty); - *closure_substs = match erased.sty { - ty::TyClosure(_, ref closure_substs) => &*closure_substs, - _ => unreachable!() - }; - } - } - self.super_rvalue(rvalue); + fn visit_ty(&mut self, ty: &mut Ty<'tcx>) { + let old_ty = *ty; + *ty = self.tcx.erase_regions(&old_ty); } - fn visit_constant(&mut self, constant: &mut Constant<'tcx>) { - constant.ty = self.tcx.erase_regions(&constant.ty); - match constant.literal { - Literal::Item { ref mut substs, .. } => { - *substs = self.tcx.mk_substs(self.tcx.erase_regions(substs)); - } - Literal::Value { .. } => { /* nothing to do */ } - } - self.super_constant(constant); + fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>) { + *substs = self.tcx.mk_substs(self.tcx.erase_regions(*substs)); } } From 944dc4aa654633c0b027fa6fe30f5ed0690a73b9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 25 Mar 2016 14:39:24 -0400 Subject: [PATCH 23/25] fix cargo.toml for new dependency --- src/libsyntax_ext/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libsyntax_ext/Cargo.toml b/src/libsyntax_ext/Cargo.toml index e137815cd32fd..671f3e4a7e330 100644 --- a/src/libsyntax_ext/Cargo.toml +++ b/src/libsyntax_ext/Cargo.toml @@ -10,4 +10,5 @@ crate-type = ["dylib"] [dependencies] fmt_macros = { path = "../libfmt_macros" } +log = { path = "../liblog" } syntax = { path = "../libsyntax" } From 2536ae55a4fe3bb9d96ddd53e790631e76a6a11b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 25 Mar 2016 15:14:45 -0400 Subject: [PATCH 24/25] fix error message --- src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs b/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs index b3e465688fdd4..c573e3e8e28b2 100644 --- a/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs +++ b/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs @@ -24,7 +24,7 @@ fn main() { let y = Foo { x: 1 }; match y { FOO => { } - //~^ ERROR must be annotated with `#[derive(Eq)]` + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` //~| WARNING will become a hard error _ => { } } From 6c10866b0241b6f37f1f5e30eaed3ac316b63ea4 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 26 Mar 2016 11:25:54 +0530 Subject: [PATCH 25/25] Fixup #32476 --- src/libstd/sys/windows/thread.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs index 6908775e86fc1..0383e92c79ec7 100644 --- a/src/libstd/sys/windows/thread.rs +++ b/src/libstd/sys/windows/thread.rs @@ -12,6 +12,7 @@ use prelude::v1::*; use alloc::boxed::FnBox; use io; +use ffi::CStr; use mem; use libc::c_void; use ptr;