From a83b64d15dd8e4ad175fec976772a624437eb9b1 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 28 Jun 2011 12:53:59 -0700 Subject: [PATCH] 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 --- src/comp/back/link.rs | 106 ++++++-------- src/comp/metadata/creader.rs | 49 ++----- src/comp/metadata/decoder.rs | 23 +-- src/comp/metadata/encoder.rs | 14 +- src/comp/middle/attr.rs | 40 +++++- src/lib/std.rc | 10 +- src/test/run-pass/item-attributes.rs | 207 +++++++++++++++++++++++++++ 7 files changed, 321 insertions(+), 128 deletions(-) create mode 100644 src/test/run-pass/item-attributes.rs diff --git a/src/comp/back/link.rs b/src/comp/back/link.rs index 25c8b8b17d8..1c1bdee8747 100644 --- a/src/comp/back/link.rs +++ b/src/comp/back/link.rs @@ -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 @@ mod write { * 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 crate_meta_extras_hash(sha1 sha, &ast::crate crate) -> str { 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 crate_meta_extras_hash(sha1 sha, &ast::crate crate) -> str { 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 { diff --git a/src/comp/metadata/creader.rs b/src/comp/metadata/creader.rs index c2899e17953..6c23a7d0568 100644 --- a/src/comp/metadata/creader.rs +++ b/src/comp/metadata/creader.rs @@ -6,6 +6,7 @@ import lib::llvm::False; 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; 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; } diff --git a/src/comp/metadata/decoder.rs b/src/comp/metadata/decoder.rs index b7751fc2303..9d0efd21337 100644 --- a/src/comp/metadata/decoder.rs +++ b/src/comp/metadata/decoder.rs @@ -20,6 +20,7 @@ export get_tag_variants; 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; diff --git a/src/comp/metadata/encoder.rs b/src/comp/metadata/encoder.rs index 27a36e82675..63d56a04a72 100644 --- a/src/comp/metadata/encoder.rs +++ b/src/comp/metadata/encoder.rs @@ -13,8 +13,6 @@ import tags::*; 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); } diff --git a/src/comp/middle/attr.rs b/src/comp/middle/attr.rs index 45b0ccb7026..d255291c1d4 100644 --- a/src/comp/middle/attr.rs +++ b/src/comp/middle/attr.rs @@ -2,8 +2,10 @@ import std::vec; 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 diff --git a/src/lib/std.rc b/src/lib/std.rc index 29de0f0ed97..c4687e6a000 100644 --- a/src/lib/std.rc +++ b/src/lib/std.rc @@ -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"]; diff --git a/src/test/run-pass/item-attributes.rs b/src/test/run-pass/item-attributes.rs new file mode 100644 index 00000000000..cf8fc4098f2 --- /dev/null +++ b/src/test/run-pass/item-attributes.rs @@ -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: +//