]> git.lizzy.rs Git - rust.git/commitdiff
auto merge of #16866 : P1start/rust/tuple-indexing, r=brson
authorbors <bors@rust-lang.org>
Thu, 11 Sep 2014 00:05:41 +0000 (00:05 +0000)
committerbors <bors@rust-lang.org>
Thu, 11 Sep 2014 00:05:41 +0000 (00:05 +0000)
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.

18 files changed:
README.md
mk/dist.mk
mk/reconfig.mk
src/doc/guide-strings.md
src/doc/guide.md
src/etc/copy-runtime-deps.py
src/etc/pkg/rust.iss
src/etc/snapshot.py
src/jemalloc
src/liballoc/heap.rs
src/libcore/fmt/mod.rs
src/libcore/fmt/rt.rs
src/libcoretest/fmt/mod.rs
src/librustc/lib.rs
src/libsyntax/ext/format.rs
src/libsyntax/parse/obsolete.rs
src/libsyntax/parse/parser.rs
src/test/pretty/issue-4264.pp

index e919f3329fe2a4725976b4147cd4790455dec204..097a2aeb6e49077f5b2e2cbc09e5840623b79b7c 100644 (file)
--- a/README.md
+++ b/README.md
@@ -101,6 +101,18 @@ There is a lot more documentation in the [wiki].
 
 [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
index 99fad94bf76ab95911b8581cf07f41e1be1b0662..4d86af37171b41561ff9e7ecfcb41afcf26ce701 100644 (file)
@@ -112,7 +112,7 @@ distcheck-tar-src: dist-tar-src
 
 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 $< $@
@@ -123,7 +123,7 @@ PKG_EXE = dist/$(PKG_NAME)-install.exe
 $(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)" $<
 
index 8b88fee0ad3d9a009478d5efe83be173195f8dd8..fc8237d32bb3e1442f3563b7ce5658739971696c 100644 (file)
@@ -15,7 +15,11 @@ rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) \
 
 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
index 6c6d6e36899353d76d1ed5784ff3a52b36ff4b6f..a49132ec8be9498dcd55bdc6243d2693ff882abf 100644 (file)
@@ -92,9 +92,33 @@ fn foo(s: String) {
 ```
 
 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
 
@@ -121,6 +145,65 @@ fn compare(string: String) {
 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)
index 6d0fd54cd4c135ea281a68f46306744fef6d23ea..ec5e544fa75298a6845e5ec9c185781e4dae965e 100644 (file)
@@ -150,7 +150,7 @@ in your file name, use an underscore. `hello_world.rs` versus `goodbye.rs`.
 
 Now that you've got your file open, type this in:
 
-```
+```{rust}
 fn main() {
     println!("Hello, world!");
 }
@@ -166,7 +166,7 @@ Hello, world!
 
 Success! Let's go over what just happened in detail.
 
-```
+```{rust}
 fn main() {
 
 }
@@ -186,7 +186,7 @@ declaration, with one space in between.
 
 Next up is this line:
 
-```
+```{rust}
     println!("Hello, world!");
 ```
 
@@ -562,7 +562,7 @@ the block is executed. If it's `false`, then it is not.
 
 If you want something to happen in the `false` case, use an `else`:
 
-```
+```{rust}
 let x = 5i;
 
 if x == 5i {
@@ -574,7 +574,8 @@ if x == 5i {
 
 This is all pretty standard. However, you can also do this:
 
-```
+
+```{rust}
 let x = 5i;
 
 let y = if x == 5i {
@@ -586,7 +587,7 @@ 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 };
@@ -643,7 +644,7 @@ every line of Rust code you see.
 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 };
@@ -989,7 +990,7 @@ notation: `origin.x`.
 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,
@@ -1013,7 +1014,7 @@ called a **tuple struct**. Tuple structs do have a name, but their fields
 don't:
 
 
-```
+```{rust}
 struct Color(int, int, int);
 struct Point(int, int, int);
 ```
@@ -1028,7 +1029,7 @@ let origin = Point(0, 0, 0);
 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,
@@ -1049,7 +1050,7 @@ There _is_ one case when a tuple struct is very useful, though, and that's a
 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);
@@ -1166,7 +1167,7 @@ what's the solution?
 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 {
@@ -1407,7 +1408,7 @@ We now loop forever with `loop`, and use `break` to break out early.
 `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; }
 
@@ -4122,7 +4123,7 @@ the ability to use this **method call syntax** via the `impl` keyword.
 
 Here's how it works:
 
-```
+```{rust}
 struct Circle {
     x: f64,
     y: f64,
@@ -4161,7 +4162,7 @@ multiplications later, and we have our area.
 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,
index 8aab3f18bdad016c0734809cb4bee4d5873dbe8f..fd829cd0ab70f93227099db1396e8c8975a8a935 100644 (file)
@@ -12,8 +12,8 @@
 
 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")
@@ -21,4 +21,4 @@ def copy_runtime_deps(dest_dir):
         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])
