Fix #1941: inlining of items that themselves contain nested items

The fix is to drop nested items from the encoded AST.  Nested items may
themselves be inlined, but that is an independent question.
This commit is contained in:
Niko Matsakis 2012-03-07 15:13:31 -08:00
parent c9375fed8d
commit 2bfed908e3
5 changed files with 132 additions and 5 deletions

View File

@ -63,7 +63,7 @@ fn encode_inlined_item(ecx: @e::encode_ctxt,
let id_range = compute_id_range(ii);
ebml_w.wr_tag(c::tag_ast as uint) {||
encode_id_range(ebml_w, id_range);
encode_ast(ebml_w, ii);
encode_ast(ebml_w, simplify_ast(ii));
encode_side_tables_for_ii(ecx, ebml_w, ii);
}
@ -326,6 +326,44 @@ fn encode_ast(ebml_w: ebml::writer, item: ast::inlined_item) {
}
}
// Produces a simplified copy of the AST which does not include things
// that we do not need to or do not want to export. For example, we
// do not include any nested items: if these nested items are to be
// inlined, their AST will be exported separately (this only makes
// sense because, in Rust, nested items are independent except for
// their visibility).
//
// As it happens, trans relies on the fact that we do not export
// nested items, as otherwise it would get confused when translating
// inlined items.
fn simplify_ast(ii: ast::inlined_item) -> ast::inlined_item {
fn drop_nested_items(blk: ast::blk_, fld: fold::ast_fold) -> ast::blk_ {
let stmts_sans_items = vec::filter(blk.stmts) {|stmt|
alt stmt.node {
ast::stmt_expr(_, _) | ast::stmt_semi(_, _) |
ast::stmt_decl(@{node: ast::decl_local(_), span: _}, _) { true }
ast::stmt_decl(@{node: ast::decl_item(_), span: _}, _) { false }
}
};
let blk_sans_items = { stmts: stmts_sans_items with blk };
fold::noop_fold_block(blk_sans_items, fld)
}
let fld = fold::make_fold({
fold_block: fold::wrap(drop_nested_items)
with *fold::default_ast_fold()
});
alt ii {
ast::ii_item(i) {
ast::ii_item(fld.fold_item(i))
}
ast::ii_method(d, m) {
ast::ii_method(d, fld.fold_method(m))
}
}
}
fn decode_ast(par_doc: ebml::doc) -> ast::inlined_item {
let chi_doc = par_doc[c::tag_tree];
let d = serialization::mk_ebml_deserializer(chi_doc);
@ -923,3 +961,26 @@ fn test_more() {
}
});
}
#[test]
fn test_simplification() {
let ext_cx = mk_ctxt();
let item_in = ast::ii_item(#ast(item) {
fn new_int_alist<B: copy>() -> alist<int, B> {
fn eq_int(&&a: int, &&b: int) -> bool { a == b }
ret {eq_fn: eq_int, mut data: []};
}
});
let item_out = simplify_ast(item_in);
let item_exp = ast::ii_item(#ast(item) {
fn new_int_alist<B: copy>() -> alist<int, B> {
ret {eq_fn: eq_int, mut data: []};
}
});
alt (item_out, item_exp) {
(ast::ii_item(item_out), ast::ii_item(item_exp)) {
assert pprust::item_to_str(item_out) == pprust::item_to_str(item_exp);
}
_ { fail; }
}
}

View File

