about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2015-01-13 15:42:53 -0800
committerAlex Crichton <alex@alexcrichton.com>2015-01-30 09:21:56 -0800
commit62273575139a80c2b208a3a27e0c2392b1425be6 (patch)
treef9381a041e8ad95efe57ee6f85954a8b063b853f /src/libsyntax
parent52c74e63dacd49017b19330e0cbecbac0a3fe62e (diff)
downloadrust-62273575139a80c2b208a3a27e0c2392b1425be6.tar.gz
rust-62273575139a80c2b208a3a27e0c2392b1425be6.zip
std: Stabilize the std::fmt module
This commit performs a final stabilization pass over the std::fmt module,
marking all necessary APIs as stable. One of the more interesting aspects of
this module is that it exposes a good deal of its runtime representation to the
outside world in order for `format_args!` to be able to construct the format
strings. Instead of hacking the compiler to assume that these items are stable,
this commit instead lays out a story for the stabilization and evolution of
these APIs.

There are three primary details used by the `format_args!` macro:

1. `Arguments` - an opaque package of a "compiled format string". This structure
   is passed around and the `write` function is the source of truth for
   transforming a compiled format string into a string at runtime. This must be
   able to be constructed in stable code.

2. `Argument` - an opaque structure representing an argument to a format string.
   This is *almost* a trait object as it's just a pointer/function pair, but due
   to the function originating from one of many traits, it's not actually a
   trait object. Like `Arguments`, this must be constructed from stable code.

3. `fmt::rt` - this module contains the runtime type definitions primarily for
   the `rt::Argument` structure. Whenever an argument is formatted with
   nonstandard flags, a corresponding `rt::Argument` is generated describing how
   the argument is being formatted. This can be used to construct an
   `Arguments`.

The primary interface to `std::fmt` is the `Arguments` structure, and as such
this type name is stabilize as-is today. It is expected for libraries to pass
around an `Arguments` structure to represent a pending formatted computation.

The remaining portions are largely "cruft" which would rather not be stabilized,
but due to the stability checks they must be. As a result, almost all pieces
have been renamed to represent that they are "version 1" of the formatting
representation. The theory is that at a later date if we change the
representation of these types we can add new definitions called "version 2" and
corresponding constructors for `Arguments`.

One of the other remaining large questions about the fmt module were how the
pending I/O reform would affect the signatures of methods in the module. Due to
[RFC 526][rfc], however, the writers of fmt are now incompatible with the
writers of io, so this question has largely been solved. As a result the
interfaces are largely stabilized as-is today.

[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0526-fmt-text-writer.md

Specifically, the following changes were made:

* The contents of `fmt::rt` were all moved under `fmt::rt::v1`
* `fmt::rt` is stable
* `fmt::rt::v1` is stable
* `Error` is stable
* `Writer` is stable
* `Writer::write_str` is stable
* `Writer::write_fmt` is stable
* `Formatter` is stable
* `Argument` has been renamed to `ArgumentV1` and is stable
* `ArgumentV1::new` is stable
* `ArgumentV1::from_uint` is stable
* `Arguments::new_v1` is stable (renamed from `new`)
* `Arguments::new_v1_formatted` is stable (renamed from `with_placeholders`)
* All formatting traits are now stable, as well as the `fmt` method.
* `fmt::write` is stable
* `fmt::format` is stable
* `Formatter::pad_integral` is stable
* `Formatter::pad` is stable
* `Formatter::write_str` is stable
* `Formatter::write_fmt` is stable
* Some assorted top level items which were only used by `format_args!` were
  removed in favor of static functions on `ArgumentV1` as well.
* The formatting-flag-accessing methods remain unstable

Within the contents of the `fmt::rt::v1` module, the following actions were
taken:

* Reexports of all enum variants were removed
* All prefixes on enum variants were removed
* A few miscellaneous enum variants were renamed
* Otherwise all structs, fields, and variants were marked stable.

In addition to these actions in the `std::fmt` module, many implementations of
`Show` and `String` were stabilized as well.

In some other modules:

* `ToString` is now stable
* `ToString::to_string` is now stable
* `Vec` no longer implements `fmt::Writer` (this has moved to `String`)

This is a breaking change due to all of the changes to the `fmt::rt` module, but
this likely will not have much impact on existing programs.

Closes #20661
[breaking-change]
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ext/format.rs136
1 files changed, 61 insertions, 75 deletions
diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs
index 2a0a352f128..36dbf117604 100644
--- a/src/libsyntax/ext/format.rs
+++ b/src/libsyntax/ext/format.rs
@@ -17,7 +17,7 @@ use ext::base::*;
 use ext::base;
 use ext::build::AstBuilder;
 use fmt_macros as parse;
-use parse::token::{InternedString, special_idents};
+use parse::token::special_idents;
 use parse::token;
 use ptr::P;
 
@@ -300,56 +300,35 @@ impl<'a, 'b> Context<'a, 'b> {
         }
     }
 