index 053fe34952c954ae4163744c8e848694f8e388c3..08c28c4399bdd343b27a906f146144c8c71383cd 100644 (file)
@@ -1,6 +1,7 @@
 #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]
 
@@ -20,7 +21,7 @@ DisableStartupPrompt=true
 
 OutputDir=.\dist\
 SourceDir=.\
-OutputBaseFilename={#CFG_PACKAGE_NAME}-install
+OutputBaseFilename={#CFG_PACKAGE_NAME}-{#CFG_BUILD}
 DefaultDirName={pf32}\Rust
 
 Compression=lzma2/ultra
index 82302da17b85082d016f23dbd13700d5ff160a11..24255c0cc5df2c706d3a935f0aceda06d83b9159 100644 (file)
@@ -39,8 +39,10 @@ snapshot_files = {
     "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
@@ -146,10 +148,14 @@ def hash_file(x):
     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:
@@ -189,7 +195,7 @@ def make_snapshot(stage, triple):
                         "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")
index 024c67ad651e1a3ca228936c4cfb13a37329baf2..aae04170ccbfeea620502106b581c3c216cd132a 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 024c67ad651e1a3ca228936c4cfb13a37329baf2
+Subproject commit aae04170ccbfeea620502106b581c3c216cd132a
index c72a77702afff4ea2bfa636528374b3b40907b70..62010ca8916d0f686cf5e54f1c44aa79fa0bae97 100644 (file)
@@ -149,12 +149,24 @@ unsafe fn closure_exchange_malloc(drop_glue: fn(*mut u8), size: uint,
     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))]
@@ -166,7 +178,10 @@ fn je_rallocx(ptr: *mut c_void, size: size_t,
                       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)>,
@@ -183,9 +198,15 @@ fn je_malloc_stats_print(write_cb: Option<extern "C" fn(cbopaque: *mut c_void,
     #[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()
         }
@@ -195,8 +216,8 @@ pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
     #[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()
         }
