rust/src/librustc/metadata/decoder.rs

1430 lines
51 KiB
Rust
Raw Normal View History

// Copyright 2012-2014 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.
2011-07-08 08:29:09 +02:00
// Decoding metadata from a single crate's metadata
2011-06-28 01:03:01 +02:00
#![allow(non_camel_case_types)]
use back::svh::Svh;
use metadata::cstore::crate_metadata;
use metadata::common::*;
use metadata::csearch::StaticMethodInfo;
use metadata::csearch;
use metadata::cstore;
use metadata::tydecode::{parse_ty_data, parse_region_data, parse_def_id,
parse_type_param_def_data, parse_bounds_data,
parse_bare_fn_ty_data, parse_trait_ref_data};
use middle::def;
use middle::lang_items;
use middle::resolve::{TraitItemKind, TypeTraitItemKind};
use middle::subst;
use middle::ty::{ImplContainer, TraitContainer};
use middle::ty;
use middle::typeck;
use middle::astencode::vtable_decoder_helpers;
use std::hash::Hash;
2014-05-16 19:15:33 +02:00
use std::hash;
2013-11-11 07:46:32 +01:00
use std::io::extensions::u64_from_be_bytes;
2014-05-16 19:15:33 +02:00
use std::io;
use std::collections::hashmap::HashMap;
2014-02-01 05:57:59 +01:00
use std::rc::Rc;
2014-05-16 19:15:33 +02:00
use std::u64;
use rbml::reader;
use rbml;
use serialize::Decodable;
2012-09-04 20:54:36 +02:00
use syntax::ast_map;
use syntax::attr;
use syntax::parse::token::{IdentInterner, special_idents};
use syntax::parse::token;
use syntax::print::pprust;
use syntax::ast;
2013-01-30 18:56:33 +01:00
use syntax::codemap;
2014-09-07 19:09:06 +02:00
use syntax::ptr::P;
2014-04-17 14:06:25 +02:00
pub type Cmd<'a> = &'a crate_metadata;
// A function that takes a def_id relative to the crate being searched and
// returns a def_id relative to the compilation environment, i.e. if we hit a
// def_id for an item defined in another crate, somebody needs to figure out
// what crate that's in and give us a def_id that makes sense for the current
// build.
2011-07-27 14:19:39 +02:00
fn lookup_hash<'a>(d: rbml::Doc<'a>, eq_fn: |&[u8]| -> bool,
hash: u64) -> Option<rbml::Doc<'a>> {
let index = reader::get_doc(d, tag_index);
let table = reader::get_doc(index, tag_index_table);
2013-08-07 04:35:09 +02:00
let hash_pos = table.start + (hash % 256 * 4) as uint;
let pos = u64_from_be_bytes(d.data, hash_pos, 4) as uint;
2014-03-29 01:05:46 +01:00
let tagged_doc = reader::doc_at(d.data, pos).unwrap();
2011-07-27 14:19:39 +02:00
let belt = tag_index_buckets_bucket_elt;
let mut ret = None;
reader::tagged_docs(tagged_doc.doc, belt, |elt| {
let pos = u64_from_be_bytes(elt.data, elt.start, 4) as uint;
if eq_fn(elt.data[elt.start + 4 .. elt.end]) {
2014-03-29 01:05:46 +01:00
ret = Some(reader::doc_at(d.data, pos).unwrap().doc);
false
} else {
true
}
});
ret
}
pub fn maybe_find_item<'a>(item_id: ast::NodeId,
items: rbml::Doc<'a>) -> Option<rbml::Doc<'a>> {
fn eq_item(bytes: &[u8], item_id: ast::NodeId) -> bool {
2013-10-22 08:06:12 +02:00
return u64_from_be_bytes(
bytes[0u..4u], 0u, 4u) as ast::NodeId
== item_id;
}
lookup_hash(items,
|a| eq_item(a, item_id),
hash::hash(&(item_id as i64)))
}
fn find_item<'a>(item_id: ast::NodeId, items: rbml::Doc<'a>) -> rbml::Doc<'a> {
match maybe_find_item(item_id, items) {
None => panic!("lookup_item: id not found: {}", item_id),
Some(d) => d
}
}
// Looks up an item in the given metadata and returns an rbml doc pointing
// to the item data.
fn lookup_item<'a>(item_id: ast::NodeId, data: &'a [u8]) -> rbml::Doc<'a> {
let items = reader::get_doc(rbml::Doc::new(data), tag_items);
find_item(item_id, items)
}
#[deriving(PartialEq)]
enum Family {
2013-06-22 03:46:34 +02:00
ImmStatic, // c
MutStatic, // b
Fn, // f
CtorFn, // o
StaticMethod, // F
Method, // h
Type, // y
ForeignType, // T
Mod, // m
ForeignMod, // n
Enum, // t
TupleVariant, // v
StructVariant, // V
Impl, // i
Trait, // I
Struct, // S
PublicField, // g
rustc: Add `const` globals to the language This change is an implementation of [RFC 69][rfc] which adds a third kind of global to the language, `const`. This global is most similar to what the old `static` was, and if you're unsure about what to use then you should use a `const`. The semantics of these three kinds of globals are: * A `const` does not represent a memory location, but only a value. Constants are translated as rvalues, which means that their values are directly inlined at usage location (similar to a #define in C/C++). Constant values are, well, constant, and can not be modified. Any "modification" is actually a modification to a local value on the stack rather than the actual constant itself. Almost all values are allowed inside constants, whether they have interior mutability or not. There are a few minor restrictions listed in the RFC, but they should in general not come up too often. * A `static` now always represents a memory location (unconditionally). Any references to the same `static` are actually a reference to the same memory location. Only values whose types ascribe to `Sync` are allowed in a `static`. This restriction is in place because many threads may access a `static` concurrently. Lifting this restriction (and allowing unsafe access) is a future extension not implemented at this time. * A `static mut` continues to always represent a memory location. All references to a `static mut` continue to be `unsafe`. This is a large breaking change, and many programs will need to be updated accordingly. A summary of the breaking changes is: * Statics may no longer be used in patterns. Statics now always represent a memory location, which can sometimes be modified. To fix code, repurpose the matched-on-`static` to a `const`. static FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } change this code to: const FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } * Statics may no longer refer to other statics by value. Due to statics being able to change at runtime, allowing them to reference one another could possibly lead to confusing semantics. If you are in this situation, use a constant initializer instead. Note, however, that statics may reference other statics by address, however. * Statics may no longer be used in constant expressions, such as array lengths. This is due to the same restrictions as listed above. Use a `const` instead. [breaking-change] [rfc]: https://github.com/rust-lang/rfcs/pull/246
2014-10-06 17:17:01 +02:00
InheritedField, // N
Constant, // C
}
fn item_family(item: rbml::Doc) -> Family {
let fam = reader::get_doc(item, tag_items_data_item_family);
match reader::doc_as_u8(fam) as char {
rustc: Add `const` globals to the language This change is an implementation of [RFC 69][rfc] which adds a third kind of global to the language, `const`. This global is most similar to what the old `static` was, and if you're unsure about what to use then you should use a `const`. The semantics of these three kinds of globals are: * A `const` does not represent a memory location, but only a value. Constants are translated as rvalues, which means that their values are directly inlined at usage location (similar to a #define in C/C++). Constant values are, well, constant, and can not be modified. Any "modification" is actually a modification to a local value on the stack rather than the actual constant itself. Almost all values are allowed inside constants, whether they have interior mutability or not. There are a few minor restrictions listed in the RFC, but they should in general not come up too often. * A `static` now always represents a memory location (unconditionally). Any references to the same `static` are actually a reference to the same memory location. Only values whose types ascribe to `Sync` are allowed in a `static`. This restriction is in place because many threads may access a `static` concurrently. Lifting this restriction (and allowing unsafe access) is a future extension not implemented at this time. * A `static mut` continues to always represent a memory location. All references to a `static mut` continue to be `unsafe`. This is a large breaking change, and many programs will need to be updated accordingly. A summary of the breaking changes is: * Statics may no longer be used in patterns. Statics now always represent a memory location, which can sometimes be modified. To fix code, repurpose the matched-on-`static` to a `const`. static FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } change this code to: const FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } * Statics may no longer refer to other statics by value. Due to statics being able to change at runtime, allowing them to reference one another could possibly lead to confusing semantics. If you are in this situation, use a constant initializer instead. Note, however, that statics may reference other statics by address, however. * Statics may no longer be used in constant expressions, such as array lengths. This is due to the same restrictions as listed above. Use a `const` instead. [breaking-change] [rfc]: https://github.com/rust-lang/rfcs/pull/246
2014-10-06 17:17:01 +02:00
'C' => Constant,
2013-06-22 03:46:34 +02:00
'c' => ImmStatic,
'b' => MutStatic,
'f' => Fn,
'o' => CtorFn,
'F' => StaticMethod,
'h' => Method,
'y' => Type,
'T' => ForeignType,
'm' => Mod,
'n' => ForeignMod,
't' => Enum,
'v' => TupleVariant,
'V' => StructVariant,
'i' => Impl,
'I' => Trait,
'S' => Struct,
'g' => PublicField,
'N' => InheritedField,
c => panic!("unexpected family char: {}", c)
}
}
fn item_visibility(item: rbml::Doc) -> ast::Visibility {
match reader::maybe_get_doc(item, tag_items_data_item_visibility) {
None => ast::Public,
Some(visibility_doc) => {
match reader::doc_as_u8(visibility_doc) as char {
'y' => ast::Public,
'i' => ast::Inherited,
_ => panic!("unknown visibility character")
}
}
}
}
fn item_sort(item: rbml::Doc) -> char {
// NB(pcwalton): The default of 'r' here is relied upon in
// `is_associated_type` below.
let mut ret = 'r';
reader::tagged_docs(item, tag_item_trait_item_sort, |doc| {
ret = doc.as_str_slice().as_bytes()[0] as char;
false
});
ret
}
fn item_symbol(item: rbml::Doc) -> String {
reader::get_doc(item, tag_items_data_item_symbol).as_str().to_string()
}
fn item_parent_item(d: rbml::Doc) -> Option<ast::DefId> {
let mut ret = None;
reader::tagged_docs(d, tag_items_data_parent_item, |did| {
ret = Some(reader::with_doc_data(did, parse_def_id));
false
});
ret
}
fn item_reqd_and_translated_parent_item(cnum: ast::CrateNum,
d: rbml::Doc) -> ast::DefId {
let trait_did = item_parent_item(d).expect("item without parent");
ast::DefId { krate: cnum, node: trait_did.node }
}
fn item_def_id(d: rbml::Doc, cdata: Cmd) -> ast::DefId {
let tagdoc = reader::get_doc(d, tag_def_id);
2013-06-08 02:32:25 +02:00
return translate_def_id(cdata, reader::with_doc_data(tagdoc, parse_def_id));
}
fn get_provided_source(d: rbml::Doc, cdata: Cmd) -> Option<ast::DefId> {
reader::maybe_get_doc(d, tag_item_method_provided_source).map(|doc| {
translate_def_id(cdata, reader::with_doc_data(doc, parse_def_id))
})
}
fn each_reexport(d: rbml::Doc, f: |rbml::Doc| -> bool) -> bool {
reader::tagged_docs(d, tag_items_data_item_reexport, f)
2013-05-03 19:08:08 +02:00
}
fn variant_disr_val(d: rbml::Doc) -> Option<ty::Disr> {
reader::maybe_get_doc(d, tag_disr_val).and_then(|val_doc| {
reader::with_doc_data(val_doc, |data| u64::parse_bytes(data, 10u))
})
}
fn doc_type(doc: rbml::Doc, tcx: &ty::ctxt, cdata: Cmd) -> ty::t {
let tp = reader::get_doc(doc, tag_items_data_item_type);
parse_ty_data(tp.data, cdata.cnum, tp.start, tcx,
|_, did| translate_def_id(cdata, did))
}
fn doc_method_fty(doc: rbml::Doc, tcx: &ty::ctxt, cdata: Cmd) -> ty::BareFnTy {
let tp = reader::get_doc(doc, tag_item_method_fty);
parse_bare_fn_ty_data(tp.data, cdata.cnum, tp.start, tcx,
|_, did| translate_def_id(cdata, did))
}
pub fn item_type(_item_id: ast::DefId, item: rbml::Doc,
2014-03-06 04:07:47 +01:00
tcx: &ty::ctxt, cdata: Cmd) -> ty::t {
2013-04-02 22:41:18 +02:00
doc_type(item, tcx, cdata)
}
fn doc_trait_ref(doc: rbml::Doc, tcx: &ty::ctxt, cdata: Cmd) -> ty::TraitRef {
parse_trait_ref_data(doc.data, cdata.cnum, doc.start, tcx,
|_, did| translate_def_id(cdata, did))
}
fn item_trait_ref(doc: rbml::Doc, tcx: &ty::ctxt, cdata: Cmd) -> ty::TraitRef {
let tp = reader::get_doc(doc, tag_item_trait_ref);
doc_trait_ref(tp, tcx, cdata)
}
fn doc_bounds(doc: rbml::Doc, tcx: &ty::ctxt, cdata: Cmd) -> ty::ParamBounds {
parse_bounds_data(doc.data, cdata.cnum, doc.start, tcx,
|_, did| translate_def_id(cdata, did))
}
fn trait_def_bounds(doc: rbml::Doc, tcx: &ty::ctxt, cdata: Cmd) -> ty::ParamBounds {
let d = reader::get_doc(doc, tag_trait_def_bounds);
doc_bounds(d, tcx, cdata)
}
fn enum_variant_ids(item: rbml::Doc, cdata: Cmd) -> Vec<ast::DefId> {
let mut ids: Vec<ast::DefId> = Vec::new();
2011-07-27 14:19:39 +02:00
let v = tag_items_data_item_variant;
reader::tagged_docs(item, v, |p| {
2013-06-08 02:32:25 +02:00
let ext = reader::with_doc_data(p, parse_def_id);
ids.push(ast::DefId { krate: cdata.cnum, node: ext.node });
true
});
2012-08-02 02:30:05 +02:00
return ids;
}
fn item_path(item_doc: rbml::Doc) -> Vec<ast_map::PathElem> {
let path_doc = reader::get_doc(item_doc, tag_path);
let len_doc = reader::get_doc(path_doc, tag_path_len);
let len = reader::doc_as_u32(len_doc) as uint;
let mut result = Vec::with_capacity(len);
reader::docs(path_doc, |tag, elt_doc| {
if tag == tag_path_elem_mod {
let s = elt_doc.as_str_slice();
result.push(ast_map::PathMod(token::intern(s)));
} else if tag == tag_path_elem_name {
let s = elt_doc.as_str_slice();
result.push(ast_map::PathName(token::intern(s)));
} else {
// ignore tag_path_len element
}
true
});
result
}
fn item_name(intr: &IdentInterner, item: rbml::Doc) -> ast::Name {
let name = reader::get_doc(item, tag_paths_data_name);
let string = name.as_str_slice();
match intr.find_equiv(string) {
None => token::intern(string),
Some(val) => val,
}
}
fn item_to_def_like(item: rbml::Doc, did: ast::DefId, cnum: ast::CrateNum)
-> DefLike {
let fam = item_family(item);
match fam {
rustc: Add `const` globals to the language This change is an implementation of [RFC 69][rfc] which adds a third kind of global to the language, `const`. This global is most similar to what the old `static` was, and if you're unsure about what to use then you should use a `const`. The semantics of these three kinds of globals are: * A `const` does not represent a memory location, but only a value. Constants are translated as rvalues, which means that their values are directly inlined at usage location (similar to a #define in C/C++). Constant values are, well, constant, and can not be modified. Any "modification" is actually a modification to a local value on the stack rather than the actual constant itself. Almost all values are allowed inside constants, whether they have interior mutability or not. There are a few minor restrictions listed in the RFC, but they should in general not come up too often. * A `static` now always represents a memory location (unconditionally). Any references to the same `static` are actually a reference to the same memory location. Only values whose types ascribe to `Sync` are allowed in a `static`. This restriction is in place because many threads may access a `static` concurrently. Lifting this restriction (and allowing unsafe access) is a future extension not implemented at this time. * A `static mut` continues to always represent a memory location. All references to a `static mut` continue to be `unsafe`. This is a large breaking change, and many programs will need to be updated accordingly. A summary of the breaking changes is: * Statics may no longer be used in patterns. Statics now always represent a memory location, which can sometimes be modified. To fix code, repurpose the matched-on-`static` to a `const`. static FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } change this code to: const FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } * Statics may no longer refer to other statics by value. Due to statics being able to change at runtime, allowing them to reference one another could possibly lead to confusing semantics. If you are in this situation, use a constant initializer instead. Note, however, that statics may reference other statics by address, however. * Statics may no longer be used in constant expressions, such as array lengths. This is due to the same restrictions as listed above. Use a `const` instead. [breaking-change] [rfc]: https://github.com/rust-lang/rfcs/pull/246
2014-10-06 17:17:01 +02:00
Constant => DlDef(def::DefConst(did)),
ImmStatic => DlDef(def::DefStatic(did, false)),
MutStatic => DlDef(def::DefStatic(did, true)),
Struct => DlDef(def::DefStruct(did)),
Fn => DlDef(def::DefFn(did, false)),
CtorFn => DlDef(def::DefFn(did, true)),
Method | StaticMethod => {
// def_static_method carries an optional field of its enclosing
// trait or enclosing impl (if this is an inherent static method).
// So we need to detect whether this is in a trait or not, which
// we do through the mildly hacky way of checking whether there is
// a trait_parent_sort.
let provenance = if reader::maybe_get_doc(
item, tag_item_trait_parent_sort).is_some() {
def::FromTrait(item_reqd_and_translated_parent_item(cnum,
item))
} else {
def::FromImpl(item_reqd_and_translated_parent_item(cnum,
item))
};
match fam {
// We don't bother to get encode/decode the trait id, we don't need it.
Method => DlDef(def::DefMethod(did, None, provenance)),
StaticMethod => DlDef(def::DefStaticMethod(did, provenance)),
_ => fail!()
}
}
Type | ForeignType => DlDef(def::DefTy(did, false)),
Mod => DlDef(def::DefMod(did)),
ForeignMod => DlDef(def::DefForeignMod(did)),
StructVariant => {
let enum_did = item_reqd_and_translated_parent_item(cnum, item);
DlDef(def::DefVariant(enum_did, did, true))
}
TupleVariant => {
let enum_did = item_reqd_and_translated_parent_item(cnum, item);
DlDef(def::DefVariant(enum_did, did, false))
}
Trait => DlDef(def::DefTrait(did)),
Enum => DlDef(def::DefTy(did, true)),
Impl => DlImpl(did),
PublicField | InheritedField => DlField,
}
}
pub fn get_trait_def(cdata: Cmd,
item_id: ast::NodeId,
2014-03-06 04:07:47 +01:00
tcx: &ty::ctxt) -> ty::TraitDef
{
let item_doc = lookup_item(item_id, cdata.data());
let generics = doc_generics(item_doc, tcx, cdata, tag_item_generics);
let bounds = trait_def_bounds(item_doc, tcx, cdata);
ty::TraitDef {
generics: generics,
bounds: bounds,
2014-04-22 01:21:52 +02:00
trait_ref: Rc::new(item_trait_ref(item_doc, tcx, cdata))
}
}
2014-03-06 04:07:47 +01:00
pub fn get_type(cdata: Cmd, id: ast::NodeId, tcx: &ty::ctxt)
-> ty::Polytype {
let item = lookup_item(id, cdata.data());
let t = item_type(ast::DefId { krate: cdata.cnum, node: id }, item, tcx,
2013-01-13 20:05:40 +01:00
cdata);
let generics = doc_generics(item, tcx, cdata, tag_item_generics);
ty::Polytype {
generics: generics,
ty: t
}
}
pub fn get_stability(cdata: Cmd, id: ast::NodeId) -> Option<attr::Stability> {
let item = lookup_item(id, cdata.data());
reader::maybe_get_doc(item, tag_items_data_item_stability).map(|doc| {
let mut decoder = reader::Decoder::new(doc);
Decodable::decode(&mut decoder).unwrap()
})
}
pub fn get_repr_attrs(cdata: Cmd, id: ast::NodeId) -> Vec<attr::ReprAttr> {
let item = lookup_item(id, cdata.data());
match reader::maybe_get_doc(item, tag_items_data_item_repr).map(|doc| {
let mut decoder = reader::Decoder::new(doc);
Decodable::decode(&mut decoder).unwrap()
}) {
Some(attrs) => attrs,
None => Vec::new(),
}
}
pub fn get_impl_trait(cdata: Cmd,
id: ast::NodeId,
2014-04-22 01:21:52 +02:00
tcx: &ty::ctxt) -> Option<Rc<ty::TraitRef>>
{
let item_doc = lookup_item(id, cdata.data());
reader::maybe_get_doc(item_doc, tag_item_trait_ref).map(|tp| {
2014-04-22 01:21:52 +02:00
Rc::new(doc_trait_ref(tp, tcx, cdata))
})
}
pub fn get_impl_vtables(cdata: Cmd,
id: ast::NodeId,
tcx: &ty::ctxt)
-> typeck::vtable_res
{
let item_doc = lookup_item(id, cdata.data());
let vtables_doc = reader::get_doc(item_doc, tag_item_impl_vtables);
let mut decoder = reader::Decoder::new(vtables_doc);
decoder.read_vtable_res(tcx, cdata)
}
pub fn get_symbol(data: &[u8], id: ast::NodeId) -> String {
2012-08-02 02:30:05 +02:00
return item_symbol(lookup_item(id, data));
}
// Something that a name can resolve to.
2013-12-21 22:58:11 +01:00
#[deriving(Clone)]
pub enum DefLike {
DlDef(def::Def),
DlImpl(ast::DefId),
DlField
}
/// Iterates over the language items in the given crate.
pub fn each_lang_item(cdata: Cmd, f: |ast::NodeId, uint| -> bool) -> bool {
let root = rbml::Doc::new(cdata.data());
2013-05-03 19:08:08 +02:00
let lang_items = reader::get_doc(root, tag_lang_items);
reader::tagged_docs(lang_items, tag_lang_items_item, |item_doc| {
2013-05-03 19:08:08 +02:00
let id_doc = reader::get_doc(item_doc, tag_lang_items_item_id);
let id = reader::doc_as_u32(id_doc) as uint;
let node_id_doc = reader::get_doc(item_doc,
tag_lang_items_item_node_id);
let node_id = reader::doc_as_u32(node_id_doc) as ast::NodeId;
2013-05-03 19:08:08 +02:00
f(node_id, id)
})
2013-05-03 19:08:08 +02:00
}
2014-04-17 14:06:25 +02:00
pub type GetCrateDataCb<'a> = |ast::CrateNum|: 'a -> Rc<crate_metadata>;
2014-03-27 18:28:38 +01:00
fn each_child_of_item_or_crate(intr: Rc<IdentInterner>,
cdata: Cmd,
item_doc: rbml::Doc,
get_crate_data: GetCrateDataCb,
callback: |DefLike,
ast::Name,
ast::Visibility|) {
// Iterate over all children.
let _ = reader::tagged_docs(item_doc, tag_mod_child, |child_info_doc| {
let child_def_id = reader::with_doc_data(child_info_doc,
parse_def_id);
let child_def_id = translate_def_id(cdata, child_def_id);
// This item may be in yet another crate if it was the child of a
// reexport.
2014-04-17 14:06:25 +02:00
let crate_data = if child_def_id.krate == cdata.cnum {
None
} else {
2014-04-17 14:06:25 +02:00
Some(get_crate_data(child_def_id.krate))
};
let crate_data = match crate_data {
Some(ref cdata) => &**cdata,
None => cdata
};
let other_crates_items = reader::get_doc(rbml::Doc::new(crate_data.data()), tag_items);
2014-04-17 14:06:25 +02:00
// Get the item.
match maybe_find_item(child_def_id.node, other_crates_items) {
None => {}
Some(child_item_doc) => {
// Hand off the item to the callback.
2014-03-27 18:28:38 +01:00
let child_name = item_name(&*intr, child_item_doc);
let def_like = item_to_def_like(child_item_doc,
child_def_id,
cdata.cnum);
let visibility = item_visibility(child_item_doc);
callback(def_like, child_name, visibility);
}
}
true
});
// As a special case, iterate over all static methods of
// associated implementations too. This is a bit of a botch.
// --pcwalton
let _ = reader::tagged_docs(item_doc,
tag_items_data_item_inherent_impl,
|inherent_impl_def_id_doc| {
let inherent_impl_def_id = item_def_id(inherent_impl_def_id_doc,
cdata);
let items = reader::get_doc(rbml::Doc::new(cdata.data()), tag_items);
match maybe_find_item(inherent_impl_def_id.node, items) {
None => {}
Some(inherent_impl_doc) => {
let _ = reader::tagged_docs(inherent_impl_doc,
tag_item_impl_item,
|impl_item_def_id_doc| {
let impl_item_def_id = item_def_id(impl_item_def_id_doc,
cdata);
match maybe_find_item(impl_item_def_id.node, items) {
None => {}
Some(impl_method_doc) => {
match item_family(impl_method_doc) {
StaticMethod => {
// Hand off the static method
// to the callback.
let static_method_name =
2014-03-27 18:28:38 +01:00
item_name(&*intr, impl_method_doc);
let static_method_def_like =
item_to_def_like(impl_method_doc,
impl_item_def_id,
cdata.cnum);
callback(static_method_def_like,
static_method_name,
item_visibility(impl_method_doc));
}
_ => {}
}
}
}
true
});
}
}
true
});
// Iterate over all reexports.
let _ = each_reexport(item_doc, |reexport_doc| {
let def_id_doc = reader::get_doc(reexport_doc,
tag_items_data_item_reexport_def_id);
let child_def_id = reader::with_doc_data(def_id_doc,
parse_def_id);
let child_def_id = translate_def_id(cdata, child_def_id);
let name_doc = reader::get_doc(reexport_doc,
tag_items_data_item_reexport_name);
let name = name_doc.as_str_slice();
// This reexport may be in yet another crate.
2014-04-17 14:06:25 +02:00
let crate_data = if child_def_id.krate == cdata.cnum {
None
} else {
2014-04-17 14:06:25 +02:00
Some(get_crate_data(child_def_id.krate))
};
let crate_data = match crate_data {
Some(ref cdata) => &**cdata,
None => cdata
};
let other_crates_items = reader::get_doc(rbml::Doc::new(crate_data.data()), tag_items);
2014-04-17 14:06:25 +02:00
// Get the item.
match maybe_find_item(child_def_id.node, other_crates_items) {
None => {}
Some(child_item_doc) => {
// Hand off the item to the callback.
let def_like = item_to_def_like(child_item_doc,
child_def_id,
child_def_id.krate);
Extract privacy checking from name resolution This commit is the culmination of my recent effort to refine Rust's notion of privacy and visibility among crates. The major goals of this commit were to remove privacy checking from resolve for the sake of sane error messages, and to attempt a much more rigid and well-tested implementation of visibility throughout rust. The implemented rules for name visibility are: 1. Everything pub from the root namespace is visible to anyone 2. You may access any private item of your ancestors. "Accessing a private item" depends on what the item is, so for a function this means that you can call it, but for a module it means that you can look inside of it. Once you look inside a private module, any accessed item must be "pub from the root" where the new root is the private module that you looked into. These rules required some more analysis results to get propagated from trans to privacy in the form of a few hash tables. I added a new test in which my goal was to showcase all of the privacy nuances of the language, and I hope to place any new bugs into this file to prevent regressions. Overall, I was unable to completely remove the notion of privacy from resolve. One use of privacy is for dealing with glob imports. Essentially a glob import can only import *public* items from the destination, and because this must be done at namespace resolution time, resolve must maintain the notion of "what items are public in a module". There are some sad approximations of privacy, but I unfortunately can't see clear methods to extract them outside. The other use case of privacy in resolve now is one that must stick around regardless of glob imports. When dealing with privacy, checking a private path needs to know "what the last private thing was" when looking at a path. Resolve is the only compiler pass which knows the answer to this question, so it maintains the answer on a per-path resolution basis (works similarly to the def_map generated). Closes #8215
2013-10-05 23:37:39 +02:00
// These items have a public visibility because they're part of
// a public re-export.
callback(def_like, token::intern(name), ast::Public);
}
}
true
});
}
/// Iterates over each child of the given item.
2014-03-27 18:28:38 +01:00
pub fn each_child_of_item(intr: Rc<IdentInterner>,
cdata: Cmd,
id: ast::NodeId,
get_crate_data: GetCrateDataCb,
callback: |DefLike, ast::Name, ast::Visibility|) {
// Find the item.
let root_doc = rbml::Doc::new(cdata.data());
let items = reader::get_doc(root_doc, tag_items);
let item_doc = match maybe_find_item(id, items) {
None => return,
Some(item_doc) => item_doc,
};
each_child_of_item_or_crate(intr,
cdata,
item_doc,
get_crate_data,
callback)
}
/// Iterates over all the top-level crate items.
2014-03-27 18:28:38 +01:00
pub fn each_top_level_item_of_crate(intr: Rc<IdentInterner>,
cdata: Cmd,
get_crate_data: GetCrateDataCb,
callback: |DefLike,
ast::Name,
ast::Visibility|) {
let root_doc = rbml::Doc::new(cdata.data());
let misc_info_doc = reader::get_doc(root_doc, tag_misc_info);
let crate_items_doc = reader::get_doc(misc_info_doc,
tag_misc_info_crate_items);
each_child_of_item_or_crate(intr,
cdata,
crate_items_doc,
get_crate_data,
callback)
}
pub fn get_item_path(cdata: Cmd, id: ast::NodeId) -> Vec<ast_map::PathElem> {
item_path(lookup_item(id, cdata.data()))
}
2014-09-07 19:09:06 +02:00
pub type DecodeInlinedItem<'a> = <'tcx> |cdata: Cmd,
tcx: &ty::ctxt<'tcx>,
path: Vec<ast_map::PathElem>,
par_doc: rbml::Doc|: 'a
-> Result<&'tcx ast::InlinedItem,
Vec<ast_map::PathElem>>;
2014-09-07 19:09:06 +02:00
pub fn maybe_get_item_ast<'tcx>(cdata: Cmd, tcx: &ty::ctxt<'tcx>, id: ast::NodeId,
decode_inlined_item: DecodeInlinedItem)
-> csearch::found_ast<'tcx> {
debug!("Looking up item: {}", id);
let item_doc = lookup_item(id, cdata.data());
let path = item_path(item_doc).init().to_vec();
match decode_inlined_item(cdata, tcx, path, item_doc) {
2014-09-07 19:09:06 +02:00
Ok(ii) => csearch::found(ii),
Err(path) => {
match item_parent_item(item_doc) {
Some(did) => {
let did = translate_def_id(cdata, did);
let parent_item = lookup_item(did.node, cdata.data());
match decode_inlined_item(cdata, tcx, path, parent_item) {
2014-09-07 19:09:06 +02:00
Ok(ii) => csearch::found_parent(did, ii),
Err(_) => csearch::not_found
}
}
None => csearch::not_found
}
}
}
}
2014-03-27 18:28:38 +01:00
pub fn get_enum_variants(intr: Rc<IdentInterner>, cdata: Cmd, id: ast::NodeId,
2014-04-22 01:21:52 +02:00
tcx: &ty::ctxt) -> Vec<Rc<ty::VariantInfo>> {
let data = cdata.data();
let items = reader::get_doc(rbml::Doc::new(data), tag_items);
let item = find_item(id, items);
let mut disr_val = 0;
2014-04-22 01:21:52 +02:00
enum_variant_ids(item, cdata).iter().map(|did| {
2011-07-27 14:19:39 +02:00
let item = find_item(did.node, items);
let ctor_ty = item_type(ast::DefId { krate: cdata.cnum, node: id},
2013-01-13 20:05:40 +01:00
item, tcx, cdata);
2014-03-27 18:28:38 +01:00
let name = item_name(&*intr, item);
let (ctor_ty, arg_tys) = match ty::get(ctor_ty).sty {
ty::ty_bare_fn(ref f) =>
(Some(ctor_ty), f.sig.inputs.clone()),
_ => // Nullary or struct enum variant.
(None, get_struct_fields(intr.clone(), cdata, did.node)
.iter()
.map(|field_ty| get_type(cdata, field_ty.id.node, tcx).ty)
.collect())
};
2012-08-06 21:34:08 +02:00
match variant_disr_val(item) {
2014-04-22 01:21:52 +02:00
Some(val) => { disr_val = val; }
_ => { /* empty */ }
}
2014-04-22 01:21:52 +02:00
let old_disr_val = disr_val;
disr_val += 1;
Rc::new(ty::VariantInfo {
args: arg_tys,
arg_names: None,
ctor_ty: ctor_ty,
name: name,
// I'm not even sure if we encode visibility
// for variants -- TEST -- tjc
id: *did,
2014-04-22 01:21:52 +02:00
disr_val: old_disr_val,
vis: ast::Inherited
})
}).collect()
}
fn get_explicit_self(item: rbml::Doc) -> ty::ExplicitSelfCategory {
fn get_mutability(ch: u8) -> ast::Mutability {
2012-08-06 21:34:08 +02:00
match ch as char {
'i' => ast::MutImmutable,
'm' => ast::MutMutable,
_ => panic!("unknown mutability character: `{}`", ch as char),
}
}
let explicit_self_doc = reader::get_doc(item, tag_item_trait_method_explicit_self);
let string = explicit_self_doc.as_str_slice();
let explicit_self_kind = string.as_bytes()[0];
match explicit_self_kind as char {
's' => ty::StaticExplicitSelfCategory,
'v' => ty::ByValueExplicitSelfCategory,
'~' => ty::ByBoxExplicitSelfCategory,
// FIXME(#4846) expl. region
'&' => {
ty::ByReferenceExplicitSelfCategory(
ty::ReEmpty,
get_mutability(string.as_bytes()[1]))
}
_ => panic!("unknown self type code: `{}`", explicit_self_kind as char)
}
}
/// Returns the def IDs of all the items in the given implementation.
pub fn get_impl_items(cdata: Cmd, impl_id: ast::NodeId)
-> Vec<ty::ImplOrTraitItemId> {
let mut impl_items = Vec::new();
2014-04-21 11:04:35 +02:00
reader::tagged_docs(lookup_item(impl_id, cdata.data()),
tag_item_impl_item, |doc| {
let def_id = item_def_id(doc, cdata);
match item_sort(doc) {
'r' | 'p' => impl_items.push(ty::MethodTraitItemId(def_id)),
't' => impl_items.push(ty::TypeTraitItemId(def_id)),
_ => panic!("unknown impl item sort"),
}
true
});
impl_items
}
pub fn get_trait_item_name_and_kind(intr: Rc<IdentInterner>,
cdata: Cmd,
id: ast::NodeId)
-> (ast::Name, TraitItemKind) {
let doc = lookup_item(id, cdata.data());
let name = item_name(&*intr, doc);
match item_sort(doc) {
'r' | 'p' => {
let explicit_self = get_explicit_self(doc);
(name, TraitItemKind::from_explicit_self_category(explicit_self))
}
't' => (name, TypeTraitItemKind),
c => {
panic!("get_trait_item_name_and_kind(): unknown trait item kind \
in metadata: `{}`", c)
}
}
}
pub fn get_impl_or_trait_item(intr: Rc<IdentInterner>,
cdata: Cmd,
id: ast::NodeId,
tcx: &ty::ctxt)
-> ty::ImplOrTraitItem {
let method_doc = lookup_item(id, cdata.data());
let def_id = item_def_id(method_doc, cdata);
let container_id = item_reqd_and_translated_parent_item(cdata.cnum,
method_doc);
let container_doc = lookup_item(container_id.node, cdata.data());
let container = match item_family(container_doc) {
Trait => TraitContainer(container_id),
_ => ImplContainer(container_id),
};
2014-03-27 18:28:38 +01:00
let name = item_name(&*intr, method_doc);
let vis = item_visibility(method_doc);
match item_sort(method_doc) {
'r' | 'p' => {
let generics = doc_generics(method_doc, tcx, cdata,
tag_method_ty_generics);
let fty = doc_method_fty(method_doc, tcx, cdata);
let explicit_self = get_explicit_self(method_doc);
let provided_source = get_provided_source(method_doc, cdata);
ty::MethodTraitItem(Rc::new(ty::Method::new(name,
generics,
fty,
explicit_self,
vis,
def_id,
container,
provided_source)))
}
't' => {
ty::TypeTraitItem(Rc::new(ty::AssociatedType {
name: name,
vis: vis,
def_id: def_id,
container: container,
}))
}
_ => panic!("unknown impl/trait item sort"),
}
}
pub fn get_trait_item_def_ids(cdata: Cmd, id: ast::NodeId)
-> Vec<ty::ImplOrTraitItemId> {
let data = cdata.data();
let item = lookup_item(id, data);
let mut result = Vec::new();
reader::tagged_docs(item, tag_item_trait_item, |mth| {
let def_id = item_def_id(mth, cdata);
match item_sort(mth) {
'r' | 'p' => result.push(ty::MethodTraitItemId(def_id)),
't' => result.push(ty::TypeTraitItemId(def_id)),
_ => panic!("unknown trait item sort"),
}
true
});
result
}
pub fn get_item_variances(cdata: Cmd, id: ast::NodeId) -> ty::ItemVariances {
let data = cdata.data();
let item_doc = lookup_item(id, data);
let variance_doc = reader::get_doc(item_doc, tag_item_variances);
let mut decoder = reader::Decoder::new(variance_doc);
2014-03-29 01:05:46 +01:00
Decodable::decode(&mut decoder).unwrap()
}
pub fn get_provided_trait_methods(intr: Rc<IdentInterner>,
cdata: Cmd,
id: ast::NodeId,
tcx: &ty::ctxt)
2014-04-22 01:21:52 +02:00
-> Vec<Rc<ty::Method>> {
let data = cdata.data();
let item = lookup_item(id, data);
let mut result = Vec::new();
reader::tagged_docs(item, tag_item_trait_item, |mth_id| {
let did = item_def_id(mth_id, cdata);
let mth = lookup_item(did.node, data);
if item_sort(mth) == 'p' {
let trait_item = get_impl_or_trait_item(intr.clone(),
cdata,
did.node,
tcx);
match trait_item {
ty::MethodTraitItem(ref method) => {
result.push((*method).clone())
}
ty::TypeTraitItem(_) => {}
}
}
true
});
2013-02-15 10:14:34 +01:00
return result;
}
/// Returns the supertraits of the given trait.
2014-03-06 04:07:47 +01:00
pub fn get_supertraits(cdata: Cmd, id: ast::NodeId, tcx: &ty::ctxt)
2014-04-22 01:21:52 +02:00
-> Vec<Rc<ty::TraitRef>> {
let mut results = Vec::new();
let item_doc = lookup_item(id, cdata.data());
reader::tagged_docs(item_doc, tag_item_super_trait_ref, |trait_doc| {
// NB. Only reads the ones that *aren't* builtin-bounds. See also
// get_trait_def() for collecting the builtin bounds.
// FIXME(#8559): The builtin bounds shouldn't be encoded in the first place.
let trait_ref = doc_trait_ref(trait_doc, tcx, cdata);
if tcx.lang_items.to_builtin_kind(trait_ref.def_id).is_none() {
2014-04-22 01:21:52 +02:00
results.push(Rc::new(trait_ref));
}
true
});
2013-03-08 00:37:14 +01:00
return results;
}
pub fn get_type_name_if_impl(cdata: Cmd,
node_id: ast::NodeId) -> Option<ast::Name> {
let item = lookup_item(node_id, cdata.data());
if item_family(item) != Impl {
return None;
}
let mut ret = None;
reader::tagged_docs(item, tag_item_impl_type_basename, |doc| {
ret = Some(token::intern(doc.as_str_slice()));
false
});
ret
}
2014-03-27 18:28:38 +01:00
pub fn get_static_methods_if_impl(intr: Rc<IdentInterner>,
cdata: Cmd,
node_id: ast::NodeId)
-> Option<Vec<StaticMethodInfo> > {
let item = lookup_item(node_id, cdata.data());
if item_family(item) != Impl {
return None;
}
// If this impl implements a trait, don't consider it.
let ret = reader::tagged_docs(item, tag_item_trait_ref, |_doc| {
false
});
if !ret { return None }
let mut impl_method_ids = Vec::new();
reader::tagged_docs(item, tag_item_impl_item, |impl_method_doc| {
impl_method_ids.push(item_def_id(impl_method_doc, cdata));
true
});
let mut static_impl_methods = Vec::new();
for impl_method_id in impl_method_ids.iter() {
let impl_method_doc = lookup_item(impl_method_id.node, cdata.data());
let family = item_family(impl_method_doc);
match family {
StaticMethod => {
static_impl_methods.push(StaticMethodInfo {
name: item_name(&*intr, impl_method_doc),
def_id: item_def_id(impl_method_doc, cdata),
vis: item_visibility(impl_method_doc),
});
}
_ => {}
}
}
2013-03-08 00:37:14 +01:00
return Some(static_impl_methods);
}
/// If node_id is the constructor of a tuple struct, retrieve the NodeId of
/// the actual type definition, otherwise, return None
pub fn get_tuple_struct_definition_if_ctor(cdata: Cmd,
node_id: ast::NodeId)
-> Option<ast::DefId>
{
let item = lookup_item(node_id, cdata.data());
let mut ret = None;
reader::tagged_docs(item, tag_items_data_item_is_tuple_struct_ctor, |_| {
ret = Some(item_reqd_and_translated_parent_item(cdata.cnum, item));
false
});
ret
}
pub fn get_item_attrs(cdata: Cmd,
orig_node_id: ast::NodeId,
f: |Vec<ast::Attribute>|) {
// The attributes for a tuple struct are attached to the definition, not the ctor;
// we assume that someone passing in a tuple struct ctor is actually wanting to
// look at the definition
let node_id = get_tuple_struct_definition_if_ctor(cdata, orig_node_id);
let node_id = node_id.map(|x| x.node).unwrap_or(orig_node_id);
let item = lookup_item(node_id, cdata.data());
f(get_attributes(item));
}
pub fn get_struct_field_attrs(cdata: Cmd) -> HashMap<ast::NodeId, Vec<ast::Attribute>> {
let data = rbml::Doc::new(cdata.data());
let fields = reader::get_doc(data, tag_struct_fields);
let mut map = HashMap::new();
reader::tagged_docs(fields, tag_struct_field, |field| {
let id = reader::doc_as_u32(reader::get_doc(field, tag_struct_field_id));
let attrs = get_attributes(field);
map.insert(id, attrs);
true
});
map
}
fn struct_field_family_to_visibility(family: Family) -> ast::Visibility {
match family {
PublicField => ast::Public,
InheritedField => ast::Inherited,
_ => panic!()
}
}
2014-03-27 18:28:38 +01:00
pub fn get_struct_fields(intr: Rc<IdentInterner>, cdata: Cmd, id: ast::NodeId)
-> Vec<ty::field_ty> {
let data = cdata.data();
let item = lookup_item(id, data);
let mut result = Vec::new();
reader::tagged_docs(item, tag_item_field, |an_item| {
let f = item_family(an_item);
if f == PublicField || f == InheritedField {
2014-03-27 18:28:38 +01:00
let name = item_name(&*intr, an_item);
let did = item_def_id(an_item, cdata);
let tagdoc = reader::get_doc(an_item, tag_item_field_origin);
let origin_id = translate_def_id(cdata, reader::with_doc_data(tagdoc, parse_def_id));
result.push(ty::field_ty {
name: name,
id: did,
vis: struct_field_family_to_visibility(f),
origin: origin_id,
});
}
true
});
reader::tagged_docs(item, tag_item_unnamed_field, |an_item| {
let did = item_def_id(an_item, cdata);
let tagdoc = reader::get_doc(an_item, tag_item_field_origin);
let f = item_family(an_item);
let origin_id = translate_def_id(cdata, reader::with_doc_data(tagdoc, parse_def_id));
result.push(ty::field_ty {
2013-09-12 01:21:03 +02:00
name: special_idents::unnamed_field.name,
id: did,
vis: struct_field_family_to_visibility(f),
origin: origin_id,
});
true
});
result
}
2014-09-07 19:09:06 +02:00
fn get_meta_items(md: rbml::Doc) -> Vec<P<ast::MetaItem>> {
let mut items: Vec<P<ast::MetaItem>> = Vec::new();
reader::tagged_docs(md, tag_meta_item_word, |meta_item_doc| {
let nd = reader::get_doc(meta_item_doc, tag_meta_item_name);
let n = token::intern_and_get_ident(nd.as_str_slice());
items.push(attr::mk_word_item(n));
true
});
reader::tagged_docs(md, tag_meta_item_name_value, |meta_item_doc| {
let nd = reader::get_doc(meta_item_doc, tag_meta_item_name);
let vd = reader::get_doc(meta_item_doc, tag_meta_item_value);
let n = token::intern_and_get_ident(nd.as_str_slice());
let v = token::intern_and_get_ident(vd.as_str_slice());
// FIXME (#623): Should be able to decode MetaNameValue variants,
// but currently the encoder just drops them
items.push(attr::mk_name_value_item_str(n, v));
true
});
reader::tagged_docs(md, tag_meta_item_list, |meta_item_doc| {
let nd = reader::get_doc(meta_item_doc, tag_meta_item_name);
let n = token::intern_and_get_ident(nd.as_str_slice());
2011-07-27 14:19:39 +02:00
let subitems = get_meta_items(meta_item_doc);
2014-09-15 05:27:36 +02:00
items.push(attr::mk_list_item(n, subitems.into_iter().collect()));
true
});
2012-08-02 02:30:05 +02:00
return items;
}
fn get_attributes(md: rbml::Doc) -> Vec<ast::Attribute> {
let mut attrs: Vec<ast::Attribute> = Vec::new();
match reader::maybe_get_doc(md, tag_attributes) {
2014-05-16 19:15:33 +02:00
Some(attrs_d) => {
reader::tagged_docs(attrs_d, tag_attribute, |attr_doc| {
let is_sugared_doc = reader::doc_as_u8(
reader::get_doc(attr_doc, tag_attribute_is_sugared_doc)
) == 1;
2011-07-27 14:19:39 +02:00
let meta_items = get_meta_items(attr_doc);
// Currently it's only possible to have a single meta item on
// an attribute
assert_eq!(meta_items.len(), 1u);
2014-09-15 05:27:36 +02:00
let meta_item = meta_items.into_iter().nth(0).unwrap();
attrs.push(
codemap::Spanned {
node: ast::Attribute_ {
2014-05-20 09:07:24 +02:00
id: attr::mk_attr_id(),
style: ast::AttrOuter,
value: meta_item,
is_sugared_doc: is_sugared_doc,
},
span: codemap::DUMMY_SP
});
true
});
2011-07-27 14:19:39 +02:00
}
2014-05-16 19:15:33 +02:00
None => ()
}
2012-08-02 02:30:05 +02:00
return attrs;
}
fn list_crate_attributes(md: rbml::Doc, hash: &Svh,
2014-01-30 03:42:19 +01:00
out: &mut io::Writer) -> io::IoResult<()> {
try!(write!(out, "=Crate Attributes ({})=\n", *hash));
let r = get_attributes(md);
for attr in r.iter() {
try!(write!(out, "{}\n", pprust::attribute_to_string(attr)));
}
2014-01-30 03:42:19 +01:00
write!(out, "\n\n")
}
pub fn get_crate_attributes(data: &[u8]) -> Vec<ast::Attribute> {
get_attributes(rbml::Doc::new(data))
}
2013-07-02 21:47:32 +02:00
#[deriving(Clone)]
pub struct CrateDep {
pub cnum: ast::CrateNum,
pub name: String,
pub hash: Svh,
}
2011-07-27 14:19:39 +02:00
pub fn get_crate_deps(data: &[u8]) -> Vec<CrateDep> {
let mut deps: Vec<CrateDep> = Vec::new();
let cratedoc = rbml::Doc::new(data);
let depsdoc = reader::get_doc(cratedoc, tag_crate_deps);
let mut crate_num = 1;
fn docstr(doc: rbml::Doc, tag_: uint) -> String {
let d = reader::get_doc(doc, tag_);
d.as_str_slice().to_string()
}
reader::tagged_docs(depsdoc, tag_crate_dep, |depdoc| {
let name = docstr(depdoc, tag_crate_dep_crate_name);
let hash = Svh::new(docstr(depdoc, tag_crate_dep_hash).as_slice());
deps.push(CrateDep {
cnum: crate_num,
name: name,
hash: hash,
});
crate_num += 1;
true
});
2012-08-02 02:30:05 +02:00
return deps;
}
2014-01-30 03:42:19 +01:00
fn list_crate_deps(data: &[u8], out: &mut io::Writer) -> io::IoResult<()> {
try!(write!(out, "=External Dependencies=\n"));
for dep in get_crate_deps(data).iter() {
try!(write!(out, "{} {}-{}\n", dep.cnum, dep.name, dep.hash));
}
try!(write!(out, "\n"));
2014-01-30 03:42:19 +01:00
Ok(())
}
pub fn maybe_get_crate_hash(data: &[u8]) -> Option<Svh> {
let cratedoc = rbml::Doc::new(data);
reader::maybe_get_doc(cratedoc, tag_crate_hash).map(|doc| {
Svh::new(doc.as_str_slice())
})
}
pub fn get_crate_hash(data: &[u8]) -> Svh {
let cratedoc = rbml::Doc::new(data);
let hashdoc = reader::get_doc(cratedoc, tag_crate_hash);
Svh::new(hashdoc.as_str_slice())
2011-12-11 16:23:38 +01:00
}
pub fn maybe_get_crate_name(data: &[u8]) -> Option<String> {
let cratedoc = rbml::Doc::new(data);
reader::maybe_get_doc(cratedoc, tag_crate_crate_name).map(|doc| {
doc.as_str_slice().to_string()
})
}
pub fn get_crate_triple(data: &[u8]) -> Option<String> {
let cratedoc = rbml::Doc::new(data);
let triple_doc = reader::maybe_get_doc(cratedoc, tag_crate_triple);
triple_doc.map(|s| s.as_str().to_string())
}
pub fn get_crate_name(data: &[u8]) -> String {
maybe_get_crate_name(data).expect("no crate name in crate")
}
pub fn list_crate_metadata(bytes: &[u8], out: &mut io::Writer) -> io::IoResult<()> {
2011-12-11 16:23:38 +01:00
let hash = get_crate_hash(bytes);
let md = rbml::Doc::new(bytes);
try!(list_crate_attributes(md, &hash, out));
2014-01-30 03:42:19 +01:00
list_crate_deps(bytes, out)
}
// Translates a def_id from an external crate to a def_id for the current
// compilation environment. We use this when trying to load types from
// external crates - if those types further refer to types in other crates
// then we must translate the crate number from that encoded in the external
// crate to the correct local crate number.
pub fn translate_def_id(cdata: Cmd, did: ast::DefId) -> ast::DefId {
if did.krate == ast::LOCAL_CRATE {
return ast::DefId { krate: cdata.cnum, node: did.node };
}
2014-04-22 18:06:43 +02:00
match cdata.cnum_map.find(&did.krate) {
2013-12-22 03:17:29 +01:00
Some(&n) => {
ast::DefId {
krate: n,
2013-12-22 03:17:29 +01:00
node: did.node,
}
}
None => panic!("didn't find a crate in the cnum_map")
}
}
pub fn each_impl(cdata: Cmd, callback: |ast::DefId|) {
let impls_doc = reader::get_doc(rbml::Doc::new(cdata.data()), tag_impls);
let _ = reader::tagged_docs(impls_doc, tag_impls_impl, |impl_doc| {
callback(item_def_id(impl_doc, cdata));
true
});
}
pub fn each_implementation_for_type(cdata: Cmd,
id: ast::NodeId,
callback: |ast::DefId|) {
let item_doc = lookup_item(id, cdata.data());
reader::tagged_docs(item_doc,
tag_items_data_item_inherent_impl,
|impl_doc| {
let implementation_def_id = item_def_id(impl_doc, cdata);
callback(implementation_def_id);
true
});
}
pub fn each_implementation_for_trait(cdata: Cmd,
id: ast::NodeId,
callback: |ast::DefId|) {
let item_doc = lookup_item(id, cdata.data());
let _ = reader::tagged_docs(item_doc,
tag_items_data_item_extension_impl,
|impl_doc| {
let implementation_def_id = item_def_id(impl_doc, cdata);
callback(implementation_def_id);
true
});
}
pub fn get_trait_of_item(cdata: Cmd, id: ast::NodeId, tcx: &ty::ctxt)
-> Option<ast::DefId> {
let item_doc = lookup_item(id, cdata.data());
let parent_item_id = match item_parent_item(item_doc) {
None => return None,
Some(item_id) => item_id,
};
let parent_item_id = translate_def_id(cdata, parent_item_id);
let parent_item_doc = lookup_item(parent_item_id.node, cdata.data());
match item_family(parent_item_doc) {
Trait => Some(item_def_id(parent_item_doc, cdata)),
Impl => {
reader::maybe_get_doc(parent_item_doc, tag_item_trait_ref)
.map(|_| item_trait_ref(parent_item_doc, tcx, cdata).def_id)
}
_ => None
}
}
Add generation of static libraries to rustc This commit implements the support necessary for generating both intermediate and result static rust libraries. This is an implementation of my thoughts in https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html. When compiling a library, we still retain the "lib" option, although now there are "rlib", "staticlib", and "dylib" as options for crate_type (and these are stackable). The idea of "lib" is to generate the "compiler default" instead of having too choose (although all are interchangeable). For now I have left the "complier default" to be a dynamic library for size reasons. Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a dynamic object. I chose this for size reasons, but also because you're probably not going to be embedding the rustc compiler anywhere any time soon. Other than the options outlined above, there are a few defaults/preferences that are now opinionated in the compiler: * If both a .dylib and .rlib are found for a rust library, the compiler will prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option * If generating a "lib", the compiler will generate a dynamic library. This is overridable by explicitly saying what flavor you'd like (rlib, staticlib, dylib). * If no options are passed to the command line, and no crate_type is found in the destination crate, then an executable is generated With this change, you can successfully build a rust program with 0 dynamic dependencies on rust libraries. There is still a dynamic dependency on librustrt, but I plan on removing that in a subsequent commit. This change includes no tests just yet. Our current testing infrastructure/harnesses aren't very amenable to doing flavorful things with linking, so I'm planning on adding a new mode of testing which I believe belongs as a separate commit. Closes #552
2013-11-15 23:03:29 +01:00
pub fn get_native_libraries(cdata: Cmd)
-> Vec<(cstore::NativeLibaryKind, String)> {
let libraries = reader::get_doc(rbml::Doc::new(cdata.data()),
tag_native_libraries);
let mut result = Vec::new();
2013-11-29 03:03:38 +01:00
reader::tagged_docs(libraries, tag_native_libraries_lib, |lib_doc| {
let kind_doc = reader::get_doc(lib_doc, tag_native_libraries_kind);
let name_doc = reader::get_doc(lib_doc, tag_native_libraries_name);
let kind: cstore::NativeLibaryKind =
FromPrimitive::from_u32(reader::doc_as_u32(kind_doc)).unwrap();
let name = name_doc.as_str().to_string();
result.push((kind, name));
Add generation of static libraries to rustc This commit implements the support necessary for generating both intermediate and result static rust libraries. This is an implementation of my thoughts in https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html. When compiling a library, we still retain the "lib" option, although now there are "rlib", "staticlib", and "dylib" as options for crate_type (and these are stackable). The idea of "lib" is to generate the "compiler default" instead of having too choose (although all are interchangeable). For now I have left the "complier default" to be a dynamic library for size reasons. Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a dynamic object. I chose this for size reasons, but also because you're probably not going to be embedding the rustc compiler anywhere any time soon. Other than the options outlined above, there are a few defaults/preferences that are now opinionated in the compiler: * If both a .dylib and .rlib are found for a rust library, the compiler will prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option * If generating a "lib", the compiler will generate a dynamic library. This is overridable by explicitly saying what flavor you'd like (rlib, staticlib, dylib). * If no options are passed to the command line, and no crate_type is found in the destination crate, then an executable is generated With this change, you can successfully build a rust program with 0 dynamic dependencies on rust libraries. There is still a dynamic dependency on librustrt, but I plan on removing that in a subsequent commit. This change includes no tests just yet. Our current testing infrastructure/harnesses aren't very amenable to doing flavorful things with linking, so I'm planning on adding a new mode of testing which I believe belongs as a separate commit. Closes #552
2013-11-15 23:03:29 +01:00
true
2013-11-29 03:03:38 +01:00
});
Add generation of static libraries to rustc This commit implements the support necessary for generating both intermediate and result static rust libraries. This is an implementation of my thoughts in https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html. When compiling a library, we still retain the "lib" option, although now there are "rlib", "staticlib", and "dylib" as options for crate_type (and these are stackable). The idea of "lib" is to generate the "compiler default" instead of having too choose (although all are interchangeable). For now I have left the "complier default" to be a dynamic library for size reasons. Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a dynamic object. I chose this for size reasons, but also because you're probably not going to be embedding the rustc compiler anywhere any time soon. Other than the options outlined above, there are a few defaults/preferences that are now opinionated in the compiler: * If both a .dylib and .rlib are found for a rust library, the compiler will prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option * If generating a "lib", the compiler will generate a dynamic library. This is overridable by explicitly saying what flavor you'd like (rlib, staticlib, dylib). * If no options are passed to the command line, and no crate_type is found in the destination crate, then an executable is generated With this change, you can successfully build a rust program with 0 dynamic dependencies on rust libraries. There is still a dynamic dependency on librustrt, but I plan on removing that in a subsequent commit. This change includes no tests just yet. Our current testing infrastructure/harnesses aren't very amenable to doing flavorful things with linking, so I'm planning on adding a new mode of testing which I believe belongs as a separate commit. Closes #552
2013-11-15 23:03:29 +01:00
return result;
}
2013-12-25 19:10:33 +01:00
pub fn get_plugin_registrar_fn(data: &[u8]) -> Option<ast::NodeId> {
reader::maybe_get_doc(rbml::Doc::new(data), tag_plugin_registrar_fn)
.map(|doc| FromPrimitive::from_u32(reader::doc_as_u32(doc)).unwrap())
2013-12-25 19:10:33 +01:00
}
pub fn get_exported_macros(data: &[u8]) -> Vec<String> {
let macros = reader::get_doc(rbml::Doc::new(data),
2013-12-25 19:10:33 +01:00
tag_exported_macros);
let mut result = Vec::new();
2013-12-25 19:10:33 +01:00
reader::tagged_docs(macros, tag_macro_def, |macro_doc| {
result.push(macro_doc.as_str().to_string());
2013-12-25 19:10:33 +01:00
true
});
result
}
pub fn get_dylib_dependency_formats(cdata: Cmd)
-> Vec<(ast::CrateNum, cstore::LinkagePreference)>
{
let formats = reader::get_doc(rbml::Doc::new(cdata.data()),
tag_dylib_dependency_formats);
let mut result = Vec::new();
debug!("found dylib deps: {}", formats.as_str_slice());
for spec in formats.as_str_slice().split(',') {
if spec.len() == 0 { continue }
let cnum = spec.split(':').nth(0).unwrap();
let link = spec.split(':').nth(1).unwrap();
let cnum = from_str(cnum).unwrap();
let cnum = match cdata.cnum_map.find(&cnum) {
Some(&n) => n,
None => panic!("didn't find a crate in the cnum_map")
};
result.push((cnum, if link == "d" {
cstore::RequireDynamic
} else {
cstore::RequireStatic
}));
}
return result;
}
rustc: Add official support for weak failure This commit is part of the ongoing libstd facade efforts (cc #13851). The compiler now recognizes some language items as "extern { fn foo(...); }" and will automatically perform the following actions: 1. The foreign function has a pre-defined name. 2. The crate and downstream crates can only be built as rlibs until a crate defines the lang item itself. 3. The actual lang item has a pre-defined name. This is essentially nicer compiler support for the hokey core-depends-on-std-failure scheme today, but it is implemented the same way. The details are a little more hidden under the covers. In addition to failure, this commit promotes the eh_personality and rust_stack_exhausted functions to official lang items. The compiler can generate calls to these functions, causing linkage errors if they are left undefined. The checking for these items is not as precise as it could be. Crates compiling with `-Z no-landing-pads` will not need the eh_personality lang item, and crates compiling with no split stacks won't need the stack exhausted lang item. For ease, however, these items are checked for presence in all final outputs of the compiler. It is quite easy to define dummy versions of the functions necessary: #[lang = "stack_exhausted"] extern fn stack_exhausted() { /* ... */ } #[lang = "eh_personality"] extern fn eh_personality() { /* ... */ } cc #11922, rust_stack_exhausted is now a lang item cc #13851, libcollections is blocked on eh_personality becoming weak
2014-05-19 18:30:09 +02:00
pub fn get_missing_lang_items(cdata: Cmd)
-> Vec<lang_items::LangItem>
{
let items = reader::get_doc(rbml::Doc::new(cdata.data()), tag_lang_items);
rustc: Add official support for weak failure This commit is part of the ongoing libstd facade efforts (cc #13851). The compiler now recognizes some language items as "extern { fn foo(...); }" and will automatically perform the following actions: 1. The foreign function has a pre-defined name. 2. The crate and downstream crates can only be built as rlibs until a crate defines the lang item itself. 3. The actual lang item has a pre-defined name. This is essentially nicer compiler support for the hokey core-depends-on-std-failure scheme today, but it is implemented the same way. The details are a little more hidden under the covers. In addition to failure, this commit promotes the eh_personality and rust_stack_exhausted functions to official lang items. The compiler can generate calls to these functions, causing linkage errors if they are left undefined. The checking for these items is not as precise as it could be. Crates compiling with `-Z no-landing-pads` will not need the eh_personality lang item, and crates compiling with no split stacks won't need the stack exhausted lang item. For ease, however, these items are checked for presence in all final outputs of the compiler. It is quite easy to define dummy versions of the functions necessary: #[lang = "stack_exhausted"] extern fn stack_exhausted() { /* ... */ } #[lang = "eh_personality"] extern fn eh_personality() { /* ... */ } cc #11922, rust_stack_exhausted is now a lang item cc #13851, libcollections is blocked on eh_personality becoming weak
2014-05-19 18:30:09 +02:00
let mut result = Vec::new();
2014-10-27 23:37:07 +01:00
reader::tagged_docs(items, tag_lang_items_missing, |missing_docs| {
rustc: Add official support for weak failure This commit is part of the ongoing libstd facade efforts (cc #13851). The compiler now recognizes some language items as "extern { fn foo(...); }" and will automatically perform the following actions: 1. The foreign function has a pre-defined name. 2. The crate and downstream crates can only be built as rlibs until a crate defines the lang item itself. 3. The actual lang item has a pre-defined name. This is essentially nicer compiler support for the hokey core-depends-on-std-failure scheme today, but it is implemented the same way. The details are a little more hidden under the covers. In addition to failure, this commit promotes the eh_personality and rust_stack_exhausted functions to official lang items. The compiler can generate calls to these functions, causing linkage errors if they are left undefined. The checking for these items is not as precise as it could be. Crates compiling with `-Z no-landing-pads` will not need the eh_personality lang item, and crates compiling with no split stacks won't need the stack exhausted lang item. For ease, however, these items are checked for presence in all final outputs of the compiler. It is quite easy to define dummy versions of the functions necessary: #[lang = "stack_exhausted"] extern fn stack_exhausted() { /* ... */ } #[lang = "eh_personality"] extern fn eh_personality() { /* ... */ } cc #11922, rust_stack_exhausted is now a lang item cc #13851, libcollections is blocked on eh_personality becoming weak
2014-05-19 18:30:09 +02:00
let item: lang_items::LangItem =
2014-10-27 23:37:07 +01:00
FromPrimitive::from_u32(reader::doc_as_u32(missing_docs)).unwrap();
rustc: Add official support for weak failure This commit is part of the ongoing libstd facade efforts (cc #13851). The compiler now recognizes some language items as "extern { fn foo(...); }" and will automatically perform the following actions: 1. The foreign function has a pre-defined name. 2. The crate and downstream crates can only be built as rlibs until a crate defines the lang item itself. 3. The actual lang item has a pre-defined name. This is essentially nicer compiler support for the hokey core-depends-on-std-failure scheme today, but it is implemented the same way. The details are a little more hidden under the covers. In addition to failure, this commit promotes the eh_personality and rust_stack_exhausted functions to official lang items. The compiler can generate calls to these functions, causing linkage errors if they are left undefined. The checking for these items is not as precise as it could be. Crates compiling with `-Z no-landing-pads` will not need the eh_personality lang item, and crates compiling with no split stacks won't need the stack exhausted lang item. For ease, however, these items are checked for presence in all final outputs of the compiler. It is quite easy to define dummy versions of the functions necessary: #[lang = "stack_exhausted"] extern fn stack_exhausted() { /* ... */ } #[lang = "eh_personality"] extern fn eh_personality() { /* ... */ } cc #11922, rust_stack_exhausted is now a lang item cc #13851, libcollections is blocked on eh_personality becoming weak
2014-05-19 18:30:09 +02:00
result.push(item);
true
});
return result;
}
pub fn get_method_arg_names(cdata: Cmd, id: ast::NodeId) -> Vec<String> {
let mut ret = Vec::new();
let method_doc = lookup_item(id, cdata.data());
match reader::maybe_get_doc(method_doc, tag_method_argument_names) {
Some(args_doc) => {
reader::tagged_docs(args_doc, tag_method_argument_name, |name_doc| {
ret.push(name_doc.as_str_slice().to_string());
true
});
}
None => {}
}
return ret;
}
pub fn get_reachable_extern_fns(cdata: Cmd) -> Vec<ast::DefId> {
let mut ret = Vec::new();
let items = reader::get_doc(rbml::Doc::new(cdata.data()),
tag_reachable_extern_fns);
reader::tagged_docs(items, tag_reachable_extern_fn_id, |doc| {
ret.push(ast::DefId {
krate: cdata.cnum,
node: reader::doc_as_u32(doc),
});
true
});
return ret;
}
pub fn is_typedef(cdata: Cmd, id: ast::NodeId) -> bool {
let item_doc = lookup_item(id, cdata.data());
match item_family(item_doc) {
Type => true,
_ => false,
}
}
fn doc_generics(base_doc: rbml::Doc,
tcx: &ty::ctxt,
cdata: Cmd,
tag: uint)
-> ty::Generics
{
let doc = reader::get_doc(base_doc, tag);
let mut types = subst::VecPerParamSpace::empty();
reader::tagged_docs(doc, tag_type_param_def, |p| {
let bd = parse_type_param_def_data(
p.data, p.start, cdata.cnum, tcx,
|_, did| translate_def_id(cdata, did));
types.push(bd.space, bd);
true
});
let mut regions = subst::VecPerParamSpace::empty();
reader::tagged_docs(doc, tag_region_param_def, |rp_doc| {
let ident_str_doc = reader::get_doc(rp_doc,
tag_region_param_def_ident);
let name = item_name(&*token::get_ident_interner(), ident_str_doc);
let def_id_doc = reader::get_doc(rp_doc,
tag_region_param_def_def_id);
let def_id = reader::with_doc_data(def_id_doc, parse_def_id);
let def_id = translate_def_id(cdata, def_id);
let doc = reader::get_doc(rp_doc, tag_region_param_def_space);
let space = subst::ParamSpace::from_uint(reader::doc_as_u64(doc) as uint);
let doc = reader::get_doc(rp_doc, tag_region_param_def_index);
let index = reader::doc_as_u64(doc) as uint;
let mut bounds = Vec::new();
reader::tagged_docs(rp_doc, tag_items_data_region, |p| {
bounds.push(
parse_region_data(
p.data, cdata.cnum, p.start, tcx,
|_, did| translate_def_id(cdata, did)));
true
});
regions.push(space, ty::RegionParameterDef { name: name,
def_id: def_id,
space: space,
index: index,
bounds: bounds });
true
});
ty::Generics { types: types, regions: regions }
}
pub fn is_associated_type(cdata: Cmd, id: ast::NodeId) -> bool {
let items = reader::get_doc(rbml::Doc::new(cdata.data()), tag_items);
match maybe_find_item(id, items) {
None => false,
Some(item) => item_sort(item) == 't',
}
}