Skip to content

Commit e364f0e

Browse files
committed
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.
1 parent c66554c commit e364f0e

File tree

11 files changed

+150
-28
lines changed

11 files changed

+150
-28
lines changed

src/librustc_driver/driver.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -406,8 +406,10 @@ pub fn phase_2_configure_and_expand(sess: &Session,
406406
//
407407
// baz! should not use this definition unless foo is enabled.
408408

409-
krate = time(time_passes, "configuration 1", move ||
410-
syntax::config::strip_unconfigured_items(sess.diagnostic(), krate));
409+
let mut feature_gated_cfgs = vec![];
410+
krate = time(time_passes, "configuration 1", ||
411+
syntax::config::strip_unconfigured_items(sess.diagnostic(), krate,
412+
&mut feature_gated_cfgs));
411413

412414
*sess.crate_types.borrow_mut() =
413415
collect_crate_types(sess, &krate.attrs);
@@ -511,6 +513,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
511513
cfg,
512514
macros,
513515
syntax_exts,
516+
&mut feature_gated_cfgs,
514517
krate);
515518
if cfg!(windows) {
516519
env::set_var("PATH", &_old_path);
@@ -536,7 +539,17 @@ pub fn phase_2_configure_and_expand(sess: &Session,
536539

537540
// strip again, in case expansion added anything with a #[cfg].
538541
krate = time(time_passes, "configuration 2", ||
539-
syntax::config::strip_unconfigured_items(sess.diagnostic(), krate));
542+
syntax::config::strip_unconfigured_items(sess.diagnostic(), krate,
543+
&mut feature_gated_cfgs));
544+
545+
time(time_passes, "gated configuration checking", || {
546+
let features = sess.features.borrow();
547+
feature_gated_cfgs.sort();
548+
feature_gated_cfgs.dedup();
549+
for cfg in &feature_gated_cfgs {
550+
cfg.check_and_emit(sess.diagnostic(), &features);
551+
}
552+
});
540553

541554
krate = time(time_passes, "maybe building test harness", ||
542555
syntax::test::modify_for_testing(&sess.parse_sess,

src/libsyntax/attr.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use ast::{AttrId, Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, Meta
1919
use codemap::{Span, Spanned, spanned, dummy_spanned};
2020
use codemap::BytePos;
2121
use diagnostic::SpanHandler;
22+
use feature_gate::GatedCfg;
2223
use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
2324
use parse::token::{InternedString, intern_and_get_ident};
2425
use parse::token;
@@ -357,24 +358,28 @@ pub fn requests_inline(attrs: &[Attribute]) -> bool {
357358
}
358359

359360
/// Tests if a cfg-pattern matches the cfg set
360-
pub fn cfg_matches(diagnostic: &SpanHandler, cfgs: &[P<MetaItem>], cfg: &ast::MetaItem) -> bool {
361+
pub fn cfg_matches(diagnostic: &SpanHandler, cfgs: &[P<MetaItem>], cfg: &ast::MetaItem,
362+
feature_gated_cfgs: &mut Vec<GatedCfg>) -> bool {
361363
match cfg.node {
362364
ast::MetaList(ref pred, ref mis) if &pred[..] == "any" =>
363-
mis.iter().any(|mi| cfg_matches(diagnostic, cfgs, &**mi)),
365+
mis.iter().any(|mi| cfg_matches(diagnostic, cfgs, &**mi, feature_gated_cfgs)),
364366
ast::MetaList(ref pred, ref mis) if &pred[..] == "all" =>
365-
mis.iter().all(|mi| cfg_matches(diagnostic, cfgs, &**mi)),
367+
mis.iter().all(|mi| cfg_matches(diagnostic, cfgs, &**mi, feature_gated_cfgs)),
366368
ast::MetaList(ref pred, ref mis) if &pred[..] == "not" => {
367369
if mis.len() != 1 {
368370
diagnostic.span_err(cfg.span, "expected 1 cfg-pattern");
369371
return false;
370372
}
371-
!cfg_matches(diagnostic, cfgs, &*mis[0])
373+
!cfg_matches(diagnostic, cfgs, &*mis[0], feature_gated_cfgs)
372374
}
373375
ast::MetaList(ref pred, _) => {
374376
diagnostic.span_err(cfg.span, &format!("invalid predicate `{}`", pred));
375377
false
376378
},
377-
ast::MetaWord(_) | ast::MetaNameValue(..) => contains(cfgs, cfg),
379+
ast::MetaWord(_) | ast::MetaNameValue(..) => {
380+
feature_gated_cfgs.extend(GatedCfg::gate(cfg));
381+
contains(cfgs, cfg)
382+
}
378383
}
379384
}
380385

src/libsyntax/config.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
use attr::AttrMetaMethods;
1212
use diagnostic::SpanHandler;
13+
use feature_gate::GatedCfg;
1314
use fold::Folder;
1415
use {ast, fold, attr};
1516
use codemap::{Spanned, respan};
@@ -25,10 +26,13 @@ struct Context<F> where F: FnMut(&[ast::Attribute]) -> bool {
2526

2627
// Support conditional compilation by transforming the AST, stripping out
2728
// any items that do not belong in the current configuration
28-
pub fn strip_unconfigured_items(diagnostic: &SpanHandler, krate: ast::Crate) -> ast::Crate {
29-
let krate = process_cfg_attr(diagnostic, krate);
29+
pub fn strip_unconfigured_items(diagnostic: &SpanHandler, krate: ast::Crate,
30+
feature_gated_cfgs: &mut Vec<GatedCfg>)
31+
-> ast::Crate
32+
{
33+
let krate = process_cfg_attr(diagnostic, krate, feature_gated_cfgs);
3034
let config = krate.config.clone();
31-
strip_items(krate, |attrs| in_cfg(diagnostic, &config, attrs))
35+
strip_items(krate, |attrs| in_cfg(diagnostic, &config, attrs, feature_gated_cfgs))
3236
}
3337

3438
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
248252

249253
// Determine if an item should be translated in the current crate
250254
// configuration based on the item's attributes
251-
fn in_cfg(diagnostic: &SpanHandler, cfg: &[P<ast::MetaItem>], attrs: &[ast::Attribute]) -> bool {
255+
fn in_cfg(diagnostic: &SpanHandler, cfg: &[P<ast::MetaItem>], attrs: &[ast::Attribute],
256+
feature_gated_cfgs: &mut Vec<GatedCfg>) -> bool {
252257
attrs.iter().all(|attr| {
253258
let mis = match attr.node.value.node {
254259
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
260265
return true;
261266
}
262267

263-
attr::cfg_matches(diagnostic, cfg, &*mis[0])
268+
attr::cfg_matches(diagnostic, cfg, &*mis[0],
269+
feature_gated_cfgs)
264270
})
265271
}
266272

267-
struct CfgAttrFolder<'a> {
273+
struct CfgAttrFolder<'a, 'b> {
268274
diag: &'a SpanHandler,
269275
config: ast::CrateConfig,
276+
feature_gated_cfgs: &'b mut Vec<GatedCfg>
270277
}
271278

272279
// Process `#[cfg_attr]`.
273-
fn process_cfg_attr(diagnostic: &SpanHandler, krate: ast::Crate) -> ast::Crate {
280+
fn process_cfg_attr(diagnostic: &SpanHandler, krate: ast::Crate,
281+
feature_gated_cfgs: &mut Vec<GatedCfg>) -> ast::Crate {
274282
let mut fld = CfgAttrFolder {
275283
diag: diagnostic,
276284
config: krate.config.clone(),
285+
feature_gated_cfgs: feature_gated_cfgs,
277286
};
278287
fld.fold_crate(krate)
279288
}
280289

281-
impl<'a> fold::Folder for CfgAttrFolder<'a> {
290+
impl<'a,'b> fold::Folder for CfgAttrFolder<'a,'b> {
282291
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
283292
if !attr.check_name("cfg_attr") {
284293
return fold::noop_fold_attribute(attr, self);
@@ -299,7 +308,8 @@ impl<'a> fold::Folder for CfgAttrFolder<'a> {
299308
}
300309
};
301310

302-
if attr::cfg_matches(self.diag, &self.config[..], &cfg) {
311+
if attr::cfg_matches(self.diag, &self.config[..], &cfg,
312+
self.feature_gated_cfgs) {
303313
Some(respan(mi.span, ast::Attribute_ {
304314
id: attr::mk_attr_id(),
305315
style: attr.node.style,

src/libsyntax/ext/base.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION, CompilerExpansion};
1717
use ext;
1818
use ext::expand;
1919
use ext::tt::macro_rules;
20+
use feature_gate::GatedCfg;
2021
use parse;
2122
use parse::parser;
2223
use parse::token;
@@ -632,6 +633,7 @@ pub struct ExtCtxt<'a> {
632633
pub backtrace: ExpnId,
633634
pub ecfg: expand::ExpansionConfig<'a>,
634635
pub crate_root: Option<&'static str>,
636+
pub feature_gated_cfgs: &'a mut Vec<GatedCfg>,
635637

636638
pub mod_path: Vec<ast::Ident> ,
637639
pub exported_macros: Vec<ast::MacroDef>,
@@ -642,7 +644,8 @@ pub struct ExtCtxt<'a> {
642644

643645
impl<'a> ExtCtxt<'a> {
644646
pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig,
645-
ecfg: expand::ExpansionConfig<'a>) -> ExtCtxt<'a> {
647+
ecfg: expand::ExpansionConfig<'a>,
648+
feature_gated_cfgs: &'a mut Vec<GatedCfg>) -> ExtCtxt<'a> {
646649
let env = initial_syntax_expander_table(&ecfg);
647650
ExtCtxt {
648651
parse_sess: parse_sess,
@@ -651,6 +654,7 @@ impl<'a> ExtCtxt<'a> {
651654
mod_path: Vec::new(),
652655
ecfg: ecfg,
653656
crate_root: None,
657+
feature_gated_cfgs: feature_gated_cfgs,
654658
exported_macros: Vec::new(),
655659
syntax_env: env,
656660
recursion_count: 0,

src/libsyntax/ext/cfg.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub fn expand_cfg<'cx>(cx: &mut ExtCtxt,
3434
return DummyResult::expr(sp);
3535
}
3636

37-
let matches_cfg = attr::cfg_matches(&cx.parse_sess.span_diagnostic, &cx.cfg, &*cfg);
37+
let matches_cfg = attr::cfg_matches(&cx.parse_sess.span_diagnostic, &cx.cfg, &*cfg,
38+
cx.feature_gated_cfgs);
3839
MacEager::expr(cx.expr_bool(sp, matches_cfg))
3940
}

src/libsyntax/ext/expand.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use attr::AttrMetaMethods;
2121
use codemap;
2222
use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute, CompilerExpansion};
2323
use ext::base::*;
24-
use feature_gate::{self, Features};
24+
use feature_gate::{self, Features, GatedCfg};
2525
use fold;
2626
use fold::*;
2727
use parse;
@@ -1687,8 +1687,10 @@ pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess,
16871687
// these are the macros being imported to this crate:
16881688
imported_macros: Vec<ast::MacroDef>,
16891689
user_exts: Vec<NamedSyntaxExtension>,
1690+
feature_gated_cfgs: &mut Vec<GatedCfg>,
16901691
c: Crate) -> Crate {
1691-
let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
1692+
let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg,
1693+
feature_gated_cfgs);
16921694
if std_inject::no_core(&c) {
16931695
cx.crate_root = None;
16941696
} else if std_inject::no_std(&c) {
@@ -1878,7 +1880,7 @@ mod tests {
18781880
src,
18791881
Vec::new(), &sess);
18801882
// should fail:
1881-
expand_crate(&sess,test_ecfg(),vec!(),vec!(),crate_ast);
1883+
expand_crate(&sess,test_ecfg(),vec!(),vec!(), &mut vec![], crate_ast);
18821884
}
18831885

18841886
// make sure that macros can't escape modules
@@ -1891,7 +1893,7 @@ mod tests {
18911893
"<test>".to_string(),
18921894
src,
18931895
Vec::new(), &sess);
1894-
expand_crate(&sess,test_ecfg(),vec!(),vec!(),crate_ast);
1896+
expand_crate(&sess,test_ecfg(),vec!(),vec!(), &mut vec![], crate_ast);
18951897
}
18961898

18971899
// macro_use modules should allow macros to escape
@@ -1903,14 +1905,14 @@ mod tests {
19031905
"<test>".to_string(),
19041906
src,
19051907
Vec::new(), &sess);
1906-
expand_crate(&sess, test_ecfg(), vec!(), vec!(), crate_ast);
1908+
expand_crate(&sess, test_ecfg(), vec!(), vec!(), &mut vec![], crate_ast);
19071909
}
19081910

19091911
fn expand_crate_str(crate_str: String) -> ast::Crate {
19101912
let ps = parse::ParseSess::new();
19111913
let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod());
19121914
// the cfg argument actually does matter, here...
1913-
expand_crate(&ps,test_ecfg(),vec!(),vec!(),crate_ast)
1915+
expand_crate(&ps,test_ecfg(),vec!(),vec!(), &mut vec![], crate_ast)
19141916
}
19151917

19161918
// find the pat_ident paths in a crate

src/libsyntax/feature_gate.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use visit::Visitor;
3737
use parse::token::{self, InternedString};
3838

3939
use std::ascii::AsciiExt;
40+
use std::cmp;
4041

4142
// If you change this list without updating src/doc/reference.md, @cmr will be sad
4243
// 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)] = &[
180181

181182
// allow `repr(simd)`, and importing the various simd intrinsics
182183
("simd_basics", "1.3.0", Active),
184+
185+
// Allows cfg(target_feature = "...").
186+
("cfg_target_feature", "1.3.0", Active),
183187
];
184188
// (changing above list without updating src/doc/reference.md makes @cmr sad)
185189

@@ -327,6 +331,59 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[
327331
("recursion_limit", CrateLevel),
328332
];
329333

334+
macro_rules! cfg_fn {
335+
(|$x: ident| $e: expr) => {{
336+
fn f($x: &Features) -> bool {
337+
$e
338+
}
339+
f as fn(&Features) -> bool
340+
}}
341+
}
342+
// cfg(...)'s that are feature gated
343+
const GATED_CFGS: &'static [(&'static str, &'static str, fn(&Features) -> bool)] = &[
344+
// (name in cfg, feature, function to check if the feature is enabled)
345+
("target_feature", "cfg_target_feature", cfg_fn!(|x| x.cfg_target_feature)),
346+
];
347+
348+
#[derive(Debug, Eq, PartialEq)]
349+
pub struct GatedCfg {
350+
span: Span,
351+
index: usize,
352+
}
353+
impl Ord for GatedCfg {
354+
fn cmp(&self, other: &GatedCfg) -> cmp::Ordering {
355+
(self.span.lo.0, self.span.hi.0, self.index)
356+
.cmp(&(other.span.lo.0, other.span.hi.0, other.index))
357+
}
358+
}
359+
impl PartialOrd for GatedCfg {
360+
fn partial_cmp(&self, other: &GatedCfg) -> Option<cmp::Ordering> {
361+
Some(self.cmp(other))
362+
}
363+
}
364+
365+
impl GatedCfg {
366+
pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> {
367+
let name = cfg.name();
368+
GATED_CFGS.iter()
369+
.position(|info| info.0 == name)
370+
.map(|idx| {
371+
GatedCfg {
372+
span: cfg.span,
373+
index: idx
374+
}
375+
})
376+
}
377+
pub fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) {
378+
let (cfg, feature, has_feature) = GATED_CFGS[self.index];
379+
if !has_feature(features) {
380+
let explain = format!("`cfg({})` is experimental and subject to change", cfg);
381+
emit_feature_err(diagnostic, feature, self.span, &explain);
382+
}
383+
}
384+
}
385+
386+
330387
#[derive(PartialEq, Copy, Clone, Debug)]
331388
pub enum AttributeType {
332389
/// Normal, builtin attribute that is consumed
@@ -373,6 +430,7 @@ pub struct Features {
373430
pub static_recursion: bool,
374431
pub default_type_parameter_fallback: bool,
375432
pub type_macros: bool,
433+
pub cfg_target_feature: bool,
376434
}
377435

378436
impl Features {
@@ -401,6 +459,7 @@ impl Features {
401459
static_recursion: false,
402460
default_type_parameter_fallback: false,
403461
type_macros: false,
462+
cfg_target_feature: false,
404463
}
405464
}
406465
}
@@ -920,6 +979,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
920979
static_recursion: cx.has_feature("static_recursion"),
921980
default_type_parameter_fallback: cx.has_feature("default_type_parameter_fallback"),
922981
type_macros: cx.has_feature("type_macros"),
982+
cfg_target_feature: cx.has_feature("cfg_target_feature"),
923983
}
924984
}
925985

src/libsyntax/test.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,13 @@ fn generate_test_harness(sess: &ParseSess,
246246
krate: ast::Crate,
247247
cfg: &ast::CrateConfig,
248248
sd: &diagnostic::SpanHandler) -> ast::Crate {
249+
let mut feature_gated_cfgs = vec![];
249250
let mut cx: TestCtxt = TestCtxt {
250251
sess: sess,
251252
span_diagnostic: sd,
252253
ext_cx: ExtCtxt::new(sess, cfg.clone(),
253-
ExpansionConfig::default("test".to_string())),
254+
ExpansionConfig::default("test".to_string()),
255+
&mut feature_gated_cfgs),
254256
path: Vec::new(),
255257
testfns: Vec::new(),
256258
reexport_test_harness_main: reexport_test_harness_main,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#[cfg(target_feature = "x")] //~ ERROR `cfg(target_feature)` is experimental
12+
#[cfg_attr(target_feature = "x", x)] //~ ERROR `cfg(target_feature)` is experimental
13+
struct Foo(u64, u64);
14+
15+
#[cfg(not(any(all(target_feature = "x"))))] //~ ERROR `cfg(target_feature)` is experimental
16+
fn foo() {}
17+
18+
fn main() {
19+
cfg!(target_feature = "x");
20+
//~^ ERROR `cfg(target_feature)` is experimental and subject to change
21+
}

0 commit comments

Comments
 (0)