-    /// These attributes are applied to all statics that this syntax extension
-    /// will generate.
-    fn static_attrs(ecx: &ExtCtxt, fmtsp: Span) -> Vec<ast::Attribute> {
-        // Flag statics as `inline` so LLVM can merge duplicate globals as much
-        // as possible (which we're generating a whole lot of).
-        let unnamed = ecx.meta_word(fmtsp, InternedString::new("inline"));
-        let unnamed = ecx.attribute(fmtsp, unnamed);
-
-        // Do not warn format string as dead code
-        let dead_code = ecx.meta_word(fmtsp, InternedString::new("dead_code"));
-        let allow_dead_code = ecx.meta_list(fmtsp,
-                                            InternedString::new("allow"),
-                                            vec![dead_code]);
-        let allow_dead_code = ecx.attribute(fmtsp, allow_dead_code);
-        vec![unnamed, allow_dead_code]
-    }
-
     fn rtpath(ecx: &ExtCtxt, s: &str) -> Vec<ast::Ident> {
-        vec![ecx.ident_of("std"), ecx.ident_of("fmt"), ecx.ident_of("rt"), ecx.ident_of(s)]
+        vec![ecx.ident_of("std"), ecx.ident_of("fmt"), ecx.ident_of("rt"),
+             ecx.ident_of("v1"), ecx.ident_of(s)]
     }
 
     fn trans_count(&self, c: parse::Count) -> P<ast::Expr> {
         let sp = self.fmtsp;
-        match c {
-            parse::CountIs(i) => {
-                self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "CountIs"),
-                                          vec!(self.ecx.expr_usize(sp, i)))
+        let count = |: c, arg| {
+            let mut path = Context::rtpath(self.ecx, "Count");
+            path.push(self.ecx.ident_of(c));
+            match arg {
+                Some(arg) => self.ecx.expr_call_global(sp, path, vec![arg]),
+                None => self.ecx.expr_path(self.ecx.path_global(sp, path)),
             }
+        };
+        match c {
+            parse::CountIs(i) => count("Is", Some(self.ecx.expr_usize(sp, i))),
             parse::CountIsParam(i) => {
-                self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "CountIsParam"),
-                                          vec!(self.ecx.expr_usize(sp, i)))
-            }
-            parse::CountImplied => {
-                let path = self.ecx.path_global(sp, Context::rtpath(self.ecx,
-                                                                    "CountImplied"));
-                self.ecx.expr_path(path)
-            }
-            parse::CountIsNextParam => {
-                let path = self.ecx.path_global(sp, Context::rtpath(self.ecx,
-                                                                    "CountIsNextParam"));
-                self.ecx.expr_path(path)
+                count("Param", Some(self.ecx.expr_usize(sp, i)))
             }
+            parse::CountImplied => count("Implied", None),
+            parse::CountIsNextParam => count("NextParam", None),
             parse::CountIsName(n) => {
                 let i = match self.name_positions.get(n) {
                     Some(&i) => i,
                     None => 0, // error already emitted elsewhere
                 };
                 let i = i + self.args.len();
-                self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "CountIsParam"),
-                                          vec!(self.ecx.expr_usize(sp, i)))
+                count("Param", Some(self.ecx.expr_usize(sp, i)))
             }
         }
     }
@@ -373,27 +352,35 @@ impl<'a, 'b> Context<'a, 'b> {
             }
             parse::NextArgument(ref arg) => {
                 // Translate the position
-                let pos = match arg.position {
-                    // These two have a direct mapping
-                    parse::ArgumentNext => {
-                        let path = self.ecx.path_global(sp, Context::rtpath(self.ecx,
-                                                                            "ArgumentNext"));
-                        self.ecx.expr_path(path)
-                    }
-                    parse::ArgumentIs(i) => {
-                        self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "ArgumentIs"),
-                                                  vec!(self.ecx.expr_usize(sp, i)))
-                    }
-                    // Named arguments are converted to positional arguments at
-                    // the end of the list of arguments
-                    parse::ArgumentNamed(n) => {
-                        let i = match self.name_positions.get(n) {
-                            Some(&i) => i,
-                            None => 0, // error already emitted elsewhere
-                        };
-                        let i = i + self.args.len();
-                        self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "ArgumentIs"),
-                                                  vec!(self.ecx.expr_usize(sp, i)))
+                let pos = {
+                    let pos = |: c, arg| {
+                        let mut path = Context::rtpath(self.ecx, "Position");
+                        path.push(self.ecx.ident_of(c));
+                        match arg {
+                            Some(i) => {
+                                let arg = self.ecx.expr_usize(sp, i);
+                                self.ecx.expr_call_global(sp, path, vec![arg])
+                            }
+                            None => {
+                                self.ecx.expr_path(self.ecx.path_global(sp, path))
+                            }
+                        }
+                    };
+                    match arg.position {
+                        // These two have a direct mapping
+                        parse::ArgumentNext => pos("Next", None),
+                        parse::ArgumentIs(i) => pos("At", Some(i)),
+
+                        // Named arguments are converted to positional arguments
+                        // at the end of the list of arguments
+                        parse::ArgumentNamed(n) => {
+                            let i = match self.name_positions.get(n) {
+                                Some(&i) => i,
+                                None => 0, // error already emitted elsewhere
+                            };
+                            let i = i + self.args.len();
+                            pos("At", Some(i))
+                        }
                     }
                 };
 
