Skip to content

Commit dd98f70

Browse files
committed
Implement feature-gating for the compiler
A few features are now hidden behind various #[feature(...)] directives. These include struct-like enum variants, glob imports, and macro_rules! invocations. Closes #9304 Closes #9305 Closes #9306 Closes #9331
1 parent acf9783 commit dd98f70

File tree

15 files changed

+320
-0
lines changed

15 files changed

+320
-0
lines changed

doc/rust.md

+52
Original file line numberDiff line numberDiff line change
@@ -1833,6 +1833,58 @@ fn main() {
18331833
> individual functions, structs, methods and enum variants, *not* to
18341834
> entire modules, traits, impls or enums themselves.
18351835
1836+
### Compiler Features
1837+
1838+
Certain aspects of Rust may be implemented in the compiler, but they're not
1839+
necessarily ready for every-day use. These features are often of "prototype
1840+
quality" or "almost production ready", but may not be stable enough to be
1841+
considered a full-fleged language feature.
1842+
1843+
For this reason, rust recognizes a special crate-level attribute of the form:
1844+
1845+
~~~ {.xfail-test}
1846+
#[feature(feature1, feature2, feature3)]
1847+
~~~
1848+
1849+
This directive informs the compiler that the feature list: `feature1`,
1850+
`feature2`, and `feature3` should all be enabled. This is only recognized at a
1851+
crate-level, not at a module-level. Without this directive, all features are
1852+
considered off, and using the features will result in a compiler error.
1853+
1854+
The currently implemented features of the compiler are:
1855+
1856+
* `macro_rules` - The definition of new macros. This does not encompass
1857+
macro-invocation, that is always enabled by default, this only
1858+
covers the definition of new macros. There are currently
1859+
various problems with invoking macros, how they interact with
1860+
their environment, and possibly how they are used outside of
1861+
location in which they are defined. Macro definitions are
1862+
likely to change slightly in the future, so they are currently
1863+
hidden behind this feature.
1864+
1865+
* `globs` - Importing everything in a module through `*`. This is currently a
1866+
large source of bugs in name resolution for Rust, and it's not clear
1867+
whether this will continue as a feature or not. For these reasons,
1868+
the glob import statement has been hidden behind this feature flag.
1869+
1870+
* `struct_variant` - Structural enum variants (those with named fields). It is
1871+
currently unknown whether this style of enum variant is as
1872+
fully supported as the tuple-forms, and it's not certain
1873+
that this style of variant should remain in the language.
1874+
For now this style of variant is hidden behind a feature
1875+
flag.
1876+
1877+
If a feature is promoted to a language feature, then all existing programs will
1878+
start to receive compilation warnings about #[feature] directives which enabled
1879+
the new feature (because the directive is no longer necessary). However, if
1880+
a feature is decided to be removed from the language, errors will be issued (if
1881+
there isn't a parser error first). The directive in this case is no longer
1882+
necessary, and it's likely that existing code will break if the feature isn't
1883+
removed.
1884+
1885+
If a unknown feature is found in a directive, it results in a compiler error. An
1886+
unknown feature is one which has never been recognized by the compiler.
1887+
18361888
# Statements and expressions
18371889

18381890
Rust is _primarily_ an expression language. This means that most forms of

doc/tutorial.md

+8
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,10 @@ fn area(sh: Shape) -> f64 {
746746
}
747747
~~~~
748748

749+
> ***Note:*** This feature of the compiler is currently gated behind the
750+
> `#[feature(struct_variant)]` directive. More about these directives can be
751+
> found in the manual.
752+
749753
## Tuples
750754

751755
Tuples in Rust behave exactly like structs, except that their fields
@@ -2665,6 +2669,10 @@ use farm::*;
26652669
# fn main() { cow(); chicken() }
26662670
~~~
26672671

2672+
> ***Note:*** This feature of the compiler is currently gated behind the
2673+
> `#[feature(globs)]` directive. More about these directives can be found in
2674+
> the manual.
2675+
26682676
However, that's not all. You can also rename an item while you're bringing it into scope:
26692677

26702678
~~~

src/libextra/extra.rs

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ Rust extras are part of the standard Rust distribution.
3333
#[license = "MIT/ASL2"];
3434
#[crate_type = "lib"];
3535

36+
#[feature(macro_rules, globs)];
37+
3638
#[deny(non_camel_case_types)];
3739
#[deny(missing_doc)];
3840

src/librustc/driver/driver.rs

+2
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ pub fn phase_2_configure_and_expand(sess: Session,
159159
*sess.building_library = session::building_library(sess.opts.crate_type,
160160
&crate, sess.opts.test);
161161

162+
time(time_passes, ~"gated feature checking", (), |_|
163+
front::feature_gate::check_crate(sess, &crate));
162164

163165
// strip before expansion to allow macros to depend on
164166
// configuration variables e.g/ in

src/librustc/front/feature_gate.rs

+176
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
// Copyright 2013 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+
//! Feature gating
12+
//!
13+
//! This modules implements the gating necessary for preventing certain compiler
14+
//! features from being used by default. This module will crawl a pre-expanded
15+
//! AST to ensure that there are no features which are used that are not
16+
//! enabled.
17+
//!
18+
//! Features are enabled in programs via the crate-level attributes of
19+
//! #[feature(...)] with a comma-separated list of features.
20+
21+
use syntax::ast;
22+
use syntax::attr::AttrMetaMethods;
23+
use syntax::codemap::Span;
24+
use syntax::visit;
25+
use syntax::visit::Visitor;
26+
27+
use driver::session::Session;
28+
29+
/// This is a list of all known features since the beginning of time. This list
30+
/// can never shrink, it may only be expanded (in order to prevent old programs
31+
/// from failing to compile). The status of each feature may change, however.
32+
static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
33+
("globs", Active),
34+
("macro_rules", Active),
35+
("struct_variant", Active),
36+
37+
// These are used to test this portion of the compiler, they don't actually
38+
// mean anything
39+
("test_accepted_feature", Accepted),
40+
("test_removed_feature", Removed),
41+
];
42+
43+
enum Status {
44+
/// Represents an active feature that is currently being implemented or
45+
/// currently being considered for addition/removal.
46+
Active,
47+
48+
/// Represents a feature which has since been removed (it was once Active)
49+
Removed,
50+
51+
/// This language feature has since been Accepted (it was once Active)
52+
Accepted,
53+
}
54+
55+
struct Context {
56+
features: ~[&'static str],
57+
sess: Session,
58+
}
59+
60+
impl Context {
61+
fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
62+
if !self.has_feature(feature) {
63+
self.sess.span_err(span, explain);
64+
self.sess.span_note(span, format!("add \\#[feature({})] to the \
65+
crate attributes to enable",
66+
feature));
67+
}
68+
}
69+
70+
fn has_feature(&self, feature: &str) -> bool {
71+
self.features.iter().any(|n| n.as_slice() == feature)
72+
}
73+
}
74+
75+
impl Visitor<()> for Context {
76+
fn visit_view_item(&mut self, i: &ast::view_item, _: ()) {
77+
match i.node {
78+
ast::view_item_use(ref paths) => {
79+
for path in paths.iter() {
80+
match path.node {
81+
ast::view_path_glob(*) => {
82+
self.gate_feature("globs", path.span,
83+
"glob import statements are \
84+
experimental and possibly buggy");
85+
}
86+
_ => {}
87+
}
88+
}
89+
}
90+
_ => {}
91+
}
92+
visit::walk_view_item(self, i, ())
93+
}
94+
95+
fn visit_item(&mut self, i: @ast::item, _:()) {
96+
match i.node {
97+
ast::item_enum(ref def, _) => {
98+
for variant in def.variants.iter() {
99+
match variant.node.kind {
100+
ast::struct_variant_kind(*) => {
101+
self.gate_feature("struct_variant", variant.span,
102+
"enum struct variants are \
103+
experimental and possibly buggy");
104+
}
105+
_ => {}
106+
}
107+
}
108+
}
109+
110+
ast::item_mac(ref mac) => {
111+
match mac.node {
112+
ast::mac_invoc_tt(ref path, _, _) => {
113+
let rules = self.sess.ident_of("macro_rules");
114+
if path.segments.last().identifier == rules {
115+
self.gate_feature("macro_rules", i.span,
116+
"macro definitions are not \
117+
stable enough for use and are \
118+
subject to change");
119+
}
120+
}
121+
}
122+
}
123+
124+
_ => {}
125+
}
126+
127+
visit::walk_item(self, i, ());
128+
}
129+
}
130+
131+
pub fn check_crate(sess: Session, crate: &ast::Crate) {
132+
let mut cx = Context {
133+
features: ~[],
134+
sess: sess,
135+
};
136+
137+
for attr in crate.attrs.iter() {
138+
if "feature" != attr.name() { continue }
139+
140+
match attr.meta_item_list() {
141+
None => {
142+
sess.span_err(attr.span, "malformed feature attribute, \
143+
expected #[feature(...)]");
144+
}
145+
Some(list) => {
146+
for &mi in list.iter() {
147+
let name = match mi.node {
148+
ast::MetaWord(word) => word,
149+
_ => {
150+
sess.span_err(mi.span, "malformed feature, expected \
151+
just one word");
152+
continue
153+
}
154+
};
155+
match KNOWN_FEATURES.iter().find(|& &(n, _)| n == name) {
156+
Some(&(name, Active)) => { cx.features.push(name); }
157+
Some(&(_, Removed)) => {
158+
sess.span_err(mi.span, "feature has been removed");
159+
}
160+
Some(&(_, Accepted)) => {
161+
sess.span_warn(mi.span, "feature has added to rust, \
162+
directive not necessary");
163+
}
164+
None => {
165+
sess.span_err(mi.span, "unknown feature");
166+
}
167+
}
168+
}
169+
}
170+
}
171+
}
172+
173+
visit::walk_crate(&mut cx, crate, ());
174+
175+
sess.abort_if_errors();
176+
}

src/librustc/rustc.rs

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#[license = "MIT/ASL2"];
1818
#[crate_type = "lib"];
1919

20+
#[feature(macro_rules, globs, struct_variant)];
21+
2022
// Rustc tasks always run on a fixed_stack_segment, so code in this
2123
// module can call C functions (in particular, LLVM functions) with
2224
// impunity.
@@ -83,6 +85,7 @@ pub mod front {
8385
pub mod test;
8486
pub mod std_inject;
8587
pub mod assign_node_ids;
88+
pub mod feature_gate;
8689
}
8790

8891
pub mod back {

src/librustdoc/rustdoc.rs

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#[license = "MIT/ASL2"];
1818
#[crate_type = "lib"];
1919

20+
#[feature(globs, struct_variant)];
21+
2022
extern mod syntax;
2123
extern mod rustc;
2224
extern mod extra;

src/librusti/rusti.rs

+2
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@
6666
#[license = "MIT/ASL2"];
6767
#[crate_type = "lib"];
6868

69+
#[feature(globs)];
70+
6971
extern mod extra;
7072
extern mod rustc;
7173
extern mod syntax;

src/librustpkg/rustpkg.rs

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#[license = "MIT/ASL2"];
1919
#[crate_type = "lib"];
2020

21+
#[feature(globs)];
22+
2123
extern mod extra;
2224
extern mod rustc;
2325
extern mod syntax;

src/libstd/std.rs

+2
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ they contained the following prologue:
6161
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
6262
html_root_url = "http://static.rust-lang.org/doc/master")];
6363

64+
#[feature(macro_rules, globs)];
65+
6466
// Don't link to std. We are std.
6567
#[no_std];
6668

src/libsyntax/syntax.rs

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#[license = "MIT/ASL2"];
2121
#[crate_type = "lib"];
2222

23+
#[feature(macro_rules, globs)];
24+
2325
extern mod extra;
2426

2527
pub mod util {
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2013 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+
#[feature(
12+
foo_bar_baz,
13+
foo(bar),
14+
foo = "baz"
15+
)];
16+
//~^^^^ ERROR: unknown feature
17+
//~^^^^ ERROR: malformed feature
18+
//~^^^^ ERROR: malformed feature
19+
20+
#[feature]; //~ ERROR: malformed feature
21+
#[feature = "foo"]; //~ ERROR: malformed feature
22+
23+
#[feature(test_removed_feature)]; //~ ERROR: feature has been removed
24+
#[feature(test_accepted_feature)]; //~ WARNING: feature has added
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2013 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+
use std::*;
12+
//~^ ERROR: glob import statements are experimental
13+
14+
fn main() {}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2013 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+
macro_rules! foo(() => ())
12+
//~^ ERROR: macro definitions are not stable enough for use
13+
14+
fn main() {}

0 commit comments

Comments
 (0)