From 2bfed908e39f162532be56d9680295bb383ddcf2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 7 Mar 2012 15:13:31 -0800 Subject: [PATCH] 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. --- src/rustc/metadata/astencode.rs | 63 +++++++++++++++++++++++++++- src/rustc/middle/trans/base.rs | 23 ++++++++-- src/rustc/syntax/fold.rs | 1 + src/test/auxiliary/cci_nested_lib.rs | 29 +++++++++++++ src/test/run-pass/cci_nested_exe.rs | 21 ++++++++++ 5 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 src/test/auxiliary/cci_nested_lib.rs create mode 100644 src/test/run-pass/cci_nested_exe.rs diff --git a/src/rustc/metadata/astencode.rs b/src/rustc/metadata/astencode.rs index d3d8593b654c..d7215fbfcab3 100644 --- a/src/rustc/metadata/astencode.rs +++ b/src/rustc/metadata/astencode.rs @@ -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() -> alist { + 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() -> alist { + 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; } + } +} \ No newline at end of file diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index b6703a8fe83e..910c6c93ac59 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -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 { diff --git a/src/rustc/syntax/fold.rs b/src/rustc/syntax/fold.rs index d8088e39cb6d..42ac5ca7da62 100644 --- a/src/rustc/syntax/fold.rs +++ b/src/rustc/syntax/fold.rs @@ -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; diff --git a/src/test/auxiliary/cci_nested_lib.rs b/src/test/auxiliary/cci_nested_lib.rs new file mode 100644 index 000000000000..9e3169119bcd --- /dev/null +++ b/src/test/auxiliary/cci_nested_lib.rs @@ -0,0 +1,29 @@ +type alist = { eq_fn: fn@(A,A) -> bool, mut data: [(A,B)] }; + +fn alist_add(lst: alist, k: A, v: B) { + lst.data += [(k, v)]; +} + +fn alist_get(lst: alist, 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() -> alist { + fn eq_int(&&a: int, &&b: int) -> bool { a == b } + ret {eq_fn: eq_int, + mut data: []}; +} + +#[inline] +fn new_int_alist_2() -> alist { + #[inline] + fn eq_int(&&a: int, &&b: int) -> bool { a == b } + ret {eq_fn: eq_int, + mut data: []}; +} \ No newline at end of file diff --git a/src/test/run-pass/cci_nested_exe.rs b/src/test/run-pass/cci_nested_exe.rs new file mode 100644 index 000000000000..c258d2d15b67 --- /dev/null +++ b/src/test/run-pass/cci_nested_exe.rs @@ -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"; +}