feature gate cfg(target_feature)
.
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.
This commit is contained in:
parent
c66554cab3
commit
e364f0eb5a
11 changed files with 150 additions and 28 deletions
|
@ -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,
|
||||
|
|
|
@ -19,6 +19,7 @@ use ast::{AttrId, Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, Meta
|
|||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 @@ impl<'a> fold::Folder for CfgAttrFolder<'a> {
|
|||
}
|
||||
};
|
||||
|
||||
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,
|
||||
|
|
|
@ -17,6 +17,7 @@ use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION, CompilerExpansion};
|
|||
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 @@ impl<'a> ExtCtxt<'a> {
|
|||
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,
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ use attr::AttrMetaMethods;
|
|||
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 @@ mod tests {
|
|||
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 @@ mod tests {
|
|||
"<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 @@ mod tests {
|
|||
"<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
|
||||
|
|
|
@ -37,6 +37,7 @@ use visit::Visitor;
|
|||
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'.
|
||||
|
@ -180,6 +181,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
|
|||
|
||||
// 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 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[
|
|||
("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 @@ impl 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"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
21
src/test/compile-fail/feature-gate-cfg-target-feature.rs
Normal file
21
src/test/compile-fail/feature-gate-cfg-target-feature.rs
Normal file
|
@ -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
|
||||
}
|
|
@ -23,9 +23,11 @@ use syntax::print::pprust;
|
|||
|
||||
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 {
|
||||
|
|
|
@ -19,9 +19,11 @@ use syntax::print::pprust::*;
|
|||
|
||||
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 {
|
||||
|
|
Loading…
Reference in a new issue