diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2015-01-13 15:42:53 -0800 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2015-01-30 09:21:56 -0800 |
| commit | 62273575139a80c2b208a3a27e0c2392b1425be6 (patch) | |
| tree | f9381a041e8ad95efe57ee6f85954a8b063b853f /src/libsyntax | |
| parent | 52c74e63dacd49017b19330e0cbecbac0a3fe62e (diff) | |
| download | rust-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.rs | 136 |
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)]) } } |
