about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-03-09 03:59:42 +0000
committerbors <bors@rust-lang.org>2018-03-09 03:59:42 +0000
commit2079a084df08c38eb4dbfc5c8de5c0245170c3d9 (patch)
tree0fcabcc7de8c86c92497c331b5be41f45f79a579
parent604d4ce7577b07b73d115c94fbd8007c1d9c9335 (diff)
parentb65b171f4433eb14b317c877ac84e4455caec837 (diff)
downloadrust-2079a084df08c38eb4dbfc5c8de5c0245170c3d9.tar.gz
rust-2079a084df08c38eb4dbfc5c8de5c0245170c3d9.zip
Auto merge of #48860 - Manishearth:rollup, r=Manishearth
Rollup of 5 pull requests

- Successful merges: #48527, #48588, #48801, #48856, #48857
- Failed merges:
-rw-r--r--src/Cargo.lock12
-rw-r--r--src/librustc/lint/builtin.rs6
-rw-r--r--src/librustc/lint/context.rs5
-rw-r--r--src/librustc/lint/mod.rs12
-rw-r--r--src/librustc/session/config.rs57
-rw-r--r--src/librustc/session/mod.rs3
-rw-r--r--src/librustc_driver/driver.rs4
-rw-r--r--src/librustc_errors/Cargo.toml2
-rw-r--r--src/librustc_errors/emitter.rs257
-rw-r--r--src/librustc_errors/lib.rs31
-rw-r--r--src/librustc_lint/builtin.rs2
-rw-r--r--src/librustc_lint/lib.rs16
-rw-r--r--src/libstd/macros.rs4
-rw-r--r--src/libsyntax/config.rs5
-rw-r--r--src/libsyntax/epoch.rs69
-rw-r--r--src/libsyntax/feature_gate.rs387
-rw-r--r--src/libsyntax/lib.rs2
-rw-r--r--src/libsyntax/parse/parser.rs44
-rw-r--r--src/test/run-pass/epoch-gate-feature.rs21
-rw-r--r--src/test/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.rs97
-rw-r--r--src/test/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr38
-rw-r--r--src/test/ui/inference-variable-behind-raw-pointer.stderr2
22 files changed, 640 insertions, 436 deletions
diff --git a/src/Cargo.lock b/src/Cargo.lock
index 34c077a6f7a..646ddf1a744 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -78,7 +78,7 @@ dependencies = [
 
 [[package]]
 name = "atty"
-version = "0.2.6"
+version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -179,7 +179,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 name = "cargo"
 version = "0.26.0"
 dependencies = [
- "atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "bufstream 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "cargotest 0.1.0",
  "core-foundation 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -289,7 +289,7 @@ version = "2.29.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -645,7 +645,7 @@ name = "env_logger"
 version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1923,9 +1923,11 @@ dependencies = [
 name = "rustc_errors"
 version = "0.0.0"
 dependencies = [
+ "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc_data_structures 0.0.0",
  "serialize 0.0.0",
  "syntax_pos 0.0.0",
+ "termcolor 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -2843,7 +2845,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455"
 "checksum ar 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "35c7a5669cb64f085739387e1308b74e6d44022464b7f1b63bbd4ceb6379ec31"
 "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
-"checksum atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8352656fd42c30a0c3c89d26dea01e3b77c0ab2af18230835c15e2e13cd51859"
+"checksum atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "af80143d6f7608d746df1520709e5d141c96f240b0e62b0aa41bdfb53374d9d4"
 "checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2"
 "checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661"
 "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index a951265d458..8e1f76c5018 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -17,7 +17,6 @@
 use errors::DiagnosticBuilder;
 use lint::{LintPass, LateLintPass, LintArray};
 use session::Session;
-use session::config::Epoch;
 use syntax::codemap::Span;
 
 declare_lint! {
@@ -264,9 +263,8 @@ declare_lint! {
 
 declare_lint! {
     pub BARE_TRAIT_OBJECT,
-    Warn,
-    "suggest using `dyn Trait` for trait objects",
-    Epoch::Epoch2018
+    Allow,
+    "suggest using `dyn Trait` for trait objects"
 }
 
 declare_lint! {
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index bfd2034dd6c..b1e28f729ed 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -42,6 +42,7 @@ use util::nodemap::FxHashMap;
 use std::default::Default as StdDefault;
 use std::cell::{Ref, RefCell};
 use syntax::ast;
+use syntax::epoch;
 use syntax_pos::{MultiSpan, Span};
 use errors::DiagnosticBuilder;
 use hir;
@@ -105,7 +106,7 @@ pub struct FutureIncompatibleInfo {
     pub reference: &'static str,
     /// If this is an epoch fixing lint, the epoch in which
     /// this lint becomes obsolete
-    pub epoch: Option<config::Epoch>,
+    pub epoch: Option<epoch::Epoch>,
 }
 
 /// The target of the `by_name` map, which accounts for renaming/deprecation.
@@ -201,7 +202,7 @@ impl LintStore {
                                         sess: Option<&Session>,
                                         lints: Vec<FutureIncompatibleInfo>) {
 
-        for epoch in config::ALL_EPOCHS {
+        for epoch in epoch::ALL_EPOCHS {
             let lints = lints.iter().filter(|f| f.epoch == Some(*epoch)).map(|f| f.id)
                              .collect::<Vec<_>>();
             if !lints.is_empty() {
diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs
index affd02aa518..668e099ebab 100644
--- a/src/librustc/lint/mod.rs
+++ b/src/librustc/lint/mod.rs
@@ -38,10 +38,11 @@ use hir::def_id::{CrateNum, LOCAL_CRATE};
 use hir::intravisit::{self, FnKind};
 use hir;
 use lint::builtin::BuiltinLintDiagnostics;
-use session::{config, Session, DiagnosticMessageId};
+use session::{Session, DiagnosticMessageId};
 use std::hash;
 use syntax::ast;
 use syntax::codemap::MultiSpan;
+use syntax::epoch::Epoch;
 use syntax::symbol::Symbol;
 use syntax::visit as ast_visit;
 use syntax_pos::Span;
@@ -77,7 +78,7 @@ pub struct Lint {
     pub desc: &'static str,
 
     /// Deny lint after this epoch
-    pub epoch_deny: Option<config::Epoch>,
+    pub epoch_deny: Option<Epoch>,
 }
 
 impl Lint {
@@ -492,9 +493,14 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
     // Check for future incompatibility lints and issue a stronger warning.
     let lints = sess.lint_store.borrow();
     if let Some(future_incompatible) = lints.future_incompatible(LintId::of(lint)) {
+        let future = if let Some(epoch) = future_incompatible.epoch {
+            format!("the {} epoch", epoch)
+        } else {
+            "a future release".to_owned()
+        };
         let explanation = format!("this was previously accepted by the compiler \
                                    but is being phased out; \
-                                   it will become a hard error in a future release!");
+                                   it will become a hard error in {}!", future);
         let citation = format!("for more information, see {}",
                                future_incompatible.reference);
         err.warn(&explanation);
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 75b4409695e..1c5cfa87ef4 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -28,6 +28,7 @@ use middle::cstore;
 
 use syntax::ast::{self, IntTy, UintTy};
 use syntax::codemap::{FileName, FilePathMapping};
+use syntax::epoch::Epoch;
 use syntax::parse::token;
 use syntax::parse;
 use syntax::symbol::Symbol;
@@ -111,59 +112,6 @@ pub enum OutputType {
     DepInfo,
 }
 
-/// The epoch of the compiler (RFC 2052)
-#[derive(Clone, Copy, Hash, PartialOrd, Ord, Eq, PartialEq, Debug)]
-#[non_exhaustive]
-pub enum Epoch {
-    // epochs must be kept in order, newest to oldest
-    /// The 2015 epoch
-    Epoch2015,
-    /// The 2018 epoch
-    Epoch2018,
-    // when adding new epochs, be sure to update:
-    //
-    // - the list in the `parse_epoch` static
-    // - the match in the `parse_epoch` function
-    // - add a `rust_####()` function to the session
-    // - update the enum in Cargo's sources as well
-    //
-    // When -Zepoch becomes --epoch, there will
-    // also be a check for the epoch being nightly-only
-    // somewhere. That will need to be updated
-    // whenever we're stabilizing/introducing a new epoch
-    // as well as changing the default Cargo template.
-}
-
-pub const ALL_EPOCHS: &[Epoch] = &[Epoch::Epoch2015, Epoch::Epoch2018];
-
-impl ToString for Epoch {
-    fn to_string(&self) -> String {
-        match *self {
-            Epoch::Epoch2015 => "2015".into(),
-            Epoch::Epoch2018 => "2018".into(),
-        }
-    }
-}
-
-impl Epoch {
-    pub fn lint_name(&self) -> &'static str {
-        match *self {
-            Epoch::Epoch2015 => "epoch_2015",
-            Epoch::Epoch2018 => "epoch_2018",
-        }
-    }
-}
-
-impl str::FromStr for Epoch {
-    type Err = ();
-    fn from_str(s: &str) -> Result<Self, ()> {
-        match s {
-            "2015" => Ok(Epoch::Epoch2015),
-            "2018" => Ok(Epoch::Epoch2018),
-            _ => Err(()),
-        }
-    }
-}
 
 impl_stable_hash_for!(enum self::OutputType {
     Bitcode,
@@ -829,9 +777,10 @@ macro_rules! options {
 
     #[allow(dead_code)]
     mod $mod_set {
-        use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer, Lto, Epoch};
+        use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer, Lto};
         use rustc_back::{LinkerFlavor, PanicStrategy, RelroLevel};
         use std::path::PathBuf;
+        use syntax::epoch::Epoch;
 
         $(
             pub fn $opt(cg: &mut $struct_name, v: Option<&str>) -> bool {
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index a355a1ca501..cdbbcf6a8dd 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -20,7 +20,7 @@ use lint::builtin::BuiltinLintDiagnostics;
 use middle::allocator::AllocatorKind;
 use middle::dependency_format;
 use session::search_paths::PathKind;
-use session::config::{DebugInfoLevel, Epoch, OutputType};
+use session::config::{DebugInfoLevel, OutputType};
 use ty::tls;
 use util::nodemap::{FxHashMap, FxHashSet};
 use util::common::{duration_to_secs_str, ErrorReported};
@@ -30,6 +30,7 @@ use rustc_data_structures::sync::Lrc;
 use syntax::ast::NodeId;
 use errors::{self, DiagnosticBuilder, DiagnosticId};
 use errors::emitter::{Emitter, EmitterWriter};
+use syntax::epoch::Epoch;
 use syntax::json::JsonEmitter;
 use syntax::feature_gate;
 use syntax::symbol::Symbol;
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index c9cf3f3b81f..f020f86b686 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -647,7 +647,9 @@ pub fn phase_2_configure_and_expand_inner<'a, F>(sess: &'a Session,
 {
     let time_passes = sess.time_passes();
 
-    let (mut krate, features) = syntax::config::features(krate, &sess.parse_sess, sess.opts.test);
+    let (mut krate, features) = syntax::config::features(krate, &sess.parse_sess,
+                                                         sess.opts.test,
+                                                         sess.opts.debugging_opts.epoch);
     // these need to be set "early" so that expansion sees `quote` if enabled.
     sess.init_features(features);
 
diff --git a/src/librustc_errors/Cargo.toml b/src/librustc_errors/Cargo.toml
index 3e15af7558d..e412d1749d1 100644
--- a/src/librustc_errors/Cargo.toml
+++ b/src/librustc_errors/Cargo.toml
@@ -13,3 +13,5 @@ serialize = { path = "../libserialize" }
 syntax_pos = { path = "../libsyntax_pos" }
 rustc_data_structures = { path = "../librustc_data_structures" }
 unicode-width = "0.1.4"
+atty = "0.2"
+termcolor = "0.3"
diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs
index 33fce7b1968..f481b36daa3 100644
--- a/src/librustc_errors/emitter.rs
+++ b/src/librustc_errors/emitter.rs
@@ -17,12 +17,14 @@ use snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, StyledStrin
 use styled_buffer::StyledBuffer;
 
 use rustc_data_structures::sync::Lrc;
+use atty;
 use std::borrow::Cow;
 use std::io::prelude::*;
 use std::io;
-use term;
 use std::collections::{HashMap, HashSet};
 use std::cmp::min;
+use termcolor::{StandardStream, ColorChoice, ColorSpec, BufferWriter};
+use termcolor::{WriteColor, Color, Buffer};
 use unicode_width;
 
 const ANONYMIZED_LINE_NUM: &str = "LL";
@@ -95,11 +97,14 @@ pub enum ColorConfig {
 }
 
 impl ColorConfig {
-    fn use_color(&self) -> bool {
+    fn to_color_choice(&self) -> ColorChoice {
         match *self {
-            ColorConfig::Always => true,
-            ColorConfig::Never => false,
-            ColorConfig::Auto => stderr_isatty(),
+            ColorConfig::Always => ColorChoice::Always,
+            ColorConfig::Never => ColorChoice::Never,
+            ColorConfig::Auto if atty::is(atty::Stream::Stderr) => {
+                ColorChoice::Auto
+            }
+            ColorConfig::Auto => ColorChoice::Never,
         }
     }
 }
@@ -123,25 +128,26 @@ impl Drop for EmitterWriter {
     fn drop(&mut self) {
         if !self.short_message && !self.error_codes.is_empty() {
             let mut error_codes = self.error_codes.clone().into_iter().collect::<Vec<_>>();
+            let mut dst = self.dst.writable();
             error_codes.sort();
             if error_codes.len() > 1 {
                 let limit = if error_codes.len() > 9 { 9 } else { error_codes.len() };
-                writeln!(self.dst,
+                writeln!(dst,
                          "You've got a few errors: {}{}",
                          error_codes[..limit].join(", "),
                          if error_codes.len() > 9 { "..." } else { "" }
                         ).expect("failed to give tips...");
-                writeln!(self.dst,
+                writeln!(dst,
                          "If you want more information on an error, try using \
                           \"rustc --explain {}\"",
                          &error_codes[0]).expect("failed to give tips...");
             } else {
-                writeln!(self.dst,
+                writeln!(dst,
                          "If you want more information on this error, try using \
                           \"rustc --explain {}\"",
                          &error_codes[0]).expect("failed to give tips...");
             }
-            self.dst.flush().expect("failed to emit errors");
+            dst.flush().expect("failed to emit errors");
         }
     }
 }
@@ -152,25 +158,14 @@ impl EmitterWriter {
                   short_message: bool,
                   teach: bool)
                   -> EmitterWriter {
-        if color_config.use_color() {
-            let dst = Destination::from_stderr();
-            EmitterWriter {
-                dst,
-                cm: code_map,
-                short_message,
-                teach,
-                error_codes: HashSet::new(),
-                ui_testing: false,
-            }
-        } else {
-            EmitterWriter {
-                dst: Raw(Box::new(io::stderr())),
-                cm: code_map,
-                short_message,
-                teach,
-                error_codes: HashSet::new(),
-                ui_testing: false,
-            }
+        let dst = Destination::from_stderr(color_config);
+        EmitterWriter {
+            dst,
+            cm: code_map,
+            short_message,
+            teach,
+            error_codes: HashSet::new(),
+            ui_testing: false,
         }
     }
 
@@ -1356,10 +1351,12 @@ impl EmitterWriter {
             }
             Err(e) => panic!("failed to emit error: {}", e),
         }
-        match write!(&mut self.dst, "\n") {
+
+        let mut dst = self.dst.writable();
+        match write!(dst, "\n") {
             Err(e) => panic!("failed to emit error: {}", e),
             _ => {
-                match self.dst.flush() {
+                match dst.flush() {
                     Err(e) => panic!("failed to emit error: {}", e),
                     _ => (),
                 }
@@ -1424,6 +1421,8 @@ fn emit_to_destination(rendered_buffer: &Vec<Vec<StyledString>>,
                        -> io::Result<()> {
     use lock;
 
+    let mut dst = dst.writable();
+
     // In order to prevent error message interleaving, where multiple error lines get intermixed
     // when multiple compiler processes error simultaneously, we emit errors with additional
     // steps.
@@ -1444,7 +1443,7 @@ fn emit_to_destination(rendered_buffer: &Vec<Vec<StyledString>>,
             if !short_message && part.text.len() == 12 && part.text.starts_with("error[E") {
                 error_codes.insert(part.text[6..11].to_owned());
             }
-            dst.reset_attrs()?;
+            dst.reset()?;
         }
         if !short_message {
             write!(dst, "\n")?;
@@ -1454,180 +1453,136 @@ fn emit_to_destination(rendered_buffer: &Vec<Vec<StyledString>>,
     Ok(())
 }
 
-#[cfg(unix)]
-fn stderr_isatty() -> bool {
-    use libc;
-    unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
-}
-#[cfg(windows)]
-fn stderr_isatty() -> bool {
-    type DWORD = u32;
-    type BOOL = i32;
-    type HANDLE = *mut u8;
-    const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD;
-    extern "system" {
-        fn GetStdHandle(which: DWORD) -> HANDLE;
-        fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: *mut DWORD) -> BOOL;
-    }
-    unsafe {
-        let handle = GetStdHandle(STD_ERROR_HANDLE);
-        let mut out = 0;
-        GetConsoleMode(handle, &mut out) != 0
-    }
-}
-
-pub type BufferedStderr = term::Terminal<Output = BufferedWriter> + Send;
-
 pub enum Destination {
-    Terminal(Box<term::StderrTerminal>),
-    BufferedTerminal(Box<BufferedStderr>),
+    Terminal(StandardStream),
+    Buffered(BufferWriter),
     Raw(Box<Write + Send>),
 }
 
-/// Buffered writer gives us a way on Unix to buffer up an entire error message before we output
-/// it.  This helps to prevent interleaving of multiple error messages when multiple compiler
-/// processes error simultaneously
-pub struct BufferedWriter {
-    buffer: Vec<u8>,
-}
-
-impl BufferedWriter {
-    // note: we use _new because the conditional compilation at its use site may make this
-    // this function unused on some platforms
-    fn _new() -> BufferedWriter {
-        BufferedWriter { buffer: vec![] }
-    }
-}
-
-impl Write for BufferedWriter {
-    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        for b in buf {
-            self.buffer.push(*b);
-        }
-        Ok(buf.len())
-    }
-    fn flush(&mut self) -> io::Result<()> {
-        let mut stderr = io::stderr();
-        let result = stderr.write_all(&self.buffer)
-                           .and_then(|_| stderr.flush());
-        self.buffer.clear();
-        result
-    }
+pub enum WritableDst<'a> {
+    Terminal(&'a mut StandardStream),
+    Buffered(&'a mut BufferWriter, Buffer),
+    Raw(&'a mut Box<Write + Send>),
 }
 
 impl Destination {
-    #[cfg(not(windows))]
-    /// When not on Windows, prefer the buffered terminal so that we can buffer an entire error
-    /// to be emitted at one time.
-    fn from_stderr() -> Destination {
-        let stderr: Option<Box<BufferedStderr>> =
-            term::TerminfoTerminal::new(BufferedWriter::_new())
-                .map(|t| Box::new(t) as Box<BufferedStderr>);
-
-        match stderr {
-            Some(t) => BufferedTerminal(t),
-            None => Raw(Box::new(io::stderr())),
+    fn from_stderr(color: ColorConfig) -> Destination {
+        let choice = color.to_color_choice();
+        // On Windows we'll be performing global synchronization on the entire
+        // system for emitting rustc errors, so there's no need to buffer
+        // anything.
+        //
+        // On non-Windows we rely on the atomicity of `write` to ensure errors
+        // don't get all jumbled up.
+        if cfg!(windows) {
+            Terminal(StandardStream::stderr(choice))
+        } else {
+            Buffered(BufferWriter::stderr(choice))
         }
     }
 
-    #[cfg(windows)]
-    /// Return a normal, unbuffered terminal when on Windows.
-    fn from_stderr() -> Destination {
-        let stderr: Option<Box<term::StderrTerminal>> = term::TerminfoTerminal::new(io::stderr())
-            .map(|t| Box::new(t) as Box<term::StderrTerminal>)
-            .or_else(|| {
-                term::WinConsole::new(io::stderr())
-                    .ok()
-                    .map(|t| Box::new(t) as Box<term::StderrTerminal>)
-            });
-
-        match stderr {
-            Some(t) => Terminal(t),
-            None => Raw(Box::new(io::stderr())),
+    fn writable<'a>(&'a mut self) -> WritableDst<'a> {
+        match *self {
+            Destination::Terminal(ref mut t) => WritableDst::Terminal(t),
+            Destination::Buffered(ref mut t) => {
+                let buf = t.buffer();
+                WritableDst::Buffered(t, buf)
+            }
+            Destination::Raw(ref mut t) => WritableDst::Raw(t),
         }
     }
+}
 
+impl<'a> WritableDst<'a> {
     fn apply_style(&mut self, lvl: Level, style: Style) -> io::Result<()> {
+        let mut spec = ColorSpec::new();
         match style {
             Style::LineAndColumn => {}
             Style::LineNumber => {
-                self.start_attr(term::Attr::Bold)?;
+                spec.set_bold(true);
+                spec.set_intense(true);
                 if cfg!(windows) {
-                    self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_CYAN))?;
+                    spec.set_fg(Some(Color::Cyan));
                 } else {
-                    self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?;
+                    spec.set_fg(Some(Color::Blue));
                 }
             }
             Style::Quotation => {}
             Style::OldSchoolNoteText | Style::HeaderMsg => {
-                self.start_attr(term::Attr::Bold)?;
+                spec.set_bold(true);
                 if cfg!(windows) {
-                    self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_WHITE))?;
+                    spec.set_intense(true)
+                        .set_fg(Some(Color::White));
                 }
             }
             Style::UnderlinePrimary | Style::LabelPrimary => {
-                self.start_attr(term::Attr::Bold)?;
-                self.start_attr(term::Attr::ForegroundColor(lvl.color()))?;
+                spec = lvl.color();
+                spec.set_bold(true);
             }
             Style::UnderlineSecondary |
             Style::LabelSecondary => {
-                self.start_attr(term::Attr::Bold)?;
+                spec.set_bold(true)
+                    .set_intense(true);
                 if cfg!(windows) {
-                    self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_CYAN))?;
+                    spec.set_fg(Some(Color::Cyan));
                 } else {
-                    self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?;
+                    spec.set_fg(Some(Color::Blue));
                 }
             }
             Style::NoStyle => {}
-            Style::Level(l) => {
-                self.start_attr(term::Attr::Bold)?;
-                self.start_attr(term::Attr::ForegroundColor(l.color()))?;
+            Style::Level(lvl) => {
+                spec = lvl.color();
+                spec.set_bold(true);
+            }
+            Style::Highlight => {
+                spec.set_bold(true);
             }
-            Style::Highlight => self.start_attr(term::Attr::Bold)?,
         }
-        Ok(())
+        self.set_color(&spec)
     }
 
-    fn start_attr(&mut self, attr: term::Attr) -> io::Result<()> {
+    fn set_color(&mut self, color: &ColorSpec) -> io::Result<()> {
         match *self {
-            Terminal(ref mut t) => {
-                t.attr(attr)?;
-            }
-            BufferedTerminal(ref mut t) => {
-                t.attr(attr)?;
-            }
-            Raw(_) => {}
+            WritableDst::Terminal(ref mut t) => t.set_color(color),
+            WritableDst::Buffered(_, ref mut t) => t.set_color(color),
+            WritableDst::Raw(_) => Ok(())
         }
-        Ok(())
     }
 
-    fn reset_attrs(&mut self) -> io::Result<()> {
+    fn reset(&mut self) -> io::Result<()> {
         match *self {
-            Terminal(ref mut t) => {
-                t.reset()?;
-            }
-            BufferedTerminal(ref mut t) => {
-                t.reset()?;
-            }
-            Raw(_) => {}
+            WritableDst::Terminal(ref mut t) => t.reset(),
+            WritableDst::Buffered(_, ref mut t) => t.reset(),
+            WritableDst::Raw(_) => Ok(()),
         }
-        Ok(())
     }
 }
 
-impl Write for Destination {
+impl<'a> Write for WritableDst<'a> {
     fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
         match *self {
-            Terminal(ref mut t) => t.write(bytes),
-            BufferedTerminal(ref mut t) => t.write(bytes),
-            Raw(ref mut w) => w.write(bytes),
+            WritableDst::Terminal(ref mut t) => t.write(bytes),
+            WritableDst::Buffered(_, ref mut buf) => buf.write(bytes),
+            WritableDst::Raw(ref mut w) => w.write(bytes),
         }
     }
+
     fn flush(&mut self) -> io::Result<()> {
         match *self {
-            Terminal(ref mut t) => t.flush(),
-            BufferedTerminal(ref mut t) => t.flush(),
-            Raw(ref mut w) => w.flush(),
+            WritableDst::Terminal(ref mut t) => t.flush(),
+            WritableDst::Buffered(_, ref mut buf) => buf.flush(),
+            WritableDst::Raw(ref mut w) => w.flush(),
+        }
+    }
+}
+
+impl<'a> Drop for WritableDst<'a> {
+    fn drop(&mut self) {
+        match *self {
+            WritableDst::Buffered(ref mut dst, ref mut buf) => {
+                drop(dst.print(buf));
+            }
+            _ => {}
         }
     }
 }
diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs
index 3eea311a5af..924ed71ef0d 100644
--- a/src/librustc_errors/lib.rs
+++ b/src/librustc_errors/lib.rs
@@ -21,7 +21,8 @@
 #![feature(i128_type)]
 #![feature(optin_builtin_traits)]
 
-extern crate term;
+extern crate atty;
+extern crate termcolor;
 #[cfg(unix)]
 extern crate libc;
 extern crate rustc_data_structures;
@@ -47,6 +48,8 @@ use std::sync::atomic::AtomicUsize;
 use std::sync::atomic::Ordering::SeqCst;
 use std::panic;
 
+use termcolor::{ColorSpec, Color};
+
 mod diagnostic;
 mod diagnostic_builder;
 pub mod emitter;
@@ -660,20 +663,28 @@ impl fmt::Display for Level {
 }
 
 impl Level {
-    fn color(self) -> term::color::Color {
+    fn color(self) -> ColorSpec {
+        let mut spec = ColorSpec::new();
         match self {
-            Bug | Fatal | PhaseFatal | Error => term::color::BRIGHT_RED,
+            Bug | Fatal | PhaseFatal | Error => {
+                spec.set_fg(Some(Color::Red))
+                    .set_intense(true);
+            }
             Warning => {
-                if cfg!(windows) {
-                    term::color::BRIGHT_YELLOW
-                } else {
-                    term::color::YELLOW
-                }
+                spec.set_fg(Some(Color::Yellow))
+                    .set_intense(cfg!(windows));
+            }
+            Note => {
+                spec.set_fg(Some(Color::Green))
+                    .set_intense(true);
+            }
+            Help => {
+                spec.set_fg(Some(Color::Cyan))
+                    .set_intense(true);
             }
-            Note => term::color::BRIGHT_GREEN,
-            Help => term::color::BRIGHT_CYAN,
             Cancelled => unreachable!(),
         }
+        return spec
     }
 
     pub fn to_str(self) -> &'static str {
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 831d4fc755f..d39e00ab18f 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -1263,7 +1263,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnionsWithDropFields {
 pub struct UnreachablePub;
 
 declare_lint! {
-    UNREACHABLE_PUB,
+    pub UNREACHABLE_PUB,
     Allow,
     "`pub` items not reachable from crate root"
 }
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index e941f2e4e1c..81609db6292 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -43,10 +43,12 @@ extern crate rustc_mir;
 extern crate syntax_pos;
 
 use rustc::lint;
+use rustc::lint::builtin::BARE_TRAIT_OBJECT;
 use rustc::session;
 use rustc::util;
 
 use session::Session;
+use syntax::epoch::Epoch;
 use lint::LintId;
 use lint::FutureIncompatibleInfo;
 
@@ -176,6 +178,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
                     UNUSED_FEATURES,
                     UNUSED_PARENS);
 
+    add_lint_group!(sess,
+                    "rust_2018_idioms",
+                    BARE_TRAIT_OBJECT,
+                    UNREACHABLE_PUB);
+
     // Guidelines for creating a future incompatibility lint:
     //
     // - Create a lint defaulting to warn as normal, with ideally the same error
@@ -274,13 +281,8 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
         FutureIncompatibleInfo {
             id: LintId::of(TYVAR_BEHIND_RAW_POINTER),
             reference: "issue #46906 <https://github.com/rust-lang/rust/issues/46906>",
-            epoch: None,
-        },
-         FutureIncompatibleInfo {
-             id: LintId::of(lint::builtin::BARE_TRAIT_OBJECT),
-             reference: "issue #48457 <https://github.com/rust-lang/rust/issues/48457>",
-             epoch: Some(session::config::Epoch::Epoch2018),
-         }
+            epoch: Some(Epoch::Epoch2018),
+        }
         ]);
 
     // Register renamed and removed lints
diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs
index a18c811d196..b804cf7cd90 100644
--- a/src/libstd/macros.rs
+++ b/src/libstd/macros.rs
@@ -472,7 +472,7 @@ pub mod builtin {
     /// The expanded expression has type `u32` and is 1-based, so the first line
     /// in each file evaluates to 1, the second to 2, etc. This is consistent
     /// with error messages by common compilers or popular editors.
-    /// The returned line is not the invocation of the `line!` macro itself,
+    /// The returned line is *not necessarily* the line of the `line!` invocation itself,
     /// but rather the first macro invocation leading up to the invocation
     /// of the `line!` macro.
     ///
@@ -497,7 +497,7 @@ pub mod builtin {
     /// The expanded expression has type `u32` and is 1-based, so the first column
     /// in each line evaluates to 1, the second to 2, etc. This is consistent
     /// with error messages by common compilers or popular editors.
-    /// The returned column is not the invocation of the `column!` macro itself,
+    /// The returned column is *not necessarily* the line of the `column!` invocation itself,
     /// but rather the first macro invocation leading up to the invocation
     /// of the `column!` macro.
     ///
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index aa360ed1bf5..6013c20daf2 100644
--- a/src/libsyntax/config.rs
+++ b/src/libsyntax/config.rs
@@ -13,6 +13,7 @@ use feature_gate::{feature_err, EXPLAIN_STMT_ATTR_SYNTAX, Features, get_features
 use {fold, attr};
 use ast;
 use codemap::Spanned;
+use epoch::Epoch;
 use parse::{token, ParseSess};
 
 use ptr::P;
@@ -26,7 +27,7 @@ pub struct StripUnconfigured<'a> {
 }
 
 // `cfg_attr`-process the crate's attributes and compute the crate's features.
-pub fn features(mut krate: ast::Crate, sess: &ParseSess, should_test: bool)
+pub fn features(mut krate: ast::Crate, sess: &ParseSess, should_test: bool, epoch: Epoch)
                 -> (ast::Crate, Features) {
     let features;
     {
@@ -46,7 +47,7 @@ pub fn features(mut krate: ast::Crate, sess: &ParseSess, should_test: bool)
             return (krate, Features::new());
         }
 
-        features = get_features(&sess.span_diagnostic, &krate.attrs);
+        features = get_features(&sess.span_diagnostic, &krate.attrs, epoch);
 
         // Avoid reconfiguring malformed `cfg_attr`s
         if err_count == sess.span_diagnostic.err_count() {
diff --git a/src/libsyntax/epoch.rs b/src/libsyntax/epoch.rs
new file mode 100644
index 00000000000..32cbc79c550
--- /dev/null
+++ b/src/libsyntax/epoch.rs
@@ -0,0 +1,69 @@
+// Copyright 2018 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::fmt;
+use std::str::FromStr;
+
+/// The epoch of the compiler (RFC 2052)
+#[derive(Clone, Copy, Hash, PartialOrd, Ord, Eq, PartialEq, Debug)]
+#[non_exhaustive]
+pub enum Epoch {
+    // epochs must be kept in order, newest to oldest
+
+    /// The 2015 epoch
+    Epoch2015,
+    /// The 2018 epoch
+    Epoch2018,
+
+    // when adding new epochs, be sure to update:
+    //
+    // - the list in the `parse_epoch` static in librustc::session::config
+    // - add a `rust_####()` function to the session
+    // - update the enum in Cargo's sources as well
+    //
+    // When -Zepoch becomes --epoch, there will
+    // also be a check for the epoch being nightly-only
+    // somewhere. That will need to be updated
+    // whenever we're stabilizing/introducing a new epoch
+    // as well as changing the default Cargo template.
+}
+
+// must be in order from oldest to newest
+pub const ALL_EPOCHS: &[Epoch] = &[Epoch::Epoch2015, Epoch::Epoch2018];
+
+impl fmt::Display for Epoch {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let s = match *self {
+            Epoch::Epoch2015 => "2015",
+            Epoch::Epoch2018 => "2018",
+        };
+        write!(f, "{}", s)
+    }
+}
+
+impl Epoch {
+    pub fn lint_name(&self) -> &'static str {
+        match *self {
+            Epoch::Epoch2015 => "epoch_2015",
+            Epoch::Epoch2018 => "epoch_2018",
+        }
+    }
+}
+
+impl FromStr for Epoch {
+    type Err = ();
+    fn from_str(s: &str) -> Result<Self, ()> {
+        match s {
+            "2015" => Ok(Epoch::Epoch2015),
+            "2018" => Ok(Epoch::Epoch2018),
+            _ => Err(())
+        }
+    }
+}
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 1a790bf78bd..ec9a15d9f2b 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -28,8 +28,9 @@ use self::AttributeGate::*;
 use abi::Abi;
 use ast::{self, NodeId, PatKind, RangeEnd, RangeSyntax};
 use attr;
+use epoch::Epoch;
 use codemap::Spanned;
-use syntax_pos::Span;
+use syntax_pos::{Span, DUMMY_SP};
 use errors::{DiagnosticBuilder, Handler, FatalError};
 use visit::{self, FnKind, Visitor};
 use parse::ParseSess;
@@ -54,12 +55,13 @@ macro_rules! set {
 }
 
 macro_rules! declare_features {
-    ($((active, $feature: ident, $ver: expr, $issue: expr),)+) => {
+    ($((active, $feature: ident, $ver: expr, $issue: expr, $epoch: expr),)+) => {
         /// Represents active features that are currently being implemented or
         /// currently being considered for addition/removal.
         const ACTIVE_FEATURES:
-                &'static [(&'static str, &'static str, Option<u32>, fn(&mut Features, Span))] =
-            &[$((stringify!($feature), $ver, $issue, set!($feature))),+];
+                &'static [(&'static str, &'static str, Option<u32>,
+                           Option<Epoch>, fn(&mut Features, Span))] =
+            &[$((stringify!($feature), $ver, $issue, $epoch, set!($feature))),+];
 
         /// A set of features to be used by later passes.
         #[derive(Clone)]
@@ -88,21 +90,21 @@ macro_rules! declare_features {
         }
     };
 
-    ($((removed, $feature: ident, $ver: expr, $issue: expr),)+) => {
+    ($((removed, $feature: ident, $ver: expr, $issue: expr, None),)+) => {
         /// Represents unstable features which have since been removed (it was once Active)
         const REMOVED_FEATURES: &'static [(&'static str, &'static str, Option<u32>)] = &[
             $((stringify!($feature), $ver, $issue)),+
         ];
     };
 
-    ($((stable_removed, $feature: ident, $ver: expr, $issue: expr),)+) => {
+    ($((stable_removed, $feature: ident, $ver: expr, $issue: expr, None),)+) => {
         /// Represents stable features which have since been removed (it was once Accepted)
         const STABLE_REMOVED_FEATURES: &'static [(&'static str, &'static str, Option<u32>)] = &[
             $((stringify!($feature), $ver, $issue)),+
         ];
     };
 
-    ($((accepted, $feature: ident, $ver: expr, $issue: expr),)+) => {
+    ($((accepted, $feature: ident, $ver: expr, $issue: expr, None),)+) => {
         /// Those language feature has since been Accepted (it was once Active)
         const ACCEPTED_FEATURES: &'static [(&'static str, &'static str, Option<u32>)] = &[
             $((stringify!($feature), $ver, $issue)),+
@@ -122,78 +124,78 @@ macro_rules! declare_features {
 // source, so take care when modifying it.
 
 declare_features! (
-    (active, asm, "1.0.0", Some(29722)),
-    (active, concat_idents, "1.0.0", Some(29599)),
-    (active, link_args, "1.0.0", Some(29596)),
-    (active, log_syntax, "1.0.0", Some(29598)),
-    (active, non_ascii_idents, "1.0.0", Some(28979)),
-    (active, plugin_registrar, "1.0.0", Some(29597)),
-    (active, thread_local, "1.0.0", Some(29594)),
-    (active, trace_macros, "1.0.0", Some(29598)),
+    (active, asm, "1.0.0", Some(29722), None),
+    (active, concat_idents, "1.0.0", Some(29599), None),
+    (active, link_args, "1.0.0", Some(29596), None),
+    (active, log_syntax, "1.0.0", Some(29598), None),
+    (active, non_ascii_idents, "1.0.0", Some(28979), None),
+    (active, plugin_registrar, "1.0.0", Some(29597), None),
+    (active, thread_local, "1.0.0", Some(29594), None),
+    (active, trace_macros, "1.0.0", Some(29598), None),
 
     // rustc internal, for now:
-    (active, intrinsics, "1.0.0", None),
-    (active, lang_items, "1.0.0", None),
+    (active, intrinsics, "1.0.0", None, None),
+    (active, lang_items, "1.0.0", None, None),
 
-    (active, link_llvm_intrinsics, "1.0.0", Some(29602)),
-    (active, linkage, "1.0.0", Some(29603)),
-    (active, quote, "1.0.0", Some(29601)),
+    (active, link_llvm_intrinsics, "1.0.0", Some(29602), None),
+    (active, linkage, "1.0.0", Some(29603), None),
+    (active, quote, "1.0.0", Some(29601), None),
 
 
     // rustc internal
-    (active, rustc_diagnostic_macros, "1.0.0", None),
-    (active, rustc_const_unstable, "1.0.0", None),
-    (active, advanced_slice_patterns, "1.0.0", Some(23121)),
-    (active, box_syntax, "1.0.0", Some(27779)),
-    (active, placement_in_syntax, "1.0.0", Some(27779)),
-    (active, unboxed_closures, "1.0.0", Some(29625)),
-
-    (active, fundamental, "1.0.0", Some(29635)),
-    (active, main, "1.0.0", Some(29634)),
-    (active, needs_allocator, "1.4.0", Some(27389)),
-    (active, on_unimplemented, "1.0.0", Some(29628)),
-    (active, plugin, "1.0.0", Some(29597)),
-    (active, simd_ffi, "1.0.0", Some(27731)),
-    (active, start, "1.0.0", Some(29633)),
-    (active, structural_match, "1.8.0", Some(31434)),
-    (active, panic_runtime, "1.10.0", Some(32837)),
-    (active, needs_panic_runtime, "1.10.0", Some(32837)),
+    (active, rustc_diagnostic_macros, "1.0.0", None, None),
+    (active, rustc_const_unstable, "1.0.0", None, None),
+    (active, advanced_slice_patterns, "1.0.0", Some(23121), None),
+    (active, box_syntax, "1.0.0", Some(27779), None),
+    (active, placement_in_syntax, "1.0.0", Some(27779), None),
+    (active, unboxed_closures, "1.0.0", Some(29625), None),
+
+    (active, fundamental, "1.0.0", Some(29635), None),
+    (active, main, "1.0.0", Some(29634), None),
+    (active, needs_allocator, "1.4.0", Some(27389), None),
+    (active, on_unimplemented, "1.0.0", Some(29628), None),
+    (active, plugin, "1.0.0", Some(29597), None),
+    (active, simd_ffi, "1.0.0", Some(27731), None),
+    (active, start, "1.0.0", Some(29633), None),
+    (active, structural_match, "1.8.0", Some(31434), None),
+    (active, panic_runtime, "1.10.0", Some(32837), None),
+    (active, needs_panic_runtime, "1.10.0", Some(32837), None),
 
     // OIBIT specific features
-    (active, optin_builtin_traits, "1.0.0", Some(13231)),
+    (active, optin_builtin_traits, "1.0.0", Some(13231), None),
 
     // macro re-export needs more discussion and stabilization
-    (active, macro_reexport, "1.0.0", Some(29638)),
+    (active, macro_reexport, "1.0.0", Some(29638), None),
 
     // Allows use of #[staged_api]
     // rustc internal
-    (active, staged_api, "1.0.0", None),
+    (active, staged_api, "1.0.0", None, None),
 
     // Allows using #![no_core]
-    (active, no_core, "1.3.0", Some(29639)),
+    (active, no_core, "1.3.0", Some(29639), None),
 
     // Allows using `box` in patterns; RFC 469
-    (active, box_patterns, "1.0.0", Some(29641)),
+    (active, box_patterns, "1.0.0", Some(29641), None),
 
     // Allows using the unsafe_destructor_blind_to_params attribute;
     // RFC 1238
-    (active, dropck_parametricity, "1.3.0", Some(28498)),
+    (active, dropck_parametricity, "1.3.0", Some(28498), None),
 
     // Allows using the may_dangle attribute; RFC 1327
-    (active, dropck_eyepatch, "1.10.0", Some(34761)),
+    (active, dropck_eyepatch, "1.10.0", Some(34761), None),
 
     // Allows the use of custom attributes; RFC 572
-    (active, custom_attribute, "1.0.0", Some(29642)),
+    (active, custom_attribute, "1.0.0", Some(29642), None),
 
     // Allows the use of #[derive(Anything)] as sugar for
     // #[derive_Anything].
-    (active, custom_derive, "1.0.0", Some(29644)),
+    (active, custom_derive, "1.0.0", Some(29644), None),
 
     // Allows the use of rustc_* attributes; RFC 572
-    (active, rustc_attrs, "1.0.0", Some(29642)),
+    (active, rustc_attrs, "1.0.0", Some(29642), None),
 
     // Allows the use of non lexical lifetimes; RFC 2094
-    (active, nll, "1.0.0", Some(43234)),
+    (active, nll, "1.0.0", Some(43234), None),
 
     // Allows the use of #[allow_internal_unstable]. This is an
     // attribute on macro_rules! and can't use the attribute handling
@@ -201,7 +203,7 @@ declare_features! (
     // macros disappear).
     //
     // rustc internal
-    (active, allow_internal_unstable, "1.0.0", None),
+    (active, allow_internal_unstable, "1.0.0", None, None),
 
     // Allows the use of #[allow_internal_unsafe]. This is an
     // attribute on macro_rules! and can't use the attribute handling
@@ -209,349 +211,349 @@ declare_features! (
     // macros disappear).
     //
     // rustc internal
-    (active, allow_internal_unsafe, "1.0.0", None),
+    (active, allow_internal_unsafe, "1.0.0", None, None),
 
     // #23121. Array patterns have some hazards yet.
-    (active, slice_patterns, "1.0.0", Some(23121)),
+    (active, slice_patterns, "1.0.0", Some(23121), None),
 
     // Allows the definition of `const fn` functions.
-    (active, const_fn, "1.2.0", Some(24111)),
+    (active, const_fn, "1.2.0", Some(24111), None),
 
     // Allows using #[prelude_import] on glob `use` items.
     //
     // rustc internal
-    (active, prelude_import, "1.2.0", None),
+    (active, prelude_import, "1.2.0", None, None),
 
     // Allows default type parameters to influence type inference.
-    (active, default_type_parameter_fallback, "1.3.0", Some(27336)),
+    (active, default_type_parameter_fallback, "1.3.0", Some(27336), None),
 
     // Allows associated type defaults
-    (active, associated_type_defaults, "1.2.0", Some(29661)),
+    (active, associated_type_defaults, "1.2.0", Some(29661), None),
 
     // allow `repr(simd)`, and importing the various simd intrinsics
-    (active, repr_simd, "1.4.0", Some(27731)),
+    (active, repr_simd, "1.4.0", Some(27731), None),
 
     // Allows cfg(target_feature = "...").
-    (active, cfg_target_feature, "1.4.0", Some(29717)),
+    (active, cfg_target_feature, "1.4.0", Some(29717), None),
 
     // allow `extern "platform-intrinsic" { ... }`
-    (active, platform_intrinsics, "1.4.0", Some(27731)),
+    (active, platform_intrinsics, "1.4.0", Some(27731), None),
 
     // allow `#[unwind(..)]`
     // rust runtime internal
-    (active, unwind_attributes, "1.4.0", None),
+    (active, unwind_attributes, "1.4.0", None, None),
 
     // allow the use of `#[naked]` on functions.
-    (active, naked_functions, "1.9.0", Some(32408)),
+    (active, naked_functions, "1.9.0", Some(32408), None),
 
     // allow `#[no_debug]`
-    (active, no_debug, "1.5.0", Some(29721)),
+    (active, no_debug, "1.5.0", Some(29721), None),
 
     // allow `#[omit_gdb_pretty_printer_section]`
     // rustc internal.
-    (active, omit_gdb_pretty_printer_section, "1.5.0", None),
+    (active, omit_gdb_pretty_printer_section, "1.5.0", None, None),
 
     // Allows cfg(target_vendor = "...").
-    (active, cfg_target_vendor, "1.5.0", Some(29718)),
+    (active, cfg_target_vendor, "1.5.0", Some(29718), None),
 
     // Allow attributes on expressions and non-item statements
-    (active, stmt_expr_attributes, "1.6.0", Some(15701)),
+    (active, stmt_expr_attributes, "1.6.0", Some(15701), None),
 
     // allow using type ascription in expressions
-    (active, type_ascription, "1.6.0", Some(23416)),
+    (active, type_ascription, "1.6.0", Some(23416), None),
 
     // Allows cfg(target_thread_local)
-    (active, cfg_target_thread_local, "1.7.0", Some(29594)),
+    (active, cfg_target_thread_local, "1.7.0", Some(29594), None),
 
     // rustc internal
-    (active, abi_vectorcall, "1.7.0", None),
+    (active, abi_vectorcall, "1.7.0", None, None),
 
     // a..=b and ..=b
-    (active, inclusive_range_syntax, "1.7.0", Some(28237)),
+    (active, inclusive_range_syntax, "1.7.0", Some(28237), None),
 
     // X..Y patterns
-    (active, exclusive_range_pattern, "1.11.0", Some(37854)),
+    (active, exclusive_range_pattern, "1.11.0", Some(37854), None),
 
     // impl specialization (RFC 1210)
-    (active, specialization, "1.7.0", Some(31844)),
+    (active, specialization, "1.7.0", Some(31844), None),
 
     // Allows cfg(target_has_atomic = "...").
-    (active, cfg_target_has_atomic, "1.9.0", Some(32976)),
+    (active, cfg_target_has_atomic, "1.9.0", Some(32976), None),
 
     // Allows `impl Trait` in function return types.
-    (active, conservative_impl_trait, "1.12.0", Some(34511)),
+    (active, conservative_impl_trait, "1.12.0", Some(34511), None),
 
     // Allows `impl Trait` in function arguments.
-    (active, universal_impl_trait, "1.23.0", Some(34511)),
+    (active, universal_impl_trait, "1.23.0", Some(34511), None),
 
     // The `!` type
-    (active, never_type, "1.13.0", Some(35121)),
+    (active, never_type, "1.13.0", Some(35121), None),
 
     // Allows all literals in attribute lists and values of key-value pairs.
-    (active, attr_literals, "1.13.0", Some(34981)),
+    (active, attr_literals, "1.13.0", Some(34981), None),
 
     // Allows untagged unions `union U { ... }`
-    (active, untagged_unions, "1.13.0", Some(32836)),
+    (active, untagged_unions, "1.13.0", Some(32836), None),
 
     // Used to identify the `compiler_builtins` crate
     // rustc internal
-    (active, compiler_builtins, "1.13.0", None),
+    (active, compiler_builtins, "1.13.0", None, None),
 
     // Allows attributes on lifetime/type formal parameters in generics (RFC 1327)
-    (active, generic_param_attrs, "1.11.0", Some(34761)),
+    (active, generic_param_attrs, "1.11.0", Some(34761), None),
 
     // Allows #[link(..., cfg(..))]
-    (active, link_cfg, "1.14.0", Some(37406)),
+    (active, link_cfg, "1.14.0", Some(37406), None),
 
-    (active, use_extern_macros, "1.15.0", Some(35896)),
+    (active, use_extern_macros, "1.15.0", Some(35896), None),
 
     // Allows #[target_feature(...)]
-    (active, target_feature, "1.15.0", None),
+    (active, target_feature, "1.15.0", None, None),
 
     // `extern "ptx-*" fn()`
-    (active, abi_ptx, "1.15.0", None),
+    (active, abi_ptx, "1.15.0", None, None),
 
     // The `i128` type
-    (active, i128_type, "1.16.0", Some(35118)),
+    (active, i128_type, "1.16.0", Some(35118), None),
 
     // The `repr(i128)` annotation for enums
-    (active, repr128, "1.16.0", Some(35118)),
+    (active, repr128, "1.16.0", Some(35118), None),
 
     // The `unadjusted` ABI. Perma unstable.
-    (active, abi_unadjusted, "1.16.0", None),
+    (active, abi_unadjusted, "1.16.0", None, None),
 
     // Procedural macros 2.0.
-    (active, proc_macro, "1.16.0", Some(38356)),
+    (active, proc_macro, "1.16.0", Some(38356), None),
 
     // Declarative macros 2.0 (`macro`).
-    (active, decl_macro, "1.17.0", Some(39412)),
+    (active, decl_macro, "1.17.0", Some(39412), None),
 
     // Allows #[link(kind="static-nobundle"...]
-    (active, static_nobundle, "1.16.0", Some(37403)),
+    (active, static_nobundle, "1.16.0", Some(37403), None),
 
     // `extern "msp430-interrupt" fn()`
-    (active, abi_msp430_interrupt, "1.16.0", Some(38487)),
+    (active, abi_msp430_interrupt, "1.16.0", Some(38487), None),
 
     // Used to identify crates that contain sanitizer runtimes
     // rustc internal
-    (active, sanitizer_runtime, "1.17.0", None),
+    (active, sanitizer_runtime, "1.17.0", None, None),
 
     // Used to identify crates that contain the profiler runtime
     // rustc internal
-    (active, profiler_runtime, "1.18.0", None),
+    (active, profiler_runtime, "1.18.0", None, None),
 
     // `extern "x86-interrupt" fn()`
-    (active, abi_x86_interrupt, "1.17.0", Some(40180)),
+    (active, abi_x86_interrupt, "1.17.0", Some(40180), None),
 
 
     // Allows the `catch {...}` expression
-    (active, catch_expr, "1.17.0", Some(31436)),
+    (active, catch_expr, "1.17.0", Some(31436), None),
 
     // Used to preserve symbols (see llvm.used)
-    (active, used, "1.18.0", Some(40289)),
+    (active, used, "1.18.0", Some(40289), None),
 
     // Allows module-level inline assembly by way of global_asm!()
-    (active, global_asm, "1.18.0", Some(35119)),
+    (active, global_asm, "1.18.0", Some(35119), None),
 
     // Allows overlapping impls of marker traits
-    (active, overlapping_marker_traits, "1.18.0", Some(29864)),
+    (active, overlapping_marker_traits, "1.18.0", Some(29864), None),
 
     // Allows use of the :vis macro fragment specifier
-    (active, macro_vis_matcher, "1.18.0", Some(41022)),
+    (active, macro_vis_matcher, "1.18.0", Some(41022), None),
 
     // rustc internal
-    (active, abi_thiscall, "1.19.0", None),
+    (active, abi_thiscall, "1.19.0", None, None),
 
     // Allows a test to fail without failing the whole suite
-    (active, allow_fail, "1.19.0", Some(42219)),
+    (active, allow_fail, "1.19.0", Some(42219), None),
 
     // Allows unsized tuple coercion.
-    (active, unsized_tuple_coercion, "1.20.0", Some(42877)),
+    (active, unsized_tuple_coercion, "1.20.0", Some(42877), None),
 
     // Generators
-    (active, generators, "1.21.0", None),
+    (active, generators, "1.21.0", None, None),
 
     // Trait aliases
-    (active, trait_alias, "1.24.0", Some(41517)),
+    (active, trait_alias, "1.24.0", Some(41517), None),
 
     // global allocators and their internals
-    (active, global_allocator, "1.20.0", None),
-    (active, allocator_internals, "1.20.0", None),
+    (active, global_allocator, "1.20.0", None, None),
+    (active, allocator_internals, "1.20.0", None, None),
 
     // #[doc(cfg(...))]
-    (active, doc_cfg, "1.21.0", Some(43781)),
+    (active, doc_cfg, "1.21.0", Some(43781), None),
     // #[doc(masked)]
-    (active, doc_masked, "1.21.0", Some(44027)),
+    (active, doc_masked, "1.21.0", Some(44027), None),
     // #[doc(spotlight)]
-    (active, doc_spotlight, "1.22.0", Some(45040)),
+    (active, doc_spotlight, "1.22.0", Some(45040), None),
     // #[doc(include="some-file")]
-    (active, external_doc, "1.22.0", Some(44732)),
+    (active, external_doc, "1.22.0", Some(44732), None),
 
     // allow `#[must_use]` on functions and comparison operators (RFC 1940)
-    (active, fn_must_use, "1.21.0", Some(43302)),
+    (active, fn_must_use, "1.21.0", Some(43302), None),
 
     // Future-proofing enums/structs with #[non_exhaustive] attribute (RFC 2008)
-    (active, non_exhaustive, "1.22.0", Some(44109)),
+    (active, non_exhaustive, "1.22.0", Some(44109), None),
 
     // Copy/Clone closures (RFC 2132)
-    (active, clone_closures, "1.22.0", Some(44490)),
-    (active, copy_closures, "1.22.0", Some(44490)),
+    (active, clone_closures, "1.22.0", Some(44490), None),
+    (active, copy_closures, "1.22.0", Some(44490), None),
 
     // allow `'_` placeholder lifetimes
-    (active, underscore_lifetimes, "1.22.0", Some(44524)),
+    (active, underscore_lifetimes, "1.22.0", Some(44524), None),
 
     // allow `..=` in patterns (RFC 1192)
-    (active, dotdoteq_in_patterns, "1.22.0", Some(28237)),
+    (active, dotdoteq_in_patterns, "1.22.0", Some(28237), None),
 
     // Default match binding modes (RFC 2005)
-    (active, match_default_bindings, "1.22.0", Some(42640)),
+    (active, match_default_bindings, "1.22.0", Some(42640), None),
 
     // Trait object syntax with `dyn` prefix
-    (active, dyn_trait, "1.22.0", Some(44662)),
+    (active, dyn_trait, "1.22.0", Some(44662), Some(Epoch::Epoch2018)),
 
     // `crate` as visibility modifier, synonymous to `pub(crate)`
-    (active, crate_visibility_modifier, "1.23.0", Some(45388)),
+    (active, crate_visibility_modifier, "1.23.0", Some(45388), None),
 
     // extern types
-    (active, extern_types, "1.23.0", Some(43467)),
+    (active, extern_types, "1.23.0", Some(43467), None),
 
     // Allow trait methods with arbitrary self types
-    (active, arbitrary_self_types, "1.23.0", Some(44874)),
+    (active, arbitrary_self_types, "1.23.0", Some(44874), None),
 
     // `crate` in paths
-    (active, crate_in_paths, "1.23.0", Some(45477)),
+    (active, crate_in_paths, "1.23.0", Some(45477), None),
 
     // In-band lifetime bindings (e.g. `fn foo(x: &'a u8) -> &'a u8`)
-    (active, in_band_lifetimes, "1.23.0", Some(44524)),
+    (active, in_band_lifetimes, "1.23.0", Some(44524), None),
 
     // generic associated types (RFC 1598)
-    (active, generic_associated_types, "1.23.0", Some(44265)),
+    (active, generic_associated_types, "1.23.0", Some(44265), None),
 
     // Resolve absolute paths as paths from other crates
-    (active, extern_absolute_paths, "1.24.0", Some(44660)),
+    (active, extern_absolute_paths, "1.24.0", Some(44660), None),
 
     // `foo.rs` as an alternative to `foo/mod.rs`
-    (active, non_modrs_mods, "1.24.0", Some(44660)),
+    (active, non_modrs_mods, "1.24.0", Some(44660), None),
 
     // Termination trait in main (RFC 1937)
-    (active, termination_trait, "1.24.0", Some(43301)),
+    (active, termination_trait, "1.24.0", Some(43301), None),
 
     // Allows use of the :lifetime macro fragment specifier
-    (active, macro_lifetime_matcher, "1.24.0", Some(46895)),
+    (active, macro_lifetime_matcher, "1.24.0", Some(46895), None),
 
     // `extern` in paths
-    (active, extern_in_paths, "1.23.0", Some(44660)),
+    (active, extern_in_paths, "1.23.0", Some(44660), None),
 
     // Allows `#[repr(transparent)]` attribute on newtype structs
-    (active, repr_transparent, "1.25.0", Some(43036)),
+    (active, repr_transparent, "1.25.0", Some(43036), None),
 
     // Use `?` as the Kleene "at most one" operator
-    (active, macro_at_most_once_rep, "1.25.0", Some(48075)),
+    (active, macro_at_most_once_rep, "1.25.0", Some(48075), None),
 
     // Multiple patterns with `|` in `if let` and `while let`
-    (active, if_while_or_patterns, "1.26.0", Some(48215)),
+    (active, if_while_or_patterns, "1.26.0", Some(48215), None),
 
     // Parentheses in patterns
-    (active, pattern_parentheses, "1.26.0", None),
+    (active, pattern_parentheses, "1.26.0", None, None),
 );
 
 declare_features! (
-    (removed, import_shadowing, "1.0.0", None),
-    (removed, managed_boxes, "1.0.0", None),
+    (removed, import_shadowing, "1.0.0", None, None),
+    (removed, managed_boxes, "1.0.0", None, None),
     // Allows use of unary negate on unsigned integers, e.g. -e for e: u8
-    (removed, negate_unsigned, "1.0.0", Some(29645)),
-    (removed, reflect, "1.0.0", Some(27749)),
+    (removed, negate_unsigned, "1.0.0", Some(29645), None),
+    (removed, reflect, "1.0.0", Some(27749), None),
     // A way to temporarily opt out of opt in copy. This will *never* be accepted.
-    (removed, opt_out_copy, "1.0.0", None),
-    (removed, quad_precision_float, "1.0.0", None),
-    (removed, struct_inherit, "1.0.0", None),
-    (removed, test_removed_feature, "1.0.0", None),
-    (removed, visible_private_types, "1.0.0", None),
-    (removed, unsafe_no_drop_flag, "1.0.0", None),
+    (removed, opt_out_copy, "1.0.0", None, None),
+    (removed, quad_precision_float, "1.0.0", None, None),
+    (removed, struct_inherit, "1.0.0", None, None),
+    (removed, test_removed_feature, "1.0.0", None, None),
+    (removed, visible_private_types, "1.0.0", None, None),
+    (removed, unsafe_no_drop_flag, "1.0.0", None, None),
     // Allows using items which are missing stability attributes
     // rustc internal
-    (removed, unmarked_api, "1.0.0", None),
-    (removed, pushpop_unsafe, "1.2.0", None),
-    (removed, allocator, "1.0.0", None),
+    (removed, unmarked_api, "1.0.0", None, None),
+    (removed, pushpop_unsafe, "1.2.0", None, None),
+    (removed, allocator, "1.0.0", None, None),
     // Allows the `#[simd]` attribute -- removed in favor of `#[repr(simd)]`
-    (removed, simd, "1.0.0", Some(27731)),
+    (removed, simd, "1.0.0", Some(27731), None),
 );
 
 declare_features! (
-    (stable_removed, no_stack_check, "1.0.0", None),
+    (stable_removed, no_stack_check, "1.0.0", None, None),
 );
 
 declare_features! (
-    (accepted, associated_types, "1.0.0", None),
+    (accepted, associated_types, "1.0.0", None, None),
     // allow overloading augmented assignment operations like `a += b`
-    (accepted, augmented_assignments, "1.8.0", Some(28235)),
+    (accepted, augmented_assignments, "1.8.0", Some(28235), None),
     // allow empty structs and enum variants with braces
-    (accepted, braced_empty_structs, "1.8.0", Some(29720)),
+    (accepted, braced_empty_structs, "1.8.0", Some(29720), None),
     // Allows indexing into constant arrays.
-    (accepted, const_indexing, "1.24.0", Some(29947)),
-    (accepted, default_type_params, "1.0.0", None),
-    (accepted, globs, "1.0.0", None),
-    (accepted, if_let, "1.0.0", None),
+    (accepted, const_indexing, "1.24.0", Some(29947), None),
+    (accepted, default_type_params, "1.0.0", None, None),
+    (accepted, globs, "1.0.0", None, None),
+    (accepted, if_let, "1.0.0", None, None),
     // A temporary feature gate used to enable parser extensions needed
     // to bootstrap fix for #5723.
-    (accepted, issue_5723_bootstrap, "1.0.0", None),
-    (accepted, macro_rules, "1.0.0", None),
+    (accepted, issue_5723_bootstrap, "1.0.0", None, None),
+    (accepted, macro_rules, "1.0.0", None, None),
     // Allows using #![no_std]
-    (accepted, no_std, "1.6.0", None),
-    (accepted, slicing_syntax, "1.0.0", None),
-    (accepted, struct_variant, "1.0.0", None),
+    (accepted, no_std, "1.6.0", None, None),
+    (accepted, slicing_syntax, "1.0.0", None, None),
+    (accepted, struct_variant, "1.0.0", None, None),
     // These are used to test this portion of the compiler, they don't actually
     // mean anything
-    (accepted, test_accepted_feature, "1.0.0", None),
-    (accepted, tuple_indexing, "1.0.0", None),
+    (accepted, test_accepted_feature, "1.0.0", None, None),
+    (accepted, tuple_indexing, "1.0.0", None, None),
     // Allows macros to appear in the type position.
-    (accepted, type_macros, "1.13.0", Some(27245)),
-    (accepted, while_let, "1.0.0", None),
+    (accepted, type_macros, "1.13.0", Some(27245), None),
+    (accepted, while_let, "1.0.0", None, None),
     // Allows `#[deprecated]` attribute
-    (accepted, deprecated, "1.9.0", Some(29935)),
+    (accepted, deprecated, "1.9.0", Some(29935), None),
     // `expr?`
-    (accepted, question_mark, "1.13.0", Some(31436)),
+    (accepted, question_mark, "1.13.0", Some(31436), None),
     // Allows `..` in tuple (struct) patterns
-    (accepted, dotdot_in_tuple_patterns, "1.14.0", Some(33627)),
-    (accepted, item_like_imports, "1.15.0", Some(35120)),
+    (accepted, dotdot_in_tuple_patterns, "1.14.0", Some(33627), None),
+    (accepted, item_like_imports, "1.15.0", Some(35120), None),
     // Allows using `Self` and associated types in struct expressions and patterns.
-    (accepted, more_struct_aliases, "1.16.0", Some(37544)),
+    (accepted, more_struct_aliases, "1.16.0", Some(37544), None),
     // elide `'static` lifetimes in `static`s and `const`s
-    (accepted, static_in_const, "1.17.0", Some(35897)),
+    (accepted, static_in_const, "1.17.0", Some(35897), None),
     // Allows field shorthands (`x` meaning `x: x`) in struct literal expressions.
-    (accepted, field_init_shorthand, "1.17.0", Some(37340)),
+    (accepted, field_init_shorthand, "1.17.0", Some(37340), None),
     // Allows the definition recursive static items.
-    (accepted, static_recursion, "1.17.0", Some(29719)),
+    (accepted, static_recursion, "1.17.0", Some(29719), None),
     // pub(restricted) visibilities (RFC 1422)
-    (accepted, pub_restricted, "1.18.0", Some(32409)),
+    (accepted, pub_restricted, "1.18.0", Some(32409), None),
     // The #![windows_subsystem] attribute
-    (accepted, windows_subsystem, "1.18.0", Some(37499)),
+    (accepted, windows_subsystem, "1.18.0", Some(37499), None),
     // Allows `break {expr}` with a value inside `loop`s.
-    (accepted, loop_break_value, "1.19.0", Some(37339)),
+    (accepted, loop_break_value, "1.19.0", Some(37339), None),
     // Permits numeric fields in struct expressions and patterns.
-    (accepted, relaxed_adts, "1.19.0", Some(35626)),
+    (accepted, relaxed_adts, "1.19.0", Some(35626), None),
     // Coerces non capturing closures to function pointers
-    (accepted, closure_to_fn_coercion, "1.19.0", Some(39817)),
+    (accepted, closure_to_fn_coercion, "1.19.0", Some(39817), None),
     // Allows attributes on struct literal fields.
-    (accepted, struct_field_attributes, "1.20.0", Some(38814)),
+    (accepted, struct_field_attributes, "1.20.0", Some(38814), None),
     // Allows the definition of associated constants in `trait` or `impl`
     // blocks.
-    (accepted, associated_consts, "1.20.0", Some(29646)),
+    (accepted, associated_consts, "1.20.0", Some(29646), None),
     // Usage of the `compile_error!` macro
-    (accepted, compile_error, "1.20.0", Some(40872)),
+    (accepted, compile_error, "1.20.0", Some(40872), None),
     // See rust-lang/rfcs#1414. Allows code like `let x: &'static u32 = &42` to work.
-    (accepted, rvalue_static_promotion, "1.21.0", Some(38865)),
+    (accepted, rvalue_static_promotion, "1.21.0", Some(38865), None),
     // Allow Drop types in constants (RFC 1440)
-    (accepted, drop_types_in_const, "1.22.0", Some(33156)),
+    (accepted, drop_types_in_const, "1.22.0", Some(33156), None),
     // Allows the sysV64 ABI to be specified on all platforms
     // instead of just the platforms on which it is the C ABI
-    (accepted, abi_sysv64, "1.24.0", Some(36167)),
+    (accepted, abi_sysv64, "1.24.0", Some(36167), None),
     // Allows `repr(align(16))` struct attribute (RFC 1358)
-    (accepted, repr_align, "1.25.0", Some(33626)),
+    (accepted, repr_align, "1.25.0", Some(33626), None),
     // allow '|' at beginning of match arms (RFC 1925)
-    (accepted, match_beginning_vert, "1.25.0", Some(44101)),
+    (accepted, match_beginning_vert, "1.25.0", Some(44101), None),
     // Nested groups in `use` (RFC 2128)
-    (accepted, use_nested_groups, "1.25.0", Some(44494)),
+    (accepted, use_nested_groups, "1.25.0", Some(44494), None),
 );
 
 // If you change this, please modify src/doc/unstable-book as well. You must
@@ -1793,11 +1795,22 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
     }
 }
 
-pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> Features {
+pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
+                    epoch: Epoch) -> Features {
     let mut features = Features::new();
 
     let mut feature_checker = FeatureChecker::default();
 
+    for &(.., f_epoch, set) in ACTIVE_FEATURES.iter() {
+        if let Some(f_epoch) = f_epoch {
+            if epoch >= f_epoch {
+                // FIXME(Manishearth) there is currently no way to set
+                // lang features by epoch
+                set(&mut features, DUMMY_SP);
+            }
+        }
+    }
+
     for attr in krate_attrs {
         if !attr.check_name("feature") {
             continue
@@ -1818,8 +1831,8 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> F
                         continue
                     };
 
-                    if let Some(&(_, _, _, set)) = ACTIVE_FEATURES.iter()
-                        .find(|& &(n, _, _, _)| name == n) {
+                    if let Some(&(_, _, _, _, set)) = ACTIVE_FEATURES.iter()
+                        .find(|& &(n, ..)| name == n) {
                         set(&mut features, mi.span);
                         feature_checker.collect(&features, mi.span);
                     }
diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs
index 14e39b5af42..50e94e5cba7 100644
--- a/src/libsyntax/lib.rs
+++ b/src/libsyntax/lib.rs
@@ -23,6 +23,7 @@
 #![feature(unicode)]
 #![feature(rustc_diagnostic_macros)]
 #![feature(match_default_bindings)]
+#![feature(non_exhaustive)]
 #![feature(i128_type)]
 #![feature(const_atomic_usize_new)]
 #![feature(rustc_attrs)]
@@ -114,6 +115,7 @@ pub mod codemap;
 #[macro_use]
 pub mod config;
 pub mod entry;
+pub mod epoch;
 pub mod feature_gate;
 pub mod fold;
 pub mod parse;
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index da2a22df997..847733e1e37 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -3318,7 +3318,7 @@ impl<'a> Parser<'a> {
                           mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
         // Parse: `for <src_pat> in <src_expr> <src_loop_block>`
 
-        let pat = self.parse_pat()?;
+        let pat = self.parse_top_level_pat()?;
         if !self.eat_keyword(keywords::In) {
             let in_span = self.prev_span.between(self.span);
             let mut err = self.sess.span_diagnostic
@@ -3528,7 +3528,7 @@ impl<'a> Parser<'a> {
     fn parse_pats(&mut self) -> PResult<'a, Vec<P<Pat>>> {
         let mut pats = Vec::new();
         loop {
-            pats.push(self.parse_pat()?);
+            pats.push(self.parse_top_level_pat()?);
 
             if self.token == token::OrOr {
                 let mut err = self.struct_span_err(self.span,
@@ -3554,7 +3554,12 @@ impl<'a> Parser<'a> {
     // Trailing commas are significant because (p) and (p,) are different patterns.
     fn parse_parenthesized_pat_list(&mut self) -> PResult<'a, (Vec<P<Pat>>, Option<usize>, bool)> {
         self.expect(&token::OpenDelim(token::Paren))?;
+        let result = self.parse_pat_list()?;
+        self.expect(&token::CloseDelim(token::Paren))?;
+        Ok(result)
+    }
 
+    fn parse_pat_list(&mut self) -> PResult<'a, (Vec<P<Pat>>, Option<usize>, bool)> {
         let mut fields = Vec::new();
         let mut ddpos = None;
         let mut trailing_comma = false;
@@ -3584,8 +3589,6 @@ impl<'a> Parser<'a> {
             self.span_err(self.prev_span, "trailing comma is not permitted after `..`");
         }
 
-        self.expect(&token::CloseDelim(token::Paren))?;
-
         Ok((fields, ddpos, trailing_comma))
     }
 
@@ -3767,6 +3770,37 @@ impl<'a> Parser<'a> {
         }))
     }
 
+    /// A wrapper around `parse_pat` with some special error handling for the
+    /// "top-level" patterns in a match arm, `for` loop, `let`, &c. (in contast
+    /// to subpatterns within such).
+    pub fn parse_top_level_pat(&mut self) -> PResult<'a, P<Pat>> {
+        let pat = self.parse_pat()?;
+        if self.token == token::Comma {
+            // An unexpected comma after a top-level pattern is a clue that the
+            // user (perhaps more accustomed to some other language) forgot the
+            // parentheses in what should have been a tuple pattern; return a
+            // suggestion-enhanced error here rather than choking on the comma
+            // later.
+            let comma_span = self.span;
+            self.bump();
+            if let Err(mut err) = self.parse_pat_list() {
+                // We didn't expect this to work anyway; we just wanted
+                // to advance to the end of the comma-sequence so we know
+                // the span to suggest parenthesizing
+                err.cancel();
+            }
+            let seq_span = pat.span.to(self.prev_span);
+            let mut err = self.struct_span_err(comma_span,
+                                               "unexpected `,` in pattern");
+            if let Ok(seq_snippet) = self.sess.codemap().span_to_snippet(seq_span) {
+                err.span_suggestion(seq_span, "try adding parentheses",
+                                    format!("({})", seq_snippet));
+            }
+            return Err(err);
+        }
+        Ok(pat)
+    }
+
     /// Parse a pattern.
     pub fn parse_pat(&mut self) -> PResult<'a, P<Pat>> {
         maybe_whole!(self, NtPat, |x| x);
@@ -3969,7 +4003,7 @@ impl<'a> Parser<'a> {
     /// Parse a local variable declaration
     fn parse_local(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Local>> {
         let lo = self.prev_span;
-        let pat = self.parse_pat()?;
+        let pat = self.parse_top_level_pat()?;
 
         let (err, ty) = if self.eat(&token::Colon) {
             // Save the state of the parser before parsing type normally, in case there is a `:`
diff --git a/src/test/run-pass/epoch-gate-feature.rs b/src/test/run-pass/epoch-gate-feature.rs
new file mode 100644
index 00000000000..37d092c06e0
--- /dev/null
+++ b/src/test/run-pass/epoch-gate-feature.rs
@@ -0,0 +1,21 @@
+// Copyright 2018 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Checks if the correct registers are being used to pass arguments
+// when the sysv64 ABI is specified.
+
+// compile-flags: -Zepoch=2018
+
+pub trait Foo {}
+
+// should compile without the dyn trait feature flag
+fn foo(x: &dyn Foo) {}
+
+pub fn main() {}
diff --git a/src/test/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.rs b/src/test/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.rs
new file mode 100644
index 00000000000..7bdaaddad84
--- /dev/null
+++ b/src/test/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.rs
@@ -0,0 +1,97 @@
+// Copyright 2018 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(unused)]
+
+#[derive(Copy, Clone)]
+enum Nucleotide {
+    Adenine,
+    Thymine,
+    Cytosine,
+    Guanine
+}
+
+#[derive(Clone)]
+struct Autosome;
+
+#[derive(Clone)]
+enum Allosome {
+    X(Vec<Nucleotide>),
+    Y(Vec<Nucleotide>)
+}
+
+impl Allosome {
+    fn is_x(&self) -> bool {
+        match *self {
+            Allosome::X(_) => true,
+            Allosome::Y(_) => false,
+        }
+    }
+}
+
+#[derive(Clone)]
+struct Genome {
+    autosomes: [Autosome; 22],
+    allosomes: (Allosome, Allosome)
+}
+
+fn find_start_codon(strand: &[Nucleotide]) -> Option<usize> {
+    let mut reading_frame = strand.windows(3);
+    // (missing parentheses in `while let` tuple pattern)
+    while let b1, b2, b3 = reading_frame.next().expect("there should be a start codon") {
+        //~^ ERROR unexpected `,` in pattern
+        // ...
+    }
+    None
+}
+
+fn find_thr(strand: &[Nucleotide]) -> Option<usize> {
+    let mut reading_frame = strand.windows(3);
+    let mut i = 0;
+    // (missing parentheses in `if let` tuple pattern)
+    if let b1, b2, b3 = reading_frame.next().unwrap() {
+        //~^ ERROR unexpected `,` in pattern
+        // ...
+    }
+    None
+}
+
+fn is_thr(codon: (Nucleotide, Nucleotide, Nucleotide)) -> bool {
+    match codon {
+        // (missing parentheses in match arm tuple pattern)
+        Nucleotide::Adenine, Nucleotide::Cytosine, _ => true
+        //~^ ERROR unexpected `,` in pattern
+        _ => false
+    }
+}
+
+fn analyze_female_sex_chromosomes(women: &[Genome]) {
+    // (missing parentheses in `for` tuple pattern)
+    for x, _barr_body in women.iter().map(|woman| woman.allosomes.clone()) {
+        //~^ ERROR unexpected `,` in pattern
+        // ...
+    }
+}
+
+fn analyze_male_sex_chromosomes(men: &[Genome]) {
+    // (missing parentheses in pattern with `@` binding)
+    for x, y @ Allosome::Y(_) in men.iter().map(|man| man.allosomes.clone()) {
+        //~^ ERROR unexpected `,` in pattern
+        // ...
+    }
+}
+
+fn main() {
+    let genomes = Vec::new();
+    // (missing parentheses in `let` pattern)
+    let women, men: (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned()
+    //~^ ERROR unexpected `,` in pattern
+        .partition(|g: &Genome| g.allosomes.0.is_x() && g.allosomes.1.is_x());
+}
diff --git a/src/test/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr b/src/test/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr
new file mode 100644
index 00000000000..db3f93af444
--- /dev/null
+++ b/src/test/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr
@@ -0,0 +1,38 @@
+error: unexpected `,` in pattern
+  --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:48:17
+   |
+LL |     while let b1, b2, b3 = reading_frame.next().expect("there should be a start codon") {
+   |               --^------- help: try adding parentheses: `(b1, b2, b3)`
+
+error: unexpected `,` in pattern
+  --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:59:14
+   |
+LL |     if let b1, b2, b3 = reading_frame.next().unwrap() {
+   |            --^------- help: try adding parentheses: `(b1, b2, b3)`
+
+error: unexpected `,` in pattern
+  --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:69:28
+   |
+LL |         Nucleotide::Adenine, Nucleotide::Cytosine, _ => true
+   |         -------------------^------------------------ help: try adding parentheses: `(Nucleotide::Adenine, Nucleotide::Cytosine, _)`
+
+error: unexpected `,` in pattern
+  --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:77:10
+   |
+LL |     for x, _barr_body in women.iter().map(|woman| woman.allosomes.clone()) {
+   |         -^----------- help: try adding parentheses: `(x, _barr_body)`
+
+error: unexpected `,` in pattern
+  --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:85:10
+   |
+LL |     for x, y @ Allosome::Y(_) in men.iter().map(|man| man.allosomes.clone()) {
+   |         -^------------------- help: try adding parentheses: `(x, y @ Allosome::Y(_))`
+
+error: unexpected `,` in pattern
+  --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:94:14
+   |
+LL |     let women, men: (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned()
+   |         -----^---- help: try adding parentheses: `(women, men)`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/test/ui/inference-variable-behind-raw-pointer.stderr b/src/test/ui/inference-variable-behind-raw-pointer.stderr
index e1d4df85c2f..eb40151615d 100644
--- a/src/test/ui/inference-variable-behind-raw-pointer.stderr
+++ b/src/test/ui/inference-variable-behind-raw-pointer.stderr
@@ -5,6 +5,6 @@ LL |     if data.is_null() {}
    |             ^^^^^^^
    |
    = note: #[warn(tyvar_behind_raw_pointer)] on by default
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 epoch!
    = note: for more information, see issue #46906 <https://github.com/rust-lang/rust/issues/46906>