rust/src/comp/metadata/encoder.rs
Marijn Haverbeke 7dacccde94 Make shared kind the default only for generic functions
You almost never want a function with pinned type params. For
types, objects, resources, and tags, pinned types are actually often
more sane. For most of these, shared rarely makes sense. Only tricky
case is objs -- you'll have to think about the kinds you want there.

Issue #1076
2011-10-28 17:00:14 +02:00

632 lines
22 KiB
Rust

// Metadata encoding
import std::{vec, str, uint, io, option, ebml, map};
import std::option::{some, none};
import syntax::ast::*;
import syntax::ast_util;
import syntax::ast_util::local_def;
import common::*;
import middle::trans_common::crate_ctxt;
import middle::ty;
import middle::ty::node_id_to_monotype;
import front::attr;
export encode_metadata;
export encoded_ty;
type abbrev_map = map::hashmap<ty::t, tyencode::ty_abbrev>;
type encode_ctxt = {ccx: @crate_ctxt, type_abbrevs: abbrev_map};
// Path table encoding
fn encode_name(ebml_w: ebml::writer, name: str) {
ebml::start_tag(ebml_w, tag_paths_data_name);
ebml_w.writer.write(str::bytes(name));
ebml::end_tag(ebml_w);
}
fn encode_def_id(ebml_w: ebml::writer, id: def_id) {
ebml::start_tag(ebml_w, tag_def_id);
ebml_w.writer.write(str::bytes(def_to_str(id)));
ebml::end_tag(ebml_w);
}
type entry<T> = {val: T, pos: uint};
fn encode_tag_variant_paths(ebml_w: ebml::writer, variants: [variant],
path: [str], &index: [entry<str>]) {
for variant: variant in variants {
add_to_index(ebml_w, path, index, variant.node.name);
ebml::start_tag(ebml_w, tag_paths_data_item);
encode_name(ebml_w, variant.node.name);
encode_def_id(ebml_w, local_def(variant.node.id));
ebml::end_tag(ebml_w);
}
}
fn add_to_index(ebml_w: ebml::writer, path: [str], &index: [entry<str>],
name: str) {
let full_path = path + [name];
index +=
[{val: str::connect(full_path, "::"), pos: ebml_w.writer.tell()}];
}
fn encode_native_module_item_paths(ebml_w: ebml::writer, nmod: native_mod,
path: [str], &index: [entry<str>]) {
for nitem: @native_item in nmod.items {
add_to_index(ebml_w, path, index, nitem.ident);
ebml::start_tag(ebml_w, tag_paths_data_item);
encode_name(ebml_w, nitem.ident);
encode_def_id(ebml_w, local_def(nitem.id));
ebml::end_tag(ebml_w);
}
}
fn encode_module_item_paths(ebml_w: ebml::writer, module: _mod, path: [str],
&index: [entry<str>]) {
for it: @item in module.items {
if !ast_util::is_exported(it.ident, module) { cont; }
alt it.node {
item_const(_, _) {
add_to_index(ebml_w, path, index, it.ident);
ebml::start_tag(ebml_w, tag_paths_data_item);
encode_name(ebml_w, it.ident);
encode_def_id(ebml_w, local_def(it.id));
ebml::end_tag(ebml_w);
}
item_fn(_, tps) {
add_to_index(ebml_w, path, index, it.ident);
ebml::start_tag(ebml_w, tag_paths_data_item);
encode_name(ebml_w, it.ident);
encode_def_id(ebml_w, local_def(it.id));
ebml::end_tag(ebml_w);
}
item_mod(_mod) {
add_to_index(ebml_w, path, index, it.ident);
ebml::start_tag(ebml_w, tag_paths_data_mod);
encode_name(ebml_w, it.ident);
encode_def_id(ebml_w, local_def(it.id));
encode_module_item_paths(ebml_w, _mod, path + [it.ident], index);
ebml::end_tag(ebml_w);
}
item_native_mod(nmod) {
add_to_index(ebml_w, path, index, it.ident);
ebml::start_tag(ebml_w, tag_paths_data_mod);
encode_name(ebml_w, it.ident);
encode_def_id(ebml_w, local_def(it.id));
encode_native_module_item_paths(ebml_w, nmod, path + [it.ident],
index);
ebml::end_tag(ebml_w);
}
item_ty(_, tps) {
add_to_index(ebml_w, path, index, it.ident);
ebml::start_tag(ebml_w, tag_paths_data_item);
encode_name(ebml_w, it.ident);
encode_def_id(ebml_w, local_def(it.id));
ebml::end_tag(ebml_w);
}
item_res(_, _, tps, ctor_id) {
add_to_index(ebml_w, path, index, it.ident);
ebml::start_tag(ebml_w, tag_paths_data_item);
encode_name(ebml_w, it.ident);
encode_def_id(ebml_w, local_def(ctor_id));
ebml::end_tag(ebml_w);
add_to_index(ebml_w, path, index, it.ident);
ebml::start_tag(ebml_w, tag_paths_data_item);
encode_name(ebml_w, it.ident);
encode_def_id(ebml_w, local_def(it.id));
ebml::end_tag(ebml_w);
}
item_tag(variants, tps) {
add_to_index(ebml_w, path, index, it.ident);
ebml::start_tag(ebml_w, tag_paths_data_item);
encode_name(ebml_w, it.ident);
encode_def_id(ebml_w, local_def(it.id));
ebml::end_tag(ebml_w);
encode_tag_variant_paths(ebml_w, variants, path, index);
}
item_obj(_, tps, ctor_id) {
add_to_index(ebml_w, path, index, it.ident);
ebml::start_tag(ebml_w, tag_paths_data_item);
encode_name(ebml_w, it.ident);
encode_def_id(ebml_w, local_def(ctor_id));
ebml::end_tag(ebml_w);
add_to_index(ebml_w, path, index, it.ident);
ebml::start_tag(ebml_w, tag_paths_data_item);
encode_name(ebml_w, it.ident);
encode_def_id(ebml_w, local_def(it.id));
ebml::end_tag(ebml_w);
}
}
}
}
fn encode_item_paths(ebml_w: ebml::writer, crate: @crate) -> [entry<str>] {
let index: [entry<str>] = [];
let path: [str] = [];
ebml::start_tag(ebml_w, tag_paths);
encode_module_item_paths(ebml_w, crate.node.module, path, index);
ebml::end_tag(ebml_w);
ret index;
}
// Item info table encoding
fn encode_family(ebml_w: ebml::writer, c: u8) {
ebml::start_tag(ebml_w, tag_items_data_item_family);
ebml_w.writer.write([c]);
ebml::end_tag(ebml_w);
}
fn encode_inlineness(ebml_w: ebml::writer, c: u8) {
ebml::start_tag(ebml_w, tag_items_data_item_inlineness);
ebml_w.writer.write([c]);
ebml::end_tag(ebml_w);
}
fn def_to_str(did: def_id) -> str { ret #fmt["%d:%d", did.crate, did.node]; }
fn encode_type_param_kinds(ebml_w: ebml::writer, tps: [ty_param]) {
ebml::start_tag(ebml_w, tag_items_data_item_ty_param_kinds);
ebml::write_vint(ebml_w.writer, vec::len::<ty_param>(tps));
for tp: ty_param in tps {
let c = alt ast_util::ty_param_kind(tp) {
kind_unique. { 'u' }
kind_shared. { 's' }
kind_pinned. { 'p' }
};
ebml_w.writer.write([c as u8]);
}
ebml::end_tag(ebml_w);
}
fn encode_variant_id(ebml_w: ebml::writer, vid: def_id) {
ebml::start_tag(ebml_w, tag_items_data_item_variant);
ebml_w.writer.write(str::bytes(def_to_str(vid)));
ebml::end_tag(ebml_w);
}
fn encode_type(ecx: @encode_ctxt, ebml_w: ebml::writer, typ: ty::t) {
ebml::start_tag(ebml_w, tag_items_data_item_type);
let f = def_to_str;
let ty_str_ctxt =
@{ds: f,
tcx: ecx.ccx.tcx,
abbrevs: tyencode::ac_use_abbrevs(ecx.type_abbrevs)};
tyencode::enc_ty(io::new_writer(ebml_w.writer), ty_str_ctxt, typ);
ebml::end_tag(ebml_w);
}
fn encode_symbol(ecx: @encode_ctxt, ebml_w: ebml::writer, id: node_id) {
ebml::start_tag(ebml_w, tag_items_data_item_symbol);
ebml_w.writer.write(str::bytes(ecx.ccx.item_symbols.get(id)));
ebml::end_tag(ebml_w);
}
fn encode_discriminant(ecx: @encode_ctxt, ebml_w: ebml::writer, id: node_id) {
ebml::start_tag(ebml_w, tag_items_data_item_symbol);
ebml_w.writer.write(str::bytes(ecx.ccx.discrim_symbols.get(id)));
ebml::end_tag(ebml_w);
}
fn encode_tag_id(ebml_w: ebml::writer, id: def_id) {
ebml::start_tag(ebml_w, tag_items_data_item_tag_id);
ebml_w.writer.write(str::bytes(def_to_str(id)));
ebml::end_tag(ebml_w);
}
fn encode_tag_variant_info(ecx: @encode_ctxt, ebml_w: ebml::writer,
id: node_id, variants: [variant],
&index: [entry<int>], ty_params: [ty_param]) {
for variant: variant in variants {
index += [{val: variant.node.id, pos: ebml_w.writer.tell()}];
ebml::start_tag(ebml_w, tag_items_data_item);
encode_def_id(ebml_w, local_def(variant.node.id));
encode_family(ebml_w, 'v' as u8);
encode_tag_id(ebml_w, local_def(id));
encode_type(ecx, ebml_w,
node_id_to_monotype(ecx.ccx.tcx, variant.node.id));
if vec::len::<variant_arg>(variant.node.args) > 0u {
encode_symbol(ecx, ebml_w, variant.node.id);
}
encode_discriminant(ecx, ebml_w, variant.node.id);
encode_type_param_kinds(ebml_w, ty_params);
ebml::end_tag(ebml_w);
}
}
fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
&index: [entry<int>]) {
alt item.node {
item_const(_, _) {
ebml::start_tag(ebml_w, tag_items_data_item);
encode_def_id(ebml_w, local_def(item.id));
encode_family(ebml_w, 'c' as u8);
encode_type(ecx, ebml_w, node_id_to_monotype(ecx.ccx.tcx, item.id));
encode_symbol(ecx, ebml_w, item.id);
ebml::end_tag(ebml_w);
}
item_fn(fd, tps) {
ebml::start_tag(ebml_w, tag_items_data_item);
encode_def_id(ebml_w, local_def(item.id));
encode_family(ebml_w,
alt fd.decl.purity {
unsafe_fn. { 'u' }
pure_fn. { 'p' }
impure_fn. { 'f' }
} as u8);
encode_inlineness(ebml_w,
alt fd.decl.il {
il_normal. { 'n' }
il_inline. { 'i' }
} as u8);
encode_type_param_kinds(ebml_w, tps);
encode_type(ecx, ebml_w, node_id_to_monotype(ecx.ccx.tcx, item.id));
encode_symbol(ecx, ebml_w, item.id);
ebml::end_tag(ebml_w);
}
item_mod(_) {
ebml::start_tag(ebml_w, tag_items_data_item);
encode_def_id(ebml_w, local_def(item.id));
encode_family(ebml_w, 'm' as u8);
ebml::end_tag(ebml_w);
}
item_native_mod(_) {
ebml::start_tag(ebml_w, tag_items_data_item);
encode_def_id(ebml_w, local_def(item.id));
encode_family(ebml_w, 'n' as u8);
ebml::end_tag(ebml_w);
}
item_ty(_, tps) {
ebml::start_tag(ebml_w, tag_items_data_item);
encode_def_id(ebml_w, local_def(item.id));
encode_family(ebml_w, 'y' as u8);
encode_type_param_kinds(ebml_w, tps);
encode_type(ecx, ebml_w, node_id_to_monotype(ecx.ccx.tcx, item.id));
ebml::end_tag(ebml_w);
}
item_tag(variants, tps) {
ebml::start_tag(ebml_w, tag_items_data_item);
encode_def_id(ebml_w, local_def(item.id));
encode_family(ebml_w, 't' as u8);
encode_type_param_kinds(ebml_w, tps);
encode_type(ecx, ebml_w, node_id_to_monotype(ecx.ccx.tcx, item.id));
for v: variant in variants {
encode_variant_id(ebml_w, local_def(v.node.id));
}
ebml::end_tag(ebml_w);
encode_tag_variant_info(ecx, ebml_w, item.id, variants, index, tps);
}
item_res(_, _, tps, ctor_id) {
let fn_ty = node_id_to_monotype(ecx.ccx.tcx, ctor_id);
ebml::start_tag(ebml_w, tag_items_data_item);
encode_def_id(ebml_w, local_def(ctor_id));
encode_family(ebml_w, 'y' as u8);
encode_type_param_kinds(ebml_w, tps);
encode_type(ecx, ebml_w, ty::ty_fn_ret(ecx.ccx.tcx, fn_ty));
encode_symbol(ecx, ebml_w, item.id);
ebml::end_tag(ebml_w);
index += [{val: ctor_id, pos: ebml_w.writer.tell()}];
ebml::start_tag(ebml_w, tag_items_data_item);
encode_def_id(ebml_w, local_def(ctor_id));
encode_family(ebml_w, 'f' as u8);
encode_type_param_kinds(ebml_w, tps);
encode_type(ecx, ebml_w, fn_ty);
encode_symbol(ecx, ebml_w, ctor_id);
ebml::end_tag(ebml_w);
}
item_obj(_, tps, ctor_id) {
let fn_ty = node_id_to_monotype(ecx.ccx.tcx, ctor_id);
ebml::start_tag(ebml_w, tag_items_data_item);
encode_def_id(ebml_w, local_def(item.id));
encode_family(ebml_w, 'y' as u8);
encode_type_param_kinds(ebml_w, tps);
encode_type(ecx, ebml_w, ty::ty_fn_ret(ecx.ccx.tcx, fn_ty));
ebml::end_tag(ebml_w);
index += [{val: ctor_id, pos: ebml_w.writer.tell()}];
ebml::start_tag(ebml_w, tag_items_data_item);
encode_def_id(ebml_w, local_def(ctor_id));
encode_family(ebml_w, 'f' as u8);
encode_type_param_kinds(ebml_w, tps);
encode_type(ecx, ebml_w, fn_ty);
encode_symbol(ecx, ebml_w, ctor_id);
ebml::end_tag(ebml_w);
}
}
}
fn encode_info_for_native_item(ecx: @encode_ctxt, ebml_w: ebml::writer,
nitem: @native_item) {
ebml::start_tag(ebml_w, tag_items_data_item);
alt nitem.node {
native_item_ty. {
encode_def_id(ebml_w, local_def(nitem.id));
encode_family(ebml_w, 'T' as u8);
encode_type(ecx, ebml_w,
ty::mk_native(ecx.ccx.tcx, local_def(nitem.id)));
}
native_item_fn(_, fn_decl, tps) {
let letter =
alt fn_decl.purity {
unsafe_fn. { 'U' }
pure_fn. { 'P' } // this is currently impossible, but hey.
impure_fn. { 'F' }
} as u8;
encode_def_id(ebml_w, local_def(nitem.id));
encode_family(ebml_w, letter);
encode_type_param_kinds(ebml_w, tps);
encode_type(ecx, ebml_w, node_id_to_monotype(ecx.ccx.tcx, nitem.id));
encode_symbol(ecx, ebml_w, nitem.id);
}
}
ebml::end_tag(ebml_w);
}
fn encode_info_for_items(ecx: @encode_ctxt, ebml_w: ebml::writer) ->
[entry<int>] {
let index: [entry<int>] = [];
ebml::start_tag(ebml_w, tag_items_data);
ecx.ccx.ast_map.items {|key, val|
alt val {
middle::ast_map::node_item(i) {
index += [{val: key, pos: ebml_w.writer.tell()}];
encode_info_for_item(ecx, ebml_w, i, index);
}
middle::ast_map::node_native_item(i) {
index += [{val: key, pos: ebml_w.writer.tell()}];
encode_info_for_native_item(ecx, ebml_w, i);
}
_ { }
}
};
ebml::end_tag(ebml_w);
ret index;
}
// Path and definition ID indexing
fn create_index<T>(index: [entry<T>], hash_fn: fn(T) -> uint) ->
[@[entry<T>]] {
let buckets: [@mutable [entry<T>]] = [];
uint::range(0u, 256u) {|_i| buckets += [@mutable []]; };
for elt: entry<T> in index {
let h = hash_fn(elt.val);
*buckets[h % 256u] += [elt];
}
let buckets_frozen = [];
for bucket: @mutable [entry<T>] in buckets {
buckets_frozen += [@*bucket];
}
ret buckets_frozen;
}
fn encode_index<T>(ebml_w: ebml::writer, buckets: [@[entry<T>]],
write_fn: fn(io::writer, T)) {
let writer = io::new_writer(ebml_w.writer);
ebml::start_tag(ebml_w, tag_index);
let bucket_locs: [uint] = [];
ebml::start_tag(ebml_w, tag_index_buckets);
for bucket: @[entry<T>] in buckets {
bucket_locs += [ebml_w.writer.tell()];
ebml::start_tag(ebml_w, tag_index_buckets_bucket);
for elt: entry<T> in *bucket {
ebml::start_tag(ebml_w, tag_index_buckets_bucket_elt);
writer.write_be_uint(elt.pos, 4u);
write_fn(writer, elt.val);
ebml::end_tag(ebml_w);
}
ebml::end_tag(ebml_w);
}
ebml::end_tag(ebml_w);
ebml::start_tag(ebml_w, tag_index_table);
for pos: uint in bucket_locs { writer.write_be_uint(pos, 4u); }
ebml::end_tag(ebml_w);
ebml::end_tag(ebml_w);
}
fn write_str(writer: io::writer, &&s: str) { writer.write_str(s); }
fn write_int(writer: io::writer, &&n: int) {
writer.write_be_uint(n as uint, 4u);
}
fn encode_meta_item(ebml_w: ebml::writer, mi: meta_item) {
alt mi.node {
meta_word(name) {
ebml::start_tag(ebml_w, tag_meta_item_word);
ebml::start_tag(ebml_w, tag_meta_item_name);
ebml_w.writer.write(str::bytes(name));
ebml::end_tag(ebml_w);
ebml::end_tag(ebml_w);
}
meta_name_value(name, value) {
alt value.node {
lit_str(value) {
ebml::start_tag(ebml_w, tag_meta_item_name_value);
ebml::start_tag(ebml_w, tag_meta_item_name);
ebml_w.writer.write(str::bytes(name));
ebml::end_tag(ebml_w);
ebml::start_tag(ebml_w, tag_meta_item_value);
ebml_w.writer.write(str::bytes(value));
ebml::end_tag(ebml_w);
ebml::end_tag(ebml_w);
}
_ {/* FIXME (#611) */ }
}
}
meta_list(name, items) {
ebml::start_tag(ebml_w, tag_meta_item_list);
ebml::start_tag(ebml_w, tag_meta_item_name);
ebml_w.writer.write(str::bytes(name));
ebml::end_tag(ebml_w);
for inner_item: @meta_item in items {
encode_meta_item(ebml_w, *inner_item);
}
ebml::end_tag(ebml_w);
}
}
}
fn encode_attributes(ebml_w: ebml::writer, attrs: [attribute]) {
ebml::start_tag(ebml_w, tag_attributes);
for attr: attribute in attrs {
ebml::start_tag(ebml_w, tag_attribute);
encode_meta_item(ebml_w, attr.node.value);
ebml::end_tag(ebml_w);
}
ebml::end_tag(ebml_w);
}
// So there's a special crate attribute called 'link' which defines the
// metadata that Rust cares about for linking crates. This attribute requires
// 'name' and 'vers' items, so if the user didn't provide them we will throw
// them in anyway with default values.
fn synthesize_crate_attrs(ecx: @encode_ctxt, crate: @crate) -> [attribute] {
fn synthesize_link_attr(ecx: @encode_ctxt, items: [@meta_item]) ->
attribute {
assert (ecx.ccx.link_meta.name != "");
assert (ecx.ccx.link_meta.vers != "");
let name_item =
attr::mk_name_value_item_str("name", ecx.ccx.link_meta.name);
let vers_item =
attr::mk_name_value_item_str("vers", ecx.ccx.link_meta.vers);
let other_items =
{
let tmp = attr::remove_meta_items_by_name(items, "name");
attr::remove_meta_items_by_name(tmp, "vers")
};
let meta_items = [name_item, vers_item] + other_items;
let link_item = attr::mk_list_item("link", meta_items);
ret attr::mk_attr(link_item);
}
let attrs: [attribute] = [];
let found_link_attr = false;
for attr: attribute in crate.node.attrs {
attrs +=
if attr::get_attr_name(attr) != "link" {
[attr]
} else {
alt attr.node.value.node {
meta_list(n, l) {
found_link_attr = true;;
[synthesize_link_attr(ecx, l)]
}
_ { [attr] }
}
};
}
if !found_link_attr { attrs += [synthesize_link_attr(ecx, [])]; }
ret attrs;
}
fn encode_crate_deps(ebml_w: ebml::writer, cstore: cstore::cstore) {
fn get_ordered_names(cstore: cstore::cstore) -> [str] {
type hashkv = @{key: crate_num, val: cstore::crate_metadata};
type numname = {crate: crate_num, ident: str};
// Pull the cnums and names out of cstore
let pairs: [mutable numname] = [mutable];
cstore::iter_crate_data(cstore) {|key, val|
pairs += [mutable {crate: key, ident: val.name}];
};
// Sort by cnum
fn lteq(kv1: numname, kv2: numname) -> bool { kv1.crate <= kv2.crate }
std::sort::quick_sort(lteq, pairs);
// Sanity-check the crate numbers
let expected_cnum = 1;
for n: numname in pairs {
assert (n.crate == expected_cnum);
expected_cnum += 1;
}
// Return just the names
fn name(kv: numname) -> str { kv.ident }
// mutable -> immutable hack for vec::map
let immpairs = vec::slice(pairs, 0u, vec::len(pairs));
ret vec::map(name, immpairs);
}
// We're just going to write a list of crate names, with the assumption
// that they are numbered 1 to n.
// FIXME: This is not nearly enough to support correct versioning
// but is enough to get transitive crate dependencies working.
ebml::start_tag(ebml_w, tag_crate_deps);
for cname: str in get_ordered_names(cstore) {
ebml::start_tag(ebml_w, tag_crate_dep);
ebml_w.writer.write(str::bytes(cname));
ebml::end_tag(ebml_w);
}
ebml::end_tag(ebml_w);
}
fn encode_metadata(cx: @crate_ctxt, crate: @crate) -> str {
let abbrevs = map::mk_hashmap(ty::hash_ty, ty::eq_ty);
let ecx = @{ccx: cx, type_abbrevs: abbrevs};
let string_w = io::string_writer();
let buf_w = string_w.get_writer().get_buf_writer();
let ebml_w = ebml::create_writer(buf_w);
let crate_attrs = synthesize_crate_attrs(ecx, crate);
encode_attributes(ebml_w, crate_attrs);
encode_crate_deps(ebml_w, cx.sess.get_cstore());
// Encode and index the paths.
ebml::start_tag(ebml_w, tag_paths);
let paths_index = encode_item_paths(ebml_w, crate);
let paths_buckets = create_index(paths_index, hash_path);
encode_index(ebml_w, paths_buckets, write_str);
ebml::end_tag(ebml_w);
// Encode and index the items.
ebml::start_tag(ebml_w, tag_items);
let items_index = encode_info_for_items(ecx, ebml_w);
let items_buckets = create_index(items_index, hash_node_id);
encode_index(ebml_w, items_buckets, write_int);
ebml::end_tag(ebml_w);
// Pad this, since something (LLVM, presumably) is cutting off the
// remaining % 4 bytes.
buf_w.write([0u8, 0u8, 0u8, 0u8]);
ret string_w.get_str();
}
// Get the encoded string for a type
fn encoded_ty(tcx: ty::ctxt, t: ty::t) -> str {
let cx = @{ds: def_to_str, tcx: tcx, abbrevs: tyencode::ac_no_abbrevs};
let sw = io::string_writer();
tyencode::enc_ty(sw.get_writer(), cx, t);
ret sw.get_str();
}
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End: