]> git.lizzy.rs Git - rust.git/commitdiff
feature gate `cfg(target_feature)`.
authorHuon Wilson <dbau.pp+github@gmail.com>
Tue, 14 Jul 2015 00:10:44 +0000 (17:10 -0700)
committerHuon Wilson <dbau.pp+github@gmail.com>
Mon, 17 Aug 2015 21:41:37 +0000 (14:41 -0700)
This is theoretically a breaking change, but GitHub search turns up no
uses of it, and most non-built-in cfg's are passed via cargo features,
which look like `feature = "..."`, and hence can't overlap.

src/librustc_driver/driver.rs
src/libsyntax/attr.rs
src/libsyntax/config.rs
src/libsyntax/ext/base.rs
src/libsyntax/ext/cfg.rs
src/libsyntax/ext/expand.rs
src/libsyntax/feature_gate.rs
src/libsyntax/test.rs
src/test/compile-fail/feature-gate-cfg-target-feature.rs [new file with mode: 0644]
src/test/run-fail-fulldeps/qquote.rs
src/test/run-pass-fulldeps/qquote.rs

index 263a8e14807027dd3743bc196389d7af0ce84375..346e7a7bf9886284802d5f8ee34ae8411ea4a830 100644 (file)
@@ -406,8 +406,10 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     //
     // baz! should not use this definition unless foo is enabled.
 
-    krate = time(time_passes, "configuration 1", move ||
-                 syntax::config::strip_unconfigured_items(sess.diagnostic(), krate));
+    let mut feature_gated_cfgs = vec![];
+    krate = time(time_passes, "configuration 1", ||
+                 syntax::config::strip_unconfigured_items(sess.diagnostic(), krate,
+                                                          &mut feature_gated_cfgs));
 
     *sess.crate_types.borrow_mut() =
         collect_crate_types(sess, &krate.attrs);
@@ -511,6 +513,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
                                           cfg,
                                           macros,
                                           syntax_exts,
+                                          &mut feature_gated_cfgs,
                                           krate);
         if cfg!(windows) {
             env::set_var("PATH", &_old_path);
@@ -536,7 +539,17 @@ pub fn phase_2_configure_and_expand(sess: &Session,
 
     // strip again, in case expansion added anything with a #[cfg].
     krate = time(time_passes, "configuration 2", ||
-                 syntax::config::strip_unconfigured_items(sess.diagnostic(), krate));
+                 syntax::config::strip_unconfigured_items(sess.diagnostic(), krate,
+                                                          &mut feature_gated_cfgs));
+
+    time(time_passes, "gated configuration checking", || {
+        let features = sess.features.borrow();
+        feature_gated_cfgs.sort();
+        feature_gated_cfgs.dedup();
+        for cfg in &feature_gated_cfgs {
+            cfg.check_and_emit(sess.diagnostic(), &features);
+        }
+    });
 
     krate = time(time_passes, "maybe building test harness", ||
                  syntax::test::modify_for_testing(&sess.parse_sess,
index 3de9ba5197489931eb64de491f651ad9387c4b33..7540c2ff831e932976a2e0feca58e781fd25a542 100644 (file)
@@ -19,6 +19,7 @@
 use codemap::{Span, Spanned, spanned, dummy_spanned};
 use codemap::BytePos;
 use diagnostic::SpanHandler;
+use feature_gate::GatedCfg;
 use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
 use parse::token::{InternedString, intern_and_get_ident};
 use parse::token;
@@ -357,24 +358,28 @@ pub fn requests_inline(attrs: &[Attribute]) -> bool {
 }
 
 /// Tests if a cfg-pattern matches the cfg set
-pub fn cfg_matches(diagnostic: &SpanHandler, cfgs: &[P<MetaItem>], cfg: &ast::MetaItem) -> bool {
+pub fn cfg_matches(diagnostic: &SpanHandler, cfgs: &[P<MetaItem>], cfg: &ast::MetaItem,
+                   feature_gated_cfgs: &mut Vec<GatedCfg>) -> bool {
     match cfg.node {
         ast::MetaList(ref pred, ref mis) if &pred[..] == "any" =>
-            mis.iter().any(|mi| cfg_matches(diagnostic, cfgs, &**mi)),
+            mis.iter().any(|mi| cfg_matches(diagnostic, cfgs, &**mi, feature_gated_cfgs)),
         ast::MetaList(ref pred, ref mis) if &pred[..] == "all" =>
-            mis.iter().all(|mi| cfg_matches(diagnostic, cfgs, &**mi)),
+            mis.iter().all(|mi| cfg_matches(diagnostic, cfgs, &**mi, feature_gated_cfgs)),
         ast::MetaList(ref pred, ref mis) if &pred[..] == "not" => {
             if mis.len() != 1 {
                 diagnostic.span_err(cfg.span, "expected 1 cfg-pattern");
                 return false;
             }
-            !cfg_matches(diagnostic, cfgs, &*mis[0])
+            !cfg_matches(diagnostic, cfgs, &*mis[0], feature_gated_cfgs)
         }
         ast::MetaList(ref pred, _) => {
             diagnostic.span_err(cfg.span, &format!("invalid predicate `{}`", pred));
             false
         },
-        ast::MetaWord(_) | ast::MetaNameValue(..) => contains(cfgs, cfg),
+        ast::MetaWord(_) | ast::MetaNameValue(..) => {
+            feature_gated_cfgs.extend(GatedCfg::gate(cfg));
+            contains(cfgs, cfg)
+        }
     }
 }
 
index 366806bc19b4962882552eb27725679acb9d1177..faf0b51c8de0eda798c088a140ed32b2c880ac73 100644 (file)
@@ -10,6 +10,7 @@
 
 use attr::AttrMetaMethods;
 use diagnostic::SpanHandler;
+use feature_gate::GatedCfg;
 use fold::Folder;
 use {ast, fold, attr};
 use codemap::{Spanned, respan};
@@ -25,10 +26,13 @@ struct Context<F> where F: FnMut(&[ast::Attribute]) -> bool {
 
 // Support conditional compilation by transforming the AST, stripping out
 // any items that do not belong in the current configuration
-pub fn strip_unconfigured_items(diagnostic: &SpanHandler, krate: ast::Crate) -> ast::Crate {
-    let krate = process_cfg_attr(diagnostic, krate);
+pub fn strip_unconfigured_items(diagnostic: &SpanHandler, krate: ast::Crate,
+                                feature_gated_cfgs: &mut Vec<GatedCfg>)
+                                -> ast::Crate
+{
+    let krate = process_cfg_attr(diagnostic, krate, feature_gated_cfgs);
     let config = krate.config.clone();
-    strip_items(krate, |attrs| in_cfg(diagnostic, &config, attrs))
+    strip_items(krate, |attrs| in_cfg(diagnostic, &config, attrs, feature_gated_cfgs))
 }
 
 impl<F> fold::Folder for Context<F> where F: FnMut(&[ast::Attribute]) -> bool {
@@ -248,7 +252,8 @@ fn foreign_item_in_cfg<F>(cx: &mut Context<F>, item: &ast::ForeignItem) -> bool
 
 // Determine if an item should be translated in the current crate
 // configuration based on the item's attributes
-fn in_cfg(diagnostic: &SpanHandler, cfg: &[P<ast::MetaItem>], attrs: &[ast::Attribute]) -> bool {
+fn in_cfg(diagnostic: &SpanHandler, cfg: &[P<ast::MetaItem>], attrs: &[ast::Attribute],
+          feature_gated_cfgs: &mut Vec<GatedCfg>) -> bool {
     attrs.iter().all(|attr| {
         let mis = match attr.node.value.node {
             ast::MetaList(_, ref mis) if attr.check_name("cfg") => mis,
@@ -260,25 +265,29 @@ fn in_cfg(diagnostic: &SpanHandler, cfg: &[P<ast::MetaItem>], attrs: &[ast::Attr
             return true;
         }
 
-        attr::cfg_matches(diagnostic, cfg, &*mis[0])
+        attr::cfg_matches(diagnostic, cfg, &*mis[0],
+                          feature_gated_cfgs)
     })
 }
 
-struct CfgAttrFolder<'a> {
+struct CfgAttrFolder<'a, 'b> {
     diag: &'a SpanHandler,
     config: ast::CrateConfig,
+    feature_gated_cfgs: &'b mut Vec<GatedCfg>
 }
 
 // Process `#[cfg_attr]`.
-fn process_cfg_attr(diagnostic: &SpanHandler, krate: ast::Crate) -> ast::Crate {
+fn process_cfg_attr(diagnostic: &SpanHandler, krate: ast::Crate,
+                    feature_gated_cfgs: &mut Vec<GatedCfg>) -> ast::Crate {
     let mut fld = CfgAttrFolder {
         diag: diagnostic,
         config: krate.config.clone(),
+        feature_gated_cfgs: feature_gated_cfgs,
     };
     fld.fold_crate(krate)
 }
 
-impl<'a> fold::Folder for CfgAttrFolder<'a> {
+impl<'a,'b> fold::Folder for CfgAttrFolder<'a,'b> {
     fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
         if !attr.check_name("cfg_attr") {
             return fold::noop_fold_attribute(attr, self);
@@ -299,7 +308,8 @@ fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
             }
         };
 
-        if attr::cfg_matches(self.diag, &self.config[..], &cfg) {
+        if attr::cfg_matches(self.diag, &self.config[..], &cfg,
+                             self.feature_gated_cfgs) {
             Some(respan(mi.span, ast::Attribute_ {
                 id: attr::mk_attr_id(),
                 style: attr.node.style,
index d4b5e67eeb4928229b71e3a9534e257863a96633..ef11a2bd66e804bd5e80a915b0785a78c6c2af42 100644 (file)
@@ -17,6 +17,7 @@
 use ext;
 use ext::expand;
 use ext::tt::macro_rules;
+use feature_gate::GatedCfg;
 use parse;
 use parse::parser;
 use parse::token;
@@ -632,6 +633,7 @@ pub struct ExtCtxt<'a> {
     pub backtrace: ExpnId,
     pub ecfg: expand::ExpansionConfig<'a>,
     pub crate_root: Option<&'static str>,
+    pub feature_gated_cfgs: &'a mut Vec<GatedCfg>,
 
     pub mod_path: Vec<ast::Ident> ,
     pub exported_macros: Vec<ast::MacroDef>,
@@ -642,7 +644,8 @@ pub struct ExtCtxt<'a> {
 
 impl<'a> ExtCtxt<'a> {
     pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig,
-               ecfg: expand::ExpansionConfig<'a>) -> ExtCtxt<'a> {
+               ecfg: expand::ExpansionConfig<'a>,
+               feature_gated_cfgs: &'a mut Vec<GatedCfg>) -> ExtCtxt<'a> {
         let env = initial_syntax_expander_table(&ecfg);
         ExtCtxt {
             parse_sess: parse_sess,
@@ -651,6 +654,7 @@ pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig,
             mod_path: Vec::new(),
             ecfg: ecfg,
             crate_root: None,
+            feature_gated_cfgs: feature_gated_cfgs,
             exported_macros: Vec::new(),
             syntax_env: env,
             recursion_count: 0,
index 8af7fb7b268afad0326211321d7405697ea7253b..aa654e30530afc1e8b01ecdb229cffc708660fc2 100644 (file)
@@ -34,6 +34,7 @@ pub fn expand_cfg<'cx>(cx: &mut ExtCtxt,
         return DummyResult::expr(sp);
     }
 
-    let matches_cfg = attr::cfg_matches(&cx.parse_sess.span_diagnostic, &cx.cfg, &*cfg);
+    let matches_cfg = attr::cfg_matches(&cx.parse_sess.span_diagnostic, &cx.cfg, &*cfg,
+                                        cx.feature_gated_cfgs);
     MacEager::expr(cx.expr_bool(sp, matches_cfg))
 }
index e61a0b5401efd96f899b0ac79feb0fad29fb21e2..4f89b3494d403954c1a6e2f768b8f555ca6a0d03 100644 (file)
@@ -21,7 +21,7 @@
 use codemap;
 use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute, CompilerExpansion};
 use ext::base::*;
-use feature_gate::{self, Features};
+use feature_gate::{self, Features, GatedCfg};
 use fold;
 use fold::*;
 use parse;
@@ -1687,8 +1687,10 @@ pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess,
                            // these are the macros being imported to this crate:
                            imported_macros: Vec<ast::MacroDef>,
                            user_exts: Vec<NamedSyntaxExtension>,
+                           feature_gated_cfgs: &mut Vec<GatedCfg>,
                            c: Crate) -> Crate {
-    let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
+    let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg,
+                              feature_gated_cfgs);
     if std_inject::no_core(&c) {
         cx.crate_root = None;
     } else if std_inject::no_std(&c) {
@@ -1878,7 +1880,7 @@ fn test_ecfg() -> ExpansionConfig<'static> {
             src,
             Vec::new(), &sess);
         // should fail:
-        expand_crate(&sess,test_ecfg(),vec!(),vec!(),crate_ast);
+        expand_crate(&sess,test_ecfg(),vec!(),vec!(), &mut vec![], crate_ast);
     }
 
     // make sure that macros can't escape modules
@@ -1891,7 +1893,7 @@ fn test_ecfg() -> ExpansionConfig<'static> {
             "<test>".to_string(),
             src,
             Vec::new(), &sess);
-        expand_crate(&sess,test_ecfg(),vec!(),vec!(),crate_ast);
+        expand_crate(&sess,test_ecfg(),vec!(),vec!(), &mut vec![], crate_ast);
     }
 
     // macro_use modules should allow macros to escape
@@ -1903,14 +1905,14 @@ fn test_ecfg() -> ExpansionConfig<'static> {
             "<test>".to_string(),
             src,
             Vec::new(), &sess);
-        expand_crate(&sess, test_ecfg(), vec!(), vec!(), crate_ast);
+        expand_crate(&sess, test_ecfg(), vec!(), vec!(), &mut vec![], crate_ast);
     }
 
     fn expand_crate_str(crate_str: String) -> ast::Crate {
         let ps = parse::ParseSess::new();
         let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod());
         // the cfg argument actually does matter, here...
-        expand_crate(&ps,test_ecfg(),vec!(),vec!(),crate_ast)
+        expand_crate(&ps,test_ecfg(),vec!(),vec!(), &mut vec![], crate_ast)
     }
 
     // find the pat_ident paths in a crate
index a12291161f74f657d2bacc5288cabdab42cc6cc5..9a1c97a4d29f567ad737481264d1309af37dabf9 100644 (file)
@@ -37,6 +37,7 @@
 use parse::token::{self, InternedString};
 
 use std::ascii::AsciiExt;
+use std::cmp;
 
 // If you change this list without updating src/doc/reference.md, @cmr will be sad
 // Don't ever remove anything from this list; set them to 'Removed'.
 
     // allow `repr(simd)`, and importing the various simd intrinsics
     ("simd_basics", "1.3.0", Active),
+
+    // Allows cfg(target_feature = "...").
+    ("cfg_target_feature", "1.3.0", Active),
 ];
 // (changing above list without updating src/doc/reference.md makes @cmr sad)
 
@@ -327,6 +331,59 @@ enum Status {
     ("recursion_limit", CrateLevel),
 ];
 
+macro_rules! cfg_fn {
+    (|$x: ident| $e: expr) => {{
+        fn f($x: &Features) -> bool {
+            $e
+        }
+        f as fn(&Features) -> bool
+    }}
+}
+// cfg(...)'s that are feature gated
+const GATED_CFGS: &'static [(&'static str, &'static str, fn(&Features) -> bool)] = &[
+    // (name in cfg, feature, function to check if the feature is enabled)
+    ("target_feature", "cfg_target_feature", cfg_fn!(|x| x.cfg_target_feature)),
+];
+
+#[derive(Debug, Eq, PartialEq)]
+pub struct GatedCfg {
+    span: Span,
+    index: usize,
+}
+impl Ord for GatedCfg {
+    fn cmp(&self, other: &GatedCfg) -> cmp::Ordering {
+        (self.span.lo.0, self.span.hi.0, self.index)
+            .cmp(&(other.span.lo.0, other.span.hi.0, other.index))
+    }
+}
+impl PartialOrd for GatedCfg {
+    fn partial_cmp(&self, other: &GatedCfg) -> Option<cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl GatedCfg {
+    pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> {
+        let name = cfg.name();
+        GATED_CFGS.iter()
+                  .position(|info| info.0 == name)
+                  .map(|idx| {
+                      GatedCfg {
+                          span: cfg.span,
+                          index: idx
+                      }
+                  })
+    }
+    pub fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) {
+        let (cfg, feature, has_feature) = GATED_CFGS[self.index];
+        if !has_feature(features) {
+            let explain = format!("`cfg({})` is experimental and subject to change", cfg);
+            emit_feature_err(diagnostic, feature, self.span, &explain);
+        }
+    }
+}
+
+
 #[derive(PartialEq, Copy, Clone, Debug)]
 pub enum AttributeType {
     /// Normal, builtin attribute that is consumed
@@ -373,6 +430,7 @@ pub struct Features {
     pub static_recursion: bool,
     pub default_type_parameter_fallback: bool,
     pub type_macros: bool,
+    pub cfg_target_feature: bool,
 }
 
 impl Features {
@@ -401,6 +459,7 @@ pub fn new() -> Features {
             static_recursion: false,
             default_type_parameter_fallback: false,
             type_macros: false,
+            cfg_target_feature: false,
         }
     }
 }
@@ -920,6 +979,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
         static_recursion: cx.has_feature("static_recursion"),
         default_type_parameter_fallback: cx.has_feature("default_type_parameter_fallback"),
         type_macros: cx.has_feature("type_macros"),
+        cfg_target_feature: cx.has_feature("cfg_target_feature"),
     }
 }
 
index ea99291d6c2911239d849ba084ce4afc20c1c9a5..26fb287ce35d1bbfc0f878952d756f0568cd7012 100644 (file)
@@ -246,11 +246,13 @@ fn generate_test_harness(sess: &ParseSess,
                          krate: ast::Crate,
                          cfg: &ast::CrateConfig,
                          sd: &diagnostic::SpanHandler) -> ast::Crate {
+    let mut feature_gated_cfgs = vec![];
     let mut cx: TestCtxt = TestCtxt {
         sess: sess,
         span_diagnostic: sd,
         ext_cx: ExtCtxt::new(sess, cfg.clone(),
-                             ExpansionConfig::default("test".to_string())),
+                             ExpansionConfig::default("test".to_string()),
+                             &mut feature_gated_cfgs),
         path: Vec::new(),
         testfns: Vec::new(),
         reexport_test_harness_main: reexport_test_harness_main,
diff --git a/src/test/compile-fail/feature-gate-cfg-target-feature.rs b/src/test/compile-fail/feature-gate-cfg-target-feature.rs
new file mode 100644 (file)
index 0000000..7832e1c
--- /dev/null
@@ -0,0 +1,21 @@
+// 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.
+
+#[cfg(target_feature = "x")] //~ ERROR `cfg(target_feature)` is experimental
+#[cfg_attr(target_feature = "x", x)] //~ ERROR `cfg(target_feature)` is experimental
+struct Foo(u64, u64);
+
+#[cfg(not(any(all(target_feature = "x"))))] //~ ERROR `cfg(target_feature)` is experimental
+fn foo() {}
+
+fn main() {
+    cfg!(target_feature = "x");
+    //~^ ERROR `cfg(target_feature)` is experimental and subject to change
+}
index 4251579bbdcd1ba21334bb2de131a762ebc7a3dc..eac38037b4bc1a12e6d0077da7806e474a2c9760 100644 (file)
 
 fn main() {
     let ps = syntax::parse::ParseSess::new();
+    let mut feature_gated_cfgs = vec![];
     let mut cx = syntax::ext::base::ExtCtxt::new(
         &ps, vec![],
-        syntax::ext::expand::ExpansionConfig::default("qquote".to_string()));
+        syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
+        &mut feature_gated_cfgs);
     cx.bt_push(syntax::codemap::ExpnInfo {
         call_site: DUMMY_SP,
         callee: syntax::codemap::NameAndSpan {
index 6670f200ba71ed37ef90f0d7e65c034ebe7de5c4..e272a5fe4f6cd091ddcb66c1e9e06a3fd5b858d4 100644 (file)
 
 fn main() {
     let ps = syntax::parse::ParseSess::new();
+    let mut feature_gated_cfgs = vec![];
     let mut cx = syntax::ext::base::ExtCtxt::new(
         &ps, vec![],
-        syntax::ext::expand::ExpansionConfig::default("qquote".to_string()));
+        syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
+        &mut feature_gated_cfgs);
     cx.bt_push(syntax::codemap::ExpnInfo {
         call_site: DUMMY_SP,
         callee: syntax::codemap::NameAndSpan {