From 2c8c50d6cb1d7cce0f597dd60daf2a5109b9c0de Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke <marijnh@gmail.com>
Date: Thu, 8 Mar 2012 23:13:57 +0100
Subject: [PATCH] Make sure enum and resource constructors are inlined properly

---
 src/rustc/metadata/astencode.rs |  1 +
 src/rustc/metadata/common.rs    |  4 ++-
 src/rustc/metadata/csearch.rs   | 10 +++++--
 src/rustc/metadata/decoder.rs   | 31 ++++++++++++++++----
 src/rustc/metadata/encoder.rs   | 17 ++++-------
 src/rustc/middle/trans/base.rs  | 52 +++++++++++++++++----------------
 6 files changed, 70 insertions(+), 45 deletions(-)

diff --git a/src/rustc/metadata/astencode.rs b/src/rustc/metadata/astencode.rs
index 3d54ffe3944..ee0afc22fb4 100644
--- a/src/rustc/metadata/astencode.rs
+++ b/src/rustc/metadata/astencode.rs
@@ -137,6 +137,7 @@ fn visit_ids(item: ast::inlined_item, vfn: fn@(ast::node_id)) {
             vfn(i.id);
             alt i.node {
               ast::item_res(_, _, _, d_id, c_id) { vfn(d_id); vfn(c_id); }
+              ast::item_enum(vs, _) { for v in vs { vfn(v.node.id); } }
               _ {}
             }
         },
diff --git a/src/rustc/metadata/common.rs b/src/rustc/metadata/common.rs
index 3d3a3fff48f..db3dc4078ed 100644
--- a/src/rustc/metadata/common.rs
+++ b/src/rustc/metadata/common.rs
@@ -28,7 +28,7 @@ const tag_items_data_item_symbol: uint = 0x0du;
 
 const tag_items_data_item_variant: uint = 0x0eu;
 
-const tag_items_data_item_enum_id: uint = 0x0fu;
+const tag_items_data_parent_item: uint = 0x0fu;
 
 const tag_index: uint = 0x11u;
 
@@ -62,6 +62,8 @@ const tag_crate_dep: uint = 0x26u;
 
 const tag_crate_hash: uint = 0x28u;
 
+const tag_parent_item: uint = 0x29u;
+
 const tag_mod_impl: uint = 0x30u;
 
 const tag_item_method: uint = 0x31u;
diff --git a/src/rustc/metadata/csearch.rs b/src/rustc/metadata/csearch.rs
index 6a336da1e52..6be4124d5e6 100644
--- a/src/rustc/metadata/csearch.rs
+++ b/src/rustc/metadata/csearch.rs
@@ -19,7 +19,7 @@ export get_type;
 export get_impl_iface;
 export get_impl_method;
 export get_item_path;
