diff --git a/man/rustc.1 b/man/rustc.1 index edbc6cea02664..0eaf89a560fb8 100644 --- a/man/rustc.1 +++ b/man/rustc.1 @@ -264,7 +264,8 @@ which link to the standard library. .TP \fBRUST_TEST_THREADS\fR The test framework Rust provides executes tests in parallel. This variable sets -the maximum number of threads used for this purpose. +the maximum number of threads used for this purpose. This setting is overridden +by the --test-threads option. .TP \fBRUST_TEST_NOCAPTURE\fR diff --git a/mk/crates.mk b/mk/crates.mk index 0bd0c70bd0519..5ff6d7a89dbe0 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -60,7 +60,7 @@ RUSTC_CRATES := rustc rustc_typeck rustc_mir rustc_borrowck rustc_resolve rustc_ rustc_data_structures rustc_platform_intrinsics rustc_errors \ rustc_plugin rustc_metadata rustc_passes rustc_save_analysis \ rustc_const_eval rustc_const_math rustc_incremental -HOST_CRATES := syntax syntax_ext syntax_pos $(RUSTC_CRATES) rustdoc fmt_macros \ +HOST_CRATES := syntax syntax_ext proc_macro syntax_pos $(RUSTC_CRATES) rustdoc fmt_macros \ flate arena graphviz rbml log serialize TOOLS := compiletest rustdoc rustc rustbook error_index_generator @@ -100,6 +100,7 @@ DEPS_test := std getopts term native:rust_test_helpers DEPS_syntax := std term serialize log arena libc rustc_bitflags rustc_unicode rustc_errors syntax_pos DEPS_syntax_ext := syntax syntax_pos rustc_errors fmt_macros +DEPS_proc_macro := syntax syntax_pos rustc_plugin log DEPS_syntax_pos := serialize DEPS_rustc_const_math := std syntax log serialize @@ -114,8 +115,9 @@ DEPS_rustc_borrowck := rustc log graphviz syntax syntax_pos rustc_errors rustc_m DEPS_rustc_data_structures := std log serialize DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \ rustc_typeck rustc_mir rustc_resolve log syntax serialize rustc_llvm \ - rustc_trans rustc_privacy rustc_lint rustc_plugin \ - rustc_metadata syntax_ext rustc_passes rustc_save_analysis rustc_const_eval \ + rustc_trans rustc_privacy rustc_lint rustc_plugin \ + rustc_metadata syntax_ext proc_macro \ + rustc_passes rustc_save_analysis rustc_const_eval \ rustc_incremental syntax_pos rustc_errors DEPS_rustc_errors := log libc serialize syntax_pos DEPS_rustc_lint := rustc log syntax syntax_pos rustc_const_eval diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index 8b4fce158de46..a6f817a89624c 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -1446,13 +1446,12 @@ impl IntoIterator for Vec { #[inline] fn into_iter(mut self) -> IntoIter { unsafe { - let ptr = self.as_mut_ptr(); - assume(!ptr.is_null()); - let begin = ptr as *const T; + let begin = self.as_mut_ptr(); + assume(!begin.is_null()); let end = if mem::size_of::() == 0 { - arith_offset(ptr as *const i8, self.len() as isize) as *const T + arith_offset(begin as *const i8, self.len() as isize) as *const T } else { - ptr.offset(self.len() as isize) as *const T + begin.offset(self.len() as isize) as *const T }; let buf = ptr::read(&self.buf); mem::forget(self); @@ -1710,10 +1709,52 @@ impl<'a, T> FromIterator for Cow<'a, [T]> where T: Clone { #[stable(feature = "rust1", since = "1.0.0")] pub struct IntoIter { _buf: RawVec, - ptr: *const T, + ptr: *mut T, end: *const T, } +impl IntoIter { + /// Returns the remaining items of this iterator as a slice. + /// + /// # Examples + /// + /// ```rust + /// # #![feature(vec_into_iter_as_slice)] + /// let vec = vec!['a', 'b', 'c']; + /// let mut into_iter = vec.into_iter(); + /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); + /// let _ = into_iter.next().unwrap(); + /// assert_eq!(into_iter.as_slice(), &['b', 'c']); + /// ``` + #[unstable(feature = "vec_into_iter_as_slice", issue = "35601")] + pub fn as_slice(&self) -> &[T] { + unsafe { + slice::from_raw_parts(self.ptr, self.len()) + } + } + + /// Returns the remaining items of this iterator as a mutable slice. + /// + /// # Examples + /// + /// ```rust + /// # #![feature(vec_into_iter_as_slice)] + /// let vec = vec!['a', 'b', 'c']; + /// let mut into_iter = vec.into_iter(); + /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); + /// into_iter.as_mut_slice()[2] = 'z'; + /// assert_eq!(into_iter.next().unwrap(), 'a'); + /// assert_eq!(into_iter.next().unwrap(), 'b'); + /// assert_eq!(into_iter.next().unwrap(), 'z'); + /// ``` + #[unstable(feature = "vec_into_iter_as_slice", issue = "35601")] + pub fn as_mut_slice(&self) -> &mut [T] { + unsafe { + slice::from_raw_parts_mut(self.ptr, self.len()) + } + } +} + #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Send for IntoIter {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1726,14 +1767,14 @@ impl Iterator for IntoIter { #[inline] fn next(&mut self) -> Option { unsafe { - if self.ptr == self.end { + if self.ptr as *const _ == self.end { None } else { if mem::size_of::() == 0 { // purposefully don't use 'ptr.offset' because for // vectors with 0-size elements this would return the // same pointer. - self.ptr = arith_offset(self.ptr as *const i8, 1) as *const T; + self.ptr = arith_offset(self.ptr as *const i8, 1) as *mut T; // Use a non-null pointer value Some(ptr::read(EMPTY as *mut T)) @@ -1776,7 +1817,7 @@ impl DoubleEndedIterator for IntoIter { } else { if mem::size_of::() == 0 { // See above for why 'ptr.offset' isn't used - self.end = arith_offset(self.end as *const i8, -1) as *const T; + self.end = arith_offset(self.end as *const i8, -1) as *mut T; // Use a non-null pointer value Some(ptr::read(EMPTY as *mut T)) @@ -1796,9 +1837,7 @@ impl ExactSizeIterator for IntoIter {} #[stable(feature = "vec_into_iter_clone", since = "1.8.0")] impl Clone for IntoIter { fn clone(&self) -> IntoIter { - unsafe { - slice::from_raw_parts(self.ptr, self.len()).to_owned().into_iter() - } + self.as_slice().to_owned().into_iter() } } diff --git a/src/libcollectionstest/lib.rs b/src/libcollectionstest/lib.rs index 8ae63808f2740..ab3231b2b9955 100644 --- a/src/libcollectionstest/lib.rs +++ b/src/libcollectionstest/lib.rs @@ -28,6 +28,7 @@ #![feature(unboxed_closures)] #![feature(unicode)] #![feature(vec_deque_contains)] +#![feature(vec_into_iter_as_slice)] extern crate collections; extern crate test; diff --git a/src/libcollectionstest/vec.rs b/src/libcollectionstest/vec.rs index 7a6bd958a5f8c..9556174bd2294 100644 --- a/src/libcollectionstest/vec.rs +++ b/src/libcollectionstest/vec.rs @@ -478,6 +478,29 @@ fn test_split_off() { assert_eq!(vec2, [5, 6]); } +#[test] +fn test_into_iter_as_slice() { + let vec = vec!['a', 'b', 'c']; + let mut into_iter = vec.into_iter(); + assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); + let _ = into_iter.next().unwrap(); + assert_eq!(into_iter.as_slice(), &['b', 'c']); + let _ = into_iter.next().unwrap(); + let _ = into_iter.next().unwrap(); + assert_eq!(into_iter.as_slice(), &[]); +} + +#[test] +fn test_into_iter_as_mut_slice() { + let vec = vec!['a', 'b', 'c']; + let mut into_iter = vec.into_iter(); + assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); + into_iter.as_mut_slice()[0] = 'x'; + into_iter.as_mut_slice()[1] = 'y'; + assert_eq!(into_iter.next().unwrap(), 'x'); + assert_eq!(into_iter.as_slice(), &['y', 'c']); +} + #[test] fn test_into_iter_count() { assert_eq!(vec![1, 2, 3].into_iter().count(), 3); diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index 434084d3af865..17ec325e257b0 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -146,6 +146,7 @@ use clone::Clone; use cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering}; +use convert::From; use default::Default; use fmt::{self, Debug, Display}; use marker::{Copy, PhantomData, Send, Sync, Sized, Unsize}; @@ -329,6 +330,13 @@ impl Ord for Cell { } } +#[stable(feature = "cell_from", since = "1.12.0")] +impl From for Cell { + fn from(t: T) -> Cell { + Cell::new(t) + } +} + /// A mutable memory location with dynamically checked borrow rules /// /// See the [module-level documentation](index.html) for more. @@ -742,6 +750,13 @@ impl Ord for RefCell { } } +#[stable(feature = "cell_from", since = "1.12.0")] +impl From for RefCell { + fn from(t: T) -> RefCell { + RefCell::new(t) + } +} + struct BorrowRef<'b> { borrow: &'b Cell, } @@ -1064,3 +1079,10 @@ impl Default for UnsafeCell { UnsafeCell::new(Default::default()) } } + +#[stable(feature = "cell_from", since = "1.12.0")] +impl From for UnsafeCell { + fn from(t: T) -> UnsafeCell { + UnsafeCell::new(t) + } +} diff --git a/src/libproc_macro/Cargo.toml b/src/libproc_macro/Cargo.toml new file mode 100644 index 0000000000000..99fb1d65cda90 --- /dev/null +++ b/src/libproc_macro/Cargo.toml @@ -0,0 +1,15 @@ +[package] +authors = ["The Rust Project Developers"] +name = "proc_macro" +version = "0.0.0" + +[lib] +name = "proc_macro" +path = "lib.rs" +crate-type = ["dylib"] + +[dependencies] +log = { path = "../liblog" } +rustc_plugin = { path = "../librustc_plugin" } +syntax = { path = "../libsyntax" } +syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/libproc_macro/build.rs b/src/libproc_macro/build.rs new file mode 100644 index 0000000000000..7b7590b863b71 --- /dev/null +++ b/src/libproc_macro/build.rs @@ -0,0 +1,89 @@ +// Copyright 2016 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. + +extern crate syntax; +extern crate syntax_pos; + +use syntax::ast::Ident; +use syntax::codemap::DUMMY_SP; +use syntax::parse::token::{self, Token, keywords, str_to_ident}; +use syntax::tokenstream::{self, TokenTree, TokenStream}; +use std::rc::Rc; + +/// A wrapper around `TokenStream::concat` to avoid extra namespace specification and +/// provide TokenStream concatenation as a generic operator. +pub fn concat(ts1: TokenStream, ts2: TokenStream) -> TokenStream { + TokenStream::concat(ts1, ts2) +} + +/// Checks if two identifiers have the same name, disregarding context. This allows us to +/// fake 'reserved' keywords. +// FIXME We really want `free-identifier-=?` (a la Dybvig 1993). von Tander 2007 is +// probably the easiest way to do that. +pub fn ident_eq(tident: &TokenTree, id: Ident) -> bool { + let tid = match *tident { + TokenTree::Token(_, Token::Ident(ref id)) => id, + _ => { + return false; + } + }; + + tid.name == id.name +} + +// ____________________________________________________________________________________________ +// Conversion operators + +/// Convert a `&str` into a Token. +pub fn str_to_token_ident(s: &str) -> Token { + Token::Ident(str_to_ident(s)) +} + +/// Converts a keyword (from `syntax::parse::token::keywords`) into a Token that +/// corresponds to it. +pub fn keyword_to_token_ident(kw: keywords::Keyword) -> Token { + Token::Ident(str_to_ident(&kw.name().as_str()[..])) +} + +// ____________________________________________________________________________________________ +// Build Procedures + +/// Generically takes a `ts` and delimiter and returns `ts` delimited by the specified +/// delimiter. +pub fn build_delimited(ts: TokenStream, delim: token::DelimToken) -> TokenStream { + let tts = ts.to_tts(); + TokenStream::from_tts(vec![TokenTree::Delimited(DUMMY_SP, + Rc::new(tokenstream::Delimited { + delim: delim, + open_span: DUMMY_SP, + tts: tts, + close_span: DUMMY_SP, + }))]) +} + +/// Takes `ts` and returns `[ts]`. +pub fn build_bracket_delimited(ts: TokenStream) -> TokenStream { + build_delimited(ts, token::DelimToken::Bracket) +} + +/// Takes `ts` and returns `{ts}`. +pub fn build_brace_delimited(ts: TokenStream) -> TokenStream { + build_delimited(ts, token::DelimToken::Brace) +} + +/// Takes `ts` and returns `(ts)`. +pub fn build_paren_delimited(ts: TokenStream) -> TokenStream { + build_delimited(ts, token::DelimToken::Paren) +} + +/// Constructs `()`. +pub fn build_empty_args() -> TokenStream { + build_paren_delimited(TokenStream::mk_empty()) +} diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs new file mode 100644 index 0000000000000..9e25cb88e015c --- /dev/null +++ b/src/libproc_macro/lib.rs @@ -0,0 +1,137 @@ +// Copyright 2016 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. + +//! # Proc_Macro +//! +//! A library for procedural macro writers. +//! +//! ## Usage +//! This package provides the `qquote!` macro for syntax creation, and the prelude +//! (at libproc_macro::prelude) provides a number of operations: +//! - `concat`, for concatenating two TokenStreams. +//! - `ident_eq`, for checking if two identifiers are equal regardless of syntax context. +//! - `str_to_token_ident`, for converting an `&str` into a Token. +//! - `keyword_to_token_delim`, for converting a `parse::token::keywords::Keyword` into a +//! Token. +//! - `build_delimited`, for creating a new TokenStream from an existing one and a delimiter +//! by wrapping the TokenStream in the delimiter. +//! - `build_bracket_delimited`, `build_brace_delimited`, and `build_paren_delimited`, for +//! easing the above. +//! - `build_empty_args`, which returns a TokenStream containing `()`. +//! - `lex`, which takes an `&str` and returns the TokenStream it represents. +//! +//! The `qquote!` macro also imports `syntax::ext::proc_macro_shim::prelude::*`, so you +//! will need to `extern crate syntax` for usage. (This is a temporary solution until more +//! of the external API in libproc_macro is stabilized to support the token construction +//! operations that the qausiquoter relies on.) The shim file also provides additional +//! operations, such as `build_block_emitter` (as used in the `cond` example below). +//! +//! ## TokenStreams +//! +//! TokenStreams serve as the basis of the macro system. They are, in essence, vectors of +//! TokenTrees, where indexing treats delimited values as a single term. That is, the term +//! `even(a+c) && even(b)` will be indexibly encoded as `even | (a+c) | even | (b)` where, +//! in reality, `(a+c)` is actually a decorated pointer to `a | + | c`. +//! +//! If a user has a TokenStream that is a single, delimited value, they can use +//! `maybe_delimited` to destruct it and receive the internal vector as a new TokenStream +//! as: +//! ``` +//! `(a+c)`.maybe_delimited() ~> Some(a | + | c)` +//! ``` +//! +//! Check the TokenStream documentation for more information; the structure also provides +//! cheap concatenation and slicing. +//! +//! ## Quasiquotation +//! +//! The quasiquoter creates output that, when run, constructs the tokenstream specified as +//! input. For example, `qquote!(5 + 5)` will produce a program, that, when run, will +//! construct the TokenStream `5 | + | 5`. +//! +//! ### Unquoting +//! +//! Unquoting is currently done as `unquote`, and works by taking the single next +//! TokenTree in the TokenStream as the unquoted term. Ergonomically, `unquote(foo)` works +//! fine, but `unquote foo` is also supported. +//! +//! A simple example might be: +//! +//!``` +//!fn double(tmp: TokenStream) -> TokenStream { +//! qquote!(unquote(tmp) * 2) +//!} +//!``` +//! +//! ### Large Example: Implementing Scheme's `cond` +//! +//! Below is the full implementation of Scheme's `cond` operator. +//! +//! ``` +//! fn cond_rec(input: TokenStream) -> TokenStream { +//! if input.is_empty() { return quote!(); } +//! +//! let next = input.slice(0..1); +//! let rest = input.slice_from(1..); +//! +//! let clause : TokenStream = match next.maybe_delimited() { +//! Some(ts) => ts, +//! _ => panic!("Invalid input"), +//! }; +//! +//! // clause is ([test]) [rhs] +//! if clause.len() < 2 { panic!("Invalid macro usage in cond: {:?}", clause) } +//! +//! let test: TokenStream = clause.slice(0..1); +//! let rhs: TokenStream = clause.slice_from(1..); +//! +//! if ident_eq(&test[0], str_to_ident("else")) || rest.is_empty() { +//! quote!({unquote(rhs)}) +//! } else { +//! quote!({if unquote(test) { unquote(rhs) } else { cond!(unquote(rest)) } }) +//! } +//! } +//! ``` +//! + +#![crate_name = "proc_macro"] +#![unstable(feature = "rustc_private", issue = "27812")] +#![feature(plugin_registrar)] +#![crate_type = "dylib"] +#![crate_type = "rlib"] +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/")] +#![cfg_attr(not(stage0), deny(warnings))] + +#![feature(staged_api)] +#![feature(rustc_diagnostic_macros)] +#![feature(rustc_private)] + +extern crate rustc_plugin; +extern crate syntax; +extern crate syntax_pos; +#[macro_use] extern crate log; + +mod qquote; +pub mod build; +pub mod parse; +pub mod prelude; +use qquote::qquote; + +use rustc_plugin::Registry; + +// ____________________________________________________________________________________________ +// Main macro definition + +#[plugin_registrar] +pub fn plugin_registrar(reg: &mut Registry) { + reg.register_macro("qquote", qquote); +} diff --git a/src/libproc_macro/parse.rs b/src/libproc_macro/parse.rs new file mode 100644 index 0000000000000..9af8a68cdcf49 --- /dev/null +++ b/src/libproc_macro/parse.rs @@ -0,0 +1,26 @@ +// Copyright 2016 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. + +//! Parsing utilities for writing procedural macros. + +extern crate syntax; + +use syntax::parse::{ParseSess, filemap_to_tts}; +use syntax::tokenstream::TokenStream; + +/// Map a string to tts, using a made-up filename. For example, `lex(15)` will return a +/// TokenStream containing the literal 15. +pub fn lex(source_str: &str) -> TokenStream { + let ps = ParseSess::new(); + TokenStream::from_tts(filemap_to_tts(&ps, + ps.codemap().new_filemap("procmacro_lex".to_string(), + None, + source_str.to_owned()))) +} diff --git a/src/libproc_macro/prelude.rs b/src/libproc_macro/prelude.rs new file mode 100644 index 0000000000000..4c0c8ba6c6684 --- /dev/null +++ b/src/libproc_macro/prelude.rs @@ -0,0 +1,12 @@ +// Copyright 2016 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. + +pub use build::*; +pub use parse::*; diff --git a/src/libproc_macro/qquote.rs b/src/libproc_macro/qquote.rs new file mode 100644 index 0000000000000..67d0c77b00d83 --- /dev/null +++ b/src/libproc_macro/qquote.rs @@ -0,0 +1,470 @@ +// Copyright 2016 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. + +//! # Quasiquoter +//! This file contains the implementation internals of the quasiquoter provided by `quote!`. +//! +//! ## Ouput +//! The quasiquoter produces output of the form: +//! let tmp0 = ...; +//! let tmp1 = ...; +//! ... +//! concat(from_tokens(...), concat(...)) +//! +//! To the more explicit, the quasiquoter produces a series of bindings that each +//! construct TokenStreams via constructing Tokens and using `from_tokens`, ultimately +//! invoking `concat` on these bindings (and inlined expressions) to construct a +//! TokenStream that resembles the output syntax. +//! + +extern crate rustc_plugin; +extern crate syntax; +extern crate syntax_pos; + +use build::*; +use parse::lex; +use qquote::int_build::*; + +use syntax::ast::Ident; +use syntax::codemap::Span; +use syntax::ext::base::*; +use syntax::ext::base; +use syntax::ext::proc_macro_shim::build_block_emitter; +use syntax::parse::token::{self, Token, gensym_ident, str_to_ident}; +use syntax::print::pprust; +use syntax::tokenstream::{TokenTree, TokenStream}; + +// ____________________________________________________________________________________________ +// Main definition +/// The user should use the macro, not this procedure. +pub fn qquote<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[TokenTree]) + -> Box { + + debug!("\nTTs in: {:?}\n", pprust::tts_to_string(&tts[..])); + let output = qquoter(cx, TokenStream::from_tts(tts.clone().to_owned())); + debug!("\nQQ out: {}\n", pprust::tts_to_string(&output.to_tts()[..])); + let imports = concat(lex("use syntax::ext::proc_macro_shim::prelude::*;"), + lex("use proc_macro::prelude::*;")); + build_block_emitter(cx, sp, build_brace_delimited(concat(imports, output))) +} + +// ____________________________________________________________________________________________ +// Datatype Definitions + +#[derive(Debug)] +struct QDelimited { + delim: token::DelimToken, + open_span: Span, + tts: Vec, + close_span: Span, +} + +#[derive(Debug)] +enum QTT { + TT(TokenTree), + QDL(QDelimited), + QIdent(TokenTree), +} + +type Bindings = Vec<(Ident, TokenStream)>; + +// ____________________________________________________________________________________________ +// Quasiquoter Algorithm +// This algorithm works as follows: +// Input: TokenStream +// 1. Walk the TokenStream, gathering up the unquoted expressions and marking them separately. +// 2. Hoist any unquoted term into its own let-binding via a gensym'd identifier +// 3. Convert the body from a `complex expression` into a simplified one via `convert_complex_tts +// 4. Stitch everything together with `concat`. +fn qquoter<'cx>(cx: &'cx mut ExtCtxt, ts: TokenStream) -> TokenStream { + if ts.is_empty() { + return lex("TokenStream::mk_empty()"); + } + let qq_res = qquote_iter(cx, 0, ts); + let mut bindings = qq_res.0; + let body = qq_res.1; + let mut cct_res = convert_complex_tts(cx, body); + + bindings.append(&mut cct_res.0); + + if bindings.is_empty() { + cct_res.1 + } else { + debug!("BINDINGS"); + for b in bindings.clone() { + debug!("{:?} = {}", b.0, pprust::tts_to_string(&b.1.to_tts()[..])); + } + TokenStream::concat(unravel(bindings), cct_res.1) + } +} + +fn qquote_iter<'cx>(cx: &'cx mut ExtCtxt, depth: i64, ts: TokenStream) -> (Bindings, Vec) { + let mut depth = depth; + let mut bindings: Bindings = Vec::new(); + let mut output: Vec = Vec::new(); + + let mut iter = ts.iter(); + + loop { + let next = iter.next(); + if next.is_none() { + break; + } + let next = next.unwrap().clone(); + match next { + TokenTree::Token(_, Token::Ident(id)) if is_unquote(id) => { + if depth == 0 { + let exp = iter.next(); + if exp.is_none() { + break; + } // produce an error or something first + let exp = vec![exp.unwrap().to_owned()]; + debug!("RHS: {:?}", exp.clone()); + let new_id = gensym_ident("tmp"); + debug!("RHS TS: {:?}", TokenStream::from_tts(exp.clone())); + debug!("RHS TS TT: {:?}", TokenStream::from_tts(exp.clone()).to_vec()); + bindings.push((new_id, TokenStream::from_tts(exp))); + debug!("BINDINGS"); + for b in bindings.clone() { + debug!("{:?} = {}", b.0, pprust::tts_to_string(&b.1.to_tts()[..])); + } + output.push(QTT::QIdent(as_tt(Token::Ident(new_id.clone())))); + } else { + depth = depth - 1; + output.push(QTT::TT(next.clone())); + } + } + TokenTree::Token(_, Token::Ident(id)) if is_qquote(id) => { + depth = depth + 1; + } + TokenTree::Delimited(_, ref dl) => { + let br = qquote_iter(cx, depth, TokenStream::from_tts(dl.tts.clone().to_owned())); + let mut bind_ = br.0; + let res_ = br.1; + bindings.append(&mut bind_); + + let new_dl = QDelimited { + delim: dl.delim, + open_span: dl.open_span, + tts: res_, + close_span: dl.close_span, + }; + + output.push(QTT::QDL(new_dl)); + } + t => { + output.push(QTT::TT(t)); + } + } + } + + (bindings, output) +} + +// ____________________________________________________________________________________________ +// Turns QQTs into a TokenStream and some Bindings. +/// Construct a chain of concatenations. +fn unravel_concats(tss: Vec) -> TokenStream { + let mut pushes: Vec = + tss.into_iter().filter(|&ref ts| !ts.is_empty()).collect(); + let mut output = match pushes.pop() { + Some(ts) => ts, + None => { + return TokenStream::mk_empty(); + } + }; + + while let Some(ts) = pushes.pop() { + output = build_fn_call(str_to_ident("concat"), + concat(concat(ts, + from_tokens(vec![Token::Comma])), + output)); + } + output +} + +/// This converts the vector of QTTs into a seet of Bindings for construction and the main +/// body as a TokenStream. +fn convert_complex_tts<'cx>(cx: &'cx mut ExtCtxt, tts: Vec) -> (Bindings, TokenStream) { + let mut pushes: Vec = Vec::new(); + let mut bindings: Bindings = Vec::new(); + + let mut iter = tts.into_iter(); + + loop { + let next = iter.next(); + if next.is_none() { + break; + } + let next = next.unwrap(); + match next { + QTT::TT(TokenTree::Token(_, t)) => { + let token_out = emit_token(t); + pushes.push(token_out); + } + // FIXME handle sequence repetition tokens + QTT::QDL(qdl) => { + debug!(" QDL: {:?} ", qdl.tts); + let new_id = gensym_ident("qdl_tmp"); + let mut cct_rec = convert_complex_tts(cx, qdl.tts); + bindings.append(&mut cct_rec.0); + bindings.push((new_id, cct_rec.1)); + + let sep = build_delim_tok(qdl.delim); + + pushes.push(build_mod_call(vec![str_to_ident("proc_macro"), + str_to_ident("build"), + str_to_ident("build_delimited")], + concat(from_tokens(vec![Token::Ident(new_id)]), + concat(lex(","), sep)))); + } + QTT::QIdent(t) => { + pushes.push(TokenStream::from_tts(vec![t])); + pushes.push(TokenStream::mk_empty()); + } + _ => panic!("Unhandled case!"), + } + + } + + (bindings, unravel_concats(pushes)) +} + +// ____________________________________________________________________________________________ +// Utilities + +/// Unravels Bindings into a TokenStream of `let` declarations. +fn unravel(binds: Bindings) -> TokenStream { + let mut output = TokenStream::mk_empty(); + + for b in binds { + output = concat(output, build_let(b.0, b.1)); + } + + output +} + +/// Checks if the Ident is `unquote`. +fn is_unquote(id: Ident) -> bool { + let qq = str_to_ident("unquote"); + id.name == qq.name // We disregard context; unquote is _reserved_ +} + +/// Checks if the Ident is `quote`. +fn is_qquote(id: Ident) -> bool { + let qq = str_to_ident("qquote"); + id.name == qq.name // We disregard context; qquote is _reserved_ +} + +mod int_build { + extern crate syntax; + extern crate syntax_pos; + + use parse::*; + use build::*; + + use syntax::ast::{self, Ident}; + use syntax::codemap::{DUMMY_SP}; + use syntax::parse::token::{self, Token, keywords, str_to_ident}; + use syntax::tokenstream::{TokenTree, TokenStream}; + + // ____________________________________________________________________________________________ + // Emitters + + pub fn emit_token(t: Token) -> TokenStream { + concat(lex("TokenStream::from_tokens"), + build_paren_delimited(build_vec(build_token_tt(t)))) + } + + pub fn emit_lit(l: token::Lit, n: Option) -> TokenStream { + let suf = match n { + Some(n) => format!("Some(ast::Name({}))", n.0), + None => "None".to_string(), + }; + + let lit = match l { + token::Lit::Byte(n) => format!("Lit::Byte(token::intern(\"{}\"))", n.to_string()), + token::Lit::Char(n) => format!("Lit::Char(token::intern(\"{}\"))", n.to_string()), + token::Lit::Integer(n) => format!("Lit::Integer(token::intern(\"{}\"))", n.to_string()), + token::Lit::Float(n) => format!("Lit::Float(token::intern(\"{}\"))", n.to_string()), + token::Lit::Str_(n) => format!("Lit::Str_(token::intern(\"{}\"))", n.to_string()), + token::Lit::ByteStr(n) => format!("Lit::ByteStr(token::intern(\"{}\"))", n.to_string()), + _ => panic!("Unsupported literal"), + }; + + let res = format!("Token::Literal({},{})", lit, suf); + debug!("{}", res); + lex(&res) + } + + // ____________________________________________________________________________________________ + // Token Builders + + pub fn build_binop_tok(bot: token::BinOpToken) -> TokenStream { + match bot { + token::BinOpToken::Plus => lex("Token::BinOp(BinOpToken::Plus)"), + token::BinOpToken::Minus => lex("Token::BinOp(BinOpToken::Minus)"), + token::BinOpToken::Star => lex("Token::BinOp(BinOpToken::Star)"), + token::BinOpToken::Slash => lex("Token::BinOp(BinOpToken::Slash)"), + token::BinOpToken::Percent => lex("Token::BinOp(BinOpToken::Percent)"), + token::BinOpToken::Caret => lex("Token::BinOp(BinOpToken::Caret)"), + token::BinOpToken::And => lex("Token::BinOp(BinOpToken::And)"), + token::BinOpToken::Or => lex("Token::BinOp(BinOpToken::Or)"), + token::BinOpToken::Shl => lex("Token::BinOp(BinOpToken::Shl)"), + token::BinOpToken::Shr => lex("Token::BinOp(BinOpToken::Shr)"), + } + } + + pub fn build_binopeq_tok(bot: token::BinOpToken) -> TokenStream { + match bot { + token::BinOpToken::Plus => lex("Token::BinOpEq(BinOpToken::Plus)"), + token::BinOpToken::Minus => lex("Token::BinOpEq(BinOpToken::Minus)"), + token::BinOpToken::Star => lex("Token::BinOpEq(BinOpToken::Star)"), + token::BinOpToken::Slash => lex("Token::BinOpEq(BinOpToken::Slash)"), + token::BinOpToken::Percent => lex("Token::BinOpEq(BinOpToken::Percent)"), + token::BinOpToken::Caret => lex("Token::BinOpEq(BinOpToken::Caret)"), + token::BinOpToken::And => lex("Token::BinOpEq(BinOpToken::And)"), + token::BinOpToken::Or => lex("Token::BinOpEq(BinOpToken::Or)"), + token::BinOpToken::Shl => lex("Token::BinOpEq(BinOpToken::Shl)"), + token::BinOpToken::Shr => lex("Token::BinOpEq(BinOpToken::Shr)"), + } + } + + pub fn build_delim_tok(dt: token::DelimToken) -> TokenStream { + match dt { + token::DelimToken::Paren => lex("DelimToken::Paren"), + token::DelimToken::Bracket => lex("DelimToken::Bracket"), + token::DelimToken::Brace => lex("DelimToken::Brace"), + token::DelimToken::NoDelim => lex("DelimToken::NoDelim"), + } + } + + pub fn build_token_tt(t: Token) -> TokenStream { + match t { + Token::Eq => lex("Token::Eq"), + Token::Lt => lex("Token::Lt"), + Token::Le => lex("Token::Le"), + Token::EqEq => lex("Token::EqEq"), + Token::Ne => lex("Token::Ne"), + Token::Ge => lex("Token::Ge"), + Token::Gt => lex("Token::Gt"), + Token::AndAnd => lex("Token::AndAnd"), + Token::OrOr => lex("Token::OrOr"), + Token::Not => lex("Token::Not"), + Token::Tilde => lex("Token::Tilde"), + Token::BinOp(tok) => build_binop_tok(tok), + Token::BinOpEq(tok) => build_binopeq_tok(tok), + Token::At => lex("Token::At"), + Token::Dot => lex("Token::Dot"), + Token::DotDot => lex("Token::DotDot"), + Token::DotDotDot => lex("Token::DotDotDot"), + Token::Comma => lex("Token::Comma"), + Token::Semi => lex("Token::Semi"), + Token::Colon => lex("Token::Colon"), + Token::ModSep => lex("Token::ModSep"), + Token::RArrow => lex("Token::RArrow"), + Token::LArrow => lex("Token::LArrow"), + Token::FatArrow => lex("Token::FatArrow"), + Token::Pound => lex("Token::Pound"), + Token::Dollar => lex("Token::Dollar"), + Token::Question => lex("Token::Question"), + Token::OpenDelim(dt) => { + match dt { + token::DelimToken::Paren => lex("Token::OpenDelim(DelimToken::Paren)"), + token::DelimToken::Bracket => lex("Token::OpenDelim(DelimToken::Bracket)"), + token::DelimToken::Brace => lex("Token::OpenDelim(DelimToken::Brace)"), + token::DelimToken::NoDelim => lex("DelimToken::NoDelim"), + } + } + Token::CloseDelim(dt) => { + match dt { + token::DelimToken::Paren => lex("Token::CloseDelim(DelimToken::Paren)"), + token::DelimToken::Bracket => lex("Token::CloseDelim(DelimToken::Bracket)"), + token::DelimToken::Brace => lex("Token::CloseDelim(DelimToken::Brace)"), + token::DelimToken::NoDelim => lex("DelimToken::NoDelim"), + } + } + Token::Underscore => lex("_"), + Token::Literal(lit, sfx) => emit_lit(lit, sfx), + // fix ident expansion information... somehow + Token::Ident(ident) => lex(&format!("Token::Ident(str_to_ident(\"{}\"))", ident.name)), + Token::Lifetime(ident) => lex(&format!("Token::Ident(str_to_ident(\"{}\"))", + ident.name)), + _ => panic!("Unhandled case!"), + } + } + + // ____________________________________________________________________________________________ + // Conversion operators + + pub fn as_tt(t: Token) -> TokenTree { + // FIXME do something nicer with the spans + TokenTree::Token(DUMMY_SP, t) + } + + // ____________________________________________________________________________________________ + // Build Procedures + + /// Takes `input` and returns `vec![input]`. + pub fn build_vec(ts: TokenStream) -> TokenStream { + build_mac_call(str_to_ident("vec"), ts) + // tts.clone().to_owned() + } + + /// Takes `ident` and `rhs` and produces `let ident = rhs;`. + pub fn build_let(id: Ident, tts: TokenStream) -> TokenStream { + concat(from_tokens(vec![keyword_to_token_ident(keywords::Let), + Token::Ident(id), + Token::Eq]), + concat(tts, from_tokens(vec![Token::Semi]))) + } + + /// Takes `ident ...`, and `args ...` and produces `ident::...(args ...)`. + pub fn build_mod_call(ids: Vec, args: TokenStream) -> TokenStream { + let call = from_tokens(intersperse(ids.into_iter().map(|id| Token::Ident(id)).collect(), + Token::ModSep)); + concat(call, build_paren_delimited(args)) + } + + /// Takes `ident` and `args ...` and produces `ident(args ...)`. + pub fn build_fn_call(name: Ident, args: TokenStream) -> TokenStream { + concat(from_tokens(vec![Token::Ident(name)]), build_paren_delimited(args)) + } + + /// Takes `ident` and `args ...` and produces `ident!(args ...)`. + pub fn build_mac_call(name: Ident, args: TokenStream) -> TokenStream { + concat(from_tokens(vec![Token::Ident(name), Token::Not]), + build_paren_delimited(args)) + } + + // ____________________________________________________________________________________________ + // Utilities + + /// A wrapper around `TokenStream::from_tokens` to avoid extra namespace specification and + /// provide it as a generic operator. + pub fn from_tokens(tokens: Vec) -> TokenStream { + TokenStream::from_tokens(tokens) + } + + pub fn intersperse(vs: Vec, t: T) -> Vec + where T: Clone + { + if vs.len() < 2 { + return vs; + } + let mut output = vec![vs.get(0).unwrap().to_owned()]; + + for v in vs.into_iter().skip(1) { + output.push(t.clone()); + output.push(v); + } + output + } +} diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 93507246241de..08614ca253be5 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -689,13 +689,17 @@ pub struct Statement<'tcx> { #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub enum StatementKind<'tcx> { Assign(Lvalue<'tcx>, Rvalue<'tcx>), + SetDiscriminant{ lvalue: Lvalue<'tcx>, variant_index: usize }, } impl<'tcx> Debug for Statement<'tcx> { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { use self::StatementKind::*; match self.kind { - Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv) + Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv), + SetDiscriminant{lvalue: ref lv, variant_index: index} => { + write!(fmt, "discriminant({:?}) = {:?}", lv, index) + } } } } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 3f714ff4d5152..d44f00ed2cbe2 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -323,6 +323,9 @@ macro_rules! make_mir_visitor { ref $($mutability)* rvalue) => { self.visit_assign(block, lvalue, rvalue); } + StatementKind::SetDiscriminant{ ref $($mutability)* lvalue, .. } => { + self.visit_lvalue(lvalue, LvalueContext::Store); + } } } diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs index 932b748520170..57b335bd5eee4 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs @@ -442,6 +442,9 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { } let bits_per_block = self.bits_per_block(ctxt); match stmt.kind { + repr::StatementKind::SetDiscriminant { .. } => { + span_bug!(stmt.source_info.span, "SetDiscriminant should not exist in borrowck"); + } repr::StatementKind::Assign(ref lvalue, _) => { // assigning into this `lvalue` kills all // MoveOuts from it, and *also* all MoveOuts diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs index d59bdf93f3225..ccde429a17113 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs @@ -104,6 +104,9 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, repr::StatementKind::Assign(ref lvalue, ref rvalue) => { (lvalue, rvalue) } + repr::StatementKind::SetDiscriminant{ .. } => + span_bug!(stmt.source_info.span, + "sanity_check should run before Deaggregator inserts SetDiscriminant"), }; if lvalue == peek_arg_lval { diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index 05412216d487c..e965dcc169c2d 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -616,6 +616,10 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD Rvalue::InlineAsm { .. } => {} } } + StatementKind::SetDiscriminant{ .. } => { + span_bug!(stmt.source_info.span, + "SetDiscriminant should not exist during borrowck"); + } } } diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index 7c912e8bac6bb..c563fdb8f44e6 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -369,6 +369,9 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>( let block = &mir[loc.block]; match block.statements.get(loc.index) { Some(stmt) => match stmt.kind { + repr::StatementKind::SetDiscriminant{ .. } => { + span_bug!(stmt.source_info.span, "SetDiscrimant should not exist during borrowck"); + } repr::StatementKind::Assign(ref lvalue, _) => { debug!("drop_flag_effects: assignment {:?}", stmt); on_all_children_bits(tcx, mir, move_data, diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index 54c62d3665994..e69de29bb2d1d 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -1,36 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_driver" -version = "0.0.0" - -[lib] -name = "rustc_driver" -path = "lib.rs" -crate-type = ["dylib"] - -[dependencies] -arena = { path = "../libarena" } -flate = { path = "../libflate" } -graphviz = { path = "../libgraphviz" } -log = { path = "../liblog" } -rustc = { path = "../librustc" } -rustc_back = { path = "../librustc_back" } -rustc_borrowck = { path = "../librustc_borrowck" } -rustc_const_eval = { path = "../librustc_const_eval" } -rustc_errors = { path = "../librustc_errors" } -rustc_lint = { path = "../librustc_lint" } -rustc_llvm = { path = "../librustc_llvm" } -rustc_mir = { path = "../librustc_mir" } -rustc_plugin = { path = "../librustc_plugin" } -rustc_passes = { path = "../librustc_passes" } -rustc_privacy = { path = "../librustc_privacy" } -rustc_incremental = { path = "../librustc_incremental" } -rustc_resolve = { path = "../librustc_resolve" } -rustc_save_analysis = { path = "../librustc_save_analysis" } -rustc_trans = { path = "../librustc_trans" } -rustc_typeck = { path = "../librustc_typeck" } -rustc_metadata = { path = "../librustc_metadata" } -serialize = { path = "../libserialize" } -syntax = { path = "../libsyntax" } -syntax_ext = { path = "../libsyntax_ext" } -syntax_pos = { path = "../libsyntax_pos" } \ No newline at end of file diff --git a/src/librustc_mir/transform/deaggregator.rs b/src/librustc_mir/transform/deaggregator.rs index fccd4a607fdcf..cd6f0ed9cbac6 100644 --- a/src/librustc_mir/transform/deaggregator.rs +++ b/src/librustc_mir/transform/deaggregator.rs @@ -39,7 +39,7 @@ impl<'tcx> MirPass<'tcx> for Deaggregator { let mut curr: usize = 0; for bb in mir.basic_blocks_mut() { - let idx = match get_aggregate_statement(curr, &bb.statements) { + let idx = match get_aggregate_statement_index(curr, &bb.statements) { Some(idx) => idx, None => continue, }; @@ -48,7 +48,11 @@ impl<'tcx> MirPass<'tcx> for Deaggregator { let src_info = bb.statements[idx].source_info; let suffix_stmts = bb.statements.split_off(idx+1); let orig_stmt = bb.statements.pop().unwrap(); - let StatementKind::Assign(ref lhs, ref rhs) = orig_stmt.kind; + let (lhs, rhs) = match orig_stmt.kind { + StatementKind::Assign(ref lhs, ref rhs) => (lhs, rhs), + StatementKind::SetDiscriminant{ .. } => + span_bug!(src_info.span, "expected aggregate, not {:?}", orig_stmt.kind), + }; let (agg_kind, operands) = match rhs { &Rvalue::Aggregate(ref agg_kind, ref operands) => (agg_kind, operands), _ => span_bug!(src_info.span, "expected aggregate, not {:?}", rhs), @@ -64,10 +68,14 @@ impl<'tcx> MirPass<'tcx> for Deaggregator { let ty = variant_def.fields[i].ty(tcx, substs); let rhs = Rvalue::Use(op.clone()); - // since we don't handle enums, we don't need a cast - let lhs_cast = lhs.clone(); - - // FIXME we cannot deaggregate enums issue: #35186 + let lhs_cast = if adt_def.variants.len() > 1 { + Lvalue::Projection(Box::new(LvalueProjection { + base: lhs.clone(), + elem: ProjectionElem::Downcast(adt_def, variant), + })) + } else { + lhs.clone() + }; let lhs_proj = Lvalue::Projection(Box::new(LvalueProjection { base: lhs_cast, @@ -80,18 +88,34 @@ impl<'tcx> MirPass<'tcx> for Deaggregator { debug!("inserting: {:?} @ {:?}", new_statement, idx + i); bb.statements.push(new_statement); } + + // if the aggregate was an enum, we need to set the discriminant + if adt_def.variants.len() > 1 { + let set_discriminant = Statement { + kind: StatementKind::SetDiscriminant { + lvalue: lhs.clone(), + variant_index: variant, + }, + source_info: src_info, + }; + bb.statements.push(set_discriminant); + }; + curr = bb.statements.len(); bb.statements.extend(suffix_stmts); } } } -fn get_aggregate_statement<'a, 'tcx, 'b>(curr: usize, +fn get_aggregate_statement_index<'a, 'tcx, 'b>(start: usize, statements: &Vec>) -> Option { - for i in curr..statements.len() { + for i in start..statements.len() { let ref statement = statements[i]; - let StatementKind::Assign(_, ref rhs) = statement.kind; + let rhs = match statement.kind { + StatementKind::Assign(_, ref rhs) => rhs, + StatementKind::SetDiscriminant{ .. } => continue, + }; let (kind, operands) = match rhs { &Rvalue::Aggregate(ref kind, ref operands) => (kind, operands), _ => continue, @@ -100,9 +124,8 @@ fn get_aggregate_statement<'a, 'tcx, 'b>(curr: usize, &AggregateKind::Adt(adt_def, variant, _) => (adt_def, variant), _ => continue, }; - if operands.len() == 0 || adt_def.variants.len() > 1 { + if operands.len() == 0 { // don't deaggregate () - // don't deaggregate enums ... for now continue; } debug!("getting variant {:?}", variant); diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index fa3490cbcf338..eb0d8697f15d4 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -219,7 +219,13 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { let (mut rvalue, mut call) = (None, None); let source_info = if stmt_idx < no_stmts { let statement = &mut self.source[bb].statements[stmt_idx]; - let StatementKind::Assign(_, ref mut rhs) = statement.kind; + let mut rhs = match statement.kind { + StatementKind::Assign(_, ref mut rhs) => rhs, + StatementKind::SetDiscriminant{ .. } => + span_bug!(statement.source_info.span, + "cannot promote SetDiscriminant {:?}", + statement), + }; if self.keep_original { rvalue = Some(rhs.clone()); } else { @@ -300,10 +306,16 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { }); let mut rvalue = match candidate { Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => { - match self.source[bb].statements[stmt_idx].kind { + let ref mut statement = self.source[bb].statements[stmt_idx]; + match statement.kind { StatementKind::Assign(_, ref mut rvalue) => { mem::replace(rvalue, Rvalue::Use(new_operand)) } + StatementKind::SetDiscriminant{ .. } => { + span_bug!(statement.source_info.span, + "cannot promote SetDiscriminant {:?}", + statement); + } } } Candidate::ShuffleIndices(bb) => { @@ -340,7 +352,11 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, let (span, ty) = match candidate { Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => { let statement = &mir[bb].statements[stmt_idx]; - let StatementKind::Assign(ref dest, _) = statement.kind; + let dest = match statement.kind { + StatementKind::Assign(ref dest, _) => dest, + StatementKind::SetDiscriminant{ .. } => + panic!("cannot promote SetDiscriminant"), + }; if let Lvalue::Temp(index) = *dest { if temps[index] == TempState::PromotedOut { // Already promoted. diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 52f41741b08d6..934357c9e1da2 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -14,7 +14,7 @@ use rustc::infer::{self, InferCtxt, InferOk}; use rustc::traits::{self, Reveal}; use rustc::ty::fold::TypeFoldable; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::{self, Ty, TyCtxt, TypeVariants}; use rustc::mir::repr::*; use rustc::mir::tcx::LvalueTy; use rustc::mir::transform::{MirPass, MirSource, Pass}; @@ -360,10 +360,27 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}", lv_ty, rv_ty, terr); } - } - // FIXME: rvalue with undeterminable type - e.g. inline // asm. + } + } + StatementKind::SetDiscriminant{ ref lvalue, variant_index } => { + let lvalue_type = lvalue.ty(mir, tcx).to_ty(tcx); + let adt = match lvalue_type.sty { + TypeVariants::TyEnum(adt, _) => adt, + _ => { + span_bug!(stmt.source_info.span, + "bad set discriminant ({:?} = {:?}): lhs is not an enum", + lvalue, + variant_index); + } + }; + if variant_index >= adt.variants.len() { + span_bug!(stmt.source_info.span, + "bad set discriminant ({:?} = {:?}): value of of range", + lvalue, + variant_index); + }; } } } diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 35ded7042969f..7ca94b6356e40 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -285,6 +285,9 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { Err(err) => if failure.is_ok() { failure = Err(err); } } } + mir::StatementKind::SetDiscriminant{ .. } => { + span_bug!(span, "SetDiscriminant should not appear in constants?"); + } } } diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs index 44d264c7e98f2..7e3074f4cedf0 100644 --- a/src/librustc_trans/mir/statement.rs +++ b/src/librustc_trans/mir/statement.rs @@ -14,6 +14,8 @@ use common::{self, BlockAndBuilder}; use super::MirContext; use super::LocalRef; +use super::super::adt; +use super::super::disr::Disr; impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { pub fn trans_statement(&mut self, @@ -57,6 +59,18 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc) } } + mir::StatementKind::SetDiscriminant{ref lvalue, variant_index} => { + let ty = self.monomorphized_lvalue_ty(lvalue); + let repr = adt::represent_type(bcx.ccx(), ty); + let lvalue_transed = self.trans_lvalue(&bcx, lvalue); + bcx.with_block(|bcx| + adt::trans_set_discr(bcx, + &repr, + lvalue_transed.llval, + Disr::from(variant_index)) + ); + bcx + } } } } diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 8c1567939fb37..5961fd59699c1 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -25,9 +25,10 @@ use cell::RefCell; use fmt; use intrinsics; use mem; +use ptr; use raw; -use sys_common::rwlock::RWLock; use sys::stdio::Stderr; +use sys_common::rwlock::RWLock; use sys_common::thread_info; use sys_common::util; use thread; @@ -255,45 +256,76 @@ pub use realstd::rt::update_panic_count; /// Invoke a closure, capturing the cause of an unwinding panic if one occurs. pub unsafe fn try R>(f: F) -> Result> { - let mut slot = None; - let mut f = Some(f); - let ret; - - { - let mut to_run = || { - slot = Some(f.take().unwrap()()); - }; - let fnptr = get_call(&mut to_run); - let dataptr = &mut to_run as *mut _ as *mut u8; - let mut any_data = 0; - let mut any_vtable = 0; - let fnptr = mem::transmute::(fnptr); - let r = __rust_maybe_catch_panic(fnptr, - dataptr, - &mut any_data, - &mut any_vtable); - if r == 0 { - ret = Ok(()); - } else { - update_panic_count(-1); - ret = Err(mem::transmute(raw::TraitObject { - data: any_data as *mut _, - vtable: any_vtable as *mut _, - })); - } + struct Data { + f: F, + r: R, } - debug_assert!(update_panic_count(0) == 0); - return ret.map(|()| { - slot.take().unwrap() - }); + // We do some sketchy operations with ownership here for the sake of + // performance. The `Data` structure is never actually fully valid, but + // instead it always contains at least one uninitialized field. We can only + // pass pointers down to `__rust_maybe_catch_panic` (can't pass objects by + // value), so we do all the ownership tracking here manully. + // + // Note that this is all invalid if any of these functions unwind, but the + // whole point of this function is to prevent that! As a result we go + // through a transition where: + // + // * First, only the closure we're going to call is initialized. The return + // value is uninitialized. + // * When we make the function call, the `do_call` function below, we take + // ownership of the function pointer, replacing it with uninitialized + // data. At this point the `Data` structure is entirely uninitialized, but + // it won't drop due to an unwind because it's owned on the other side of + // the catch panic. + // * If the closure successfully returns, we write the return value into the + // data's return slot. Note that `ptr::write` is used as it's overwriting + // uninitialized data. + // * Finally, when we come back out of the `__rust_maybe_catch_panic` we're + // in one of two states: + // + // 1. The closure didn't panic, in which case the return value was + // filled in. We have to be careful to `forget` the closure, + // however, as ownership was passed to the `do_call` function. + // 2. The closure panicked, in which case the return value wasn't + // filled in. In this case the entire `data` structure is invalid, + // so we forget the entire thing. + // + // Once we stack all that together we should have the "most efficient' + // method of calling a catch panic whilst juggling ownership. + let mut any_data = 0; + let mut any_vtable = 0; + let mut data = Data { + f: f, + r: mem::uninitialized(), + }; - fn get_call(_: &mut F) -> fn(&mut F) { - call - } + let r = __rust_maybe_catch_panic(do_call::, + &mut data as *mut _ as *mut u8, + &mut any_data, + &mut any_vtable); + + return if r == 0 { + let Data { f, r } = data; + mem::forget(f); + debug_assert!(update_panic_count(0) == 0); + Ok(r) + } else { + mem::forget(data); + update_panic_count(-1); + debug_assert!(update_panic_count(0) == 0); + Err(mem::transmute(raw::TraitObject { + data: any_data as *mut _, + vtable: any_vtable as *mut _, + })) + }; - fn call(f: &mut F) { - f() + fn do_call R, R>(data: *mut u8) { + unsafe { + let data = data as *mut Data; + let f = ptr::read(&mut (*data).f); + ptr::write(&mut (*data).r, f()); + } } } diff --git a/src/libsyntax/ext/proc_macro_shim.rs b/src/libsyntax/ext/proc_macro_shim.rs new file mode 100644 index 0000000000000..fa37e9b54e457 --- /dev/null +++ b/src/libsyntax/ext/proc_macro_shim.rs @@ -0,0 +1,69 @@ +// Copyright 2016 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. + +//! This is a shim file to ease the transition to the final procedural macro interface for +//! Macros 2.0. It currently exposes the `libsyntax` operations that the quasiquoter's +//! output needs to compile correctly, along with the following operators: +//! +//! - `build_block_emitter`, which produces a `block` output macro result from the +//! provided TokenStream. + +use ast; +use codemap::Span; +use parse::parser::Parser; +use ptr::P; +use tokenstream::TokenStream; +use ext::base::*; + +/// Take a `ExtCtxt`, `Span`, and `TokenStream`, and produce a Macro Result that parses +/// the TokenStream as a block and returns it as an `Expr`. +pub fn build_block_emitter<'cx>(cx: &'cx mut ExtCtxt, sp: Span, output: TokenStream) + -> Box { + let parser = cx.new_parser_from_tts(&output.to_tts()); + + struct Result<'a> { + prsr: Parser<'a>, + span: Span, + }; //FIXME is this the right lifetime + + impl<'a> Result<'a> { + fn block(&mut self) -> P { + let res = self.prsr.parse_block().unwrap(); + res + } + } + + impl<'a> MacResult for Result<'a> { + fn make_expr(self: Box) -> Option> { + let mut me = *self; + Some(P(ast::Expr { + id: ast::DUMMY_NODE_ID, + node: ast::ExprKind::Block(me.block()), + span: me.span, + attrs: ast::ThinVec::new(), + })) + + } + } + + Box::new(Result { + prsr: parser, + span: sp, + }) +} + +pub mod prelude { + pub use ext::proc_macro_shim::build_block_emitter; + pub use ast::Ident; + pub use codemap::{DUMMY_SP, Span}; + pub use ext::base::{ExtCtxt, MacResult}; + pub use parse::token::{self, Token, DelimToken, keywords, str_to_ident}; + pub use tokenstream::{TokenTree, TokenStream}; +} diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 5ad1744418890..b4311fc007d3d 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -128,6 +128,7 @@ pub mod ext { pub mod build; pub mod expand; pub mod hygiene; + pub mod proc_macro_shim; pub mod quote; pub mod source_util; diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 4c279b2fe4832..e174f3ad08d6a 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3788,19 +3788,18 @@ impl<'a> Parser<'a> { } /// Parse a structure field - fn parse_name_and_ty(&mut self, pr: Visibility, - attrs: Vec ) -> PResult<'a, StructField> { - let lo = match pr { - Visibility::Inherited => self.span.lo, - _ => self.last_span.lo, - }; + fn parse_name_and_ty(&mut self, + lo: BytePos, + vis: Visibility, + attrs: Vec) + -> PResult<'a, StructField> { let name = self.parse_ident()?; self.expect(&token::Colon)?; let ty = self.parse_ty_sum()?; Ok(StructField { span: mk_sp(lo, self.last_span.hi), ident: Some(name), - vis: pr, + vis: vis, id: ast::DUMMY_NODE_ID, ty: ty, attrs: attrs, @@ -5120,10 +5119,11 @@ impl<'a> Parser<'a> { /// Parse a structure field declaration pub fn parse_single_struct_field(&mut self, + lo: BytePos, vis: Visibility, attrs: Vec ) -> PResult<'a, StructField> { - let a_var = self.parse_name_and_ty(vis, attrs)?; + let a_var = self.parse_name_and_ty(lo, vis, attrs)?; match self.token { token::Comma => { self.bump(); @@ -5144,8 +5144,9 @@ impl<'a> Parser<'a> { /// Parse an element of a struct definition fn parse_struct_decl_field(&mut self) -> PResult<'a, StructField> { let attrs = self.parse_outer_attributes()?; + let lo = self.span.lo; let vis = self.parse_visibility(true)?; - self.parse_single_struct_field(vis, attrs) + self.parse_single_struct_field(lo, vis, attrs) } // If `allow_path` is false, just parse the `pub` in `pub(path)` (but still parse `pub(crate)`) diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs index 89ead21cc10cb..7b1df6f0e97be 100644 --- a/src/libsyntax/tokenstream.rs +++ b/src/libsyntax/tokenstream.rs @@ -340,6 +340,11 @@ pub struct TokenStream { ts: InternalTS, } +// This indicates the maximum size for a leaf in the concatenation algorithm. +// If two leafs will be collectively smaller than this, they will be merged. +// If a leaf is larger than this, it will be concatenated at the top. +const LEAF_SIZE : usize = 32; + // NB If Leaf access proves to be slow, inroducing a secondary Leaf without the bounds // for unsliced Leafs may lead to some performance improvemenet. #[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] @@ -483,6 +488,37 @@ impl InternalTS { } } } + + fn to_vec(&self) -> Vec<&TokenTree> { + let mut res = Vec::with_capacity(self.len()); + fn traverse_and_append<'a>(res: &mut Vec<&'a TokenTree>, ts: &'a InternalTS) { + match *ts { + InternalTS::Empty(..) => {}, + InternalTS::Leaf { ref tts, offset, len, .. } => { + let mut to_app = tts[offset..offset + len].iter().collect(); + res.append(&mut to_app); + } + InternalTS::Node { ref left, ref right, .. } => { + traverse_and_append(res, left); + traverse_and_append(res, right); + } + } + } + traverse_and_append(&mut res, self); + res + } + + fn to_tts(&self) -> Vec { + self.to_vec().into_iter().cloned().collect::>() + } + + // Returns an internal node's children. + fn children(&self) -> Option<(Rc, Rc)> { + match *self { + InternalTS::Node { ref left, ref right, .. } => Some((left.clone(), right.clone())), + _ => None, + } + } } /// TokenStream operators include basic destructuring, boolean operations, `maybe_...` @@ -496,14 +532,17 @@ impl InternalTS { /// /// `maybe_path_prefix("a::b::c(a,b,c).foo()") -> (a::b::c, "(a,b,c).foo()")` impl TokenStream { + // Construct an empty node with a dummy span. pub fn mk_empty() -> TokenStream { TokenStream { ts: InternalTS::Empty(DUMMY_SP) } } + // Construct an empty node with the provided span. fn mk_spanned_empty(sp: Span) -> TokenStream { TokenStream { ts: InternalTS::Empty(sp) } } + // Construct a leaf node with a 0 offset and length equivalent to the input. fn mk_leaf(tts: Rc>, sp: Span) -> TokenStream { let len = tts.len(); TokenStream { @@ -516,6 +555,7 @@ impl TokenStream { } } + // Construct a leaf node with the provided values. fn mk_sub_leaf(tts: Rc>, offset: usize, len: usize, sp: Span) -> TokenStream { TokenStream { ts: InternalTS::Leaf { @@ -527,6 +567,7 @@ impl TokenStream { } } + // Construct an internal node with the provided values. fn mk_int_node(left: Rc, right: Rc, len: usize, @@ -548,6 +589,12 @@ impl TokenStream { TokenStream::mk_leaf(Rc::new(trees), span) } + /// Convert a vector of Tokens into a TokenStream. + pub fn from_tokens(tokens: Vec) -> TokenStream { + // FIXME do something nicer with the spans + TokenStream::from_tts(tokens.into_iter().map(|t| TokenTree::Token(DUMMY_SP, t)).collect()) + } + /// Manually change a TokenStream's span. pub fn respan(self, span: Span) -> TokenStream { match self.ts { @@ -561,11 +608,56 @@ impl TokenStream { } } - /// Concatenates two TokenStreams into a new TokenStream + /// Concatenates two TokenStreams into a new TokenStream. pub fn concat(left: TokenStream, right: TokenStream) -> TokenStream { - let new_len = left.len() + right.len(); - let new_span = combine_spans(left.span(), right.span()); - TokenStream::mk_int_node(Rc::new(left.ts), Rc::new(right.ts), new_len, new_span) + // This internal procedure performs 'aggressive compacting' during concatenation as + // follows: + // - If the nodes' combined total total length is less than 32, we copy both of + // them into a new vector and build a new leaf node. + // - If one node is an internal node and the other is a 'small' leaf (length<32), + // we recur down the internal node on the appropriate side. + // - Otherwise, we construct a new internal node that points to them as left and + // right. + fn concat_internal(left: Rc, right: Rc) -> TokenStream { + let llen = left.len(); + let rlen = right.len(); + let len = llen + rlen; + let span = combine_spans(left.span(), right.span()); + if len <= LEAF_SIZE { + let mut new_vec = left.to_tts(); + let mut rvec = right.to_tts(); + new_vec.append(&mut rvec); + return TokenStream::mk_leaf(Rc::new(new_vec), span); + } + + match (left.children(), right.children()) { + (Some((lleft, lright)), None) => { + if rlen <= LEAF_SIZE { + let new_right = concat_internal(lright, right); + TokenStream::mk_int_node(lleft, Rc::new(new_right.ts), len, span) + } else { + TokenStream::mk_int_node(left, right, len, span) + } + } + (None, Some((rleft, rright))) => { + if rlen <= LEAF_SIZE { + let new_left = concat_internal(left, rleft); + TokenStream::mk_int_node(Rc::new(new_left.ts), rright, len, span) + } else { + TokenStream::mk_int_node(left, right, len, span) + } + } + (_, _) => TokenStream::mk_int_node(left, right, len, span), + } + } + + if left.is_empty() { + right + } else if right.is_empty() { + left + } else { + concat_internal(Rc::new(left.ts), Rc::new(right.ts)) + } } /// Indicate if the TokenStream is empty. @@ -580,27 +672,13 @@ impl TokenStream { /// Convert a TokenStream into a vector of borrowed TokenTrees. pub fn to_vec(&self) -> Vec<&TokenTree> { - fn internal_to_vec(ts: &InternalTS) -> Vec<&TokenTree> { - match *ts { - InternalTS::Empty(..) => Vec::new(), - InternalTS::Leaf { ref tts, offset, len, .. } => { - tts[offset..offset + len].iter().collect() - } - InternalTS::Node { ref left, ref right, .. } => { - let mut v1 = internal_to_vec(left); - let mut v2 = internal_to_vec(right); - v1.append(&mut v2); - v1 - } - } - } - internal_to_vec(&self.ts) + self.ts.to_vec() } /// Convert a TokenStream into a vector of TokenTrees (by cloning the TokenTrees). /// (This operation is an O(n) deep copy of the underlying structure.) pub fn to_tts(&self) -> Vec { - self.to_vec().into_iter().cloned().collect::>() + self.ts.to_tts() } /// Return the TokenStream's span. diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 64515b900d5b1..850127d9f2950 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -303,6 +303,7 @@ pub struct TestOpts { pub nocapture: bool, pub color: ColorConfig, pub quiet: bool, + pub test_threads: Option, } impl TestOpts { @@ -317,6 +318,7 @@ impl TestOpts { nocapture: false, color: AutoColor, quiet: false, + test_threads: None, } } } @@ -334,6 +336,8 @@ fn optgroups() -> Vec { of stdout", "PATH"), getopts::optflag("", "nocapture", "don't capture stdout/stderr of each \ task, allow printing directly"), + getopts::optopt("", "test-threads", "Number of threads used for running tests \ + in parallel", "n_threads"), getopts::optflag("q", "quiet", "Display one character per test instead of one line"), getopts::optopt("", "color", "Configure coloring of output: auto = colorize if stdout is a tty and tests are run on serially (default); @@ -349,7 +353,8 @@ The FILTER string is tested against the name of all tests, and only those tests whose names contain the filter are run. By default, all tests are run in parallel. This can be altered with the -RUST_TEST_THREADS environment variable when running tests (set it to 1). +--test-threads flag or the RUST_TEST_THREADS environment variable when running +tests (set it to 1). All tests have their standard output and standard error captured by default. This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE @@ -408,6 +413,18 @@ pub fn parse_opts(args: &[String]) -> Option { }; } + let test_threads = match matches.opt_str("test-threads") { + Some(n_str) => + match n_str.parse::() { + Ok(n) => Some(n), + Err(e) => + return Some(Err(format!("argument for --test-threads must be a number > 0 \ + (error: {})", e))) + }, + None => + None, + }; + let color = match matches.opt_str("color").as_ref().map(|s| &**s) { Some("auto") | None => AutoColor, Some("always") => AlwaysColor, @@ -429,6 +446,7 @@ pub fn parse_opts(args: &[String]) -> Option { nocapture: nocapture, color: color, quiet: quiet, + test_threads: test_threads, }; Some(Ok(test_opts)) @@ -871,9 +889,10 @@ fn run_tests(opts: &TestOpts, tests: Vec, mut callback: F) -> } }); - // It's tempting to just spawn all the tests at once, but since we have - // many tests that run in other processes we would be making a big mess. - let concurrency = get_concurrency(); + let concurrency = match opts.test_threads { + Some(n) => n, + None => get_concurrency(), + }; let mut remaining = filtered_tests; remaining.reverse(); diff --git a/src/test/mir-opt/deaggregator_test_enum.rs b/src/test/mir-opt/deaggregator_test_enum.rs new file mode 100644 index 0000000000000..ccfa760a28c76 --- /dev/null +++ b/src/test/mir-opt/deaggregator_test_enum.rs @@ -0,0 +1,45 @@ +// Copyright 2016 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 Baz { + Empty, + Foo { x: usize }, +} + +fn bar(a: usize) -> Baz { + Baz::Foo { x: a } +} + +fn main() { + let x = bar(10); + match x { + Baz::Empty => println!("empty"), + Baz::Foo { x } => println!("{}", x), + }; +} + +// END RUST SOURCE +// START rustc.node10.Deaggregator.before.mir +// bb0: { +// var0 = arg0; // scope 0 at main.rs:7:8: 7:9 +// tmp0 = var0; // scope 1 at main.rs:8:19: 8:20 +// return = Baz::Foo { x: tmp0 }; // scope 1 at main.rs:8:5: 8:21 +// goto -> bb1; // scope 1 at main.rs:7:1: 9:2 +// } +// END rustc.node10.Deaggregator.before.mir +// START rustc.node10.Deaggregator.after.mir +// bb0: { +// var0 = arg0; // scope 0 at main.rs:7:8: 7:9 +// tmp0 = var0; // scope 1 at main.rs:8:19: 8:20 +// ((return as Foo).0: usize) = tmp0; // scope 1 at main.rs:8:5: 8:21 +// discriminant(return) = 1; // scope 1 at main.rs:8:5: 8:21 +// goto -> bb1; // scope 1 at main.rs:7:1: 9:2 +// } +// END rustc.node10.Deaggregator.after.mir \ No newline at end of file diff --git a/src/test/run-pass-fulldeps/auxiliary/cond_noprelude_plugin.rs b/src/test/run-pass-fulldeps/auxiliary/cond_noprelude_plugin.rs new file mode 100644 index 0000000000000..6aee63e2858e2 --- /dev/null +++ b/src/test/run-pass-fulldeps/auxiliary/cond_noprelude_plugin.rs @@ -0,0 +1,65 @@ +// Copyright 2015 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. + +#![allow(unused_parens)] +#![feature(plugin)] +#![feature(plugin_registrar)] +#![feature(rustc_private)] +#![plugin(proc_macro)] + +extern crate rustc_plugin; +extern crate proc_macro; +extern crate syntax; + +use proc_macro::build::ident_eq; + +use syntax::ext::base::{ExtCtxt, MacResult}; +use syntax::ext::proc_macro_shim::build_block_emitter; +use syntax::tokenstream::{TokenTree, TokenStream}; +use syntax::parse::token::str_to_ident; +use syntax::codemap::Span; + +use rustc_plugin::Registry; + +#[plugin_registrar] +pub fn plugin_registrar(reg: &mut Registry) { + reg.register_macro("cond", cond); +} + +fn cond<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box { + let output = cond_rec(TokenStream::from_tts(tts.clone().to_owned())); + build_block_emitter(cx, sp, output) +} + +fn cond_rec(input: TokenStream) -> TokenStream { + if input.is_empty() { + return qquote!(); + } + + let next = input.slice(0..1); + let rest = input.slice_from(1..); + + let clause : TokenStream = match next.maybe_delimited() { + Some(ts) => ts, + _ => panic!("Invalid input"), + }; + + // clause is ([test]) [rhs] + if clause.len() < 2 { panic!("Invalid macro usage in cond: {:?}", clause) } + + let test: TokenStream = clause.slice(0..1); + let rhs: TokenStream = clause.slice_from(1..); + + if ident_eq(&test[0], str_to_ident("else")) || rest.is_empty() { + qquote!({unquote(rhs)}) + } else { + qquote!({if unquote(test) { unquote(rhs) } else { cond!(unquote(rest)) } }) + } +} diff --git a/src/test/run-pass-fulldeps/auxiliary/cond_plugin.rs b/src/test/run-pass-fulldeps/auxiliary/cond_plugin.rs new file mode 100644 index 0000000000000..8291c8a1e41c6 --- /dev/null +++ b/src/test/run-pass-fulldeps/auxiliary/cond_plugin.rs @@ -0,0 +1,66 @@ +// Copyright 2016 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. + +#![allow(unused_parens)] +#![feature(plugin)] +#![feature(plugin_registrar)] +#![feature(rustc_private)] +#![plugin(proc_macro)] + +extern crate rustc_plugin; +extern crate proc_macro; +extern crate syntax; + +use proc_macro::prelude::*; + +use rustc_plugin::Registry; + +use syntax::ast::Ident; +use syntax::codemap::{DUMMY_SP, Span}; +use syntax::ext::proc_macro_shim::build_block_emitter; +use syntax::ext::base::{ExtCtxt, MacResult}; +use syntax::parse::token::{self, Token, DelimToken, keywords, str_to_ident}; +use syntax::tokenstream::{TokenTree, TokenStream}; + +#[plugin_registrar] +pub fn plugin_registrar(reg: &mut Registry) { + reg.register_macro("cond", cond); +} + +fn cond<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box { + let output = cond_rec(TokenStream::from_tts(tts.clone().to_owned())); + build_block_emitter(cx, sp, output) +} + +fn cond_rec(input: TokenStream) -> TokenStream { + if input.is_empty() { + return qquote!(); + } + + let next = input.slice(0..1); + let rest = input.slice_from(1..); + + let clause : TokenStream = match next.maybe_delimited() { + Some(ts) => ts, + _ => panic!("Invalid input"), + }; + + // clause is ([test]) [rhs] + if clause.len() < 2 { panic!("Invalid macro usage in cond: {:?}", clause) } + + let test: TokenStream = clause.slice(0..1); + let rhs: TokenStream = clause.slice_from(1..); + + if ident_eq(&test[0], str_to_ident("else")) || rest.is_empty() { + qquote!({unquote(rhs)}) + } else { + qquote!({if unquote(test) { unquote(rhs) } else { cond!(unquote(rest)) } }) + } +} diff --git a/src/test/run-pass-fulldeps/auxiliary/cond_prelude_plugin.rs b/src/test/run-pass-fulldeps/auxiliary/cond_prelude_plugin.rs new file mode 100644 index 0000000000000..2d92a0ef18199 --- /dev/null +++ b/src/test/run-pass-fulldeps/auxiliary/cond_prelude_plugin.rs @@ -0,0 +1,60 @@ +// Copyright 2015 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. + +#![allow(unused_parens)] +#![feature(plugin)] +#![feature(plugin_registrar)] +#![feature(rustc_private)] +#![plugin(proc_macro)] + +extern crate rustc_plugin; +extern crate proc_macro; +extern crate syntax; + +use syntax::ext::proc_macro_shim::prelude::*; +use proc_macro::prelude::*; + +use rustc_plugin::Registry; + +#[plugin_registrar] +pub fn plugin_registrar(reg: &mut Registry) { + reg.register_macro("cond", cond); +} + +fn cond<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box { + let output = cond_rec(TokenStream::from_tts(tts.clone().to_owned())); + build_block_emitter(cx, sp, output) +} + +fn cond_rec(input: TokenStream) -> TokenStream { + if input.is_empty() { + return qquote!(); + } + + let next = input.slice(0..1); + let rest = input.slice_from(1..); + + let clause : TokenStream = match next.maybe_delimited() { + Some(ts) => ts, + _ => panic!("Invalid input"), + }; + + // clause is ([test]) [rhs] + if clause.len() < 2 { panic!("Invalid macro usage in cond: {:?}", clause) } + + let test: TokenStream = clause.slice(0..1); + let rhs: TokenStream = clause.slice_from(1..); + + if ident_eq(&test[0], str_to_ident("else")) || rest.is_empty() { + qquote!({unquote(rhs)}) + } else { + qquote!({if unquote(test) { unquote(rhs) } else { cond!(unquote(rest)) } }) + } +} diff --git a/src/test/run-pass-fulldeps/macro-quote-1.rs b/src/test/run-pass-fulldeps/macro-quote-1.rs new file mode 100644 index 0000000000000..4ee775dec0cef --- /dev/null +++ b/src/test/run-pass-fulldeps/macro-quote-1.rs @@ -0,0 +1,28 @@ +// Copyright 2012-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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-stage1 + +#![feature(plugin)] +#![feature(rustc_private)] +#![plugin(proc_macro)] + +extern crate proc_macro; +use proc_macro::prelude::*; + +extern crate syntax; +use syntax::ast::Ident; +use syntax::codemap::DUMMY_SP; +use syntax::parse::token::{self, Token, keywords, str_to_ident}; + +fn main() { + let lex_true = lex("true"); + assert_eq!(qquote!(true).eq_unspanned(&lex_true), true); +} diff --git a/src/test/run-pass-fulldeps/macro-quote-cond.rs b/src/test/run-pass-fulldeps/macro-quote-cond.rs new file mode 100644 index 0000000000000..fa969b6a087cf --- /dev/null +++ b/src/test/run-pass-fulldeps/macro-quote-cond.rs @@ -0,0 +1,54 @@ +// Copyright 2012-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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:cond_plugin.rs +// ignore-stage1 + +#![feature(plugin)] +#![feature(rustc_private)] +#![plugin(cond_plugin)] + +fn fact(n : i64) -> i64 { + if n == 0 { + 1 + } else { + n * fact(n - 1) + } +} + +fn fact_cond(n : i64) -> i64 { + cond!( + ((n == 0) 1) + (else (n * fact_cond(n-1))) + ) +} + +fn fib(n : i64) -> i64 { + if n == 0 || n == 1 { + 1 + } else { + fib(n-1) + fib(n-2) + } +} + +fn fib_cond(n : i64) -> i64 { + cond!( + ((n == 0) 1) + ((n == 1) 1) + (else (fib_cond(n-1) + fib_cond(n-2))) + ) +} + +fn main() { + assert_eq!(fact(3), fact_cond(3)); + assert_eq!(fact(5), fact_cond(5)); + assert_eq!(fib(5), fib_cond(5)); + assert_eq!(fib(8), fib_cond(8)); +} diff --git a/src/test/run-pass-fulldeps/macro-quote-noprelude.rs b/src/test/run-pass-fulldeps/macro-quote-noprelude.rs new file mode 100644 index 0000000000000..4184ca7be372f --- /dev/null +++ b/src/test/run-pass-fulldeps/macro-quote-noprelude.rs @@ -0,0 +1,54 @@ +// Copyright 2012-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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:cond_noprelude_plugin.rs +// ignore-stage1 + +#![feature(plugin)] +#![feature(rustc_private)] +#![plugin(cond_noprelude_plugin)] + +fn fact(n : i64) -> i64 { + if n == 0 { + 1 + } else { + n * fact(n - 1) + } +} + +fn fact_cond(n : i64) -> i64 { + cond!( + ((n == 0) 1) + (else (n * fact_cond(n-1))) + ) +} + +fn fib(n : i64) -> i64 { + if n == 0 || n == 1 { + 1 + } else { + fib(n-1) + fib(n-2) + } +} + +fn fib_cond(n : i64) -> i64 { + cond!( + ((n == 0) 1) + ((n == 1) 1) + (else (fib_cond(n-1) + fib_cond(n-2))) + ) +} + +fn main() { + assert_eq!(fact(3), fact_cond(3)); + assert_eq!(fact(5), fact_cond(5)); + assert_eq!(fib(5), fib_cond(5)); + assert_eq!(fib(8), fib_cond(8)); +} diff --git a/src/test/run-pass-fulldeps/macro-quote-prelude.rs b/src/test/run-pass-fulldeps/macro-quote-prelude.rs new file mode 100644 index 0000000000000..5b703a5bc2668 --- /dev/null +++ b/src/test/run-pass-fulldeps/macro-quote-prelude.rs @@ -0,0 +1,54 @@ +// Copyright 2012-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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:cond_prelude_plugin.rs +// ignore-stage1 + +#![feature(plugin)] +#![feature(rustc_private)] +#![plugin(cond_prelude_plugin)] + +fn fact(n : i64) -> i64 { + if n == 0 { + 1 + } else { + n * fact(n - 1) + } +} + +fn fact_cond(n : i64) -> i64 { + cond!( + ((n == 0) 1) + (else (n * fact_cond(n-1))) + ) +} + +fn fib(n : i64) -> i64 { + if n == 0 || n == 1 { + 1 + } else { + fib(n-1) + fib(n-2) + } +} + +fn fib_cond(n : i64) -> i64 { + cond!( + ((n == 0) 1) + ((n == 1) 1) + (else (fib_cond(n-1) + fib_cond(n-2))) + ) +} + +fn main() { + assert_eq!(fact(3), fact_cond(3)); + assert_eq!(fact(5), fact_cond(5)); + assert_eq!(fib(5), fib_cond(5)); + assert_eq!(fib(8), fib_cond(8)); +} diff --git a/src/test/run-pass/issue-22894.rs b/src/test/run-pass/issue-22894.rs new file mode 100644 index 0000000000000..8acd88deef2d0 --- /dev/null +++ b/src/test/run-pass/issue-22894.rs @@ -0,0 +1,13 @@ +// Copyright 2016 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. + +#[allow(dead_code)] +static X: &'static str = &*""; +fn main() {} diff --git a/src/test/compile-fail/pub-struct-field-span-26083.rs b/src/test/ui/span/pub-struct-field.rs similarity index 52% rename from src/test/compile-fail/pub-struct-field-span-26083.rs rename to src/test/ui/span/pub-struct-field.rs index 0dc7e09f0e4db..9f8f871200ca5 100644 --- a/src/test/compile-fail/pub-struct-field-span-26083.rs +++ b/src/test/ui/span/pub-struct-field.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,23 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Regression test for issue #26083 -// Test that span for public struct fields start at `pub` instead of the identifier +// Regression test for issue #26083 and #35435 +// Test that span for public struct fields start at `pub` -struct Foo { - pub bar: u8, +#![feature(pub_restricted)] - pub - //~^ error: field `bar` is already declared [E0124] +struct Foo { bar: u8, - - pub bar: - //~^ error: field `bar` is already declared [E0124] - u8, - - bar: - //~^ error: field `bar` is already declared [E0124] - u8, + pub bar: u8, + pub(crate) bar: u8, } -fn main() { } +fn main() {} diff --git a/src/test/ui/span/pub-struct-field.stderr b/src/test/ui/span/pub-struct-field.stderr new file mode 100644 index 0000000000000..2c002c34736c5 --- /dev/null +++ b/src/test/ui/span/pub-struct-field.stderr @@ -0,0 +1,19 @@ +error[E0124]: field `bar` is already declared + --> $DIR/pub-struct-field.rs:18:5 + | +17 | bar: u8, + | ------- `bar` first declared here +18 | pub bar: u8, + | ^^^^^^^^^^^ field already declared + +error[E0124]: field `bar` is already declared + --> $DIR/pub-struct-field.rs:19:5 + | +17 | bar: u8, + | ------- `bar` first declared here +18 | pub bar: u8, +19 | pub(crate) bar: u8, + | ^^^^^^^^^^^^^^^^^^ field already declared + +error: aborting due to 2 previous errors + diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index cefcc11486fe2..90641b5c476d7 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -310,6 +310,7 @@ pub fn test_opts(config: &Config) -> test::TestOpts { Err(_) => false }, color: test::AutoColor, + test_threads: None, } }