This allows code to access the fields of tuples and tuple structs behind the feature gate `tuple_indexing`:
```rust
#![feature(tuple_indexing)]
let x = (1i, 2i);
assert_eq!(x.1, 2);
struct Point(int, int);
let origin = Point(0, 0);
assert_eq!(origin.0, 0);
assert_eq!(origin.1, 0);
```
Implements [RFC 53](https://github.com/rust-lang/rfcs/blob/master/active/0053-tuple-accessors.md). Closes #16950.
[wiki]: https://github.com/rust-lang/rust/wiki
+## Getting help and getting involved
+
+The Rust community congregates in a few places:
+
+* [StackOverflow] - Get help here.
+* [/r/rust] - General discussion.
+* [discuss.rust-lang.org] - For development of the Rust language itself.
+
+[StackOverflow]: http://stackoverflow.com/questions/tagged/rust
+[/r/rust]: http://reddit.com/r/rust
+[discuss.rust-lang.org]: http://discuss.rust-lang.org/
+
## License
Rust is primarily distributed under the terms of both the MIT license
ifdef CFG_ISCC
-PKG_EXE = dist/$(PKG_NAME)-install.exe
+PKG_EXE = dist/$(PKG_NAME)-$(CFG_BUILD).exe
%.iss: $(S)src/etc/pkg/%.iss
cp $< $@
$(PKG_EXE): rust.iss modpath.iss upgrade.iss LICENSE.txt rust-logo.ico \
$(CSREQ3_T_$(CFG_BUILD)_H_$(CFG_BUILD)) \
dist-prepare-win
- $(CFG_PYTHON) $(S)src/etc/copy-runtime-deps.py tmp/dist/win/bin
+ $(CFG_PYTHON) $(S)src/etc/copy-runtime-deps.py tmp/dist/win/bin $(CFG_BUILD)
@$(call E, ISCC: $@)
$(Q)"$(CFG_ISCC)" $<
ifndef CFG_DISABLE_MANAGE_SUBMODULES
# This is a pretty expensive operation but I don't see any way to avoid it
-NEED_GIT_RECONFIG=$(shell cd "$(CFG_SRC_DIR)" && "$(CFG_GIT)" submodule status | grep -c '^\(+\|-\)')
+# NB: This only looks for '+' status (wrong commit checked out), not '-' status
+# (nothing checked out at all). `./configure --{llvm,jemalloc,libuv}-root`
+# will explicitly deinitialize the corresponding submodules, and we don't
+# want to force constant rebuilds in that case.
+NEED_GIT_RECONFIG=$(shell cd "$(CFG_SRC_DIR)" && "$(CFG_GIT)" submodule status | grep -c '^+')
else
NEED_GIT_RECONFIG=0
endif
```
If you have good reason. It's not polite to hold on to ownership you don't
-need, and it can make your lifetimes more complex. Furthermore, you can pass
-either kind of string into `foo` by using `.as_slice()` on any `String` you
-need to pass in, so the `&str` version is more flexible.
+need, and it can make your lifetimes more complex.
+
+## Generic functions
+
+To write a function that's generic over types of strings, use [the `Str`
+trait](http://doc.rust-lang.org/std/str/trait.Str.html):
+
+```{rust}
+fn some_string_length<T: Str>(x: T) -> uint {
+ x.as_slice().len()
+}
+
+fn main() {
+ let s = "Hello, world";
+
+ println!("{}", some_string_length(s));
+
+ let s = "Hello, world".to_string();
+
+ println!("{}", some_string_length(s));
+}
+```
+
+Both of these lines will print `12`.
+
+The only method that the `Str` trait has is `as_slice()`, which gives you
+access to a `&str` value from the underlying string.
## Comparisons
Converting a `String` to a `&str` is cheap, but converting the `&str` to a
`String` involves an allocation.
+## Indexing strings
+
+You may be tempted to try to access a certain character of a `String`, like
+this:
+
+```{rust,ignore}
+let s = "hello".to_string();
+
+println!("{}", s[0]);
+```
+
+This does not compile. This is on purpose. In the world of UTF-8, direct
+indexing is basically never what you want to do. The reason is that each
+character can be a variable number of bytes. This means that you have to iterate
+through the characters anyway, which is a O(n) operation.
+
+To iterate over a string, use the `graphemes()` method on `&str`:
+
+```{rust}
+let s = "αἰθήρ";
+
+for l in s.graphemes(true) {
+ println!("{}", l);
+}
+```
+
+Note that `l` has the type `&str` here, since a single grapheme can consist of
+multiple codepoints, so a `char` wouldn't be appropriate.
+
+This will print out each character in turn, as you'd expect: first "α", then
+"ἰ", etc. You can see that this is different than just the individual bytes.
+Here's a version that prints out each byte:
+
+```{rust}
+let s = "αἰθήρ";
+
+for l in s.bytes() {
+ println!("{}", l);
+}
+```
+
+This will print:
+
+```{notrust,ignore}
+206
+177
+225
+188
+176
+206
+184
+206
+174
+207
+129
+```
+
+Many more bytes than graphemes!
+
# Other Documentation
* [the `&str` API documentation](/std/str/index.html)
Now that you've got your file open, type this in:
-```
+```{rust}
fn main() {
println!("Hello, world!");
}
Success! Let's go over what just happened in detail.
-```
+```{rust}
fn main() {
}
Next up is this line:
-```
+```{rust}
println!("Hello, world!");
```
If you want something to happen in the `false` case, use an `else`:
-```
+```{rust}
let x = 5i;
if x == 5i {
This is all pretty standard. However, you can also do this:
-```
+
+```{rust}
let x = 5i;
let y = if x == 5i {
Which we can (and probably should) write like this:
-```
+```{rust}
let x = 5i;
let y = if x == 5i { 10i } else { 15i };
What is this exception that makes us say 'almost?' You saw it already, in this
code:
-```
+```{rust}
let x = 5i;
let y: int = if x == 5i { 10i } else { 15i };
The values in structs are immutable, like other bindings in Rust. However, you
can use `mut` to make them mutable:
-```rust
+```{rust}
struct Point {
x: int,
y: int,
don't:
-```
+```{rust}
struct Color(int, int, int);
struct Point(int, int, int);
```
It is almost always better to use a struct than a tuple struct. We would write
`Color` and `Point` like this instead:
-```rust
+```{rust}
struct Color {
red: int,
blue: int,
tuple struct with only one element. We call this a 'newtype,' because it lets
you create a new type that's a synonym for another one:
-```
+```{rust}
struct Inches(int);
let length = Inches(10);
Rust has a keyword, `match`, that allows you to replace complicated `if`/`else`
groupings with something more powerful. Check it out:
-```rust
+```{rust}
let x = 5i;
match x {
`continue` is similar, but instead of ending the loop, goes to the next
iteration: This will only print the odd numbers:
-```
+```{rust}
for x in range(0i, 10i) {
if x % 2 == 0 { continue; }
Here's how it works:
-```
+```{rust}
struct Circle {
x: f64,
y: f64,
You can also define methods that do not take a `self` parameter. Here's a
pattern that's very common in Rust code:
-```
+```{rust}
struct Circle {
x: f64,
y: f64,
import snapshot, sys, os, shutil
-def copy_runtime_deps(dest_dir):
- for path in snapshot.get_winnt_runtime_deps():
+def copy_runtime_deps(dest_dir, triple):
+ for path in snapshot.get_winnt_runtime_deps(snapshot.get_platform(triple)):
shutil.copy(path, dest_dir)
lic_dest = os.path.join(dest_dir, "third-party")
shutil.rmtree(lic_dest) # copytree() won't overwrite existing files
shutil.copytree(os.path.join(os.path.dirname(__file__), "third-party"), lic_dest)
-copy_runtime_deps(sys.argv[1])
+copy_runtime_deps(sys.argv[1], sys.argv[2])
#define CFG_VERSION_WIN GetEnv("CFG_VERSION_WIN")
#define CFG_RELEASE GetEnv("CFG_RELEASE")
#define CFG_PACKAGE_NAME GetEnv("CFG_PACKAGE_NAME")
+#define CFG_BUILD GetEnv("CFG_BUILD")
[Setup]
OutputDir=.\dist\
SourceDir=.\
-OutputBaseFilename={#CFG_PACKAGE_NAME}-install
+OutputBaseFilename={#CFG_PACKAGE_NAME}-{#CFG_BUILD}
DefaultDirName={pf32}\Rust
Compression=lzma2/ultra
"freebsd": ["bin/rustc"],
}
-winnt_runtime_deps = ["libgcc_s_dw2-1.dll",
- "libstdc++-6.dll"]
+winnt_runtime_deps_32 = ["libgcc_s_dw2-1.dll",
+ "libstdc++-6.dll"]
+winnt_runtime_deps_64 = ["libgcc_s_seh-1.dll",
+ "libstdc++-6.dll"]
def parse_line(n, line):
global snapshotfile
return scrub(h.hexdigest())
# Returns a list of paths of Rust's system runtime dependencies
-def get_winnt_runtime_deps():
+def get_winnt_runtime_deps(platform):
+ if platform == "winnt-x86_64":
+ deps = winnt_runtime_deps_64
+ else:
+ deps = winnt_runtime_deps_32
runtime_deps = []
- path_dirs = os.environ["PATH"].split(';')
- for name in winnt_runtime_deps:
+ path_dirs = os.environ["PATH"].split(os.pathsep)
+ for name in deps:
for dir in path_dirs:
matches = glob.glob(os.path.join(dir, name))
if matches:
"Please make a clean build." % "\n ".join(matches))
if kernel=="winnt":
- for path in get_winnt_runtime_deps():
+ for path in get_winnt_runtime_deps(platform):
tar.add(path, "rust-stage0/bin/" + os.path.basename(path))
tar.add(os.path.join(os.path.dirname(__file__), "third-party"),
"rust-stage0/bin/third-party")
-Subproject commit 024c67ad651e1a3ca228936c4cfb13a37329baf2
+Subproject commit aae04170ccbfeea620502106b581c3c216cd132a
alloc as *mut u8
}
+// The minimum alignment guaranteed by the architecture. This value is used to
+// add fast paths for low alignment values. In practice, the alignment is a
+// constant at the call site and the branch will be optimized out.
+#[cfg(target_arch = "arm")]
+#[cfg(target_arch = "mips")]
+#[cfg(target_arch = "mipsel")]
+static MIN_ALIGN: uint = 8;
+#[cfg(target_arch = "x86")]
+#[cfg(target_arch = "x86_64")]
+static MIN_ALIGN: uint = 16;
+
#[cfg(jemalloc)]
mod imp {
use core::option::{None, Option};
use core::ptr::{RawPtr, mut_null, null};
use core::num::Int;
use libc::{c_char, c_int, c_void, size_t};
+ use super::MIN_ALIGN;
#[link(name = "jemalloc", kind = "static")]
#[cfg(not(test))]
flags: c_int) -> *mut c_void;
fn je_xallocx(ptr: *mut c_void, size: size_t, extra: size_t,
flags: c_int) -> size_t;
+ #[cfg(stage0)]
fn je_dallocx(ptr: *mut c_void, flags: c_int);
+ #[cfg(not(stage0))]
+ fn je_sdallocx(ptr: *mut c_void, size: size_t, flags: c_int);
fn je_nallocx(size: size_t, flags: c_int) -> size_t;
fn je_malloc_stats_print(write_cb: Option<extern "C" fn(cbopaque: *mut c_void,
*const c_char)>,
#[inline(always)]
fn mallocx_align(a: uint) -> c_int { a.trailing_zeros() as c_int }
+ #[inline(always)]
+ fn align_to_flags(align: uint) -> c_int {
+ if align <= MIN_ALIGN { 0 } else { mallocx_align(align) }
+ }
+
#[inline]
pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
- let ptr = je_mallocx(size as size_t, mallocx_align(align)) as *mut u8;
+ let flags = align_to_flags(align);
+ let ptr = je_mallocx(size as size_t, flags) as *mut u8;
if ptr.is_null() {
::oom()
}
#[inline]
pub unsafe fn reallocate(ptr: *mut u8, size: uint, align: uint,
_old_size: uint) -> *mut u8 {
- let ptr = je_rallocx(ptr as *mut c_void, size as size_t,
- mallocx_align(align)) as *mut u8;
+ let flags = align_to_flags(align);
+ let ptr = je_rallocx(ptr as *mut c_void, size as size_t, flags) as *mut u8;
if ptr.is_null() {
::oom()
}
#[inline]
pub unsafe fn reallocate_inplace(ptr: *mut u8, size: uint, align: uint,
_old_size: uint) -> bool {
- je_xallocx(ptr as *mut c_void, size as size_t, 0,
- mallocx_align(align)) == size as size_t
+ let flags = align_to_flags(align);
+ je_xallocx(ptr as *mut c_void, size as size_t, 0, flags) == size as size_t
}
#[inline]
+ #[cfg(stage0)]
pub unsafe fn deallocate(ptr: *mut u8, _size: uint, align: uint) {
- je_dallocx(ptr as *mut c_void, mallocx_align(align))
+ let flags = align_to_flags(align);
+ je_dallocx(ptr as *mut c_void, flags)
+ }
+
+ #[inline]
+ #[cfg(not(stage0))]
+ pub unsafe fn deallocate(ptr: *mut u8, size: uint, align: uint) {
+ let flags = align_to_flags(align);
+ je_sdallocx(ptr as *mut c_void, size as size_t, flags)
}
#[inline]
pub fn usable_size(size: uint, align: uint) -> uint {
- unsafe { je_nallocx(size as size_t, mallocx_align(align)) as uint }
+ let flags = align_to_flags(align);
+ unsafe { je_nallocx(size as size_t, flags) as uint }
}
pub fn stats_print() {
use core::ptr;
use libc;
use libc_heap;
+ use super::MIN_ALIGN;
extern {
fn posix_memalign(memptr: *mut *mut libc::c_void,
#[inline]
pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
- // The posix_memalign manpage states
- //
- // alignment [...] must be a power of and a multiple of
- // sizeof(void *)
- //
- // The `align` parameter to this function is the *minimum* alignment for
- // a block of memory, so we special case everything under `*uint` to
- // just pass it to malloc, which is guaranteed to align to at least the
- // size of `*uint`.
- if align < mem::size_of::<uint>() {
+ if align <= MIN_ALIGN {
libc_heap::malloc_raw(size)
} else {
let mut out = 0 as *mut libc::c_void;
#[inline]
pub unsafe fn reallocate(ptr: *mut u8, size: uint, align: uint,
old_size: uint) -> *mut u8 {
- let new_ptr = allocate(size, align);
- ptr::copy_memory(new_ptr, ptr as *const u8, cmp::min(size, old_size));
- deallocate(ptr, old_size, align);
- return new_ptr;
+ if align <= MIN_ALIGN {
+ libc_heap::realloc_raw(ptr, size)
+ } else {
+ let new_ptr = allocate(size, align);
+ ptr::copy_memory(new_ptr, ptr as *const u8, cmp::min(size, old_size));
+ deallocate(ptr, old_size, align);
+ new_ptr
+ }
}
#[inline]
size
}
- pub fn stats_print() {
- }
+ pub fn stats_print() {}
}
#[cfg(not(jemalloc), windows)]
mod imp {
use libc::{c_void, size_t};
+ use libc;
+ use libc_heap;
use core::ptr::RawPtr;
+ use super::MIN_ALIGN;
extern {
fn _aligned_malloc(size: size_t, align: size_t) -> *mut c_void;
#[inline]
pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
- let ptr = _aligned_malloc(size as size_t, align as size_t);
- if ptr.is_null() {
- ::oom();
+ if align <= MIN_ALIGN {
+ libc_heap::malloc_raw(size)
+ } else {
+ let ptr = _aligned_malloc(size as size_t, align as size_t);
+ if ptr.is_null() {
+ ::oom();
+ }
+ ptr as *mut u8
}
- ptr as *mut u8
}
#[inline]
pub unsafe fn reallocate(ptr: *mut u8, size: uint, align: uint,
_old_size: uint) -> *mut u8 {
- let ptr = _aligned_realloc(ptr as *mut c_void, size as size_t,
- align as size_t);
- if ptr.is_null() {
- ::oom();
+ if align <= MIN_ALIGN {
+ libc_heap::realloc_raw(ptr, size)
+ } else {
+ let ptr = _aligned_realloc(ptr as *mut c_void, size as size_t,
+ align as size_t);
+ if ptr.is_null() {
+ ::oom();
+ }
+ ptr as *mut u8
}
- ptr as *mut u8
}
#[inline]
}
#[inline]
- pub unsafe fn deallocate(ptr: *mut u8, _size: uint, _align: uint) {
- _aligned_free(ptr as *mut c_void)
+ pub unsafe fn deallocate(ptr: *mut u8, _size: uint, align: uint) {
+ if align <= MIN_ALIGN {
+ libc::free(ptr as *mut libc::c_void)
+ } else {
+ _aligned_free(ptr as *mut c_void)
+ }
}
#[inline]
/// Arguments structure. The compiler inserts an `unsafe` block to call this,
/// which is valid because the compiler performs all necessary validation to
/// ensure that the resulting call to format/write would be safe.
+ #[cfg(not(stage0))]
+ #[doc(hidden)] #[inline]
+ pub unsafe fn new<'a>(pieces: &'static [&'static str],
+ args: &'a [Argument<'a>]) -> Arguments<'a> {
+ Arguments {
+ pieces: mem::transmute(pieces),
+ fmt: None,
+ args: args
+ }
+ }
+
+ /// This function is used to specify nonstandard formatting parameters.
+ /// The `pieces` array must be at least as long as `fmt` to construct
+ /// a valid Arguments structure.
+ #[cfg(not(stage0))]
+ #[doc(hidden)] #[inline]
+ pub unsafe fn with_placeholders<'a>(pieces: &'static [&'static str],
+ fmt: &'static [rt::Argument<'static>],
+ args: &'a [Argument<'a>]) -> Arguments<'a> {
+ Arguments {
+ pieces: mem::transmute(pieces),
+ fmt: Some(mem::transmute(fmt)),
+ args: args
+ }
+ }
+
+ #[cfg(stage0)]
#[doc(hidden)] #[inline]
pub unsafe fn new<'a>(fmt: &'static [rt::Piece<'static>],
args: &'a [Argument<'a>]) -> Arguments<'a> {
/// and pass it to a function or closure, passed as the first argument. The
/// macro validates the format string at compile-time so usage of the `write`
/// and `format` functions can be safely performed.
+#[cfg(not(stage0))]
+pub struct Arguments<'a> {
+ // Format string pieces to print.
+ pieces: &'a [&'a str],
+
+ // Placeholder specs, or `None` if all specs are default (as in "{}{}").
+ fmt: Option<&'a [rt::Argument<'a>]>,
+
+ // Dynamic arguments for interpolation, to be interleaved with string
+ // pieces. (Every argument is preceded by a string piece.)
+ args: &'a [Argument<'a>],
+}
+
+#[cfg(stage0)] #[doc(hidden)]
pub struct Arguments<'a> {
fmt: &'a [rt::Piece<'a>],
args: &'a [Argument<'a>],
secret_upper_exp, UpperExp;
}
+#[cfg(not(stage0))]
+static DEFAULT_ARGUMENT: rt::Argument<'static> = rt::Argument {
+ position: rt::ArgumentNext,
+ format: rt::FormatSpec {
+ fill: ' ',
+ align: rt::AlignUnknown,
+ flags: 0,
+ precision: rt::CountImplied,
+ width: rt::CountImplied,
+ }
+};
+
/// The `write` function takes an output stream, a precompiled format string,
/// and a list of arguments. The arguments will be formatted according to the
/// specified format string into the output stream provided.
///
/// * output - the buffer to write output to
/// * args - the precompiled arguments generated by `format_args!`
+#[cfg(not(stage0))]
+pub fn write(output: &mut FormatWriter, args: &Arguments) -> Result {
+ let mut formatter = Formatter {
+ flags: 0,
+ width: None,
+ precision: None,
+ buf: output,
+ align: rt::AlignUnknown,
+ fill: ' ',
+ args: args.args,
+ curarg: args.args.iter(),
+ };
+
+ let mut pieces = args.pieces.iter();
+
+ match args.fmt {
+ None => {
+ // We can use default formatting parameters for all arguments.
+ for _ in range(0, args.args.len()) {
+ try!(formatter.buf.write(pieces.next().unwrap().as_bytes()));
+ try!(formatter.run(&DEFAULT_ARGUMENT));
+ }
+ }
+ Some(fmt) => {
+ // Every spec has a corresponding argument that is preceded by
+ // a string piece.
+ for (arg, piece) in fmt.iter().zip(pieces.by_ref()) {
+ try!(formatter.buf.write(piece.as_bytes()));
+ try!(formatter.run(arg));
+ }
+ }
+ }
+
+ // There can be only one trailing string piece left.
+ match pieces.next() {
+ Some(piece) => {
+ try!(formatter.buf.write(piece.as_bytes()));
+ }
+ None => {}
+ }
+
+ Ok(())
+}
+
+#[cfg(stage0)] #[doc(hidden)]
pub fn write(output: &mut FormatWriter, args: &Arguments) -> Result {
let mut formatter = Formatter {
flags: 0,
// First up is the collection of functions used to execute a format string
// at runtime. This consumes all of the compile-time statics generated by
// the format! syntax extension.
+ #[cfg(not(stage0))]
+ fn run(&mut self, arg: &rt::Argument) -> Result {
+ // Fill in the format parameters into the formatter
+ self.fill = arg.format.fill;
+ self.align = arg.format.align;
+ self.flags = arg.format.flags;
+ self.width = self.getcount(&arg.format.width);
+ self.precision = self.getcount(&arg.format.precision);
+
+ // Extract the correct argument
+ let value = match arg.position {
+ rt::ArgumentNext => { *self.curarg.next().unwrap() }
+ rt::ArgumentIs(i) => self.args[i],
+ };
+
+ // Then actually do some printing
+ (value.formatter)(value.value, self)
+ }
+ #[cfg(stage0)] #[doc(hidden)]
fn run(&mut self, piece: &rt::Piece) -> Result {
match *piece {
rt::String(s) => self.buf.write(s.as_bytes()),
//! These definitions are similar to their `ct` equivalents, but differ in that
//! these can be statically allocated and are slightly optimized for the runtime
-
+#[cfg(stage0)]
#[doc(hidden)]
pub enum Piece<'a> {
String(&'a str),
// except according to those terms.
mod num;
+
+#[test]
+fn test_format_flags() {
+ // No residual flags left by pointer formatting
+ let p = "".as_ptr();
+ assert_eq!(format!("{:p} {:x}", p, 16u), format!("{:p} 10", p));
+}
extern crate getopts;
extern crate graphviz;
extern crate libc;
-extern crate "rustc_llvm" as llvm;
-extern crate "rustc_back" as rustc_back;
+extern crate rustc_llvm;
+extern crate rustc_back;
extern crate serialize;
extern crate rbml;
extern crate time;
#[cfg(test)]
extern crate test;
+pub use rustc_llvm as llvm;
+
mod diagnostics;
pub mod back {
name_types: HashMap<String, ArgumentType>,
name_ordering: Vec<String>,
- /// The latest consecutive literal strings
- literal: Option<String>,
+ /// The latest consecutive literal strings, or empty if there weren't any.
+ literal: String,
- /// Collection of the compiled `rt::Piece` structures
+ /// Collection of the compiled `rt::Argument` structures
pieces: Vec<Gc<ast::Expr>>,
+ /// Collection of string literals
+ str_pieces: Vec<Gc<ast::Expr>>,
+ /// Stays `true` if all formatting parameters are default (as in "{}{}").
+ all_pieces_simple: bool,
+
name_positions: HashMap<String, uint>,
method_statics: Vec<Gc<ast::Item>>,
}
}
- /// Translate the accumulated string literals to a static `rt::Piece`
- fn trans_literal_string(&mut self) -> Option<Gc<ast::Expr>> {
+ /// Translate the accumulated string literals to a literal expression
+ fn trans_literal_string(&mut self) -> Gc<ast::Expr> {
let sp = self.fmtsp;
- self.literal.take().map(|s| {
- let s = token::intern_and_get_ident(s.as_slice());
- self.ecx.expr_call_global(sp,
- self.rtpath("String"),
- vec!(
- self.ecx.expr_str(sp, s)
- ))
- })
+ let s = token::intern_and_get_ident(self.literal.as_slice());
+ self.literal.clear();
+ self.ecx.expr_str(sp, s)
}
- /// Translate a `parse::Piece` to a static `rt::Piece`
+ /// Translate a `parse::Piece` to a static `rt::Argument` or append
+ /// to the `literal` string.
fn trans_piece(&mut self, piece: &parse::Piece) -> Option<Gc<ast::Expr>> {
let sp = self.fmtsp;
match *piece {
parse::String(s) => {
- match self.literal {
- Some(ref mut sb) => sb.push_str(s),
- ref mut empty => *empty = Some(String::from_str(s)),
- }
+ self.literal.push_str(s);
None
}
parse::Argument(ref arg) => {
}
};
- // Translate the format
+ let simple_arg = parse::Argument {
+ position: parse::ArgumentNext,
+ format: parse::FormatSpec {
+ fill: arg.format.fill,
+ align: parse::AlignUnknown,
+ flags: 0,
+ precision: parse::CountImplied,
+ width: parse::CountImplied,
+ ty: arg.format.ty
+ }
+ };
+
let fill = match arg.format.fill { Some(c) => c, None => ' ' };
+
+ if *arg != simple_arg || fill != ' ' {
+ self.all_pieces_simple = false;
+ }
+
+ // Translate the format
let fill = self.ecx.expr_lit(sp, ast::LitChar(fill));
let align = match arg.format.align {
parse::AlignLeft => {
self.ecx.field_imm(sp, self.ecx.ident_of("width"), width)));
let path = self.ecx.path_global(sp, self.rtpath("Argument"));
- let s = self.ecx.expr_struct(sp, path, vec!(
+ Some(self.ecx.expr_struct(sp, path, vec!(
self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos),
- self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt)));
- Some(self.ecx.expr_call_global(sp, self.rtpath("Argument"), vec!(s)))
+ self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt))))
}
}
}
+ fn item_static_array(&self,
+ name: ast::Ident,
+ piece_ty: Gc<ast::Ty>,
+ pieces: Vec<Gc<ast::Expr>>)
+ -> ast::Stmt
+ {
+ let pieces_len = self.ecx.expr_uint(self.fmtsp, pieces.len());
+ let fmt = self.ecx.expr_vec(self.fmtsp, pieces);
+ let ty = ast::TyFixedLengthVec(
+ piece_ty,
+ pieces_len
+ );
+ let ty = self.ecx.ty(self.fmtsp, ty);
+ let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
+ let item = self.ecx.item(self.fmtsp, name,
+ self.static_attrs(), st);
+ let decl = respan(self.fmtsp, ast::DeclItem(item));
+ respan(self.fmtsp, ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID))
+ }
+
/// Actually builds the expression which the iformat! block will be expanded
/// to
fn to_expr(&self, invocation: Invocation) -> Gc<ast::Expr> {
// Next, build up the static array which will become our precompiled
// format "string"
- let fmt = self.ecx.expr_vec(self.fmtsp, self.pieces.clone());
- let piece_ty = self.ecx.ty_path(self.ecx.path_all(
+ let static_str_name = self.ecx.ident_of("__STATIC_FMTSTR");
+ let static_lifetime = self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("'static").name);
+ let piece_ty = self.ecx.ty_rptr(
self.fmtsp,
- true, vec!(
- self.ecx.ident_of("std"),
- self.ecx.ident_of("fmt"),
- self.ecx.ident_of("rt"),
- self.ecx.ident_of("Piece")),
- vec!(self.ecx.lifetime(self.fmtsp,
- self.ecx.ident_of("'static").name)),
- Vec::new()
- ), None);
- let ty = ast::TyFixedLengthVec(
- piece_ty,
- self.ecx.expr_uint(self.fmtsp, self.pieces.len())
- );
- let ty = self.ecx.ty(self.fmtsp, ty);
- let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
- let static_name = self.ecx.ident_of("__STATIC_FMTSTR");
- let item = self.ecx.item(self.fmtsp, static_name,
- self.static_attrs(), st);
- let decl = respan(self.fmtsp, ast::DeclItem(item));
- lets.push(box(GC) respan(self.fmtsp,
- ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID)));
+ self.ecx.ty_ident(self.fmtsp, self.ecx.ident_of("str")),
+ Some(static_lifetime),
+ ast::MutImmutable);
+ lets.push(box(GC) self.item_static_array(static_str_name,
+ piece_ty,
+ self.str_pieces.clone()));
+
+ // Then, build up the static array which will store our precompiled
+ // nonstandard placeholders, if there are any.
+ let static_args_name = self.ecx.ident_of("__STATIC_FMTARGS");
+ if !self.all_pieces_simple {
+ let piece_ty = self.ecx.ty_path(self.ecx.path_all(
+ self.fmtsp,
+ true, self.rtpath("Argument"),
+ vec![static_lifetime],
+ vec![]
+ ), None);
+ lets.push(box(GC) self.item_static_array(static_args_name,
+ piece_ty,
+ self.pieces.clone()));
+ }
// Right now there is a bug such that for the expression:
// foo(bar(&1))
}
// Now create the fmt::Arguments struct with all our locals we created.
- let fmt = self.ecx.expr_ident(self.fmtsp, static_name);
+ let pieces = self.ecx.expr_ident(self.fmtsp, static_str_name);
let args_slice = self.ecx.expr_ident(self.fmtsp, slicename);
+
+ let (fn_name, fn_args) = if self.all_pieces_simple {
+ ("new", vec![pieces, args_slice])
+ } else {
+ let fmt = self.ecx.expr_ident(self.fmtsp, static_args_name);
+ ("with_placeholders", vec![pieces, fmt, args_slice])
+ };
+
let result = self.ecx.expr_call_global(self.fmtsp, vec!(
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("Arguments"),
- self.ecx.ident_of("new")), vec!(fmt, args_slice));
+ self.ecx.ident_of(fn_name)), fn_args);
// We did all the work of making sure that the arguments
// structure is safe, so we can safely have an unsafe block.
name_ordering: name_ordering,
nest_level: 0,
next_arg: 0,
- literal: None,
+ literal: String::new(),
pieces: Vec::new(),
+ str_pieces: Vec::new(),
+ all_pieces_simple: true,
method_statics: Vec::new(),
fmtsp: sp,
};
cx.verify_piece(&piece);
match cx.trans_piece(&piece) {
Some(piece) => {
- cx.trans_literal_string().map(|piece|
- cx.pieces.push(piece));
+ let s = cx.trans_literal_string();
+ cx.str_pieces.push(s);
cx.pieces.push(piece);
}
None => {}
}
None => {}
}
- cx.trans_literal_string().map(|piece| cx.pieces.push(piece));
+ if !cx.literal.is_empty() {
+ let s = cx.trans_literal_string();
+ cx.str_pieces.push(s);
+ }
// Make sure that all arguments were used and all arguments have types.
for (i, ty) in cx.arg_types.iter().enumerate() {
ObsoleteManagedExpr,
ObsoleteImportRenaming,
ObsoleteSubsliceMatch,
+ ObsoleteExternCrateRenaming,
}
pub trait ParserObsoleteMethods {
ObsoleteSubsliceMatch => (
"subslice match syntax",
"instead of `..xs`, write `xs..` in a pattern"
+ ),
+ ObsoleteExternCrateRenaming => (
+ "`extern crate foo = bar` syntax",
+ "write `extern crate bar as foo` instead"
)
};
self.bump();
let path = self.parse_str();
let span = self.span;
- self.span_warn(span,
- format!("this extern crate syntax is deprecated. \
- Use: extern crate \"{}\" as {};",
- path.ref0().get(), the_ident.as_str() ).as_slice()
- );
+ self.obsolete(span, ObsoleteExternCrateRenaming);
Some(path)
} else {None};
() => {
#[inline]
#[allow(dead_code)]
- static __STATIC_FMTSTR:
- [::std::fmt::rt::Piece<'static>, ..(1u as uint)] =
- ([((::std::fmt::rt::String as
- fn(&'static str) -> core::fmt::rt::Piece<'static>)(("test"
- as
- &'static str))
- as core::fmt::rt::Piece<'static>)] as
- [core::fmt::rt::Piece<'static>, .. 1]);
+ static __STATIC_FMTSTR: [&'static str, ..(1u as uint)] =
+ ([("test" as &'static str)] as [&'static str, .. 1]);
let __args_vec =
(&([] as [core::fmt::Argument<'_>, .. 0]) as
&[core::fmt::Argument<'_>, .. 0]);
let __args =
(unsafe {
((::std::fmt::Arguments::new as
- unsafe fn(&'static [core::fmt::rt::Piece<'static>], &'a [core::fmt::Argument<'a>]) -> core::fmt::Arguments<'a>)((__STATIC_FMTSTR
- as
- [core::fmt::rt::Piece<'static>, .. 1]),
- (__args_vec
- as
- &[core::fmt::Argument<'_>, .. 0]))
+ unsafe fn(&'static [&'static str], &'a [core::fmt::Argument<'a>]) -> core::fmt::Arguments<'a>)((__STATIC_FMTSTR
+ as
+ [&'static str, .. 1]),
+ (__args_vec
+ as
+ &[core::fmt::Argument<'_>, .. 0]))
as core::fmt::Arguments<'_>)
} as core::fmt::Arguments<'_>);