Skip to content

Add initial support for a new formatting syntax #8182

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/librustc/driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,13 +224,16 @@ pub fn phase_3_run_analysis_passes(sess: Session,
let lang_items = time(time_passes, ~"language item collection", ||
middle::lang_items::collect_language_items(crate, sess));

let fmt_traits = time(time_passes, ~"finding fmt traits", ||
middle::fmt::find_fmt_traits(crate, sess));

let middle::resolve::CrateMap {
def_map: def_map,
exp_map2: exp_map2,
trait_map: trait_map
} =
time(time_passes, ~"resolution", ||
middle::resolve::resolve_crate(sess, lang_items, crate));
middle::resolve::resolve_crate(sess, lang_items, fmt_traits, crate));

time(time_passes, ~"looking for entry point",
|| middle::entry::find_entry_point(sess, crate, ast_map));
Expand All @@ -245,7 +248,7 @@ pub fn phase_3_run_analysis_passes(sess: Session,
middle::region::determine_rp_in_crate(sess, ast_map, def_map, crate));

let ty_cx = ty::mk_ctxt(sess, def_map, ast_map, freevars,
region_map, rp_set, lang_items);
region_map, rp_set, lang_items, fmt_traits);

// passes are timed inside typeck
let (method_map, vtable_map) = typeck::check_crate(
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/metadata/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ pub static tag_misc_info_crate_items: uint = 0x80;
pub static tag_item_method_provided_source: uint = 0x81;
pub static tag_item_impl_vtables: uint = 0x82;

pub static tag_fmt_traits: uint = 0x83;
pub static tag_fmt_traits_item: uint = 0x84;
pub static tag_fmt_traits_item_node_id: uint = 0x85;
pub static tag_fmt_traits_item_trait_id: uint = 0x86;
pub static tag_fmt_traits_item_name: uint = 0x87;

pub struct LinkMeta {
name: @str,
vers: @str,
Expand Down
10 changes: 10 additions & 0 deletions src/librustc/metadata/csearch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ pub fn each_lang_item(cstore: @mut cstore::CStore,
decoder::each_lang_item(crate_data, f)
}

/// Iterates over all the fmt traits in the given crate. The yielded arguments
/// are the format specifier for the trait, the NodeId of the `fmt` function,
/// and the NodeId of the trait with the fmt function.
pub fn each_fmt_trait(cstore: @mut cstore::CStore,
cnum: ast::CrateNum,
f: &fn(@str, ast::NodeId, ast::NodeId) -> bool) -> bool {
let crate_data = cstore::get_crate_data(cstore, cnum);
decoder::each_fmt_trait(crate_data, f)
}

/// Iterates over all the paths in the given crate.
pub fn each_path(cstore: @mut cstore::CStore,
cnum: ast::CrateNum,
Expand Down
29 changes: 29 additions & 0 deletions src/librustc/metadata/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,35 @@ pub fn each_lang_item(cdata: cmd, f: &fn(ast::NodeId, uint) -> bool) -> bool {
return true;
}

/// Iterates over the fmt trait items in the given crate.
#[cfg(stage0)]
pub fn each_fmt_trait(_: cmd, _: &fn(@str, ast::NodeId, ast::NodeId) -> bool) -> bool {
true
}

/// Iterates over the fmt trait items in the given crate.
#[cfg(not(stage0))]
pub fn each_fmt_trait(cdata: cmd,
f: &fn(@str, ast::NodeId, ast::NodeId) -> bool) -> bool {
let root = reader::Doc(cdata.data);
let fmt_traits = reader::get_doc(root, tag_fmt_traits);
for reader::tagged_docs(fmt_traits, tag_fmt_traits_item) |trait_doc| {
let id_doc = reader::get_doc(trait_doc, tag_fmt_traits_item_node_id);
let node_id = reader::doc_as_u32(id_doc) as ast::NodeId;

let trait_id_doc = reader::get_doc(trait_doc, tag_fmt_traits_item_trait_id);
let trait_id = reader::doc_as_u32(trait_id_doc) as ast::NodeId;

let name_doc = reader::get_doc(trait_doc, tag_fmt_traits_item_name);
let name = name_doc.as_str().to_managed();

if !f(name, node_id, trait_id) {
return false;
}
}
return true;
}

struct EachItemContext<'self> {
intr: @ident_interner,
cdata: cmd,
Expand Down
39 changes: 39 additions & 0 deletions src/librustc/metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ struct Stats {
dep_bytes: uint,
lang_item_bytes: uint,
link_args_bytes: uint,
fmt_trait_bytes: uint,
misc_bytes: uint,
item_bytes: uint,
index_bytes: uint,
Expand Down Expand Up @@ -1478,6 +1479,37 @@ fn encode_lang_items(ecx: &EncodeContext, ebml_w: &mut writer::Encoder) {
ebml_w.end_tag(); // tag_lang_items
}

fn encode_fmt_traits(ecx: &EncodeContext, ebml_w: &mut writer::Encoder) {
ebml_w.start_tag(tag_fmt_traits);

for ecx.tcx.fmt_traits.iter().advance |(name, &(def_id, def))| {
if !is_local(def_id) { loop }

ebml_w.start_tag(tag_fmt_traits_item);

ebml_w.start_tag(tag_fmt_traits_item_node_id);
ebml_w.writer.write_be_u32(def_id.node as u32);
ebml_w.end_tag(); // tag_fmt_traits_item_id

match def {
ast::def_static_method(_, Some(trait_def_id), _) => {
ebml_w.start_tag(tag_fmt_traits_item_trait_id);
ebml_w.writer.write_be_u32(trait_def_id.node as u32);
ebml_w.end_tag(); // tag_fmt_traits_item_trait_id
}
_ => fail!()
}

ebml_w.start_tag(tag_fmt_traits_item_name);
ebml_w.writer.write_str(*name);
ebml_w.end_tag(); // tag_fmt_traits_item_name

ebml_w.end_tag(); // tag_fmt_traits_item
}

ebml_w.end_tag(); // tag_fmt_traits
}

fn encode_link_args(ecx: &EncodeContext, ebml_w: &mut writer::Encoder) {
ebml_w.start_tag(tag_link_args);

Expand Down Expand Up @@ -1554,6 +1586,7 @@ pub fn encode_metadata(parms: EncodeParams, crate: &Crate) -> ~[u8] {
dep_bytes: 0,
lang_item_bytes: 0,
link_args_bytes: 0,
fmt_trait_bytes: 0,
misc_bytes: 0,
item_bytes: 0,
index_bytes: 0,
Expand Down Expand Up @@ -1612,6 +1645,11 @@ pub fn encode_metadata(parms: EncodeParams, crate: &Crate) -> ~[u8] {
encode_link_args(&ecx, &mut ebml_w);
ecx.stats.link_args_bytes = *wr.pos - i;

// Encode the format traits.
i = *wr.pos;
encode_fmt_traits(&ecx, &mut ebml_w);
ecx.stats.fmt_trait_bytes = *wr.pos - i;

// Encode miscellaneous info.
i = *wr.pos;
encode_misc_info(&ecx, crate, &mut ebml_w);
Expand Down Expand Up @@ -1643,6 +1681,7 @@ pub fn encode_metadata(parms: EncodeParams, crate: &Crate) -> ~[u8] {
printfln!(" attribute bytes: %u", ecx.stats.attr_bytes);
printfln!(" dep bytes: %u", ecx.stats.dep_bytes);
printfln!(" lang item bytes: %u", ecx.stats.lang_item_bytes);
printfln!(" fmt trait bytes: %u", ecx.stats.fmt_trait_bytes);
printfln!(" link args bytes: %u", ecx.stats.link_args_bytes);
printfln!(" misc bytes: %u", ecx.stats.misc_bytes);
printfln!(" item bytes: %u", ecx.stats.item_bytes);
Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/cfg/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ impl CFGBuilder {

ast::expr_mac(*) |
ast::expr_inline_asm(*) |
ast::expr_extfmt_fn(*) |
ast::expr_self |
ast::expr_fn_block(*) |
ast::expr_lit(*) |
Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/dataflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,7 @@ impl<'self, O:DataFlowOperator> PropagationContext<'self, O> {

ast::expr_lit(*) |
ast::expr_path(*) |
ast::expr_extfmt_fn(*) |
ast::expr_self => {
}

Expand Down
183 changes: 183 additions & 0 deletions src/librustc/middle/fmt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module implements the collection of #[fmt]-tagged traits in both the
//! local crate and external crates. This merges these two lists of fmt-traits
//! into one `TraitMap` which is then used during resolve and finally shoved
//! into the tcx.

use driver::session::Session;
use metadata::csearch;
use metadata::cstore::iter_crate_data;

use syntax::ast;
use syntax::ast_util;
use syntax::attr::AttrMetaMethods;
use syntax::codemap::span;
use syntax::visit;
use syntax::parse::token::str_to_ident;

use std::hashmap::HashMap;
use std::util;

/// Mapping of format string names to a (def_id, def) pair. The def_id is the
/// actual id of the formatting function while the `def` is the definition
/// corresponding for it.
pub type TraitMap = HashMap<@str, (ast::def_id, ast::def)>;

struct Context {
/// Cached ident for the word "fmt" (what the format function is called)
fmt_ident: ast::ident,
sess: Session,

/// The `fmts` map is returned at the end of the collection, and in the
/// interim the `fmt_loc` map keeps track of where the traits came from so
/// collisions can be diagnosed sanely.
fmts: TraitMap,
fmt_loc: HashMap<@str, Either<span, @str>>,
}

impl Context {
/// Registers a new format-trait
fn register(&mut self, name: @str, src: Either<span, @str>,
fmt_id: ast::def_id, trait_id: ast::def_id) {
match self.fmts.find(&name) {
Some(*) => {
match src {
Left(sp) => {
self.sess.span_err(sp, fmt!("duplicate fmt trait \
for `%s`", name));
}
Right(src) => {
self.sess.err(fmt!("duplicate fmt trait `%s` found in \
external crate `%s`", name, src));
}
}
match *self.fmt_loc.get(&name) {
Left(sp) => {
self.sess.span_note(sp, "previous definition here");
}
Right(name) => {
self.sess.note(fmt!("previous definition found in the \
external crate `%s`", name));
}
}
return;
}
None => {}
}
let def = ast::def_static_method(fmt_id, Some(trait_id),
ast::impure_fn);
debug!("found %s at %?", name, src);
self.fmts.insert(name, (fmt_id, def));
self.fmt_loc.insert(name, src);
}

/// Iterates over the local crate to identify all #[fmt]-tagged traits and
/// adds them to the local trait map
fn collect_local_traits(@mut self, crate: &ast::Crate) {
visit::visit_crate(crate, (self, visit::mk_vt(@visit::Visitor {
visit_item: |it, (cx, vt)| {
let cx: @mut Context = cx;
for it.attrs.iter().advance |attr| {
if "fmt" != attr.name() { loop }
match attr.value_str() {
Some(name) => {
match it.node {
ast::item_trait(_, _, ref methods) => {
cx.find_fmt_method(name, it.span, it.id,
*methods);
}
_ => {
cx.sess.span_err(attr.span,
"fmt attribute can only be \
specified on traits");
}
}
}
None => {
cx.sess.span_err(attr.span,
"fmt attribute must have a value \
specified (fmt=\"...\")");
}
}
}
visit::visit_item(it, (cx, vt));
},
.. *visit::default_visitor()
})));
}

fn find_fmt_method(@mut self, name: @str, sp: span, id: ast::NodeId,
methods: &[ast::trait_method]) {
fn check_purity(sess: Session, p: ast::purity, sp: span) {
match p {
ast::unsafe_fn => {
sess.span_err(sp, "the `fmt` function must not be unsafe");
}
ast::extern_fn => {
sess.span_err(sp, "the `fmt` function must not be extern");
}
ast::impure_fn => {}
}
}

let mut found = false;
for methods.iter().advance |m| {
match *m {
ast::required(ref m) if m.ident == self.fmt_ident => {
check_purity(self.sess, m.purity, sp);
self.register(name, Left(sp), ast_util::local_def(m.id),
ast_util::local_def(id));
found = true;
}
ast::provided(ref m) if m.ident == self.fmt_ident => {
check_purity(self.sess, m.purity, sp);
self.register(name, Left(sp), ast_util::local_def(m.id),
ast_util::local_def(id));
found = true;
}

ast::provided(*) | ast::required(*) => {}
}
}
if !found {
self.sess.span_err(sp, "no function named `fmt` on format trait");
}
}

/// Iterates over all of the external crates and loads all of the external
/// fmt traits into the local maps.
fn collect_external_traits(&mut self) {
let cstore = self.sess.cstore;
do iter_crate_data(cstore) |n, metadata| {
for csearch::each_fmt_trait(cstore, n) |name, fmt_id, trait_id| {
let fmt_id = ast::def_id { crate: n, node: fmt_id };
let trait_id = ast::def_id { crate: n, node: trait_id };
self.register(name, Right(metadata.name), fmt_id, trait_id);
}
}
}
}

pub fn find_fmt_traits(crate: &ast::Crate, session: Session) -> @TraitMap {
let cx = @mut Context {
fmt_ident: str_to_ident("fmt"),
sess: session,
fmts: HashMap::new(),
fmt_loc: HashMap::new(),
};

cx.collect_external_traits();
cx.collect_local_traits(crate);
session.abort_if_errors();

@util::replace(&mut cx.fmts, HashMap::new())
}
2 changes: 1 addition & 1 deletion src/librustc/middle/kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ pub fn check_expr(e: @expr, (cx, v): (Context, visit::vt<Context>)) {
let r = cx.tcx.node_type_substs.find(&type_parameter_id);
for r.iter().advance |ts| {
let type_param_defs = match e.node {
expr_path(_) => {
expr_path(*) | expr_extfmt_fn(*) => {
let did = ast_util::def_id_of_def(cx.tcx.def_map.get_copy(&e.id));
ty::lookup_item_type(cx.tcx, did).generics.type_param_defs
}
Expand Down
Loading