@@ -417,19 +404,16 @@ impl<'a, 'b> Context<'a, 'b> {
 
                 // Translate the format
                 let fill = self.ecx.expr_lit(sp, ast::LitChar(fill));
+                let align = |:name| {
+                    let mut p = Context::rtpath(self.ecx, "Alignment");
+                    p.push(self.ecx.ident_of(name));
+                    self.ecx.path_global(sp, p)
+                };
                 let align = match arg.format.align {
-                    parse::AlignLeft => {
-                        self.ecx.path_global(sp, Context::rtpath(self.ecx, "AlignLeft"))
-                    }
-                    parse::AlignRight => {
-                        self.ecx.path_global(sp, Context::rtpath(self.ecx, "AlignRight"))
-                    }
-                    parse::AlignCenter => {
-                        self.ecx.path_global(sp, Context::rtpath(self.ecx, "AlignCenter"))
-                    }
-                    parse::AlignUnknown => {
-                        self.ecx.path_global(sp, Context::rtpath(self.ecx, "AlignUnknown"))
-                    }
+                    parse::AlignLeft => align("Left"),
+                    parse::AlignRight => align("Right"),
+                    parse::AlignCenter => align("Center"),
+                    parse::AlignUnknown => align("Unknown"),
                 };
                 let align = self.ecx.expr_path(align);
                 let flags = self.ecx.expr_usize(sp, arg.format.flags);
@@ -465,7 +449,7 @@ impl<'a, 'b> Context<'a, 'b> {
         let st = ast::ItemStatic(ty, ast::MutImmutable, slice);
 
         let name = ecx.ident_of(name);
-        let item = ecx.item(fmtsp, name, Context::static_attrs(ecx, fmtsp), st);
+        let item = ecx.item(fmtsp, name, vec![], st);
         let decl = respan(fmtsp, ast::DeclItem(item));
 
         // Wrap the declaration in a block so that it forms a single expression.
@@ -575,7 +559,7 @@ impl<'a, 'b> Context<'a, 'b> {
 
         // Now create the fmt::Arguments struct with all our locals we created.
         let (fn_name, fn_args) = if self.all_pieces_simple {
-            ("new", vec![pieces, args_slice])
+            ("new_v1", vec![pieces, args_slice])
         } else {
             // Build up the static array which will store our precompiled
             // nonstandard placeholders, if there are any.
@@ -587,7 +571,7 @@ impl<'a, 'b> Context<'a, 'b> {
                                             piece_ty,
                                             self.pieces);
 
-            ("with_placeholders", vec![pieces, fmt, args_slice])
+            ("new_v1_formatted", vec![pieces, args_slice, fmt])
         };
 
         self.ecx.expr_call_global(self.fmtsp, vec!(
@@ -624,7 +608,8 @@ impl<'a, 'b> Context<'a, 'b> {
                 return ecx.expr_call_global(sp, vec![
                         ecx.ident_of("std"),
                         ecx.ident_of("fmt"),
-                        ecx.ident_of("argumentuint")], vec![arg])
+                        ecx.ident_of("ArgumentV1"),
+                        ecx.ident_of("from_uint")], vec![arg])
             }
         };
 
@@ -636,7 +621,8 @@ impl<'a, 'b> Context<'a, 'b> {
         ecx.expr_call_global(sp, vec![
                 ecx.ident_of("std"),
                 ecx.ident_of("fmt"),
-                ecx.ident_of("argument")], vec![ecx.expr_path(format_fn), arg])
+                ecx.ident_of("ArgumentV1"),
+                ecx.ident_of("new")], vec![arg, ecx.expr_path(format_fn)])
     }
 }