.SH OPTIONS
+.TP
+\fB\-\-crate-name NAME\fR
+Specify the name of the crate being built
.TP
\fB\-\-crate-type=[bin|lib|dylib|rlib|staticlib]\fR
Configure the flavor of rust crate that is generated (default `bin`)
MAYBE_DISABLE_VERIFY=
endif
-install: dist-install-dir-$(CFG_BUILD)-with-target-libs
- $(Q)sh tmp/dist/$(PKG_NAME)-$(CFG_BUILD)/install.sh --prefix="$(DESTDIR)$(CFG_PREFIX)" --libdir="$(DESTDIR)$(CFG_LIBDIR)" --mandir="$(DESTDIR)$(CFG_MANDIR)" "$(MAYBE_DISABLE_VERIFY)"
+install: dist-install-dir-$(CFG_BUILD)-with-target-libs | tmp/empty_dir
+ $(Q)cd tmp/empty_dir && sh ../../tmp/dist/$(PKG_NAME)-$(CFG_BUILD)/install.sh --prefix="$(DESTDIR)$(CFG_PREFIX)" --libdir="$(DESTDIR)$(CFG_LIBDIR)" --mandir="$(DESTDIR)$(CFG_MANDIR)" "$(MAYBE_DISABLE_VERIFY)"
# Remove tmp files while we can because they may have been created under sudo
$(Q)rm -R tmp/dist
-uninstall: dist-install-dir-$(CFG_BUILD)-with-target-libs
- $(Q)sh tmp/dist/$(PKG_NAME)-$(CFG_BUILD)/install.sh --uninstall --prefix="$(DESTDIR)$(CFG_PREFIX)" --libdir="$(DESTDIR)$(CFG_LIBDIR)" --mandir="$(DESTDIR)$(CFG_MANDIR)"
+uninstall: dist-install-dir-$(CFG_BUILD)-with-target-libs | tmp/empty_dir
+ $(Q)cd tmp/empty_dir && sh ../../tmp/dist/$(PKG_NAME)-$(CFG_BUILD)/install.sh --uninstall --prefix="$(DESTDIR)$(CFG_PREFIX)" --libdir="$(DESTDIR)$(CFG_LIBDIR)" --mandir="$(DESTDIR)$(CFG_MANDIR)"
# Remove tmp files while we can because they may have been created under sudo
$(Q)rm -R tmp/dist
+tmp/empty_dir:
+ mkdir -p $@
######################################################################
# Android remote installation
-c'[Compile and assemble, but do not link]'
--cfg'[Configure the compilation environment]'
--crate-id'[Output the crate id and exit]'
- --crate-file-name'[Output the file(s) that would be written if compilation continued and exit]'
- --crate-name'[Output the crate name and exit]'
- --dep-info'[Output dependency info to <filename> after compiling]'
+ --crate-file-name'[deprecated in favor of --print-file-name]'
+ --crate-name'[Specify the name of the crate being built]'
--crate-type'[Specify the type of crate to crate]'
+ --debuginfo'[Emit DWARF debug info to the objects created: 0 = no debug info, 1 = line-tables only (for stacktraces and breakpoints), 2 = full debug info with variable and type information (same as -g)]'
+ --dep-info'[Output dependency info to <filename> after compiling]'
+ -g'[Equivalent to --debuginfo=2]'
{-h,--help}'[Display this message]'
-L'[Add a directory to the library search path]'
--linker'[Program to use for linking instead of the default.]'
--parse-only'[Parse only; do not compile, assemble, or link]'
--passes'[Comma or space separated list of pass names to use]'
--pretty'[Pretty-print the input instead of compiling]'
+ --print-crate-name'[Output the crate name and exit]'
+ --print-file-name'[Output the file(s) that would be written if compilation continued and exit]'
--save-temps'[Write intermediate files (.bc, .opt.bc, .o) in addition to normal output]'
--sysroot'[Override the system root]'
--test'[Build a test harness]'
mod tests {
use test::Bencher;
use std::prelude::*;
- use std::num::ToStrRadix;
+ use std::fmt;
use str::Str;
use string::String;
fn to_hex_str(r: &[u8, ..8]) -> String {
let mut s = String::new();
for b in r.iter() {
- s.push_str((*b as uint).to_str_radix(16u).as_slice());
+ s.push_str(format!("{}", fmt::radix(*b, 16)).as_slice());
}
s
}
let r = result_bytes(h);
let mut s = String::new();
for b in r.iter() {
- s.push_str((*b as uint).to_str_radix(16u).as_slice());
+ s.push_str(format!("{}", fmt::radix(*b, 16)).as_slice());
}
s
}
///
/// fn with_lock(spinlock: &Arc<AtomicBool>, f: || -> ()) {
/// // CAS loop until we are able to replace `false` with `true`
- /// while spinlock.compare_and_swap(false, true, SeqCst) == false {
+ /// while spinlock.compare_and_swap(false, true, SeqCst) != false {
/// // Since tasks may not be preemptive (if they are green threads)
/// // yield to the scheduler to let the other task run. Low level
/// // concurrent code needs to take into account Rust's two threading
//! possibly pinned to a particular scheduler thread:
//!
//! ```rust
+//! extern crate green;
+//! extern crate rustuv;
+//!
+//! # fn main() {
//! use std::task::TaskBuilder;
//! use green::{SchedPool, PoolConfig, GreenTaskBuilder};
//!
-//! let config = PoolConfig::new();
+//! let mut config = PoolConfig::new();
+//!
+//! // Optional: Set the event loop to be rustuv's to allow I/O to work
+//! config.event_loop_factory = rustuv::event_loop;
+//!
//! let mut pool = SchedPool::new(config);
//!
//! // Spawn tasks into the pool of schedulers
//! // Required to shut down this scheduler pool.
//! // The task will fail if `shutdown` is not called.
//! pool.shutdown();
+//! # }
//! ```
#![crate_name = "green"]
should_match_name: true,
};
let library = match load_ctxt.maybe_load_library_crate() {
- Some (l) => l,
+ Some(l) => l,
None if is_cross => {
// try loading from target crates (only valid if there are
// no syntax extensions)
let registrar = decoder::get_plugin_registrar_fn(library.metadata.as_slice()).map(|id| {
decoder::get_symbol(library.metadata.as_slice(), id)
});
+ if library.dylib.is_none() && registrar.is_some() {
+ let message = format!("plugin crate `{}` only found in rlib format, \
+ but must be available in dylib format",
+ info.ident);
+ self.env.sess.span_err(krate.span, message.as_slice());
+ // No need to abort because the loading code will just ignore this
+ // empty dylib.
+ }
let pc = PluginMetadata {
lib: library.dylib.clone(),
macros: macros,
encode_name(ebml_w, nm);
encode_type(ecx, ebml_w, node_id_to_type(tcx, id));
encode_def_id(ebml_w, local_def(id));
+
+ let stab = stability::lookup(ecx.tcx, field.id);
+ encode_stability(ebml_w, stab);
+
ebml_w.end_tag();
}
index
use util::nodemap::{NodeMap, DefIdMap};
use syntax::codemap::Span;
use syntax::{attr, visit};
+use syntax::ast;
use syntax::ast::{Attribute, Block, Crate, DefId, FnDecl, NodeId, Variant};
use syntax::ast::{Item, Required, Provided, TraitMethod, TypeMethod, Method};
-use syntax::ast::{Generics, StructDef, Ident};
+use syntax::ast::{Generics, StructDef, StructField, Ident};
use syntax::ast_util::is_local;
use syntax::attr::Stability;
use syntax::visit::{FnKind, FkMethod, Visitor};
s.ctor_id.map(|id| self.annotate(id, &[], parent.clone()));
visit::walk_struct_def(self, s, parent)
}
+
+ fn visit_struct_field(&mut self, s: &StructField, parent: Option<Stability>) {
+ let stab = self.annotate(s.node.id, s.node.attrs.as_slice(), parent);
+ visit::walk_struct_field(self, s, stab)
+ }
}
impl Index {
extern_cache: DefIdMap::new()
}
};
- visit::walk_crate(&mut annotator, krate,
- attr::find_stability(krate.attrs.as_slice()));
+ let stab = annotator.annotate(ast::CRATE_NODE_ID, krate.attrs.as_slice(), None);
+ visit::walk_crate(&mut annotator, krate, stab);
annotator.index
}
}
name: Some(name.clean()),
attrs: Vec::new(),
visibility: Some(ast::Public),
- stability: get_stability(self.id),
// FIXME: this is not accurate, we need an id for
// the specific field but we're using the id
- // for the whole variant. Nothing currently
- // uses this so we should be good for now.
+ // for the whole variant. Thus we read the
+ // stability from the whole variant as well.
+ // Struct variants are experimental and need
+ // more infrastructure work before we can get
+ // at the needed information here.
def_id: self.id,
+ stability: get_stability(self.id),
inner: StructFieldItem(
TypedStructField(ty.clean())
)
visibility: Some(ast::Public),
def_id: self.id,
inner: VariantItem(Variant { kind: kind }),
- stability: None,
+ stability: get_stability(self.id),
}
}
}
source: self.span.clean(),
def_id: ast_util::local_def(self.id),
visibility: self.vis.clean(),
- stability: None,
+ stability: get_stability(ast_util::local_def(self.id)),
inner: inner,
}
}
use syntax::ast_util;
use clean;
+use stability_summary::ModuleSummary;
use html::item_type;
use html::item_type::ItemType;
use html::render;
}
}
}
+
+impl fmt::Show for ModuleSummary {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fn fmt_inner<'a>(f: &mut fmt::Formatter,
+ context: &mut Vec<&'a str>,
+ m: &'a ModuleSummary)
+ -> fmt::Result {
+ let cnt = m.counts;
+ let tot = cnt.total();
+ if tot == 0 { return Ok(()) }
+
+ context.push(m.name.as_slice());
+ let path = context.connect("::");
+
+ // the total width of each row's stability summary, in pixels
+ let width = 500;
+
+ try!(write!(f, "<tr>"));
+ try!(write!(f, "<td class='summary'>\
+ <a class='summary' href='{}'>{}</a></td>",
+ Vec::from_slice(context.slice_from(1))
+ .append_one("index.html").connect("/"),
+ path));
+ try!(write!(f, "<td>"));
+ try!(write!(f, "<span class='summary Stable' \
+ style='width: {}px; display: inline-block'> </span>",
+ (width * cnt.stable)/tot));
+ try!(write!(f, "<span class='summary Unstable' \
+ style='width: {}px; display: inline-block'> </span>",
+ (width * cnt.unstable)/tot));
+ try!(write!(f, "<span class='summary Experimental' \
+ style='width: {}px; display: inline-block'> </span>",
+ (width * cnt.experimental)/tot));
+ try!(write!(f, "<span class='summary Deprecated' \
+ style='width: {}px; display: inline-block'> </span>",
+ (width * cnt.deprecated)/tot));
+ try!(write!(f, "<span class='summary Unmarked' \
+ style='width: {}px; display: inline-block'> </span>",
+ (width * cnt.unmarked)/tot));
+ try!(write!(f, "</td></tr>"));
+
+ for submodule in m.submodules.iter() {
+ try!(fmt_inner(f, context, submodule));
+ }
+ context.pop();
+ Ok(())
+ }
+
+ let mut context = Vec::new();
+
+ try!(write!(f,
+r"<h1 class='fqn'>Stability dashboard: crate <a class='mod' href='index.html'>{}</a></h1>
+This dashboard summarizes the stability levels for all of the public modules of
+the crate, according to the total number of items at each level in the module and its children:
+<blockquote>
+<a class='stability Stable'></a> stable,<br/>
+<a class='stability Unstable'></a> unstable,<br/>
+<a class='stability Experimental'></a> experimental,<br/>
+<a class='stability Deprecated'></a> deprecated,<br/>
+<a class='stability Unmarked'></a> unmarked
+</blockquote>
+The counts do not include methods or trait
+implementations that are visible only through a re-exported type.",
+self.name));
+ try!(write!(f, "<table>"))
+ try!(fmt_inner(f, &mut context, self));
+ write!(f, "</table>")
+ }
+}
use externalfiles::ExternalHtml;
+use serialize::json;
+use serialize::Encodable;
use serialize::json::ToJson;
use syntax::ast;
use syntax::ast_util;
use html::layout;
use html::markdown::Markdown;
use html::markdown;
+use stability_summary;
/// Major driving force in all rustdoc rendering. This contains information
/// about where in the tree-like hierarchy rendering is occurring and controls
try!(mkdir(&cx.dst));
+ // Crawl the crate, building a summary of the stability levels. NOTE: this
+ // summary *must* be computed with the original `krate`; the folding below
+ // removes the impls from their modules.
+ let summary = stability_summary::build(&krate);
+
// Crawl the crate attributes looking for attributes which control how we're
// going to emit HTML
match krate.module.as_ref().map(|m| m.doc_list().unwrap_or(&[])) {
let krate = try!(render_sources(&mut cx, krate));
// And finally render the whole crate's documentation
- cx.krate(krate)
+ cx.krate(krate, summary)
}
fn build_index(krate: &clean::Crate, cache: &mut Cache) -> io::IoResult<String> {
///
/// This currently isn't parallelized, but it'd be pretty easy to add
/// parallelization to this function.
- fn krate(self, mut krate: clean::Crate) -> io::IoResult<()> {
+ fn krate(mut self, mut krate: clean::Crate,
+ stability: stability_summary::ModuleSummary) -> io::IoResult<()> {
let mut item = match krate.module.take() {
Some(i) => i,
None => return Ok(())
};
item.name = Some(krate.name);
+ // render stability dashboard
+ try!(self.recurse(stability.name.clone(), |this| {
+ let json_dst = &this.dst.join("stability.json");
+ let mut json_out = BufferedWriter::new(try!(File::create(json_dst)));
+ try!(stability.encode(&mut json::Encoder::new(&mut json_out)));
+
+ let title = stability.name.clone().append(" - Stability dashboard");
+ let page = layout::Page {
+ ty: "mod",
+ root_path: this.root_path.as_slice(),
+ title: title.as_slice(),
+ };
+ let html_dst = &this.dst.join("stability.html");
+ let mut html_out = BufferedWriter::new(try!(File::create(html_dst)));
+ layout::render(&mut html_out, &this.layout, &page,
+ &Sidebar{ cx: this, item: &item },
+ &stability)
+ }));
+
+ // render the crate documentation
let mut work = vec!((self, item));
loop {
match work.pop() {
None => break,
}
}
+
Ok(())
}
}
}
+
+
impl<'a> fmt::Show for Item<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
// Write the breadcrumb trail header for the top
// Write stability level
try!(write!(fmt, "{}", Stability(&self.item.stability)));
+ // Links to out-of-band information, i.e. src and stability dashboard
+ try!(write!(fmt, "<span class='out-of-band'>"));
+
+ // Write stability dashboard link
+ match self.item.inner {
+ clean::ModuleItem(ref m) if m.is_crate => {
+ try!(write!(fmt, "<a href='stability.html'>[stability dashboard]</a> "));
+ }
+ _ => {}
+ };
+
// Write `src` tag
//
// When this item is part of a `pub use` in a downstream crate, the
if self.cx.include_sources && !is_primitive {
match self.href() {
Some(l) => {
- try!(write!(fmt,
- "<a class='source' id='src-{}' \
- href='{}'>[src]</a>",
+ try!(write!(fmt, "<a id='src-{}' href='{}'>[src]</a>",
self.item.def_id.node, l));
}
None => {}
}
}
+
+ try!(write!(fmt, "</span>"));
+
try!(write!(fmt, "</h1>\n"));
match self.item.inner {
fn item_module(w: &mut fmt::Formatter, cx: &Context,
item: &clean::Item, items: &[clean::Item]) -> fmt::Result {
try!(document(w, item));
+
let mut indices = range(0, items.len()).filter(|i| {
!ignore_private_item(&items[*i])
}).collect::<Vec<uint>>();
}
}
}
+
write!(w, "</table>")
}
.docblock h2 { font-size: 1.15em; }
.docblock h3, .docblock h4, .docblock h5 { font-size: 1em; }
-.content .source {
+.content .out-of-band {
float: right;
font-size: 23px;
}
.stability.Locked { border-color: #0084B6; color: #00668c; }
.stability.Unmarked { border-color: #FFFFFF; }
+.summary {
+ padding-right: 0px;
+}
+.summary.Deprecated { background-color: #A071A8; }
+.summary.Experimental { background-color: #D46D6A; }
+.summary.Unstable { background-color: #D4B16A; }
+.summary.Stable { background-color: #54A759; }
+.summary.Unmarked { background-color: #FFFFFF; }
+
:target { background: #FDFFD3; }
/* Code highlighting */
pub mod markdown;
pub mod passes;
pub mod plugins;
+pub mod stability_summary;
pub mod visit_ast;
pub mod test;
mod flock;
--- /dev/null
+// Copyright 2014 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.
+
+//! This module crawls a `clean::Crate` and produces a summarization of the
+//! stability levels within the crate. The summary contains the module
+//! hierarchy, with item counts for every stability level per module. A parent
+//! module's count includes its childrens's.
+
+use std::ops::Add;
+use std::num::Zero;
+use std::iter::AdditiveIterator;
+
+use syntax::attr::{Deprecated, Experimental, Unstable, Stable, Frozen, Locked};
+use syntax::ast::Public;
+
+use clean::{Crate, Item, ModuleItem, Module, StructItem, Struct, EnumItem, Enum};
+use clean::{ImplItem, Impl, TraitItem, Trait, TraitMethod, Provided, Required};
+use clean::{ViewItemItem, PrimitiveItem};
+
+#[deriving(Zero, Encodable, Decodable, PartialEq, Eq)]
+/// The counts for each stability level.
+pub struct Counts {
+ pub deprecated: uint,
+ pub experimental: uint,
+ pub unstable: uint,
+ pub stable: uint,
+ pub frozen: uint,
+ pub locked: uint,
+
+ /// No stability level, inherited or otherwise.
+ pub unmarked: uint,
+}
+
+impl Add<Counts, Counts> for Counts {
+ fn add(&self, other: &Counts) -> Counts {
+ Counts {
+ deprecated: self.deprecated + other.deprecated,
+ experimental: self.experimental + other.experimental,
+ unstable: self.unstable + other.unstable,
+ stable: self.stable + other.stable,
+ frozen: self.frozen + other.frozen,
+ locked: self.locked + other.locked,
+ unmarked: self.unmarked + other.unmarked,
+ }
+ }
+}
+
+impl Counts {
+ pub fn total(&self) -> uint {
+ self.deprecated + self.experimental + self.unstable + self.stable +
+ self.frozen + self.locked + self.unmarked
+ }
+}
+
+#[deriving(Encodable, Decodable, PartialEq, Eq)]
+/// A summarized module, which includes total counts and summarized chilcren
+/// modules.
+pub struct ModuleSummary {
+ pub name: String,
+ pub counts: Counts,
+ pub submodules: Vec<ModuleSummary>,
+}
+
+impl PartialOrd for ModuleSummary {
+ fn partial_cmp(&self, other: &ModuleSummary) -> Option<Ordering> {
+ self.name.partial_cmp(&other.name)
+ }
+}
+
+impl Ord for ModuleSummary {
+ fn cmp(&self, other: &ModuleSummary) -> Ordering {
+ self.name.cmp(&other.name)
+ }
+}
+
+// is the item considered publically visible?
+fn visible(item: &Item) -> bool {
+ match item.inner {
+ ImplItem(_) => true,
+ _ => item.visibility == Some(Public)
+ }
+}
+
+// Produce the summary for an arbitrary item. If the item is a module, include a
+// module summary. The counts for items with nested items (e.g. modules, traits,
+// impls) include all children counts.
+fn summarize_item(item: &Item) -> (Counts, Option<ModuleSummary>) {
+ // count this item
+ let item_counts = match item.stability {
+ None => Counts { unmarked: 1, .. Zero::zero() },
+ Some(ref stab) => match stab.level {
+ Deprecated => Counts { deprecated: 1, .. Zero::zero() },
+ Experimental => Counts { experimental: 1, .. Zero::zero() },
+ Unstable => Counts { unstable: 1, .. Zero::zero() },
+ Stable => Counts { stable: 1, .. Zero::zero() },
+ Frozen => Counts { frozen: 1, .. Zero::zero() },
+ Locked => Counts { locked: 1, .. Zero::zero() },
+ }
+ };
+
+ // Count this item's children, if any. Note that a trait impl is
+ // considered to have no children.
+ match item.inner {
+ // Require explicit `pub` to be visible
+ StructItem(Struct { fields: ref subitems, .. }) |
+ ImplItem(Impl { methods: ref subitems, trait_: None, .. }) => {
+ let subcounts = subitems.iter().filter(|i| visible(*i))
+ .map(summarize_item)
+ .map(|s| s.val0())
+ .sum();
+ (item_counts + subcounts, None)
+ }
+ // `pub` automatically
+ EnumItem(Enum { variants: ref subitems, .. }) => {
+ let subcounts = subitems.iter().map(summarize_item)
+ .map(|s| s.val0())
+ .sum();
+ (item_counts + subcounts, None)
+ }
+ TraitItem(Trait { methods: ref methods, .. }) => {
+ fn extract_item<'a>(meth: &'a TraitMethod) -> &'a Item {
+ match *meth {
+ Provided(ref item) | Required(ref item) => item
+ }
+ }
+ let subcounts = methods.iter().map(extract_item)
+ .map(summarize_item)
+ .map(|s| s.val0())
+ .sum();
+ (item_counts + subcounts, None)
+ }
+ ModuleItem(Module { items: ref items, .. }) => {
+ let mut counts = item_counts;
+ let mut submodules = Vec::new();
+
+ for (subcounts, submodule) in items.iter().filter(|i| visible(*i))
+ .map(summarize_item) {
+ counts = counts + subcounts;
+ submodule.map(|m| submodules.push(m));
+ }
+ submodules.sort();
+
+ (counts, Some(ModuleSummary {
+ name: item.name.as_ref().map_or("".to_string(), |n| n.clone()),
+ counts: counts,
+ submodules: submodules,
+ }))
+ }
+ // no stability information for the following items:
+ ViewItemItem(_) | PrimitiveItem(_) => (Zero::zero(), None),
+ _ => (item_counts, None)
+ }
+}
+
+/// Summarizes the stability levels in a crate.
+pub fn build(krate: &Crate) -> ModuleSummary {
+ match krate.module {
+ None => ModuleSummary {
+ name: krate.name.clone(),
+ counts: Zero::zero(),
+ submodules: Vec::new(),
+ },
+ Some(ref item) => ModuleSummary {
+ name: krate.name.clone(), .. summarize_item(item).val1().unwrap()
+ }
+ }
+}
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
html_root_url = "http://doc.rust-lang.org/0.11.0/")]
+#![feature(default_type_params)]
use std::char;
use std::cmp;
-use std::fmt;
use std::fmt::Show;
-use std::option::{Option, Some, None};
-use std::string::String;
+use std::fmt;
+use std::hash;
/// An identifier in the pre-release or build metadata. If the identifier can
/// be parsed as a decimal value, it will be represented with `Numeric`.
-#[deriving(Clone, PartialEq)]
+#[deriving(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(missing_doc)]
pub enum Identifier {
Numeric(uint),
AlphaNumeric(String)
}
-impl cmp::PartialOrd for Identifier {
- #[inline]
- fn partial_cmp(&self, other: &Identifier) -> Option<Ordering> {
- match (self, other) {
- (&Numeric(a), &Numeric(ref b)) => a.partial_cmp(b),
- (&Numeric(_), _) => Some(Less),
- (&AlphaNumeric(ref a), &AlphaNumeric(ref b)) => a.partial_cmp(b),
- (&AlphaNumeric(_), _) => Some(Greater)
- }
- }
-}
-
impl fmt::Show for Identifier {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
/// Represents a version number conforming to the semantic versioning scheme.
-#[deriving(Clone)]
+#[deriving(Clone, Eq)]
pub struct Version {
/// The major version, to be incremented on incompatible changes.
pub major: uint,
}
impl cmp::PartialOrd for Version {
- #[inline]
fn partial_cmp(&self, other: &Version) -> Option<Ordering> {
- match self.major.partial_cmp(&other.major) {
- Some(Equal) => {}
+ Some(self.cmp(other))
+ }
+}
+
+impl cmp::Ord for Version {
+ fn cmp(&self, other: &Version) -> Ordering {
+ match self.major.cmp(&other.major) {
+ Equal => {}
r => return r,
}
- match self.minor.partial_cmp(&other.minor) {
- Some(Equal) => {}
+ match self.minor.cmp(&other.minor) {
+ Equal => {}
r => return r,
}
- match self.patch.partial_cmp(&other.patch) {
- Some(Equal) => {}
+ match self.patch.cmp(&other.patch) {
+ Equal => {}
r => return r,
}
// but the version of ord defined for vec
// says that [] < [pre] so we alter it here
match (self.pre.len(), other.pre.len()) {
- (0, 0) => Some(Equal),
- (0, _) => Some(Greater),
- (_, 0) => Some(Less),
- (_, _) => self.pre.partial_cmp(&other.pre)
+ (0, 0) => Equal,
+ (0, _) => Greater,
+ (_, 0) => Less,
+ (_, _) => self.pre.cmp(&other.pre)
}
}
}
+impl<S: hash::Writer> hash::Hash<S> for Version {
+ fn hash(&self, into: &mut S) {
+ self.major.hash(into);
+ self.minor.hash(into);
+ self.patch.hash(into);
+ self.pre.hash(into);
+ }
+}
+
fn take_nonempty_prefix<T:Iterator<char>>(rdr: &mut T, pred: |char| -> bool)
-> (String, Option<char>) {
let mut buf = String::new();
// The test runner requires std::slice::Vector, so re-export std::slice just for it.
#[cfg(test)] pub use slice;
}
-
-#[deprecated]
-#[allow(missing_doc)]
-#[doc(hiden)]
-pub mod unstable {
- #[deprecated = "use std::dynamic_lib"]
- pub use dynamic_lib;
-}
use c_str::{CString, ToCStr};
use clone::Clone;
-use cmp::{PartialEq, Eq};
+use cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering};
use collections::Collection;
use from_str::FromStr;
use hash;
impl Eq for Path {}
+impl PartialOrd for Path {
+ fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Path {
+ fn cmp(&self, other: &Path) -> Ordering {
+ self.repr.cmp(&other.repr)
+ }
+}
+
impl FromStr for Path {
fn from_str(s: &str) -> Option<Path> {
Path::new_opt(s)
use ascii::AsciiCast;
use c_str::{CString, ToCStr};
use clone::Clone;
-use cmp::{PartialEq, Eq};
+use cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering};
use collections::Collection;
use from_str::FromStr;
use hash;
impl Eq for Path {}
+impl PartialOrd for Path {
+ fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Path {
+ fn cmp(&self, other: &Path) -> Ordering {
+ self.repr.cmp(&other.repr)
+ }
+}
+
impl FromStr for Path {
fn from_str(s: &str) -> Option<Path> {
Path::new_opt(s)
args: Vec::new(),
ret_ty: Self,
attributes: attrs,
- const_nonmatching: false,
combine_substructure: combine_substructure(|c, s, sub| {
cs_clone("Clone", c, s, sub)
}),
ctor_ident = variant.node.name;
all_fields = af;
},
- EnumNonMatching(..) => {
+ EnumNonMatchingCollapsed (..) => {
cx.span_bug(trait_span,
format!("non-matching enum variants in \
`deriving({})`",
args: vec!(borrowed_self()),
ret_ty: Literal(Path::new(vec!("bool"))),
attributes: attrs,
- const_nonmatching: true,
combine_substructure: combine_substructure(|a, b, c| {
$f(a, b, c)
})
args: vec!(borrowed_self()),
ret_ty: Literal(Path::new(vec!("bool"))),
attributes: attrs,
- const_nonmatching: false,
combine_substructure: combine_substructure(|cx, span, substr| {
cs_op($op, $equal, cx, span, substr)
})
args: vec![borrowed_self()],
ret_ty: ret_ty,
attributes: attrs,
- const_nonmatching: false,
combine_substructure: combine_substructure(|cx, span, substr| {
cs_partial_cmp(cx, span, substr)
})
trait_def.expand(cx, mitem, item, push)
}
-pub fn some_ordering_const(cx: &mut ExtCtxt, span: Span, cnst: Ordering) -> Gc<ast::Expr> {
- let cnst = match cnst {
- Less => "Less",
- Equal => "Equal",
- Greater => "Greater"
+pub enum OrderingOp {
+ PartialCmpOp, LtOp, LeOp, GtOp, GeOp,
+}
+
+pub fn some_ordering_collapsed(cx: &mut ExtCtxt,
+ span: Span,
+ op: OrderingOp,
+ self_arg_tags: &[ast::Ident]) -> Gc<ast::Expr> {
+ let lft = cx.expr_ident(span, self_arg_tags[0]);
+ let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1]));
+ let op_str = match op {
+ PartialCmpOp => "partial_cmp",
+ LtOp => "lt", LeOp => "le",
+ GtOp => "gt", GeOp => "ge",
};
- let ordering = cx.path_global(span,
- vec!(cx.ident_of("std"),
- cx.ident_of("cmp"),
- cx.ident_of(cnst)));
- let ordering = cx.expr_path(ordering);
- cx.expr_some(span, ordering)
+ cx.expr_method_call(span, lft, cx.ident_of(op_str), vec![rgt])
}
pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span,
substr: &Substructure) -> Gc<Expr> {
let test_id = cx.ident_of("__test");
- let equals_expr = some_ordering_const(cx, span, Equal);
+ let ordering = cx.path_global(span,
+ vec!(cx.ident_of("std"),
+ cx.ident_of("cmp"),
+ cx.ident_of("Equal")));
+ let ordering = cx.expr_path(ordering);
+ let equals_expr = cx.expr_some(span, ordering);
/*
Builds:
cx.expr_block(cx.block(span, vec!(assign), Some(if_)))
},
equals_expr.clone(),
- |cx, span, list, _| {
- match list {
- // an earlier nonmatching variant is Less than a
- // later one.
- [(self_var, _, _), (other_var, _, _)] =>
- some_ordering_const(cx, span, self_var.cmp(&other_var)),
- _ => cx.span_bug(span, "not exactly 2 arguments in `deriving(Ord)`")
+ |cx, span, (self_args, tag_tuple), _non_self_args| {
+ if self_args.len() != 2 {
+ cx.span_bug(span, "not exactly 2 arguments in `deriving(Ord)`")
+ } else {
+ some_ordering_collapsed(cx, span, PartialCmpOp, tag_tuple)
}
},
cx, span, substr)
cx.expr_binary(span, ast::BiOr, cmp, and)
},
cx.expr_bool(span, equal),
- |cx, span, args, _| {
- // nonmatching enums, order by the order the variants are
- // written
- match args {
- [(self_var, _, _),
- (other_var, _, _)] =>
- cx.expr_bool(span,
- if less {
- self_var < other_var
- } else {
- self_var > other_var
- }),
- _ => cx.span_bug(span, "not exactly 2 arguments in `deriving(Ord)`")
+ |cx, span, (self_args, tag_tuple), _non_self_args| {
+ if self_args.len() != 2 {
+ cx.span_bug(span, "not exactly 2 arguments in `deriving(Ord)`")
+ } else {
+ let op = match (less, equal) {
+ (true, true) => LeOp, (true, false) => LtOp,
+ (false, true) => GeOp, (false, false) => GtOp,
+ };
+ some_ordering_collapsed(cx, span, op, tag_tuple)
}
},
cx, span, substr)
args: vec!(),
ret_ty: nil_ty(),
attributes: attrs,
- const_nonmatching: true,
combine_substructure: combine_substructure(|a, b, c| {
cs_total_eq_assert(a, b, c)
})
use ext::deriving::generic::ty::*;
use parse::token::InternedString;
-use std::cmp::{Ordering, Equal, Less, Greater};
use std::gc::Gc;
pub fn expand_deriving_totalord(cx: &mut ExtCtxt,
args: vec!(borrowed_self()),
ret_ty: Literal(Path::new(vec!("std", "cmp", "Ordering"))),
attributes: attrs,
- const_nonmatching: false,
combine_substructure: combine_substructure(|a, b, c| {
cs_cmp(a, b, c)
}),
}
-pub fn ordering_const(cx: &mut ExtCtxt, span: Span, cnst: Ordering) -> ast::Path {
- let cnst = match cnst {
- Less => "Less",
- Equal => "Equal",
- Greater => "Greater"
- };
- cx.path_global(span,
- vec!(cx.ident_of("std"),
- cx.ident_of("cmp"),
- cx.ident_of(cnst)))
+pub fn ordering_collapsed(cx: &mut ExtCtxt,
+ span: Span,
+ self_arg_tags: &[ast::Ident]) -> Gc<ast::Expr> {
+ let lft = cx.expr_ident(span, self_arg_tags[0]);
+ let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1]));
+ cx.expr_method_call(span, lft, cx.ident_of("cmp"), vec![rgt])
}
pub fn cs_cmp(cx: &mut ExtCtxt, span: Span,
substr: &Substructure) -> Gc<Expr> {
let test_id = cx.ident_of("__test");
- let equals_path = ordering_const(cx, span, Equal);
+ let equals_path = cx.path_global(span,
+ vec!(cx.ident_of("std"),
+ cx.ident_of("cmp"),
+ cx.ident_of("Equal")));
/*
Builds:
cx.expr_block(cx.block(span, vec!(assign), Some(if_)))
},
cx.expr_path(equals_path.clone()),
- |cx, span, list, _| {
- match list {
- // an earlier nonmatching variant is Less than a
- // later one.
- [(self_var, _, _),
- (other_var, _, _)] => {
- let order = ordering_const(cx, span, self_var.cmp(&other_var));
- cx.expr_path(order)
- }
- _ => cx.span_bug(span, "not exactly 2 arguments in `deriving(Ord)`")
+ |cx, span, (self_args, tag_tuple), _non_self_args| {
+ if self_args.len() != 2 {
+ cx.span_bug(span, "not exactly 2 arguments in `deriving(TotalOrd)`")
+ } else {
+ ordering_collapsed(cx, span, tag_tuple)
}
},
cx, span, substr)
vec!(box Self,
box Literal(Path::new_local("__E"))), true)),
attributes: Vec::new(),
- const_nonmatching: true,
combine_substructure: combine_substructure(|a, b, c| {
decodable_substructure(a, b, c)
}),
args: Vec::new(),
ret_ty: Self,
attributes: attrs,
- const_nonmatching: false,
combine_substructure: combine_substructure(|a, b, c| {
default_substructure(a, b, c)
})
box Literal(Path::new_local("__E"))),
true)),
attributes: Vec::new(),
- const_nonmatching: true,
combine_substructure: combine_substructure(|a, b, c| {
encodable_substructure(a, b, c)
}),
//! `struct T(int, char)`).
//! - `EnumMatching`, when `Self` is an enum and all the arguments are the
//! same variant of the enum (e.g. `Some(1)`, `Some(3)` and `Some(4)`)
-//! - `EnumNonMatching` when `Self` is an enum and the arguments are not
-//! the same variant (e.g. `None`, `Some(1)` and `None`). If
-//! `const_nonmatching` is true, this will contain an empty list.
+//! - `EnumNonMatchingCollapsed` when `Self` is an enum and the arguments
+//! are not the same variant (e.g. `None`, `Some(1)` and `None`).
//! - `StaticEnum` and `StaticStruct` for static methods, where the type
//! being derived upon is either an enum or struct respectively. (Any
//! argument with type Self is just grouped among the non-self
//! arguments.)
//!
//! In the first two cases, the values from the corresponding fields in
-//! all the arguments are grouped together. In the `EnumNonMatching` case
+//! all the arguments are grouped together. For `EnumNonMatchingCollapsed`
//! this isn't possible (different variants have different fields), so the
-//! fields are grouped by which argument they come from. There are no
+//! fields are inaccessible. (Previous versions of the deriving infrastructure
+//! had a way to expand into code that could access them, at the cost of
+//! generating exponential amounts of code; see issue #15375). There are no
//! fields with values in the static cases, so these are treated entirely
//! differently.
//!
//! For `C0(a)` and `C1 {x}` ,
//!
//! ~~~text
-//! EnumNonMatching(~[(0, <ast::Variant for B0>,
-//! ~[(<span of int>, None, <expr for &a>)]),
-//! (1, <ast::Variant for B1>,
-//! ~[(<span of x>, Some(<ident of x>),
-//! <expr for &other.x>)])])
+//! EnumNonMatchingCollapsed(
+//! ~[<ident of self>, <ident of __arg_1>],
+//! &[<ast::Variant for C0>, <ast::Variant for C1>],
+//! &[<ident for self index value>, <ident of __arg_1 index value>])
//! ~~~
//!
-//! (and vice versa, but with the order of the outermost list flipped.)
+//! It is the same for when the arguments are flipped to `C1 {x}` and
+//! `C0(a)`; the only difference is what the values of the identifiers
+//! <ident for self index value> and <ident of __arg_1 index value> will
+//! be in the generated code.
+//!
+//! `EnumNonMatchingCollapsed` deliberately provides far less information
+//! than is generally available for a given pair of variants; see #15375
+//! for discussion.
//!
//! ## Static
//!
pub attributes: Vec<ast::Attribute>,
- /// if the value of the nonmatching enums is independent of the
- /// actual enum variants, i.e. can use _ => .. match.
- pub const_nonmatching: bool,
-
pub combine_substructure: RefCell<CombineSubstructureFunc<'a>>,
}
EnumMatching(uint, &'a ast::Variant, Vec<FieldInfo>),
/**
- non-matching variants of the enum, [(variant index, ast::Variant,
- [field span, field ident, fields])] \(i.e. all fields for self are in the
- first tuple, for other1 are in the second tuple, etc.)
+ non-matching variants of the enum, but with all state hidden from
+ the consequent code. The first component holds Idents for all of
+ the Self arguments; the second component is a slice of all of the
+ variants for the enum itself, and the third component is a list of
+ Idents bound to the variant index values for each of the actual
+ input Self arguments.
*/
- EnumNonMatching(&'a [(uint, P<ast::Variant>,
- Vec<(Span, Option<Ident>, Gc<Expr>)>)]),
+ EnumNonMatchingCollapsed(Vec<Ident>, &'a [Gc<ast::Variant>], &'a [Ident]),
/// A static method where Self is a struct.
StaticStruct(&'a ast::StructDef, StaticFields),
|&mut ExtCtxt, Span, &Substructure|: 'a -> Gc<Expr>;
/**
-Deal with non-matching enum variants, the arguments are a list
-representing each variant: (variant index, ast::Variant instance,
-[variant fields]), and a list of the nonself args of the type
+Deal with non-matching enum variants. The tuple is a list of
+identifiers (one for each Self argument, which could be any of the
+variants since they have been collapsed together) and the identifiers
+holding the variant index value for each of the Self arguments. The
+last argument is all the non-Self args of the method being derived.
*/
-pub type EnumNonMatchFunc<'a> =
+pub type EnumNonMatchCollapsedFunc<'a> =
|&mut ExtCtxt,
Span,
- &[(uint, P<ast::Variant>, Vec<(Span, Option<Ident>, Gc<Expr>)>)],
+ (&[Ident], &[Ident]),
&[Gc<Expr>]|: 'a
-> Gc<Expr>;
}
}
+fn variant_to_pat(cx: &mut ExtCtxt, sp: Span, variant: &ast::Variant)
+ -> Gc<ast::Pat> {
+ let ident = cx.path_ident(sp, variant.node.name);
+ cx.pat(sp, match variant.node.kind {
+ ast::TupleVariantKind(..) => ast::PatEnum(ident, None),
+ ast::StructVariantKind(..) => ast::PatStruct(ident, Vec::new(), true),
+ })
+}
+
impl<'a> MethodDef<'a> {
fn call_substructure_method(&self,
cx: &mut ExtCtxt,
~~~
#[deriving(PartialEq)]
enum A {
- A1
+ A1,
A2(int)
}
- // is equivalent to (with const_nonmatching == false)
+ // is equivalent to
impl PartialEq for A {
- fn eq(&self, __arg_1: &A) {
- match *self {
- A1 => match *__arg_1 {
- A1 => true
- A2(ref __arg_1_1) => false
- },
- A2(self_1) => match *__arg_1 {
- A1 => false,
- A2(ref __arg_1_1) => self_1.eq(__arg_1_1)
+ fn eq(&self, __arg_1: &A) -> ::bool {
+ match (&*self, &*__arg_1) {
+ (&A1, &A1) => true,
+ (&A2(ref __self_0),
+ &A2(ref __arg_1_0)) => (*__self_0).eq(&(*__arg_1_0)),
+ _ => {
+ let __self_vi = match *self { A1(..) => 0u, A2(..) => 1u };
+ let __arg_1_vi = match *__arg_1 { A1(..) => 0u, A2(..) => 1u };
+ false
}
}
}
}
~~~
+
+ (Of course `__self_vi` and `__arg_1_vi` are unused for
+ `PartialEq`, and those subcomputations will hopefully be removed
+ as their results are unused. The point of `__self_vi` and
+ `__arg_1_vi` is for `PartialOrd`; see #15503.)
*/
fn expand_enum_method_body(&self,
cx: &mut ExtCtxt,
self_args: &[Gc<Expr>],
nonself_args: &[Gc<Expr>])
-> Gc<Expr> {
- let mut matches = Vec::new();
- self.build_enum_match(cx, trait_, enum_def, type_ident,
- self_args, nonself_args,
- None, &mut matches, 0)
+ self.build_enum_match_tuple(
+ cx, trait_, enum_def, type_ident, self_args, nonself_args)
}
/**
- Creates the nested matches for an enum definition recursively, i.e.
-
- ~~~text
- match self {
- Variant1 => match other { Variant1 => matching, Variant2 => nonmatching, ... },
- Variant2 => match other { Variant1 => nonmatching, Variant2 => matching, ... },
- ...
+ Creates a match for a tuple of all `self_args`, where either all
+ variants match, or it falls into a catch-all for when one variant
+ does not match.
+
+ There are N + 1 cases because is a case for each of the N
+ variants where all of the variants match, and one catch-all for
+ when one does not match.
+
+ The catch-all handler is provided access the variant index values
+ for each of the self-args, carried in precomputed variables. (Nota
+ bene: the variant index values are not necessarily the
+ discriminant values. See issue #15523.)
+
+ ~~~text
+ match (this, that, ...) {
+ (Variant1, Variant1, Variant1) => ... // delegate Matching on Variant1
+ (Variant2, Variant2, Variant2) => ... // delegate Matching on Variant2
+ ...
+ _ => {
+ let __this_vi = match this { Variant1 => 0u, Variant2 => 1u, ... };
+ let __that_vi = match that { Variant1 => 0u, Variant2 => 1u, ... };
+ ... // catch-all remainder can inspect above variant index values.
+ }
}
- ~~~
-
- It acts in the most naive way, so every branch (and subbranch,
- subsubbranch, etc) exists, not just the ones where all the variants in
- the tree are the same. Hopefully the optimisers get rid of any
- repetition, otherwise derived methods with many Self arguments will be
- exponentially large.
-
- `matching` is Some(n) if all branches in the tree above the
- current position are variant `n`, `None` otherwise (including on
- the first call).
+ ~~~
*/
- fn build_enum_match(&self,
- cx: &mut ExtCtxt,
- trait_: &TraitDef,
- enum_def: &EnumDef,
- type_ident: Ident,
- self_args: &[Gc<Expr>],
- nonself_args: &[Gc<Expr>],
- matching: Option<uint>,
- matches_so_far: &mut Vec<(uint, P<ast::Variant>,
- Vec<(Span, Option<Ident>, Gc<Expr>)>)> ,
- match_count: uint) -> Gc<Expr> {
- if match_count == self_args.len() {
- // we've matched against all arguments, so make the final
- // expression at the bottom of the match tree
- if matches_so_far.len() == 0 {
- cx.span_bug(trait_.span,
- "no self match on an enum in \
- generic `deriving`");
- }
-
- // `ref` inside let matches is buggy. Causes havoc with rusc.
- // let (variant_index, ref self_vec) = matches_so_far[0];
- let (variant, self_vec) = match matches_so_far.get(0) {
- &(_, v, ref s) => (v, s)
- };
-
- // we currently have a vec of vecs, where each
- // subvec is the fields of one of the arguments,
- // but if the variants all match, we want this as
- // vec of tuples, where each tuple represents a
- // field.
-
- // most arms don't have matching variants, so do a
- // quick check to see if they match (even though
- // this means iterating twice) instead of being
- // optimistic and doing a pile of allocations etc.
- let substructure = match matching {
- Some(variant_index) => {
- let mut enum_matching_fields = Vec::from_elem(self_vec.len(), Vec::new());
-
- for triple in matches_so_far.tail().iter() {
- match triple {
- &(_, _, ref other_fields) => {
- for (i, &(_, _, e)) in other_fields.iter().enumerate() {
- enum_matching_fields.get_mut(i).push(e);
- }
- }
- }
- }
- let field_tuples =
- self_vec.iter()
- .zip(enum_matching_fields.iter())
- .map(|(&(span, id, self_f), other)| {
- FieldInfo {
- span: span,
- name: id,
- self_: self_f,
- other: (*other).clone()
- }
- }).collect();
- EnumMatching(variant_index, &*variant, field_tuples)
- }
- None => {
- EnumNonMatching(matches_so_far.as_slice())
+ fn build_enum_match_tuple(
+ &self,
+ cx: &mut ExtCtxt,
+ trait_: &TraitDef,
+ enum_def: &EnumDef,
+ type_ident: Ident,
+ self_args: &[Gc<Expr>],
+ nonself_args: &[Gc<Expr>]) -> Gc<Expr> {
+
+ let sp = trait_.span;
+ let variants = &enum_def.variants;
+
+ let self_arg_names = self_args.iter().enumerate()
+ .map(|(arg_count, _self_arg)| {
+ if arg_count == 0 {
+ "__self".to_string()
+ } else {
+ format!("__arg_{}", arg_count)
}
- };
- self.call_substructure_method(cx, trait_, type_ident,
- self_args, nonself_args,
- &substructure)
+ })
+ .collect::<Vec<String>>();
+
+ let self_arg_idents = self_arg_names.iter()
+ .map(|name|cx.ident_of(name.as_slice()))
+ .collect::<Vec<ast::Ident>>();
+
+ // The `vi_idents` will be bound, solely in the catch-all, to
+ // a series of let statements mapping each self_arg to a uint
+ // corresponding to its variant index.
+ let vi_idents : Vec<ast::Ident> = self_arg_names.iter()
+ .map(|name| { let vi_suffix = format!("{:s}_vi", name.as_slice());
+ cx.ident_of(vi_suffix.as_slice()) })
+ .collect::<Vec<ast::Ident>>();
+
+ // Builds, via callback to call_substructure_method, the
+ // delegated expression that handles the catch-all case,
+ // using `__variants_tuple` to drive logic if necessary.
+ let catch_all_substructure = EnumNonMatchingCollapsed(
+ self_arg_idents, variants.as_slice(), vi_idents.as_slice());
+
+ // These arms are of the form:
+ // (Variant1, Variant1, ...) => Body1
+ // (Variant2, Variant2, ...) => Body2
+ // ...
+ // where each tuple has length = self_args.len()
+ let mut match_arms : Vec<ast::Arm> = variants.iter().enumerate()
+ .map(|(index, &variant)| {
+
+ // These self_pats have form Variant1, Variant2, ...
+ let self_pats : Vec<(Gc<ast::Pat>,
+ Vec<(Span, Option<Ident>, Gc<Expr>)>)>;
+ self_pats = self_arg_names.iter()
+ .map(|self_arg_name|
+ trait_.create_enum_variant_pattern(
+ cx, &*variant, self_arg_name.as_slice(),
+ ast::MutImmutable))
+ .collect();
+
+ // A single arm has form (&VariantK, &VariantK, ...) => BodyK
+ // (see "Final wrinkle" note below for why.)
+ let subpats = self_pats.iter()
+ .map(|&(p, ref _idents)| cx.pat(sp, ast::PatRegion(p)))
+ .collect::<Vec<Gc<ast::Pat>>>();
+
+ // Here is the pat = `(&VariantK, &VariantK, ...)`
+ let single_pat = cx.pat(sp, ast::PatTup(subpats));
+
+ // For the BodyK, we need to delegate to our caller,
+ // passing it an EnumMatching to indicate which case
+ // we are in.
+
+ // All of the Self args have the same variant in these
+ // cases. So we transpose the info in self_pats to
+ // gather the getter expressions together, in the form
+ // that EnumMatching expects.
+
+ // The transposition is driven by walking across the
+ // arg fields of the variant for the first self pat.
+ let &(_, ref self_arg_fields) = self_pats.get(0);
+
+ let field_tuples : Vec<FieldInfo>;
+
+ field_tuples = self_arg_fields.iter().enumerate()
+ // For each arg field of self, pull out its getter expr ...
+ .map(|(field_index, &(sp, opt_ident, self_getter_expr))| {
+ // ... but FieldInfo also wants getter expr
+ // for matching other arguments of Self type;
+ // so walk across the *other* self_pats and
+ // pull out getter for same field in each of
+ // them (using `field_index` tracked above).
+ // That is the heart of the transposition.
+ let others = self_pats.tail().iter()
+ .map(|&(_pat, ref fields)| {
+
+ let &(_, _opt_ident, other_getter_expr) =
+ fields.get(field_index);
+
+ // All Self args have same variant, so
+ // opt_idents are the same. (Assert
+ // here to make it self-evident that
+ // it is okay to ignore `_opt_ident`.)
+ assert!(opt_ident == _opt_ident);
+
+ other_getter_expr
+ }).collect::<Vec<Gc<Expr>>>();
+
+ FieldInfo { span: sp,
+ name: opt_ident,
+ self_: self_getter_expr,
+ other: others,
+ }
+ }).collect::<Vec<FieldInfo>>();
+
+ // Now, for some given VariantK, we have built up
+ // expressions for referencing every field of every
+ // Self arg, assuming all are instances of VariantK.
+ // Build up code associated with such a case.
+ let substructure = EnumMatching(index, variant, field_tuples);
+ let arm_expr = self.call_substructure_method(
+ cx, trait_, type_ident, self_args, nonself_args,
+ &substructure);
+
+ cx.arm(sp, vec![single_pat], arm_expr)
+ }).collect();
- } else { // there are still matches to create
- let current_match_str = if match_count == 0 {
- "__self".to_string()
- } else {
- format!("__arg_{}", match_count)
- };
+ // We will usually need the catch-all after matching the
+ // tuples `(VariantK, VariantK, ...)` for each VariantK of the
+ // enum. But:
+ //
+ // * when there is only one Self arg, the arms above suffice
+ // (and the deriving we call back into may not be prepared to
+ // handle EnumNonMatchCollapsed), and,
+ //
+ // * when the enum has only one variant, the single arm that
+ // is already present always suffices.
+ //
+ // * In either of the two cases above, if we *did* add a
+ // catch-all `_` match, it would trigger the
+ // unreachable-pattern error.
+ //
+ if variants.len() > 1 && self_args.len() > 1 {
+ let arms : Vec<ast::Arm> = variants.iter().enumerate()
+ .map(|(index, &variant)| {
+ let pat = variant_to_pat(cx, sp, &*variant);
+ let lit = ast::LitUint(index as u64, ast::TyU);
+ cx.arm(sp, vec![pat], cx.expr_lit(sp, lit))
+ }).collect();
- let mut arms = Vec::new();
-
- // the code for nonmatching variants only matters when
- // we've seen at least one other variant already
- if self.const_nonmatching && match_count > 0 {
- // make a matching-variant match, and a _ match.
- let index = match matching {
- Some(i) => i,
- None => cx.span_bug(trait_.span,
- "non-matching variants when required to \
- be matching in generic `deriving`")
- };
-
- // matching-variant match
- let variant = *enum_def.variants.get(index);
- let (pattern, idents) = trait_.create_enum_variant_pattern(
- cx,
- &*variant,
- current_match_str.as_slice(),
- ast::MutImmutable);
-
- matches_so_far.push((index, variant, idents));
- let arm_expr = self.build_enum_match(cx,
- trait_,
- enum_def,
- type_ident,
- self_args, nonself_args,
- matching,
- matches_so_far,
- match_count + 1);
- matches_so_far.pop().unwrap();
- arms.push(cx.arm(trait_.span, vec!( pattern ), arm_expr));
-
- if enum_def.variants.len() > 1 {
- let e = &EnumNonMatching(&[]);
- let wild_expr = self.call_substructure_method(cx, trait_, type_ident,
- self_args, nonself_args,
- e);
- let wild_arm = cx.arm(
- trait_.span,
- vec!( cx.pat_wild(trait_.span) ),
- wild_expr);
- arms.push(wild_arm);
- }
- } else {
- // create an arm matching on each variant
- for (index, &variant) in enum_def.variants.iter().enumerate() {
- let (pattern, idents) =
- trait_.create_enum_variant_pattern(
- cx,
- &*variant,
- current_match_str.as_slice(),
- ast::MutImmutable);
-
- matches_so_far.push((index, variant, idents));
- let new_matching =
- match matching {
- _ if match_count == 0 => Some(index),
- Some(i) if index == i => Some(i),
- _ => None
- };
- let arm_expr = self.build_enum_match(cx,
- trait_,
- enum_def,
- type_ident,
- self_args, nonself_args,
- new_matching,
- matches_so_far,
- match_count + 1);
- matches_so_far.pop().unwrap();
-
- let arm = cx.arm(trait_.span, vec!( pattern ), arm_expr);
- arms.push(arm);
- }
+ // Build a series of let statements mapping each self_arg
+ // to a uint corresponding to its variant index.
+ // i.e. for `enum E<T> { A, B(1), C(T, T) }`, and a deriving
+ // with three Self args, builds three statements:
+ //
+ // ```
+ // let __self0_vi = match self {
+ // A => 0u, B(..) => 1u, C(..) => 2u
+ // };
+ // let __self1_vi = match __arg1 {
+ // A => 0u, B(..) => 1u, C(..) => 2u
+ // };
+ // let __self2_vi = match __arg2 {
+ // A => 0u, B(..) => 1u, C(..) => 2u
+ // };
+ // ```
+ let mut index_let_stmts : Vec<Gc<ast::Stmt>> = Vec::new();
+ for (&ident, &self_arg) in vi_idents.iter().zip(self_args.iter()) {
+ let variant_idx = cx.expr_match(sp, self_arg, arms.clone());
+ let let_stmt = cx.stmt_let(sp, false, ident, variant_idx);
+ index_let_stmts.push(let_stmt);
}
- // match foo { arm, arm, arm, ... }
- cx.expr_match(trait_.span, self_args[match_count], arms)
+ let arm_expr = self.call_substructure_method(
+ cx, trait_, type_ident, self_args, nonself_args,
+ &catch_all_substructure);
+
+ // Builds the expression:
+ // {
+ // let __self0_vi = ...;
+ // let __self1_vi = ...;
+ // ...
+ // <delegated expression referring to __self0_vi, et al.>
+ // }
+ let arm_expr = cx.expr_block(
+ cx.block_all(sp, Vec::new(), index_let_stmts, Some(arm_expr)));
+
+ // Builds arm:
+ // _ => { let __self0_vi = ...;
+ // let __self1_vi = ...;
+ // ...
+ // <delegated expression as above> }
+ let catch_all_match_arm =
+ cx.arm(sp, vec![cx.pat_wild(sp)], arm_expr);
+
+ match_arms.push(catch_all_match_arm);
+
+ } else if variants.len() == 0 {
+ // As an additional wrinkle, For a zero-variant enum A,
+ // currently the compiler
+ // will accept `fn (a: &Self) { match *a { } }`
+ // but rejects `fn (a: &Self) { match (&*a,) { } }`
+ // as well as `fn (a: &Self) { match ( *a,) { } }`
+ //
+ // This means that the strategy of building up a tuple of
+ // all Self arguments fails when Self is a zero variant
+ // enum: rustc rejects the expanded program, even though
+ // the actual code tends to be impossible to execute (at
+ // least safely), according to the type system.
+ //
+ // The most expedient fix for this is to just let the
+ // code fall through to the catch-all. But even this is
+ // error-prone, since the catch-all as defined above would
+ // generate code like this:
+ //
+ // _ => { let __self0 = match *self { };
+ // let __self1 = match *__arg_0 { };
+ // <catch-all-expr> }
+ //
+ // Which is yields bindings for variables which type
+ // inference cannot resolve to unique types.
+ //
+ // One option to the above might be to add explicit type
+ // annotations. But the *only* reason to go down that path
+ // would be to try to make the expanded output consistent
+ // with the case when the number of enum variants >= 1.
+ //
+ // That just isn't worth it. In fact, trying to generate
+ // sensible code for *any* deriving on a zero-variant enum
+ // does not make sense. But at the same time, for now, we
+ // do not want to cause a compile failure just because the
+ // user happened to attach a deriving to their
+ // zero-variant enum.
+ //
+ // Instead, just generate a failing expression for the
+ // zero variant case, skipping matches and also skipping
+ // delegating back to the end user code entirely.
+ //
+ // (See also #4499 and #12609; note that some of the
+ // discussions there influence what choice we make here;
+ // e.g. if we feature-gate `match x { ... }` when x refers
+ // to an uninhabited type (e.g. a zero-variant enum or a
+ // type holding such an enum), but do not feature-gate
+ // zero-variant enums themselves, then attempting to
+ // derive Show on such a type could here generate code
+ // that needs the feature gate enabled.)
+
+ return cx.expr_unreachable(sp);
}
+
+ // Final wrinkle: the self_args are expressions that deref
+ // down to desired l-values, but we cannot actually deref
+ // them when they are fed as r-values into a tuple
+ // expression; here add a layer of borrowing, turning
+ // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
+ let borrowed_self_args = self_args.iter()
+ .map(|&self_arg| cx.expr_addr_of(sp, self_arg))
+ .collect::<Vec<Gc<ast::Expr>>>();
+ let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args));
+ cx.expr_match(sp, match_arg, match_arms)
}
fn expand_static_enum_method_body(&self,
pub fn cs_fold(use_foldl: bool,
f: |&mut ExtCtxt, Span, Gc<Expr>, Gc<Expr>, &[Gc<Expr>]| -> Gc<Expr>,
base: Gc<Expr>,
- enum_nonmatch_f: EnumNonMatchFunc,
+ enum_nonmatch_f: EnumNonMatchCollapsedFunc,
cx: &mut ExtCtxt,
trait_span: Span,
substructure: &Substructure)
})
}
},
- EnumNonMatching(ref all_enums) => enum_nonmatch_f(cx, trait_span,
- *all_enums,
- substructure.nonself_args),
+ EnumNonMatchingCollapsed(ref all_args, _, tuple) =>
+ enum_nonmatch_f(cx, trait_span, (all_args.as_slice(), tuple),
+ substructure.nonself_args),
StaticEnum(..) | StaticStruct(..) => {
cx.span_bug(trait_span, "static function in `deriving`")
}
*/
#[inline]
pub fn cs_same_method(f: |&mut ExtCtxt, Span, Vec<Gc<Expr>>| -> Gc<Expr>,
- enum_nonmatch_f: EnumNonMatchFunc,
+ enum_nonmatch_f: EnumNonMatchCollapsedFunc,
cx: &mut ExtCtxt,
trait_span: Span,
substructure: &Substructure)
f(cx, trait_span, called)
},
- EnumNonMatching(ref all_enums) => enum_nonmatch_f(cx, trait_span,
- *all_enums,
- substructure.nonself_args),
+ EnumNonMatchingCollapsed(ref all_self_args, _, tuple) =>
+ enum_nonmatch_f(cx, trait_span, (all_self_args.as_slice(), tuple),
+ substructure.nonself_args),
StaticEnum(..) | StaticStruct(..) => {
cx.span_bug(trait_span, "static function in `deriving`")
}
pub fn cs_same_method_fold(use_foldl: bool,
f: |&mut ExtCtxt, Span, Gc<Expr>, Gc<Expr>| -> Gc<Expr>,
base: Gc<Expr>,
- enum_nonmatch_f: EnumNonMatchFunc,
+ enum_nonmatch_f: EnumNonMatchCollapsedFunc,
cx: &mut ExtCtxt,
trait_span: Span,
substructure: &Substructure)
*/
#[inline]
pub fn cs_binop(binop: ast::BinOp, base: Gc<Expr>,
- enum_nonmatch_f: EnumNonMatchFunc,
+ enum_nonmatch_f: EnumNonMatchCollapsedFunc,
cx: &mut ExtCtxt, trait_span: Span,
substructure: &Substructure) -> Gc<Expr> {
cs_same_method_fold(
/// cs_binop with binop == or
#[inline]
-pub fn cs_or(enum_nonmatch_f: EnumNonMatchFunc,
+pub fn cs_or(enum_nonmatch_f: EnumNonMatchCollapsedFunc,
cx: &mut ExtCtxt, span: Span,
substructure: &Substructure) -> Gc<Expr> {
cs_binop(ast::BiOr, cx.expr_bool(span, false),
/// cs_binop with binop == and
#[inline]
-pub fn cs_and(enum_nonmatch_f: EnumNonMatchFunc,
+pub fn cs_and(enum_nonmatch_f: EnumNonMatchCollapsedFunc,
cx: &mut ExtCtxt, span: Span,
substructure: &Substructure) -> Gc<Expr> {
cs_binop(ast::BiAnd, cx.expr_bool(span, true),
args: vec!(Ptr(box Literal(args), Borrowed(None, MutMutable))),
ret_ty: nil_ty(),
attributes: attrs,
- const_nonmatching: false,
combine_substructure: combine_substructure(|a, b, c| {
hash_substructure(a, b, c)
})
true)),
// #[inline] liable to cause code-bloat
attributes: attrs.clone(),
- const_nonmatching: false,
combine_substructure: combine_substructure(|c, s, sub| {
cs_from("i64", c, s, sub)
}),
true)),
// #[inline] liable to cause code-bloat
attributes: attrs,
- const_nonmatching: false,
combine_substructure: combine_substructure(|c, s, sub| {
cs_from("u64", c, s, sub)
}),
),
ret_ty: Self,
attributes: Vec::new(),
- const_nonmatching: false,
combine_substructure: combine_substructure(|a, b, c| {
rand_substructure(a, b, c)
})
args: vec!(fmtr),
ret_ty: Literal(Path::new(vec!("std", "fmt", "Result"))),
attributes: Vec::new(),
- const_nonmatching: false,
combine_substructure: combine_substructure(|a, b, c| {
show_substructure(a, b, c)
})
let name = match *substr.fields {
Struct(_) => substr.type_ident,
EnumMatching(_, v, _) => v.node.name,
-
- EnumNonMatching(..) | StaticStruct(..) | StaticEnum(..) => {
+ EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => {
cx.span_bug(span, "nonsensical .fields in `#[deriving(Show)]`")
}
};
args: Vec::new(),
ret_ty: Self,
attributes: attrs.clone(),
- const_nonmatching: false,
combine_substructure: combine_substructure(|a, b, c| {
zero_substructure(a, b, c)
})
args: Vec::new(),
ret_ty: Literal(Path::new(vec!("bool"))),
attributes: attrs,
- const_nonmatching: false,
combine_substructure: combine_substructure(|cx, span, substr| {
cs_and(|cx, span, _, _| cx.span_bug(span,
"Non-matching enum \
--- /dev/null
+// Copyright 2013-2014 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.
+
+// no-prefer-dynamic
+
+#![crate_type = "rlib"]
+#![feature(plugin_registrar)]
+
+extern crate rustc;
+
+use rustc::plugin::Registry;
+
+#[plugin_registrar]
+pub fn plugin_registrar(_: &mut Registry) {}
--- /dev/null
+// Copyright 2013-2014 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.
+
+// aux-build:rlib_crate_test.rs
+// ignore-stage1
+// ignore-tidy-linelength
+// ignore-android
+
+#![feature(phase)]
+#[phase(plugin)] extern crate rlib_crate_test;
+//~^ ERROR: plugin crate `rlib_crate_test` only found in rlib format, but must be available in dylib format
+
+fn main() {}