diff --git a/doc/rust.md b/doc/rust.md index 56d116804f5..e998f97869f 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -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 diff --git a/doc/tutorial.md b/doc/tutorial.md index 64230a27637..49ba38954b3 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -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: ~~~ diff --git a/src/libextra/extra.rs b/src/libextra/extra.rs index 74787e66fec..45e4fe50f25 100644 --- a/src/libextra/extra.rs +++ b/src/libextra/extra.rs @@ -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)]; diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 00f722e7890..4dff3abda30 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -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 diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs new file mode 100644 index 00000000000..5986409c843 --- /dev/null +++ b/src/librustc/front/feature_gate.rs @@ -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 or the MIT license +// , 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(); +} diff --git a/src/librustc/rustc.rs b/src/librustc/rustc.rs index 2cf04e8d3e1..a8a255669ca 100644 --- a/src/librustc/rustc.rs +++ b/src/librustc/rustc.rs @@ -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 { diff --git a/src/librustdoc/rustdoc.rs b/src/librustdoc/rustdoc.rs index b953fe1ed5d..d72612256a7 100644 --- a/src/librustdoc/rustdoc.rs +++ b/src/librustdoc/rustdoc.rs @@ -17,6 +17,8 @@ #[license = "MIT/ASL2"]; #[crate_type = "lib"]; +#[feature(globs, struct_variant)]; + extern mod syntax; extern mod rustc; extern mod extra; diff --git a/src/librusti/rusti.rs b/src/librusti/rusti.rs index 9da8c58fd05..3775d175166 100644 --- a/src/librusti/rusti.rs +++ b/src/librusti/rusti.rs @@ -66,6 +66,8 @@ #[license = "MIT/ASL2"]; #[crate_type = "lib"]; +#[feature(globs)]; + extern mod extra; extern mod rustc; extern mod syntax; diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs index 1ece56df60a..cd4badfab31 100644 --- a/src/librustpkg/rustpkg.rs +++ b/src/librustpkg/rustpkg.rs @@ -18,6 +18,8 @@ #[license = "MIT/ASL2"]; #[crate_type = "lib"]; +#[feature(globs)]; + extern mod extra; extern mod rustc; extern mod syntax; diff --git a/src/libstd/std.rs b/src/libstd/std.rs index 5501cdfdcd5..53837f96593 100644 --- a/src/libstd/std.rs +++ b/src/libstd/std.rs @@ -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]; diff --git a/src/libsyntax/syntax.rs b/src/libsyntax/syntax.rs index 74f695d301b..1f385a00fa6 100644 --- a/src/libsyntax/syntax.rs +++ b/src/libsyntax/syntax.rs @@ -20,6 +20,8 @@ #[license = "MIT/ASL2"]; #[crate_type = "lib"]; +#[feature(macro_rules, globs)]; + extern mod extra; pub mod util { diff --git a/src/test/compile-fail/gated-bad-feature.rs b/src/test/compile-fail/gated-bad-feature.rs new file mode 100644 index 00000000000..0bf2d5ad78b --- /dev/null +++ b/src/test/compile-fail/gated-bad-feature.rs @@ -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 or the MIT license +// , 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 diff --git a/src/test/compile-fail/gated-glob-imports.rs b/src/test/compile-fail/gated-glob-imports.rs new file mode 100644 index 00000000000..cc7ba785e7e --- /dev/null +++ b/src/test/compile-fail/gated-glob-imports.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/compile-fail/gated-macro-rules.rs b/src/test/compile-fail/gated-macro-rules.rs new file mode 100644 index 00000000000..7f771c72416 --- /dev/null +++ b/src/test/compile-fail/gated-macro-rules.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/compile-fail/gated-struct-enums.rs b/src/test/compile-fail/gated-struct-enums.rs new file mode 100644 index 00000000000..f1bd9362bb7 --- /dev/null +++ b/src/test/compile-fail/gated-struct-enums.rs @@ -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 or the MIT license +// , 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() {} +