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
This commit is contained in:
Alex Crichton 2013-10-02 18:10:16 -07:00
parent acf9783879
commit dd98f7089f
15 changed files with 320 additions and 0 deletions

View File

@ -1833,6 +1833,58 @@ fn main() {
> individual functions, structs, methods and enum variants, *not* to
> entire modules, traits, impls or enums themselves.
### Compiler Features
Certain aspects of Rust may be implemented in the compiler, but they're not
necessarily ready for every-day use. These features are often of "prototype
quality" or "almost production ready", but may not be stable enough to be
considered a full-fleged language feature.
For this reason, rust recognizes a special crate-level attribute of the form:
~~~ {.xfail-test}
#[feature(feature1, feature2, feature3)]
~~~
This directive informs the compiler that the feature list: `feature1`,
`feature2`, and `feature3` should all be enabled. This is only recognized at a
crate-level, not at a module-level. Without this directive, all features are
considered off, and using the features will result in a compiler error.
The currently implemented features of the compiler are:
* `macro_rules` - The definition of new macros. This does not encompass
macro-invocation, that is always enabled by default, this only
covers the definition of new macros. There are currently
various problems with invoking macros, how they interact with
their environment, and possibly how they are used outside of
location in which they are defined. Macro definitions are
likely to change slightly in the future, so they are currently
hidden behind this feature.
* `globs` - Importing everything in a module through `*`. This is currently a
large source of bugs in name resolution for Rust, and it's not clear
whether this will continue as a feature or not. For these reasons,
the glob import statement has been hidden behind this feature flag.
* `struct_variant` - Structural enum variants (those with named fields). It is
currently unknown whether this style of enum variant is as
fully supported as the tuple-forms, and it's not certain
that this style of variant should remain in the language.
For now this style of variant is hidden behind a feature
flag.
If a feature is promoted to a language feature, then all existing programs will
start to receive compilation warnings about #[feature] directives which enabled
the new feature (because the directive is no longer necessary). However, if
a feature is decided to be removed from the language, errors will be issued (if
there isn't a parser error first). The directive in this case is no longer
necessary, and it's likely that existing code will break if the feature isn't
removed.
If a unknown feature is found in a directive, it results in a compiler error. An
unknown feature is one which has never been recognized by the compiler.
# Statements and expressions
Rust is _primarily_ an expression language. This means that most forms of

View File

@ -746,6 +746,10 @@ fn area(sh: Shape) -> f64 {
}
~~~~
> ***Note:*** This feature of the compiler is currently gated behind the
> `#[feature(struct_variant)]` directive. More about these directives can be
> found in the manual.
## Tuples
Tuples in Rust behave exactly like structs, except that their fields
@ -2665,6 +2669,10 @@ use farm::*;
# fn main() { cow(); chicken() }
~~~
> ***Note:*** This feature of the compiler is currently gated behind the
> `#[feature(globs)]` directive. More about these directives can be found in
> the manual.
However, that's not all. You can also rename an item while you're bringing it into scope:
~~~

View File

@ -33,6 +33,8 @@ Rust extras are part of the standard Rust distribution.
#[license = "MIT/ASL2"];
#[crate_type = "lib"];
#[feature(macro_rules, globs)];
#[deny(non_camel_case_types)];
#[deny(missing_doc)];

View File

@ -159,6 +159,8 @@ pub fn phase_2_configure_and_expand(sess: Session,
*sess.building_library = session::building_library(sess.opts.crate_type,
&crate, sess.opts.test);
time(time_passes, ~"gated feature checking", (), |_|
front::feature_gate::check_crate(sess, &crate));
// strip before expansion to allow macros to depend on
// configuration variables e.g/ in

View File

@ -0,0 +1,176 @@
// Copyright 2013 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.
//! Feature gating
//!
//! This modules implements the gating necessary for preventing certain compiler
//! features from being used by default. This module will crawl a pre-expanded
//! AST to ensure that there are no features which are used that are not
//! enabled.
//!
//! Features are enabled in programs via the crate-level attributes of
//! #[feature(...)] with a comma-separated list of features.
use syntax::ast;
use syntax::attr::AttrMetaMethods;
use syntax::codemap::Span;
use syntax::visit;
use syntax::visit::Visitor;
use driver::session::Session;
/// This is a list of all known features since the beginning of time. This list
/// can never shrink, it may only be expanded (in order to prevent old programs
/// from failing to compile). The status of each feature may change, however.
static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
("globs", Active),
("macro_rules", Active),
("struct_variant", Active),
// These are used to test this portion of the compiler, they don't actually
// mean anything
("test_accepted_feature", Accepted),
("test_removed_feature", Removed),
];
enum Status {
/// Represents an active feature that is currently being implemented or
/// currently being considered for addition/removal.
Active,
/// Represents a feature which has since been removed (it was once Active)
Removed,
/// This language feature has since been Accepted (it was once Active)
Accepted,
}
struct Context {
features: ~[&'static str],
sess: Session,
}
impl Context {
fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
if !self.has_feature(feature) {
self.sess.span_err(span, explain);
self.sess.span_note(span, format!("add \\#[feature({})] to the \
crate attributes to enable",
feature));
}
}
fn has_feature(&self, feature: &str) -> bool {
self.features.iter().any(|n| n.as_slice() == feature)
}
}
impl Visitor<()> for Context {
fn visit_view_item(&mut self, i: &ast::view_item, _: ()) {
match i.node {
ast::view_item_use(ref paths) => {
for path in paths.iter() {
match path.node {
ast::view_path_glob(*) => {
self.gate_feature("globs", path.span,
"glob import statements are \
experimental and possibly buggy");
}
_ => {}
}
}
}
_ => {}
}
visit::walk_view_item(self, i, ())
}
fn visit_item(&mut self, i: @ast::item, _:()) {
match i.node {
ast::item_enum(ref def, _) => {
for variant in def.variants.iter() {
match variant.node.kind {
ast::struct_variant_kind(*) => {
self.gate_feature("struct_variant", variant.span,
"enum struct variants are \
experimental and possibly buggy");
}
_ => {}
}
}
}
ast::item_mac(ref mac) => {
match mac.node {
ast::mac_invoc_tt(ref path, _, _) => {
let rules = self.sess.ident_of("macro_rules");
if path.segments.last().identifier == rules {
self.gate_feature("macro_rules", i.span,
"macro definitions are not \
stable enough for use and are \
subject to change");
}
}
}
}
_ => {}
}
visit::walk_item(self, i, ());
}
}
pub fn check_crate(sess: Session, crate: &ast::Crate) {
let mut cx = Context {
features: ~[],
sess: sess,
};
for attr in crate.attrs.iter() {
if "feature" != attr.name() { continue }
match attr.meta_item_list() {
None => {
sess.span_err(attr.span, "malformed feature attribute, \
expected #[feature(...)]");
}
Some(list) => {
for &mi in list.iter() {
let name = match mi.node {
ast::MetaWord(word) => word,
_ => {
sess.span_err(mi.span, "malformed feature, expected \
just one word");
continue
}
};
match KNOWN_FEATURES.iter().find(|& &(n, _)| n == name) {
Some(&(name, Active)) => { cx.features.push(name); }
Some(&(_, Removed)) => {
sess.span_err(mi.span, "feature has been removed");
}
Some(&(_, Accepted)) => {
sess.span_warn(mi.span, "feature has added to rust, \
directive not necessary");
}
None => {
sess.span_err(mi.span, "unknown feature");
}
}
}
}
}
}
visit::walk_crate(&mut cx, crate, ());
sess.abort_if_errors();
}

View File

@ -17,6 +17,8 @@
#[license = "MIT/ASL2"];
#[crate_type = "lib"];
#[feature(macro_rules, globs, struct_variant)];
// Rustc tasks always run on a fixed_stack_segment, so code in this
// module can call C functions (in particular, LLVM functions) with
// impunity.
@ -83,6 +85,7 @@ pub mod front {
pub mod test;
pub mod std_inject;
pub mod assign_node_ids;
pub mod feature_gate;
}
pub mod back {

View File

@ -17,6 +17,8 @@
#[license = "MIT/ASL2"];
#[crate_type = "lib"];
#[feature(globs, struct_variant)];
extern mod syntax;
extern mod rustc;
extern mod extra;

View File

@ -66,6 +66,8 @@
#[license = "MIT/ASL2"];
#[crate_type = "lib"];
#[feature(globs)];
extern mod extra;
extern mod rustc;
extern mod syntax;

View File

@ -18,6 +18,8 @@
#[license = "MIT/ASL2"];
#[crate_type = "lib"];
#[feature(globs)];
extern mod extra;
extern mod rustc;
extern mod syntax;

View File

@ -61,6 +61,8 @@ they contained the following prologue:
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
html_root_url = "http://static.rust-lang.org/doc/master")];
#[feature(macro_rules, globs)];
// Don't link to std. We are std.
#[no_std];

View File

@ -20,6 +20,8 @@
#[license = "MIT/ASL2"];
#[crate_type = "lib"];
#[feature(macro_rules, globs)];
extern mod extra;
pub mod util {

View File

@ -0,0 +1,24 @@
// Copyright 2013 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.
#[feature(
foo_bar_baz,
foo(bar),
foo = "baz"
)];
//~^^^^ ERROR: unknown feature
//~^^^^ ERROR: malformed feature
//~^^^^ ERROR: malformed feature
#[feature]; //~ ERROR: malformed feature
#[feature = "foo"]; //~ ERROR: malformed feature
#[feature(test_removed_feature)]; //~ ERROR: feature has been removed
#[feature(test_accepted_feature)]; //~ WARNING: feature has added

View File

@ -0,0 +1,14 @@
// Copyright 2013 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.
use std::*;
//~^ ERROR: glob import statements are experimental
fn main() {}

View File

@ -0,0 +1,14 @@
// Copyright 2013 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.
macro_rules! foo(() => ())
//~^ ERROR: macro definitions are not stable enough for use
fn main() {}

View File

@ -0,0 +1,15 @@
// Copyright 2013 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.
enum A { B { foo: int } }
//~^ ERROR: enum struct variants are experimental
fn main() {}