@@ -206,18 +227,28 @@ pub unsafe fn reallocate(ptr: *mut u8, size: uint, align: uint,
     #[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() {
@@ -234,6 +265,7 @@ mod imp {
     use core::ptr;
     use libc;
     use libc_heap;
+    use super::MIN_ALIGN;
 
     extern {
         fn posix_memalign(memptr: *mut *mut libc::c_void,
@@ -243,16 +275,7 @@ 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;
@@ -269,10 +292,14 @@ pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
     #[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]
@@ -291,14 +318,16 @@ pub fn usable_size(size: uint, _align: uint) -> uint {
         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;
@@ -309,22 +338,30 @@ fn _aligned_realloc(block: *mut c_void, size: size_t,
 
     #[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]
@@ -334,8 +371,12 @@ pub unsafe fn reallocate_inplace(_ptr: *mut u8, _size: uint, _align: uint,
     }
 
     #[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]
index 7c4494358b1bf8055d281e981f9cd04cfa24f217..be75bfec32c86dd9fb172a5e42ce8dbbee0c39fd 100644 (file)
@@ -113,6 +113,33 @@ impl<'a> Arguments<'a> {
     /// 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> {
@@ -129,6 +156,20 @@ pub unsafe fn new<'a>(fmt: &'static [rt::Piece<'static>],
 /// 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>],
@@ -255,6 +296,18 @@ pub fn $name<T: $trait_>(x: &T, fmt: &mut Formatter) -> Result {
     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.
@@ -263,6 +316,51 @@ pub fn $name<T: $trait_>(x: &T, fmt: &mut Formatter) -> Result {
 ///
 ///   * 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,
@@ -285,7 +383,26 @@ impl<'a> Formatter<'a> {
     // 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()),
index 1f5449130ecf34b94b0787154cc7a9acf6a44299..59fbde88d8be3ad9e1979e250334e0ba8c44b4c0 100644 (file)
@@ -14,7 +14,7 @@
 //! 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),
index 9fc09b3124ef381864c131bbe8710639faa65ed4..3f2208e6c2dadedaac61638a8912838f95f01caf 100644 (file)
@@ -9,3 +9,10 @@
 // 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));
+}
index 75a48fdd596ea7dc821a4edc711585154c6ba838..ce262bf449a159dc1ccabceb1e24c660d8bdb006 100644 (file)
@@ -42,8 +42,8 @@
 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;
@@ -53,6 +53,8 @@
 #[cfg(test)]
 extern crate test;
 
+pub use rustc_llvm as llvm;
+
 mod diagnostics;
 
 pub mod back {
index b3bb01e1d0409881735cc831a04f895e74edbdfe..0bb32c73ca264ce666fb956819cb376515be6efa 100644 (file)
@@ -49,11 +49,16 @@ struct Context<'a, 'b:'a> {
     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>>,
 
@@ -370,28 +375,21 @@ fn trans_count(&self, c: parse::Count) -> Gc<ast::Expr> {
         }
     }
 
-    /// 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) => {
@@ -420,8 +418,25 @@ fn trans_piece(&mut self, piece: &parse::Piece) -> Option<Gc<ast::Expr>> {
                     }
                 };
 
-                // 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 => {
@@ -450,14 +465,33 @@ fn trans_piece(&mut self, piece: &parse::Piece) -> Option<Gc<ast::Expr>> {
                     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> {
@@ -476,30 +510,31 @@ 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))
@@ -545,13 +580,21 @@ fn to_expr(&self, invocation: Invocation) -> Gc<ast::Expr> {
         }
 
         // 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.
@@ -718,8 +761,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
         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,
     };
@@ -739,8 +784,8 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
                 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 => {}
@@ -758,7 +803,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
         }
         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() {
index ec6fd013d08aef274a6081217f87774831ad56ac..9ed9e626c3d32f40150363d6e56b806d146084c2 100644 (file)
@@ -36,6 +36,7 @@ pub enum ObsoleteSyntax {
     ObsoleteManagedExpr,
     ObsoleteImportRenaming,
     ObsoleteSubsliceMatch,
+    ObsoleteExternCrateRenaming,
 }
 
 pub trait ParserObsoleteMethods {
@@ -92,6 +93,10 @@ fn obsolete(&mut self, sp: Span, kind: ObsoleteSyntax) {
             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"
             )
         };
 
index 718d75aad8908da824d3528e577d615fdfc82144..328bdf883356c9234c28d98f52e3791d7786be0b 100644 (file)
@@ -4823,11 +4823,7 @@ fn parse_item_extern_crate(&mut self,
                     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};
 
index 86e394e5408781e0120a3bbb32378fa46840157d..376a3a38fda1ed3d566a8dc2550ff1b6008f37e5 100644 (file)
@@ -39,26 +39,20 @@ pub fn bar() {
          () => {
              #[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<'_>);