-export maybe_get_item_ast;
+export maybe_get_item_ast, found_ast, found, found_parent, not_found;
 
 fn get_symbol(cstore: cstore::cstore, def: ast::def_id) -> str {
     let cdata = cstore::get_crate_data(cstore, def.crate).data;
@@ -80,11 +80,17 @@ fn get_item_path(tcx: ty::ctxt, def: ast::def_id) -> ast_map::path {
     [ast_map::path_mod(cdata.name)] + path
 }
 
+enum found_ast {
+    found(ast::inlined_item),
+    found_parent(ast::def_id, ast::inlined_item),
+    not_found,
+}
+
 // Finds the AST for this item in the crate metadata, if any.  If the item was
 // not marked for inlining, then the AST will not be present and hence none
 // will be returned.
 fn maybe_get_item_ast(tcx: ty::ctxt, maps: maps, def: ast::def_id)
-    -> option<ast::inlined_item> {
+    -> found_ast {
     let cstore = tcx.sess.cstore;
     let cdata = cstore::get_crate_data(cstore, def.crate);
     decoder::maybe_get_item_ast(cdata, tcx, maps, def.node)
diff --git a/src/rustc/metadata/decoder.rs b/src/rustc/metadata/decoder.rs
index f78c0ade6f1..ac6e32f292d 100644
--- a/src/rustc/metadata/decoder.rs
+++ b/src/rustc/metadata/decoder.rs
@@ -96,9 +96,12 @@ fn item_symbol(item: ebml::doc) -> str {
     ret str::from_bytes(ebml::doc_data(sym));
 }
 
-fn variant_enum_id(d: ebml::doc) -> ast::def_id {
-    let tagdoc = ebml::get_doc(d, tag_items_data_item_enum_id);
-    ret parse_def_id(ebml::doc_data(tagdoc));
+fn item_parent_item(d: ebml::doc) -> option<ast::def_id> {
+    let found = none;
+    ebml::tagged_docs(d, tag_items_data_parent_item) {|did|
+        found = some(parse_def_id(ebml::doc_data(did)));
+    }
+    found
 }
 
 fn variant_disr_val(d: ebml::doc) -> option<int> {
@@ -230,7 +233,7 @@ fn lookup_def(cnum: ast::crate_num, data: @[u8], did_: ast::def_id) ->
       'm' { ast::def_mod(did) }
       'n' { ast::def_native_mod(did) }
       'v' {
-        let tid = variant_enum_id(item);
+        let tid = option::get(item_parent_item(item));
         tid = {crate: cnum, node: tid.node};
         ast::def_variant(tid, did)
       }
@@ -278,10 +281,26 @@ fn get_item_path(cdata: cmd, id: ast::node_id) -> ast_map::path {
 }
 
 fn maybe_get_item_ast(cdata: cmd, tcx: ty::ctxt, maps: maps,
-                      id: ast::node_id) -> option<ast::inlined_item> {
+                      id: ast::node_id) -> csearch::found_ast {
     let item_doc = lookup_item(id, cdata.data);
     let path = vec::init(item_path(item_doc));
-    astencode::decode_inlined_item(cdata, tcx, maps, path, item_doc)
+    alt astencode::decode_inlined_item(cdata, tcx, maps, path, item_doc) {
+      some(ii) { csearch::found(ii) }
+      none {
+        alt item_parent_item(item_doc) {
+          some(did) {
+            let did = translate_def_id(cdata, did);
+            let parent_item = lookup_item(did.node, cdata.data);
+            alt astencode::decode_inlined_item(cdata, tcx, maps, path,
+                                               parent_item) {
+              some(ii) { csearch::found_parent(did, ii) }
+              none { csearch::not_found }
+            }
+          }
+          none { csearch::not_found }
+        }
+      }
+    }
 }
 
 fn get_enum_variants(cdata: cmd, id: ast::node_id, tcx: ty::ctxt)
diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs
index 6a8a77b1191..ae9ab788ac0 100644
--- a/src/rustc/metadata/encoder.rs
+++ b/src/rustc/metadata/encoder.rs
@@ -241,8 +241,8 @@ fn encode_disr_val(_ecx: @encode_ctxt, ebml_w: ebml::writer, disr_val: int) {
     ebml_w.end_tag();
 }
 
-fn encode_enum_id(ebml_w: ebml::writer, id: def_id) {
-    ebml_w.start_tag(tag_items_data_item_enum_id);
+fn encode_parent_item(ebml_w: ebml::writer, id: def_id) {
+    ebml_w.start_tag(tag_items_data_parent_item);
     ebml_w.writer.write(str::bytes(def_to_str(id)));
     ebml_w.end_tag();
 }
@@ -260,7 +260,7 @@ fn encode_enum_variant_info(ecx: @encode_ctxt, ebml_w: ebml::writer,
         encode_def_id(ebml_w, local_def(variant.node.id));
         encode_family(ebml_w, 'v');
         encode_name(ebml_w, variant.node.name);
-        encode_enum_id(ebml_w, local_def(id));
+        encode_parent_item(ebml_w, local_def(id));
         encode_type(ecx, ebml_w,
                     node_id_to_type(ecx.ccx.tcx, variant.node.id));
         if vec::len(variant.node.args) > 0u && ty_params.len() == 0u {
@@ -397,6 +397,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
         for v: variant in variants {
             encode_variant_id(ebml_w, local_def(v.node.id));
         }
+        astencode::encode_inlined_item(ecx, ebml_w, path, ii_item(item));
         encode_path(ebml_w, path, ast_map::path_name(item.ident));
         ebml_w.end_tag();
         encode_enum_variant_info(ecx, ebml_w, item.id, variants,
@@ -414,11 +415,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
         encode_type_param_bounds(ebml_w, ecx, tps);
         encode_type(ecx, ebml_w, ty::ty_fn_ret(fn_ty));
         encode_name(ebml_w, item.ident);
-        if tps.len() == 0u {
-            encode_symbol(ecx, ebml_w, item.id);
-        } else {
-            astencode::encode_inlined_item(ecx, ebml_w, path, ii_item(item));
-        }
+        astencode::encode_inlined_item(ecx, ebml_w, path, ii_item(item));
         encode_path(ebml_w, path, ast_map::path_name(item.ident));
         ebml_w.end_tag();
 
@@ -428,9 +425,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
         encode_family(ebml_w, 'f');
         encode_type_param_bounds(ebml_w, ecx, tps);
         encode_type(ecx, ebml_w, fn_ty);
-        if tps.len() == 0u {
-            encode_symbol(ecx, ebml_w, ctor_id);
-        }
+        encode_parent_item(ebml_w, local_def(item.id));
         encode_path(ebml_w, path, ast_map::path_name(item.ident));
         ebml_w.end_tag();
       }
diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs
index 0dde3983dbd..c3d2f886d42 100644
--- a/src/rustc/middle/trans/base.rs
+++ b/src/rustc/middle/trans/base.rs
@@ -864,17 +864,8 @@ fn get_res_dtor(ccx: @crate_ctxt, did: ast::def_id, substs: [ty::t])
     let did = if did.crate != ast::local_crate && substs.len() > 0u {
         maybe_instantiate_inline(ccx, did)
     } else { did };
-    if did.crate == ast::local_crate {
-        option::get(monomorphic_fn(ccx, did, substs, none))
-    } else {
-        assert substs.len() == 0u;
-        let nil = ty::mk_nil(ccx.tcx);
-        let arg = {mode: ast::expl(ast::by_ref),
-                   ty: ty::mk_mut_ptr(ccx.tcx, nil)};
-        let f_t = type_of::type_of_fn(ccx, [arg], nil, 0u);
-        get_extern_const(ccx.externs, ccx.llmod,
-                         csearch::get_symbol(ccx.sess.cstore, did), f_t)
-    }
+    assert did.crate == ast::local_crate;
+    option::get(monomorphic_fn(ccx, did, substs, none))
 }
 
 fn trans_res_drop(bcx: block, rs: ValueRef, did: ast::def_id,
@@ -2094,22 +2085,34 @@ fn maybe_instantiate_inline(ccx: @crate_ctxt, fn_id: ast::def_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];
+        alt check csearch::maybe_get_item_ast(ccx.tcx, ccx.maps, fn_id) {
+          csearch::not_found {
+            ccx.external.insert(fn_id, none);
+            fn_id
+          }
+          csearch::found(ast::ii_item(item)) {
             ccx.external.insert(fn_id, some(item.id));
             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];
+          csearch::found_parent(parent_id, ast::ii_item(item)) {
+            ccx.external.insert(parent_id, some(item.id));
+            let my_id = 0;
+            alt check item.node {
+              ast::item_enum(_, _) {
+                let vs_here = ty::enum_variants(ccx.tcx, local_def(item.id));
+                let vs_there = ty::enum_variants(ccx.tcx, parent_id);
+                vec::iter2(*vs_here, *vs_there) {|here, there|
+                    if there.id == fn_id { my_id = here.id.node; }
+                    ccx.external.insert(there.id, some(here.id.node));
+                }
+              }
+              ast::item_res(_, _, _, _, ctor_id) { my_id = ctor_id; }
+            }
+            trans_item(ccx, *item);
+            local_def(my_id)
+          }
+          csearch::found(ast::ii_method(impl_did, mth)) {
             ccx.external.insert(fn_id, some(mth.id));
             compute_ii_method_info(ccx, impl_did, mth) {|ty, bounds, path|
                 if bounds.len() == 0u {
@@ -2143,8 +2146,7 @@ fn lval_static_fn(bcx: block, fn_id: ast::def_id, id: ast::node_id,
     // monomorphized and non-monomorphized functions at the moment. If
     // monomorphizing becomes the only approach, this'll be much simpler.
     if (option::is_some(substs) || tys.len() > 0u) &&
-       fn_id.crate == ast::local_crate &&
-       !vec::any(tys, {|t| ty::type_has_params(t)}) {
+       fn_id.crate == ast::local_crate {
         let mono = alt substs {
           some((stys, vtables)) {
             if (stys.len() + tys.len()) > 0u {