@ -2146,18 +2146,31 @@ fn monomorphic_fn(ccx: crate_ctxt, fn_id: ast::def_id, substs: [ty::t],
fn maybe_instantiate_inline(ccx: crate_ctxt, fn_id: ast::def_id)
-> ast::def_id {
alt ccx.external.find(fn_id) {
some(some(node_id)) { local_def(node_id) } // Already inline
some(some(node_id)) {
// Already inline
#debug["maybe_instantiate_inline(%s): already inline as node id %d",
ty::item_path_str(ccx.tcx, fn_id), node_id];
local_def(node_id)
}
some(none) { fn_id } // Not inlinable
none { // Not seen yet
alt csearch::maybe_get_item_ast(ccx.tcx, ccx.maps, fn_id) {
none { ccx.external.insert(fn_id, none); fn_id }
some(ast::ii_item(item)) {
#debug["maybe_instantiate_inline(%s): inlining to local id %d",
ty::item_path_str(ccx.tcx, fn_id),
item.id];
ccx.external.insert(fn_id, some(item.id));
collect_item(ccx, @mutable none, item);
trans_item(ccx, *item);
local_def(item.id)
}
some(ast::ii_method(impl_did, mth)) {
#debug["maybe_instantiate_inline(%s): \
inlining method of %s to %d",
ty::item_path_str(ccx.tcx, fn_id),
ty::item_path_str(ccx.tcx, impl_did),
mth.id];
ccx.external.insert(fn_id, some(mth.id));
compute_ii_method_info(ccx, impl_did, mth) {|ty, bounds, path|
let mth_ty = ty::node_id_to_type(ccx.tcx, mth.id);
@ -3586,7 +3599,7 @@ fn zero_alloca(cx: block, llptr: ValueRef, t: ty::t)
}
fn trans_stmt(cx: block, s: ast::stmt) -> block {
#debug["trans_expr(%s)", stmt_to_str(s)];
#debug["trans_stmt(%s)", stmt_to_str(s)];
if (!cx.sess().opts.no_asm_comments) {
add_span_comment(cx, s.span, stmt_to_str(s));
@ -4330,8 +4343,10 @@ fn trans_item(ccx: crate_ctxt, item: ast::item) {
let llfndecl = alt ccx.item_ids.find(item.id) {
some(llfndecl) { llfndecl }
_ {
ccx.sess.span_bug(item.span,
"unbound function item in trans_item");
ccx.sess.span_bug(
item.span,
#fmt["unbound function item %s in trans_item",
ast_map::path_to_str(*path)]);
}
};
if decl.purity != ast::crust_fn {

View File

@ -11,6 +11,7 @@ export noop_fold_expr;
export noop_fold_pat;
export noop_fold_mod;
export noop_fold_ty;
export noop_fold_block;
export wrap;
type ast_fold = @mutable a_f;

View File

@ -0,0 +1,29 @@
type alist<A,B> = { eq_fn: fn@(A,A) -> bool, mut data: [(A,B)] };
fn alist_add<A: copy, B: copy>(lst: alist<A,B>, k: A, v: B) {
lst.data += [(k, v)];
}
fn alist_get<A: copy, B: copy>(lst: alist<A,B>, k: A) -> B {
let eq_fn = lst.eq_fn;
for pair in lst.data {
let (ki, vi) = pair; // copy req'd for alias analysis
if eq_fn(k, ki) { ret vi; }
}
fail;
}
#[inline]
fn new_int_alist<B: copy>() -> alist<int, B> {
fn eq_int(&&a: int, &&b: int) -> bool { a == b }
ret {eq_fn: eq_int,
mut data: []};
}
#[inline]
fn new_int_alist_2<B: copy>() -> alist<int, B> {
#[inline]
fn eq_int(&&a: int, &&b: int) -> bool { a == b }
ret {eq_fn: eq_int,
mut data: []};
}

View File

@ -0,0 +1,21 @@
// xfail-fast - check-fast doesn't understand aux-build
// aux-build:cci_nested_lib.rs
use std;
use cci_nested_lib;
import std::io;
import cci_nested_lib::*;
fn main() {
let lst = new_int_alist();
alist_add(lst, 22, "hi");
alist_add(lst, 44, "ho");
assert alist_get(lst, 22) == "hi";
assert alist_get(lst, 44) == "ho";
let lst = new_int_alist_2();
alist_add(lst, 22, "hi");
alist_add(lst, 44, "ho");
assert alist_get(lst, 22) == "hi";
assert alist_get(lst, 44) == "ho";
}