diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 80c4fc28703ac..ae6136a049ade 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -547,7 +547,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
                                                   sess.diagnostic()));
 
     krate = time(time_passes, "prelude injection", krate, |krate|
-                 syntax::std_inject::maybe_inject_prelude(krate));
+                 syntax::std_inject::maybe_inject_prelude(&sess.parse_sess, krate));
 
     time(time_passes, "checking that all macro invocations are gone", &krate, |krate|
          syntax::ext::expand::check_for_macros(&sess.parse_sess, krate));
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 3d0cf9236c25c..ab8cf9ae6b64f 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -155,6 +155,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
 
     // Allows the definition of `const fn` functions.
     ("const_fn", "1.2.0", Active),
+
+    // Allows using #[prelude_import] on glob `use` items.
+    ("prelude_import", "1.2.0", Active),
 ];
 // (changing above list without updating src/doc/reference.md makes @cmr sad)
 
@@ -265,7 +268,8 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[
                                    and may be removed in the future")),
 
     // used in resolve
-    ("prelude_import", Whitelisted),
+    ("prelude_import", Gated("prelude_import",
+                             "`#[prelude_import]` is for use by rustc only")),
 
     // FIXME: #14407 these are only looked at on-demand so we can't
     // guarantee they'll have already been checked
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 3adb73cfa5d6f..6693eed6aced5 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -120,11 +120,13 @@ pub fn print_crate<'a>(cm: &'a CodeMap,
         // of the feature gate, so we fake them up here.
 
         let no_std_meta = attr::mk_word_item(InternedString::new("no_std"));
+        let prelude_import_meta = attr::mk_word_item(InternedString::new("prelude_import"));
 
         // #![feature(no_std)]
         let fake_attr = attr::mk_attr_inner(attr::mk_attr_id(),
                                             attr::mk_list_item(InternedString::new("feature"),
-                                                               vec![no_std_meta.clone()]));
+                                                               vec![no_std_meta.clone(),
+                                                                    prelude_import_meta]));
         try!(s.print_attribute(&fake_attr));
 
         // #![no_std]
diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs
index 021ec4738ed94..3655058653188 100644
--- a/src/libsyntax/std_inject.rs
+++ b/src/libsyntax/std_inject.rs
@@ -10,16 +10,35 @@
 
 use ast;
 use attr;
-use codemap::DUMMY_SP;
+use codemap::{DUMMY_SP, Span, ExpnInfo, NameAndSpan, MacroAttribute};
 use codemap;
 use fold::Folder;
 use fold;
 use parse::token::InternedString;
 use parse::token::special_idents;
-use parse::token;
+use parse::{token, ParseSess};
 use ptr::P;
 use util::small_vector::SmallVector;
 
+/// Craft a span that will be ignored by the stability lint's
+/// call to codemap's is_internal check.
+/// The expanded code uses the unstable `#[prelude_import]` attribute.
+fn ignored_span(sess: &ParseSess, sp: Span) -> Span {
+    let info = ExpnInfo {
+        call_site: DUMMY_SP,
+        callee: NameAndSpan {
+            name: "std_inject".to_string(),
+            format: MacroAttribute,
+            span: None,
+            allow_internal_unstable: true,
+        }
+    };
+    let expn_id = sess.codemap().record_expansion(info);
+    let mut sp = sp;
+    sp.expn_id = expn_id;
+    return sp;
+}
+
 pub fn maybe_inject_crates_ref(krate: ast::Crate, alt_std_name: Option<String>)
                                -> ast::Crate {
     if use_std(&krate) {
@@ -29,9 +48,12 @@ pub fn maybe_inject_crates_ref(krate: ast::Crate, alt_std_name: Option<String>)
     }
 }
 
-pub fn maybe_inject_prelude(krate: ast::Crate) -> ast::Crate {
+pub fn maybe_inject_prelude(sess: &ParseSess, krate: ast::Crate) -> ast::Crate {
     if use_std(&krate) {
-        inject_prelude(krate)
+        let mut fold = PreludeInjector {
+            span: ignored_span(sess, DUMMY_SP)
+        };
+        fold.fold_crate(krate)
     } else {
         krate
     }
@@ -80,8 +102,9 @@ fn inject_crates_ref(krate: ast::Crate, alt_std_name: Option<String>) -> ast::Cr
     fold.fold_crate(krate)
 }
 
-struct PreludeInjector;
-
+struct PreludeInjector {
+    span: Span
+}
 
 impl fold::Folder for PreludeInjector {
     fn fold_crate(&mut self, mut krate: ast::Crate) -> ast::Crate {
@@ -107,7 +130,7 @@ impl fold::Folder for PreludeInjector {
 
     fn fold_mod(&mut self, mut mod_: ast::Mod) -> ast::Mod {
         let prelude_path = ast::Path {
-            span: DUMMY_SP,
+            span: self.span,
             global: false,
             segments: vec![
                 ast::PathSegment {
@@ -131,12 +154,12 @@ impl fold::Folder for PreludeInjector {
             ident: special_idents::invalid,
             node: ast::ItemUse(vp),
             attrs: vec![ast::Attribute {
-                span: DUMMY_SP,
+                span: self.span,
                 node: ast::Attribute_ {
                     id: attr::mk_attr_id(),
                     style: ast::AttrOuter,
                     value: P(ast::MetaItem {
-                        span: DUMMY_SP,
+                        span: self.span,
                         node: ast::MetaWord(token::get_name(
                                 special_idents::prelude_import.name)),
                     }),
@@ -144,14 +167,9 @@ impl fold::Folder for PreludeInjector {
                 },
             }],
             vis: ast::Inherited,
-            span: DUMMY_SP,
+            span: self.span,
         }));
 
         fold::noop_fold_mod(mod_, self)
     }
 }
-
-fn inject_prelude(krate: ast::Crate) -> ast::Crate {
-    let mut fold = PreludeInjector;
-    fold.fold_crate(krate)
-}
diff --git a/src/test/compile-fail/feature-gate-prelude_import.rs b/src/test/compile-fail/feature-gate-prelude_import.rs
new file mode 100644
index 0000000000000..8bc3df247ec12
--- /dev/null
+++ b/src/test/compile-fail/feature-gate-prelude_import.rs
@@ -0,0 +1,14 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <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.
+
+#[prelude_import] //~ ERROR `#[prelude_import]` is for use by rustc only
+use std::prelude::v1::*;
+
+fn main() {}
diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp
index c2ed10ce6a187..5f7ce68348a3d 100644
--- a/src/test/pretty/issue-4264.pp
+++ b/src/test/pretty/issue-4264.pp
@@ -1,4 +1,4 @@
-#![feature(no_std)]
+#![feature(no_std, prelude_import)]
 #![no_std]
 #[prelude_import]
 use std::prelude::v1::*;