Rework how linkage attributes are determined

The meta items within a crate's link attribute are used in linkage:

 #[link(name = "std",
        vers = "1.0",
        custom = "whatever")];

Name and vers are treated specially, and everything else is hashed together
into the crate meta hash.

Issue #487
This commit is contained in:
Brian Anderson 2011-06-28 12:53:59 -07:00
parent 0608e277b6
commit a83b64d15d
7 changed files with 321 additions and 128 deletions

View File

@ -1,6 +1,7 @@
import driver::session;
import lib::llvm::llvm;
import middle::attr;
import middle::trans;
import middle::ty;
import std::str;
@ -274,57 +275,38 @@ fn run_passes(session::session sess, ModuleRef llmod, str output) {
* system linkers understand.
*
*/
iter crate_export_metas(&ast::crate c) -> @ast::meta_item {
// FIXME: Need to identify exported attributes as described above,
// reevaluate how the above strategy fits in with attributes
for (ast::attribute attr in c.node.attrs) {
put @attr.node.value;
}
}
type link_metas = rec(option::t[str] name,
option::t[str] vers,
vec[@ast::meta_item] cmh_items);
iter crate_local_metas(&ast::crate c) -> @ast::meta_item {
// FIXME: As above
}
fn get_crate_meta_export(&session::session sess, &ast::crate c, str k,
str default, bool warn_default) -> str {
let vec[@ast::meta_item] v = [];
for each (@ast::meta_item mi in crate_export_metas(c)) {
// FIXME (#487): Support all variants of meta_item
alt (mi.node) {
case (ast::meta_name_value(?name, ?value)) {
if (name == k) { v += [mi]; }
fn crate_link_metas(&ast::crate c) -> link_metas {
let option::t[str] name = none;
let option::t[str] vers = none;
let vec[@ast::meta_item] cmh_items = [];
for (@ast::meta_item meta in
attr::find_linkage_metas(c.node.attrs)) {
alt (meta.node) {
case (ast::meta_name_value("name", ?v)) {
// FIXME: Should probably warn about duplicate name items
name = some(v);
}
case (_) {}
}
}
alt (vec::len(v)) {
case (0u) {
if (warn_default) {
sess.warn(#fmt("missing meta '%s', using '%s' as default", k,
default));
case (ast::meta_name_value("value", ?v)) {
// FIXME: Should probably warn about duplicate value items
vers = some(v);
}
ret default;
}
case (1u) {
alt (v.(0).node) {
case (ast::meta_name_value(_, ?value)) {
ret value;
}
case (_) {
ret default;
}
case (_) {
cmh_items += [meta];
}
}
case (_) {
sess.span_fatal(v.(1).span, #fmt("duplicate meta '%s'", k));
}
}
ret rec(name = name,
vers = vers,
cmh_items = cmh_items);
}
// This calculates CMH as defined above
fn crate_meta_extras_hash(sha1 sha, &ast::crate crate) -> str {
// FIXME (#487) Move this sorting stuff into middle::attr
fn lteq(&@ast::meta_item ma, &@ast::meta_item mb) -> bool {
fn key(&@ast::meta_item m) -> ast::ident {
alt (m.node) {
@ -342,18 +324,10 @@ fn key(&@ast::meta_item m) -> ast::ident {
ret key(ma) <= key(mb);
}
fn len_and_str(&str s) -> str { ret #fmt("%u_%s", str::byte_len(s), s); }
let vec[mutable @ast::meta_item] v = [mutable ];
for each (@ast::meta_item mi in crate_export_metas(crate)) {
alt (mi.node) {
case (ast::meta_name_value(?name, _)) {
if (name != "name" && name != "vers") {
v += [mutable mi];
}
}
case (_) {
v += [mutable mi];
}
}
for (@ast::meta_item mi in crate_link_metas(crate).cmh_items) {
v += [mutable mi];
}
sort::quick_sort(lteq, v);
sha.reset();
@ -367,24 +341,32 @@ fn key(&@ast::meta_item m) -> ast::ident {
case (ast::meta_word(?name)) {
sha.input_str(len_and_str(name));
}
case (_) {}
case (ast::meta_list(_, _)) {
fail "unimplemented meta_item variant";
}
}
}
ret truncated_sha1_result(sha);
}
fn crate_meta_name(&session::session sess, &ast::crate crate, &str output) ->
str {
auto os = str::split(fs::basename(output), '.' as u8);
assert (vec::len(os) >= 2u);
vec::pop(os);
ret get_crate_meta_export(sess, crate, "name", str::connect(os, "."),
sess.get_opts().shared);
fn crate_meta_name(&session::session sess, &ast::crate crate,
&str output) -> str {
ret alt (crate_link_metas(crate).name) {
case (some(?v)) { v }
case (none) {
auto os = str::split(fs::basename(output), '.' as u8);
assert (vec::len(os) >= 2u);
vec::pop(os);
str::connect(os, ".")
}
};
}
fn crate_meta_vers(&session::session sess, &ast::crate crate) -> str {
ret get_crate_meta_export(sess, crate, "vers", "0.0",
sess.get_opts().shared);
ret alt (crate_link_metas(crate).vers) {
case (some(?v)) { v }
case (none) { "0.0" }
};
}
fn truncated_sha1_result(sha1 sha) -> str {

View File

@ -6,6 +6,7 @@
import lib::llvm::llvm;
import lib::llvm::mk_object_file;
import lib::llvm::mk_section_iter;
import middle::attr;
import middle::resolve;
import middle::walk;
import back::x86;
@ -19,42 +20,24 @@
import std::option::none;
import std::option::some;
import std::map::hashmap;
import pretty::pprust;
import tags::*;
export read_crates;
export list_file_metadata;
fn metadata_matches(hashmap[str, str] mm, &vec[@ast::meta_item] metas) ->
bool {
log #fmt("matching %u metadata requirements against %u metadata items",
vec::len(metas), mm.size());
for (@ast::meta_item mi in metas) {
alt (mi.node) {
case (ast::meta_name_value(?name, ?value)) {
alt (mm.find(name)) {
case (some(?v)) {
if (v == value) {
log #fmt("matched '%s': '%s'", name,
value);
} else {
log #fmt("missing '%s': '%s' (got '%s')",
name,
value, v);
ret false;
}
}
case (none) {
log #fmt("missing '%s': '%s'",
name, value);
ret false;
}
}
}
case (_) {
// FIXME (#487): Support all forms of meta_item
log_err "unimplemented meta_item variant in metadata_matches";
ret false;
}
fn metadata_matches(&vec[u8] crate_data,
&vec[@ast::meta_item] metas) -> bool {
auto attrs = decoder::get_crate_attributes(crate_data);
auto linkage_metas = attr::find_linkage_metas(attrs);
log #fmt("matching %u metadata requirements against %u items",
vec::len(metas), vec::len(linkage_metas));
for (@ast::meta_item needed in metas) {
if (!attr::contains(linkage_metas, needed)) {
log #fmt("missing %s", pprust::meta_item_to_str(*needed));
ret false;
}
}
ret true;
@ -106,9 +89,7 @@ fn find_library_crate(&session::session sess, &ast::ident ident,
}
alt (get_metadata_section(path)) {
case (option::some(?cvec)) {
auto mm = decoder::get_exported_metadata(sess,
path, cvec);
if (!metadata_matches(mm, metas)) {
if (!metadata_matches(cvec, metas)) {
log #fmt("skipping %s, metadata doesn't match", path);
cont;
}

View File

@ -20,6 +20,7 @@
export get_type;
export lookup_defs;
export get_type;
export get_crate_attributes;
export list_crate_metadata;
export get_exported_metadata;
@ -294,7 +295,6 @@ fn get_attributes(&ebml::doc md) -> vec[ast::attribute] {
auto meta_items = get_meta_items(attr_doc);
// Currently it's only possible to have a single meta item on
// an attribute
log_err vec::len(meta_items);
assert (vec::len(meta_items) == 1u);
auto meta_item = meta_items.(0);
attrs += [rec(node=rec(style=ast::attr_outer,
@ -316,7 +316,7 @@ fn list_meta_items(&ebml::doc meta_items, io::writer out) {
fn list_crate_attributes(&ebml::doc md, io::writer out) {
out.write_str("=Crate=");
// FIXME: This is transitional until attributes are snapshotted
// FIXME (#487): This is transitional until attributes are snapshotted
out.write_str("old-style:\n");
auto meta_items = ebml::get_doc(md, tag_meta_export);
list_meta_items(meta_items, out);
@ -359,25 +359,6 @@ fn list_crate_metadata(vec[u8] bytes, io::writer out) {
list_crate_items(bytes, md, out);
}
fn get_exported_metadata(&session::session sess, &str path, &vec[u8] data) ->
hashmap[str, str] {
auto meta_items =
ebml::get_doc(ebml::new_doc(data), tag_meta_export);
auto mm = common::new_str_hash[str]();
for each (ebml::doc m in
ebml::tagged_docs(meta_items, tag_meta_item_name_value)) {
auto nd = ebml::get_doc(m, tag_meta_item_name);
auto vd = ebml::get_doc(m, tag_meta_item_value);
auto n = str::unsafe_from_bytes(ebml::doc_data(nd));
auto v = str::unsafe_from_bytes(ebml::doc_data(vd));
log #fmt("metadata in %s: %s = %s", path, n, v);
if (!mm.insert(n, v)) {
sess.warn(#fmt("Duplicate metadata item in %s: %s", path, n));
}
}
ret mm;
}
// Local Variables:
// mode: rust
// fill-column: 78;

View File

@ -13,8 +13,6 @@
import middle::trans::crate_ctxt;
import middle::trans::node_id_type;
import middle::ty;
import back::link::crate_export_metas;
import back::link::crate_local_metas;
export def_to_str;
export hash_path;
@ -459,16 +457,20 @@ fn encode_attributes(&ebml::writer ebml_w, &vec[attribute] attrs) {
ebml::end_tag(ebml_w);
}
// FIXME (#487): Transitional
fn encode_meta_items(&ebml::writer ebml_w, &crate crate) {
auto name = middle::attr::find_attrs_by_name(crate.node.attrs,
"name");
auto value = middle::attr::find_attrs_by_name(crate.node.attrs,
"value");
auto name_and_val = middle::attr::attr_metas(name + value);
ebml::start_tag(ebml_w, tag_meta_export);
for each (@meta_item mi in crate_export_metas(crate)) {
for (@meta_item mi in name_and_val) {
encode_meta_item(ebml_w, *mi);
}
ebml::end_tag(ebml_w);
ebml::start_tag(ebml_w, tag_meta_local);
for each (@meta_item mi in crate_local_metas(crate)) {
encode_meta_item(ebml_w, *mi);
}
ebml::end_tag(ebml_w);
}

View File

@ -2,8 +2,10 @@
import std::option;
import front::ast;
export get_linkage_metas;
export attr_metas;
export find_linkage_metas;
export find_attrs_by_name;
export contains;
// From a list of crate attributes get only the meta_items that impact crate
// linkage
@ -62,6 +64,42 @@ fn get_meta_item_name(&@ast::meta_item meta) -> ast::ident {
}
}
fn attr_meta(&ast::attribute attr) -> @ast::meta_item { @attr.node.value }
fn attr_metas(&vec[ast::attribute] attrs) -> vec[@ast::meta_item] {
ret vec::map(attr_meta, attrs);
}
fn eq(@ast::meta_item a, @ast::meta_item b) -> bool {
ret alt (a.node) {
case (ast::meta_word(?na)) {
alt (b.node) {
case(ast::meta_word(?nb)) { na == nb }
case(_) { false }
}
}
case (ast::meta_name_value(?na, ?va)) {
alt (b.node) {
case (ast::meta_name_value(?nb, ?vb)) { na == nb && va == vb }
case (_) { false }
}
}
case (ast::meta_list(?na, ?la)) {
// FIXME (#487): This involves probably sorting the list by name
fail "unimplemented meta_item variant"
}
}
}
fn contains(&vec[@ast::meta_item] haystack, @ast::meta_item needle) -> bool {
for (@ast::meta_item item in haystack) {
if (eq(item, needle)) {
ret true;
}
}
ret false;
}
//
// Local Variables:
// mode: rust

View File

@ -1,7 +1,9 @@
#[name = "std"];
#[vers = "0.1"];
#[uuid = "122bed0b-c19b-4b82-b0b7-7ae8aead7297"];
#[url = "http://rust-lang.org/src/std"];
#[link(name = "std",
vers = "0.1",
uuid = "122bed0b-c19b-4b82-b0b7-7ae8aead7297",
url = "http://rust-lang.org/src/std")];
#[comment = "Rust standard library"];
#[license = "BSD"];

View File

@ -0,0 +1,207 @@
// xfail-stage0
// These are attributes of the implicit crate. Really this just needs to parse
// for completeness since .rs files linked from .rc files support this
// notation to specify their module's attributes
#[attr1 = "val"];
#[attr2 = "val"];
#[attr3];
#[attr4(attr5)];
// Special linkage attributes for the crate
#[link(name = "std",
vers = "0.1",
uuid = "122bed0b-c19b-4b82-b0b7-7ae8aead7297",
url = "http://rust-lang.org/src/std")];
// These are are attributes of the following mod
#[attr1 = "val"]
#[attr2 = "val"]
mod test_first_item_in_file_mod {
}
mod test_single_attr_outer {
#[attr = "val"]
const int x = 10;
#[attr = "val"]
fn f() {}
#[attr = "val"]
mod mod1 {
}
#[attr = "val"]
native "rust" mod rustrt { }
#[attr = "val"]
type t = obj { };
#[attr = "val"]
obj o() { }
}
mod test_multi_attr_outer {
#[attr1 = "val"]
#[attr2 = "val"]
const int x = 10;
#[attr1 = "val"]
#[attr2 = "val"]
fn f() {}
#[attr1 = "val"]
#[attr2 = "val"]
mod mod1 {
}
#[attr1 = "val"]
#[attr2 = "val"]
native "rust" mod rustrt { }
#[attr1 = "val"]
#[attr2 = "val"]
type t = obj { };
#[attr1 = "val"]
#[attr2 = "val"]
obj o() { }
}
mod test_stmt_single_attr_outer {
fn f() {
#[attr = "val"]
const int x = 10;
#[attr = "val"]
fn f() {}
/* FIXME: Issue #493
#[attr = "val"]
mod mod1 {
}
#[attr = "val"]
native "rust" mod rustrt {
}
*/
#[attr = "val"]
type t = obj { };
#[attr = "val"]
obj o() { }
}
}
mod test_stmt_multi_attr_outer {
fn f() {
#[attr1 = "val"]
#[attr2 = "val"]
const int x = 10;
#[attr1 = "val"]
#[attr2 = "val"]
fn f() {}
/* FIXME: Issue #493
#[attr1 = "val"]
#[attr2 = "val"]
mod mod1 {
}
#[attr1 = "val"]
#[attr2 = "val"]
native "rust" mod rustrt {
}
*/
#[attr1 = "val"]
#[attr2 = "val"]
type t = obj { };
#[attr1 = "val"]
#[attr2 = "val"]
obj o() { }
}
}
mod test_attr_inner {
mod m {
// This is an attribute of mod m
#[attr = "val"];
}
}
mod test_attr_inner_then_outer {
mod m {
// This is an attribute of mod m
#[attr = "val"];
// This is an attribute of fn f
#[attr = "val"]
fn f() {
}
}
}
mod test_attr_inner_then_outer_multi {
mod m {
// This is an attribute of mod m
#[attr1 = "val"];
#[attr2 = "val"];
// This is an attribute of fn f
#[attr1 = "val"]
#[attr2 = "val"]
fn f() {
}
}
}
mod test_distinguish_syntax_ext {
use std;
fn f() {
#fmt("test%s", "s");
#[attr = "val"]
fn g() {
}
}
}
mod test_other_forms {
#[attr]
#[attr(word)]
#[attr(attr(word))]
#[attr(key1 = "val",
key2 = "val",
attr)]
fn f() {
}
}
fn main() {
}
//
// 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:
//