From 862911df9af8216edc94458df2084143aad7be5b Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 2 Nov 2015 14:46:39 +0100 Subject: [PATCH] Implement the translation item collector. The purpose of the translation item collector is to find all monomorphic instances of functions, methods and statics that need to be translated into LLVM IR in order to compile the current crate. So far these instances have been discovered lazily during the trans path. For incremental compilation we want to know the set of these instances in advance, and that is what the trans::collect module provides. In the future, incremental and regular translation will be driven by the collector implemented here. --- configure | 1 + mk/tests.mk | 12 +- src/compiletest/common.rs | 3 + src/compiletest/runtest.rs | 45 +- src/librustc/front/map/definitions.rs | 14 +- src/librustc/middle/cstore.rs | 5 + src/librustc/middle/ty/context.rs | 2 +- src/librustc/middle/ty/maps.rs | 3 +- src/librustc/mir/repr.rs | 2 +- src/librustc/session/config.rs | 2 + src/librustc_metadata/astencode.rs | 1 + src/librustc_metadata/csearch.rs | 5 + src/librustc_metadata/decoder.rs | 10 + src/librustc_trans/trans/base.rs | 101 +- src/librustc_trans/trans/collector.rs | 1590 +++++++++++++++++ src/librustc_trans/trans/consts.rs | 4 + src/librustc_trans/trans/context.rs | 23 +- src/librustc_trans/trans/expr.rs | 23 +- src/librustc_trans/trans/glue.rs | 8 +- src/librustc_trans/trans/meth.rs | 53 +- src/librustc_trans/trans/mod.rs | 1 + src/test/auxiliary/cgu_export_trait_method.rs | 34 + src/test/auxiliary/cgu_extern_closures.rs | 33 + src/test/auxiliary/cgu_generic_function.rs | 36 + .../codegen-units/cross-crate-closures.rs | 35 + .../cross-crate-generic-functions.rs | 34 + .../codegen-units/cross-crate-trait-method.rs | 60 + .../codegen-units/function-as-argument.rs | 46 + src/test/codegen-units/generic-drop-glue.rs | 98 + src/test/codegen-units/generic-functions.rs | 64 + src/test/codegen-units/generic-impl.rs | 81 + .../impl-in-non-instantiated-generic.rs | 36 + .../instantiation-through-vtable.rs | 42 + .../items-within-generic-items.rs | 44 + .../codegen-units/non-generic-closures.rs | 63 + .../codegen-units/non-generic-drop-glue.rs | 56 + .../codegen-units/non-generic-functions.rs | 81 + .../codegen-units/overloaded-operators.rs | 72 + src/test/codegen-units/statics-and-consts.rs | 64 + .../codegen-units/trait-implementations.rs | 82 + .../codegen-units/trait-method-as-argument.rs | 62 + .../trait-method-default-impl.rs | 70 + .../codegen-units/transitive-drop-glue.rs | 55 + src/test/codegen-units/tuple-drop-glue.rs | 32 + src/test/codegen-units/unsizing.rs | 80 + .../unused-traits-and-generics.rs | 89 + 46 files changed, 3296 insertions(+), 61 deletions(-) create mode 100644 src/librustc_trans/trans/collector.rs create mode 100644 src/test/auxiliary/cgu_export_trait_method.rs create mode 100644 src/test/auxiliary/cgu_extern_closures.rs create mode 100644 src/test/auxiliary/cgu_generic_function.rs create mode 100644 src/test/codegen-units/cross-crate-closures.rs create mode 100644 src/test/codegen-units/cross-crate-generic-functions.rs create mode 100644 src/test/codegen-units/cross-crate-trait-method.rs create mode 100644 src/test/codegen-units/function-as-argument.rs create mode 100644 src/test/codegen-units/generic-drop-glue.rs create mode 100644 src/test/codegen-units/generic-functions.rs create mode 100644 src/test/codegen-units/generic-impl.rs create mode 100644 src/test/codegen-units/impl-in-non-instantiated-generic.rs create mode 100644 src/test/codegen-units/instantiation-through-vtable.rs create mode 100644 src/test/codegen-units/items-within-generic-items.rs create mode 100644 src/test/codegen-units/non-generic-closures.rs create mode 100644 src/test/codegen-units/non-generic-drop-glue.rs create mode 100644 src/test/codegen-units/non-generic-functions.rs create mode 100644 src/test/codegen-units/overloaded-operators.rs create mode 100644 src/test/codegen-units/statics-and-consts.rs create mode 100644 src/test/codegen-units/trait-implementations.rs create mode 100644 src/test/codegen-units/trait-method-as-argument.rs create mode 100644 src/test/codegen-units/trait-method-default-impl.rs create mode 100644 src/test/codegen-units/transitive-drop-glue.rs create mode 100644 src/test/codegen-units/tuple-drop-glue.rs create mode 100644 src/test/codegen-units/unsizing.rs create mode 100644 src/test/codegen-units/unused-traits-and-generics.rs diff --git a/configure b/configure index f284d13ee3b..2693e9bcd23 100755 --- a/configure +++ b/configure @@ -1409,6 +1409,7 @@ do make_dir $h/test/debuginfo-gdb make_dir $h/test/debuginfo-lldb make_dir $h/test/codegen + make_dir $h/test/codegen-units make_dir $h/test/rustdoc done diff --git a/mk/tests.mk b/mk/tests.mk index b3f7278ad62..5ca27c489ed 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -310,6 +310,7 @@ check-stage$(1)-T-$(2)-H-$(3)-exec: \ check-stage$(1)-T-$(2)-H-$(3)-debuginfo-gdb-exec \ check-stage$(1)-T-$(2)-H-$(3)-debuginfo-lldb-exec \ check-stage$(1)-T-$(2)-H-$(3)-codegen-exec \ + check-stage$(1)-T-$(2)-H-$(3)-codegen-units-exec \ check-stage$(1)-T-$(2)-H-$(3)-doc-exec \ check-stage$(1)-T-$(2)-H-$(3)-pretty-exec @@ -473,6 +474,7 @@ DEBUGINFO_GDB_RS := $(wildcard $(S)src/test/debuginfo/*.rs) DEBUGINFO_LLDB_RS := $(wildcard $(S)src/test/debuginfo/*.rs) CODEGEN_RS := $(wildcard $(S)src/test/codegen/*.rs) CODEGEN_CC := $(wildcard $(S)src/test/codegen/*.cc) +CODEGEN_UNITS_RS := $(wildcard $(S)src/test/codegen-units/*.rs) RUSTDOCCK_RS := $(wildcard $(S)src/test/rustdoc/*.rs) RPASS_TESTS := $(RPASS_RS) @@ -488,6 +490,7 @@ PRETTY_TESTS := $(PRETTY_RS) DEBUGINFO_GDB_TESTS := $(DEBUGINFO_GDB_RS) DEBUGINFO_LLDB_TESTS := $(DEBUGINFO_LLDB_RS) CODEGEN_TESTS := $(CODEGEN_RS) $(CODEGEN_CC) +CODEGEN_UNITS_TESTS := $(CODEGEN_UNITS_RS) RUSTDOCCK_TESTS := $(RUSTDOCCK_RS) CTEST_SRC_BASE_rpass = run-pass @@ -550,6 +553,11 @@ CTEST_BUILD_BASE_codegen = codegen CTEST_MODE_codegen = codegen CTEST_RUNTOOL_codegen = $(CTEST_RUNTOOL) +CTEST_SRC_BASE_codegen-units = codegen-units +CTEST_BUILD_BASE_codegen-units = codegen-units +CTEST_MODE_codegen-units = codegen-units +CTEST_RUNTOOL_codegen-units = $(CTEST_RUNTOOL) + CTEST_SRC_BASE_rustdocck = rustdoc CTEST_BUILD_BASE_rustdocck = rustdoc CTEST_MODE_rustdocck = rustdoc @@ -673,6 +681,7 @@ CTEST_DEPS_debuginfo-lldb_$(1)-T-$(2)-H-$(3) = $$(DEBUGINFO_LLDB_TESTS) \ $(S)src/etc/lldb_batchmode.py \ $(S)src/etc/lldb_rust_formatters.py CTEST_DEPS_codegen_$(1)-T-$(2)-H-$(3) = $$(CODEGEN_TESTS) +CTEST_DEPS_codegen-units_$(1)-T-$(2)-H-$(3) = $$(CODEGEN_UNITS_TESTS) CTEST_DEPS_rustdocck_$(1)-T-$(2)-H-$(3) = $$(RUSTDOCCK_TESTS) \ $$(HBIN$(1)_H_$(3))/rustdoc$$(X_$(3)) \ $(S)src/etc/htmldocck.py @@ -739,7 +748,7 @@ endif endef CTEST_NAMES = rpass rpass-valgrind rpass-full rfail-full cfail-full rfail cfail pfail \ - bench debuginfo-gdb debuginfo-lldb codegen rustdocck + bench debuginfo-gdb debuginfo-lldb codegen codegen-units rustdocck $(foreach host,$(CFG_HOST), \ $(eval $(foreach target,$(CFG_TARGET), \ @@ -917,6 +926,7 @@ TEST_GROUPS = \ debuginfo-gdb \ debuginfo-lldb \ codegen \ + codegen-units \ doc \ $(foreach docname,$(DOC_NAMES),doc-$(docname)) \ pretty \ diff --git a/src/compiletest/common.rs b/src/compiletest/common.rs index eb6c29eefbe..e66094dc395 100644 --- a/src/compiletest/common.rs +++ b/src/compiletest/common.rs @@ -25,6 +25,7 @@ pub enum Mode { DebugInfoLldb, Codegen, Rustdoc, + CodegenUnits } impl FromStr for Mode { @@ -41,6 +42,7 @@ fn from_str(s: &str) -> Result { "debuginfo-gdb" => Ok(DebugInfoGdb), "codegen" => Ok(Codegen), "rustdoc" => Ok(Rustdoc), + "codegen-units" => Ok(CodegenUnits), _ => Err(()), } } @@ -59,6 +61,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { DebugInfoLldb => "debuginfo-lldb", Codegen => "codegen", Rustdoc => "rustdoc", + CodegenUnits => "codegen-units", }, f) } } diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 459b43b4ffe..c7561248eb7 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -10,7 +10,7 @@ use common::Config; use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind}; -use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc}; +use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits}; use errors; use header::TestProps; use header; @@ -18,6 +18,7 @@ use util::logv; use std::env; +use std::collections::HashSet; use std::fmt; use std::fs::{self, File}; use std::io::BufReader; @@ -56,6 +57,7 @@ pub fn run(config: Config, testfile: &Path) { DebugInfoLldb => run_debuginfo_lldb_test(&config, &props, &testfile), Codegen => run_codegen_test(&config, &props, &testfile), Rustdoc => run_rustdoc_test(&config, &props, &testfile), + CodegenUnits => run_codegen_units_test(&config, &props, &testfile), } } @@ -1747,3 +1749,44 @@ fn run_rustdoc_test(config: &Config, props: &TestProps, testfile: &Path) { fatal_proc_rec("htmldocck failed!", &res); } } + +fn run_codegen_units_test(config: &Config, props: &TestProps, testfile: &Path) { + let proc_res = compile_test(config, props, testfile); + + if !proc_res.status.success() { + fatal_proc_rec("compilation failed!", &proc_res); + } + + check_no_compiler_crash(&proc_res); + + let prefix = "TRANS_ITEM "; + + let actual: HashSet = proc_res + .stdout + .lines() + .filter(|line| line.starts_with(prefix)) + .map(|s| (&s[prefix.len()..]).to_string()) + .collect(); + + let expected: HashSet = errors::load_errors(testfile) + .iter() + .map(|e| e.msg.trim().to_string()) + .collect(); + + if actual != expected { + let mut missing: Vec<_> = expected.difference(&actual).collect(); + missing.sort(); + + let mut too_much: Vec<_> = actual.difference(&expected).collect(); + too_much.sort(); + + println!("Expected and actual sets of codegen-items differ.\n\ + These items should have been contained but were not:\n\n\ + {}\n\n\ + These items were contained but should not have been:\n\n\ + {}\n\n", + missing.iter().fold("".to_string(), |s1, s2| s1 + "\n" + s2), + too_much.iter().fold("".to_string(), |s1, s2| s1 + "\n" + s2)); + panic!(); + } +} diff --git a/src/librustc/front/map/definitions.rs b/src/librustc/front/map/definitions.rs index e903fcf6a56..10c1372cd86 100644 --- a/src/librustc/front/map/definitions.rs +++ b/src/librustc/front/map/definitions.rs @@ -196,33 +196,33 @@ pub fn as_interned_str(&self) -> InternedString { PositionalField | Field(hir::StructFieldKind::UnnamedField(_)) => { - InternedString::new("") + InternedString::new("{{field}}") } // note that this does not show up in user printouts CrateRoot => { - InternedString::new("") + InternedString::new("{{root}}") } // note that this does not show up in user printouts InlinedRoot(_) => { - InternedString::new("") + InternedString::new("{{inlined-root}}") } Misc => { - InternedString::new("?") + InternedString::new("{{?}}") } ClosureExpr => { - InternedString::new("") + InternedString::new("{{closure}}") } StructCtor => { - InternedString::new("") + InternedString::new("{{constructor}}") } Initializer => { - InternedString::new("") + InternedString::new("{{initializer}}") } } } diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 27745a85935..cb7c6a6432a 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -223,6 +223,8 @@ fn maybe_get_item_ast(&'tcx self, tcx: &ty::ctxt<'tcx>, def: DefId) -> FoundAst<'tcx>; fn maybe_get_item_mir(&self, tcx: &ty::ctxt<'tcx>, def: DefId) -> Option>; + fn is_item_mir_available(&self, def: DefId) -> bool; + // This is basically a 1-based range of ints, which is a little // silly - I may fix that. fn crates(&self) -> Vec; @@ -397,6 +399,9 @@ fn maybe_get_item_ast(&'tcx self, tcx: &ty::ctxt<'tcx>, def: DefId) -> FoundAst<'tcx> { unimplemented!() } fn maybe_get_item_mir(&self, tcx: &ty::ctxt<'tcx>, def: DefId) -> Option> { unimplemented!() } + fn is_item_mir_available(&self, def: DefId) -> bool { + unimplemented!() + } // This is basically a 1-based range of ints, which is a little // silly - I may fix that. diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs index 7010bae7ee5..0fd6b933ed5 100644 --- a/src/librustc/middle/ty/context.rs +++ b/src/librustc/middle/ty/context.rs @@ -563,7 +563,7 @@ pub fn create_and_enter(s: &'tcx Session, const_qualif_map: RefCell::new(NodeMap()), custom_coerce_unsized_kinds: RefCell::new(DefIdMap()), cast_kinds: RefCell::new(NodeMap()), - fragment_infos: RefCell::new(DefIdMap()), + fragment_infos: RefCell::new(DefIdMap()) }, f) } } diff --git a/src/librustc/middle/ty/maps.rs b/src/librustc/middle/ty/maps.rs index 7d5276f379f..8d8afc199f5 100644 --- a/src/librustc/middle/ty/maps.rs +++ b/src/librustc/middle/ty/maps.rs @@ -13,7 +13,7 @@ use middle::ty; use std::marker::PhantomData; use std::rc::Rc; -use syntax::attr; +use syntax::{attr, ast}; macro_rules! dep_map_ty { ($ty_name:ident : $node_name:ident ($key:ty) -> $value:ty) => { @@ -42,3 +42,4 @@ fn to_dep_node(key: &$key) -> DepNode { DepNode::$node_name(*key) } dep_map_ty! { ImplItems: ImplItems(DefId) -> Vec } dep_map_ty! { TraitItems: TraitItems(DefId) -> Rc>> } dep_map_ty! { ReprHints: ReprHints(DefId) -> Rc> } +dep_map_ty! { InlinedClosures: Hir(DefId) -> ast::NodeId } diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 3e71d3ba9f3..de7a694a646 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -721,7 +721,7 @@ pub enum Rvalue<'tcx> { InlineAsm(InlineAsm), } -#[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub enum CastKind { Misc, diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 1a99aba591a..6956ee8eac2 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -643,6 +643,8 @@ fn parse_passes(slot: &mut Passes, v: Option<&str>) -> bool { "keep the AST after lowering it to HIR"), show_span: Option = (None, parse_opt_string, "show spans for compiler debugging (expr|pat|ty)"), + print_trans_items: Option = (None, parse_opt_string, + "print the result of the translation item collection pass"), } pub fn default_lib_output() -> CrateType { diff --git a/src/librustc_metadata/astencode.rs b/src/librustc_metadata/astencode.rs index e91c7e6ac45..cdbb6844703 100644 --- a/src/librustc_metadata/astencode.rs +++ b/src/librustc_metadata/astencode.rs @@ -177,6 +177,7 @@ pub fn decode_inlined_item<'tcx>(cdata: &cstore::crate_metadata, } _ => { } } + Ok(ii) } } diff --git a/src/librustc_metadata/csearch.rs b/src/librustc_metadata/csearch.rs index 802629e8f3e..bc52921e1b8 100644 --- a/src/librustc_metadata/csearch.rs +++ b/src/librustc_metadata/csearch.rs @@ -445,6 +445,11 @@ fn maybe_get_item_mir(&self, tcx: &ty::ctxt<'tcx>, def: DefId) decoder::maybe_get_item_mir(&*cdata, tcx, def.index) } + fn is_item_mir_available(&self, def: DefId) -> bool { + let cdata = self.get_crate_data(def.krate); + decoder::is_item_mir_available(&*cdata, def.index) + } + fn crates(&self) -> Vec { let mut result = vec![]; diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index bca8cb3995a..326f68561b0 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -830,6 +830,14 @@ pub fn maybe_get_item_ast<'tcx>(cdata: Cmd, } } +pub fn is_item_mir_available<'tcx>(cdata: Cmd, id: DefIndex) -> bool { + if let Some(item_doc) = cdata.get_item(id) { + return reader::maybe_get_doc(item_doc, tag_mir as usize).is_some(); + } + + false +} + pub fn maybe_get_item_mir<'tcx>(cdata: Cmd, tcx: &ty::ctxt<'tcx>, id: DefIndex) @@ -849,6 +857,8 @@ pub fn maybe_get_item_mir<'tcx>(cdata: Cmd, }) }).unwrap(); + assert!(decoder.position() == mir_doc.end); + let mut def_id_and_span_translator = MirDefIdAndSpanTranslator { crate_metadata: cdata, codemap: tcx.sess.codemap(), diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 7460ef82ebe..68f8d9d2adb 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -41,8 +41,10 @@ use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem}; use middle::weak_lang_items; use middle::pat_util::simple_name; -use middle::subst::Substs; +use middle::subst::{self, Substs}; +use middle::traits; use middle::ty::{self, Ty, TypeFoldable}; +use middle::ty::adjustment::CustomCoerceUnsized; use rustc::dep_graph::DepNode; use rustc::front::map as hir_map; use rustc::util::common::time; @@ -59,10 +61,11 @@ use trans::cleanup::{self, CleanupMethods, DropHint}; use trans::closure; use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_uint, C_integral}; +use trans::collector::{self, TransItem, TransItemState, TransItemCollectionMode}; use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef}; use trans::common::{CrateContext, DropFlagHintsMap, Field, FunctionContext}; use trans::common::{Result, NodeIdAndSpan, VariantInfo}; -use trans::common::{node_id_type, return_type_is_void}; +use trans::common::{node_id_type, return_type_is_void, fulfill_obligation}; use trans::common::{type_is_immediate, type_is_zero_size, val_ty}; use trans::common; use trans::consts; @@ -98,7 +101,7 @@ use std::str; use std::{i8, i16, i32, i64}; use syntax::abi::{Rust, RustCall, RustIntrinsic, PlatformIntrinsic, Abi}; -use syntax::codemap::Span; +use syntax::codemap::{Span, DUMMY_SP}; use syntax::parse::token::InternedString; use syntax::attr::AttrMetaMethods; use syntax::attr; @@ -736,6 +739,29 @@ pub fn coerce_unsized_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } } +pub fn custom_coerce_unsize_info<'ccx, 'tcx>(ccx: &CrateContext<'ccx, 'tcx>, + source_ty: Ty<'tcx>, + target_ty: Ty<'tcx>) + -> CustomCoerceUnsized { + let trait_substs = Substs::erased(subst::VecPerParamSpace::new(vec![target_ty], + vec![source_ty], + Vec::new())); + let trait_ref = ty::Binder(ty::TraitRef { + def_id: ccx.tcx().lang_items.coerce_unsized_trait().unwrap(), + substs: ccx.tcx().mk_substs(trait_substs) + }); + + match fulfill_obligation(ccx, DUMMY_SP, trait_ref) { + traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => { + ccx.tcx().custom_coerce_unsized_kind(impl_def_id) + } + vtable => { + ccx.sess().bug(&format!("invalid CoerceUnsized vtable: {:?}", + vtable)); + } + } +} + pub fn cast_shift_expr_rhs(cx: Block, op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef) -> ValueRef { cast_shift_rhs(op, lhs, rhs, |a, b| Trunc(cx, a, b), |a, b| ZExt(cx, a, b)) } @@ -1965,6 +1991,8 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, closure_env: closure::ClosureEnv<'b>) { ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1); + record_translation_item_as_generated(ccx, fn_ast_id, param_substs); + let _icx = push_ctxt("trans_closure"); attributes::emit_uwtable(llfndecl, true); @@ -2078,6 +2106,24 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // Insert the mandatory first few basic blocks before lltop. finish_fn(&fcx, bcx, output_type, ret_debug_loc); + + fn record_translation_item_as_generated<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + node_id: ast::NodeId, + param_substs: &'tcx Substs<'tcx>) { + let def_id = match ccx.tcx().node_id_to_type(node_id).sty { + ty::TyClosure(def_id, _) => def_id, + _ => ccx.external_srcs() + .borrow() + .get(&node_id) + .map(|did| *did) + .unwrap_or_else(|| ccx.tcx().map.local_def_id(node_id)), + }; + + ccx.record_translation_item_as_generated(TransItem::Fn{ + def_id: def_id, + substs: ccx.tcx().mk_substs(ccx.tcx().erase_regions(param_substs)), + }); + } } /// Creates an LLVM function corresponding to a source language function. @@ -3161,6 +3207,8 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>, // First, verify intrinsics. intrinsic::check_intrinsics(&ccx); + collect_translation_items(&ccx); + // Next, translate all items. See `TransModVisitor` for // details on why we walk in this particular way. { @@ -3168,6 +3216,8 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>, intravisit::walk_mod(&mut TransItemsWithinModVisitor { ccx: &ccx }, &krate.module); krate.visit_all_items(&mut TransModVisitor { ccx: &ccx }); } + + collector::print_collection_results(&ccx); } for ccx in shared_ccx.iter() { @@ -3339,3 +3389,48 @@ fn visit_item(&mut self, i: &hir::Item) { } } } + +fn collect_translation_items<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>) { + let time_passes = ccx.sess().time_passes(); + + let collection_mode = match ccx.sess().opts.debugging_opts.print_trans_items { + Some(ref s) => { + let mode_string = s.to_lowercase(); + let mode_string = mode_string.trim(); + if mode_string == "eager" { + TransItemCollectionMode::Eager + } else { + if mode_string != "lazy" { + let message = format!("Unknown codegen-item collection mode '{}'. \ + Falling back to 'lazy' mode.", + mode_string); + ccx.sess().warn(&message); + } + + TransItemCollectionMode::Lazy + } + } + None => TransItemCollectionMode::Lazy + }; + + let items = time(time_passes, "translation item collection", || { + collector::collect_crate_translation_items(&ccx, collection_mode) + }); + + if ccx.sess().opts.debugging_opts.print_trans_items.is_some() { + let mut item_keys: Vec<_> = items.iter() + .map(|i| i.to_string(ccx)) + .collect(); + item_keys.sort(); + + for item in item_keys { + println!("TRANS_ITEM {}", item); + } + + let mut ccx_map = ccx.translation_items().borrow_mut(); + + for cgi in items { + ccx_map.insert(cgi, TransItemState::PredictedButNotGenerated); + } + } +} diff --git a/src/librustc_trans/trans/collector.rs b/src/librustc_trans/trans/collector.rs new file mode 100644 index 00000000000..574cba2f0ee --- /dev/null +++ b/src/librustc_trans/trans/collector.rs @@ -0,0 +1,1590 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Translation Item Collection +//! =========================== +//! +//! This module is responsible for discovering all items that will contribute to +//! to code generation of the crate. The important part here is that it not only +//! needs to find syntax-level items (functions, structs, etc) but also all +//! their monomorphized instantiations. Every non-generic, non-const function +//! maps to one LLVM artifact. Every generic function can produce +//! from zero to N artifacts, depending on the sets of type arguments it +//! is instantiated with. +//! This also applies to generic items from other crates: A generic definition +//! in crate X might produce monomorphizations that are compiled into crate Y. +//! We also have to collect these here. +//! +//! The following kinds of "translation items" are handled here: +//! +//! - Functions +//! - Methods +//! - Closures +//! - Statics +//! - Drop glue +//! +//! The following things also result in LLVM artifacts, but are not collected +//! here, since we instantiate them locally on demand when needed in a given +//! codegen unit: +//! +//! - Constants +//! - Vtables +//! - Object Shims +//! +//! +//! General Algorithm +//! ----------------- +//! Let's define some terms first: +//! +//! - A "translation item" is something that results in a function or global in +//! the LLVM IR of a codegen unit. Translation items do not stand on their +//! own, they can reference other translation items. For example, if function +//! `foo()` calls function `bar()` then the translation item for `foo()` +//! references the translation item for function `bar()`. In general, the +//! definition for translation item A referencing a translation item B is that +//! the LLVM artifact produced for A references the LLVM artifact produced +//! for B. +//! +//! - Translation items and the references between them for a directed graph, +//! where the translation items are the nodes and references form the edges. +//! Let's call this graph the "translation item graph". +//! +//! - The translation item graph for a program contains all translation items +//! that are needed in order to produce the complete LLVM IR of the program. +//! +//! The purpose of the algorithm implemented in this module is to build the +//! translation item graph for the current crate. It runs in two phases: +//! +//! 1. Discover the roots of the graph by traversing the HIR of the crate. +//! 2. Starting from the roots, find neighboring nodes by inspecting the MIR +//! representation of the item corresponding to a given node, until no more +//! new nodes are found. +//! +//! ### Discovering roots +//! +//! The roots of the translation item graph correspond to the non-generic +//! syntactic items in the source code. We find them by walking the HIR of the +//! crate, and whenever we hit upon a function, method, or static item, we +//! create a translation item consisting of the items DefId and, since we only +//! consider non-generic items, an empty type-substitution set. +//! +//! ### Finding neighbor nodes +//! Given a translation item node, we can discover neighbors by inspecting its +//! MIR. We walk the MIR and any time we hit upon something that signifies a +//! reference to another translation item, we have found a neighbor. Since the +//! translation item we are currently at is always monomorphic, we also know the +//! concrete type arguments of its neighbors, and so all neighbors again will be +//! monomorphic. The specific forms a reference to a neighboring node can take +//! in MIR are quite diverse. Here is an overview: +//! +//! #### Calling Functions/Methods +//! The most obvious form of one translation item referencing another is a +//! function or method call (represented by a CALL terminator in MIR). But +//! calls are not the only thing that might introduce a reference between two +//! function translation items, and as we will see below, they are just a +//! specialized of the form described next, and consequently will don't get any +//! special treatment in the algorithm. +//! +//! #### Taking a reference to a function or method +//! A function does not need to actually be called in order to be a neighbor of +//! another function. It suffices to just take a reference in order to introduce +//! an edge. Consider the following example: +//! +//! ```rust +//! fn print_val(x: T) { +//! println!("{}", x); +//! } +//! +//! fn call_fn(f: &Fn(i32), x: i32) { +//! f(x); +//! } +//! +//! fn main() { +//! let print_i32 = print_val::; +//! call_fn(&print_i32, 0); +//! } +//! ``` +//! The MIR of none of these functions will contain an explicit call to +//! `print_val::`. Nonetheless, in order to translate this program, we need +//! an instance of this function. Thus, whenever we encounter a function or +//! method in operand position, we treat it as a neighbor of the current +//! translation item. Calls are just a special case of that. +//! +//! #### Closures +//! In a way, closures are a simple case. Since every closure object needs to be +//! constructed somewhere, we can reliably discover them by observing +//! `RValue::Aggregate` expressions with `AggregateKind::Closure`. This is also +//! true for closures inlined from other crates. +//! +//! #### Drop glue +//! Drop glue translation items are introduced by MIR drop-statements. The +//! generated translation item will again have drop-glue item neighbors if the +//! type to be dropped contains nested values that also need to be dropped. It +//! might also have a function item neighbor for the explicit `Drop::drop` +//! implementation of its type. +//! +//! #### Unsizing Casts +//! A subtle way of introducing neighbor edges is by casting to a trait object. +//! Since the resulting fat-pointer contains a reference to a vtable, we need to +//! instantiate all object-save methods of the trait, as we need to store +//! pointers to these functions even if they never get called anywhere. This can +//! be seen as a special case of taking a function reference. +//! +//! #### Boxes +//! Since `Box` expression have special compiler support, no explicit calls to +//! `exchange_malloc()` and `exchange_free()` may show up in MIR, even if the +//! compiler will generate them. We have to observe `Rvalue::Box` expressions +//! and Box-typed drop-statements for that purpose. +//! +//! +//! Interaction with Cross-Crate Inlining +//! ------------------------------------- +//! The binary of a crate will not only contain machine code for the items +//! defined in the source code of that crate. It will also contain monomorphic +//! instantiations of any extern generic functions and of functions marked with +//! #[inline]. +//! The collection algorithm handles this more or less transparently. If it is +//! about to create a translation item for something with an external `DefId`, +//! it will take a look if the MIR for that item is available, and if so just +//! proceed normally. If the MIR is not available, it assumes that that item is +//! just linked to and no node is created; which is exactly what we want, since +//! no machine code should be generated in the current crate for such an item. +//! +//! Eager and Lazy Collection Mode +//! ------------------------------ +//! Translation item collection can be performed in one of two modes: +//! +//! - Lazy mode means that items will only be instantiated when actually +//! referenced. The goal is to produce the least amount of machine code +//! possible. +//! +//! - Eager mode is meant to be used in conjunction with incremental compilation +//! where a stable set of translation items is more important than a minimal +//! one. Thus, eager mode will instantiate drop-glue for every drop-able type +//! in the crate, even of no drop call for that type exists (yet). It will +//! also instantiate default implementations of trait methods, something that +//! otherwise is only done on demand. +//! +//! +//! Open Issues +//! ----------- +//! Some things are not yet fully implemented in the current version of this +//! module. +//! +//! ### Initializers of Constants and Statics +//! Since no MIR is constructed yet for initializer expressions of constants and +//! statics we cannot inspect these properly. +//! +//! ### Const Fns +//! Ideally, no translation item should be generated for const fns unless there +//! is a call to them that cannot be evaluated at compile time. At the moment +//! this is not implemented however: a translation item will be produced +//! regardless of whether it is actually needed or not. + +use rustc_front::hir; +use rustc_front::intravisit as hir_visit; + +use rustc::front::map as hir_map; +use rustc::middle::def_id::DefId; +use rustc::middle::lang_items::{ExchangeFreeFnLangItem, ExchangeMallocFnLangItem}; +use rustc::middle::{ty, traits}; +use rustc::middle::subst::{self, Substs, Subst}; +use rustc::middle::ty::adjustment::CustomCoerceUnsized; +use rustc::middle::ty::fold::TypeFoldable; +use rustc::mir::repr as mir; +use rustc::mir::visit as mir_visit; +use rustc::mir::visit::Visitor as MirVisitor; + +use syntax::ast::{self, NodeId}; +use syntax::codemap::DUMMY_SP; +use syntax::errors; +use syntax::parse::token; + +use trans::base::custom_coerce_unsize_info; +use trans::context::CrateContext; +use trans::common::{fulfill_obligation, normalize_and_test_predicates, + type_is_sized}; +use trans::glue; +use trans::meth; +use trans::monomorphize; +use util::nodemap::{FnvHashSet, FnvHashMap, DefIdMap}; + +use std::hash::{Hash, Hasher}; + +#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] +pub enum TransItemCollectionMode { + Eager, + Lazy +} + +#[derive(Eq, Clone, Copy, Debug)] +pub enum TransItem<'tcx> { + DropGlue(ty::Ty<'tcx>), + Fn { + def_id: DefId, + substs: &'tcx Substs<'tcx> + }, + Static(NodeId) +} + +impl<'tcx> Hash for TransItem<'tcx> { + fn hash(&self, s: &mut H) { + match *self { + TransItem::DropGlue(t) => { + 0u8.hash(s); + t.hash(s); + }, + TransItem::Fn { def_id, substs } => { + 1u8.hash(s); + def_id.hash(s); + (substs as *const Substs<'tcx> as usize).hash(s); + } + TransItem::Static(node_id) => { + 3u8.hash(s); + node_id.hash(s); + } + }; + } +} + +impl<'tcx> PartialEq for TransItem<'tcx> { + fn eq(&self, other: &Self) -> bool { + match (*self, *other) { + (TransItem::DropGlue(t1), TransItem::DropGlue(t2)) => t1 == t2, + (TransItem::Fn { def_id: def_id1, substs: substs1 }, + TransItem::Fn { def_id: def_id2, substs: substs2 }) => { + def_id1 == def_id2 && substs1 == substs2 + }, + (TransItem::Static(node_id1), TransItem::Static(node_id2)) => { + node_id1 == node_id2 + }, + _ => false + } + } +} + +pub fn collect_crate_translation_items<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + mode: TransItemCollectionMode) + -> FnvHashSet> { + // We are not tracking dependencies of this pass as it has to be re-executed + // every time no matter what. + ccx.tcx().dep_graph.with_ignore(|| { + let roots = collect_roots(ccx, mode); + + debug!("Building translation item graph, beginning at roots"); + let mut visited = FnvHashSet(); + let mut recursion_depths = DefIdMap(); + + for root in roots { + collect_items_rec(ccx, root, &mut visited, &mut recursion_depths); + } + + visited + }) +} + +// Find all non-generic items by walking the HIR. These items serve as roots to +// start monomorphizing from. +fn collect_roots<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + mode: TransItemCollectionMode) + -> Vec> { + debug!("Collecting roots"); + let mut roots = Vec::new(); + + { + let mut visitor = RootCollector { + ccx: ccx, + mode: mode, + output: &mut roots, + enclosing_item: None, + trans_empty_substs: ccx.tcx().mk_substs(Substs::trans_empty()), + }; + + ccx.tcx().map.krate().visit_all_items(&mut visitor); + } + + roots +} + +// Collect all monomorphized translation items reachable from `starting_point` +fn collect_items_rec<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>, + starting_point: TransItem<'tcx>, + visited: &mut FnvHashSet>, + recursion_depths: &mut DefIdMap) { + if !visited.insert(starting_point.clone()) { + // We've been here already, no need to search again. + return; + } + debug!("BEGIN collect_items_rec({})", starting_point.to_string(ccx)); + + let mut neighbors = Vec::new(); + let recursion_depth_reset; + + match starting_point { + TransItem::DropGlue(_) | + TransItem::Static(_) => { + recursion_depth_reset = None; + } + TransItem::Fn { + def_id, + substs: ref param_substs + } => { + // Keep track of the monomorphization recursion depth + recursion_depth_reset = Some(check_recursion_limit(ccx, + def_id, + recursion_depths)); + + // Scan the MIR in order to find function calls, closures, and + // drop-glue + let mir_not_found_error_message = || { + format!("Could not find MIR for function: {}", + ccx.tcx().item_path_str(def_id)) + }; + + let external_mir = if !def_id.is_local() { + ccx.sess().cstore.maybe_get_item_mir(ccx.tcx(), def_id) + } else { + None + }; + + let mir_opt = match external_mir { + Some(ref mir) => Some(mir), + None => { + let node_id = ccx.tcx().map.as_local_node_id(def_id).unwrap(); + ccx.mir_map().get(&node_id) + } + }; + + let mir = errors::expect(ccx.sess().diagnostic(), + mir_opt, + mir_not_found_error_message); + + let mut visitor = MirNeighborCollector { + ccx: ccx, + mir: mir, + output: &mut neighbors, + param_substs: param_substs + }; + + visitor.visit_mir(mir); + } + } + + for neighbour in neighbors { + collect_items_rec(ccx, neighbour, visited, recursion_depths); + } + + if let Some((def_id, depth)) = recursion_depth_reset { + recursion_depths.insert(def_id, depth); + } + + debug!("END collect_items_rec({})", starting_point.to_string(ccx)); +} + +fn check_recursion_limit<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId, + recursion_depths: &mut DefIdMap) + -> (DefId, usize) { + let recursion_depth = recursion_depths.get(&def_id) + .map(|x| *x) + .unwrap_or(0); + debug!(" => recursion depth={}", recursion_depth); + + // Code that needs to instantiate the same function recursively + // more than the recursion limit is assumed to be causing an + // infinite expansion. + if recursion_depth > ccx.sess().recursion_limit.get() { + if let Some(node_id) = ccx.tcx().map.as_local_node_id(def_id) { + ccx.sess().span_fatal(ccx.tcx().map.span(node_id), + "reached the recursion limit during monomorphization"); + } else { + let error = format!("reached the recursion limit during \ + monomorphization of '{}'", + ccx.tcx().item_path_str(def_id)); + ccx.sess().fatal(&error[..]); + } + } + + recursion_depths.insert(def_id, recursion_depth + 1); + + (def_id, recursion_depth) +} + +struct MirNeighborCollector<'a, 'tcx: 'a> { + ccx: &'a CrateContext<'a, 'tcx>, + mir: &'a mir::Mir<'tcx>, + output: &'a mut Vec>, + param_substs: &'tcx Substs<'tcx> +} + +impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { + + fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>) { + debug!("visiting rvalue {:?}", *rvalue); + + match *rvalue { + mir::Rvalue::Aggregate(mir::AggregateKind::Closure(def_id, + ref substs), _) => { + assert!(can_have_local_instance(self.ccx, def_id)); + let trans_item = create_fn_trans_item(self.ccx, + def_id, + substs.func_substs, + self.param_substs); + self.output.push(trans_item); + } + // When doing an cast from a regular pointer to a fat pointer, we + // have to instantiate all methods of the trait being cast to, so we + // can build the appropriate vtable. + mir::Rvalue::Cast(mir::CastKind::Unsize, ref operand, target_ty) => { + let target_ty = monomorphize::apply_param_substs(self.ccx.tcx(), + self.param_substs, + &target_ty); + let source_ty = self.mir.operand_ty(self.ccx.tcx(), operand); + let source_ty = monomorphize::apply_param_substs(self.ccx.tcx(), + self.param_substs, + &source_ty); + let (source_ty, target_ty) = find_vtable_types_for_unsizing(self.ccx, + source_ty, + target_ty); + // This could also be a different Unsize instruction, like + // from a fixed sized array to a slice. But we are only + // interested in things that produce a vtable. + if target_ty.is_trait() && !source_ty.is_trait() { + create_trans_items_for_vtable_methods(self.ccx, + target_ty, + source_ty, + self.output); + } + } + mir::Rvalue::Box(_) => { + let exchange_malloc_fn_def_id = + self.ccx + .tcx() + .lang_items + .require(ExchangeMallocFnLangItem) + .expect("Could not find ExchangeMallocFnLangItem"); + + assert!(can_have_local_instance(self.ccx, exchange_malloc_fn_def_id)); + let exchange_malloc_fn_trans_item = + create_fn_trans_item(self.ccx, + exchange_malloc_fn_def_id, + &Substs::trans_empty(), + self.param_substs); + + self.output.push(exchange_malloc_fn_trans_item); + } + _ => { /* not interesting */ } + } + + self.super_rvalue(rvalue); + } + + fn visit_lvalue(&mut self, + lvalue: &mir::Lvalue<'tcx>, + context: mir_visit::LvalueContext) { + debug!("visiting lvalue {:?}", *lvalue); + + if let mir_visit::LvalueContext::Drop = context { + let ty = self.mir.lvalue_ty(self.ccx.tcx(), lvalue) + .to_ty(self.ccx.tcx()); + + let ty = monomorphize::apply_param_substs(self.ccx.tcx(), + self.param_substs, + &ty); + let ty = self.ccx.tcx().erase_regions(&ty); + + create_drop_glue_trans_items(self.ccx, + ty, + self.param_substs, + &mut self.output); + } + + self.super_lvalue(lvalue, context); + } + + fn visit_operand(&mut self, operand: &mir::Operand<'tcx>) { + debug!("visiting operand {:?}", *operand); + + let callee = match *operand { + mir::Operand::Constant(mir::Constant { + literal: mir::Literal::Item { + def_id, + kind, + substs + }, + .. + }) if is_function_or_method(kind) => Some((def_id, substs)), + _ => None + }; + + if let Some((callee_def_id, callee_substs)) = callee { + debug!(" => operand is callable"); + + // `callee_def_id` might refer to a trait method instead of a + // concrete implementation, so we have to find the actual + // implementation. For example, the call might look like + // + // std::cmp::partial_cmp(0i32, 1i32) + // + // Calling do_static_dispatch() here will map the def_id of + // `std::cmp::partial_cmp` to the def_id of `i32::partial_cmp` + let dispatched = do_static_dispatch(self.ccx, + callee_def_id, + callee_substs, + self.param_substs); + + if let Some((callee_def_id, callee_substs)) = dispatched { + // if we have a concrete impl (which we might not have + // in the case of something compiler generated like an + // object shim or a closure that is handled differently), + // we check if the callee is something that will actually + // result in a translation item ... + if can_result_in_trans_item(self.ccx, callee_def_id) { + // ... and create one if it does. + let trans_item = create_fn_trans_item(self.ccx, + callee_def_id, + callee_substs, + self.param_substs); + self.output.push(trans_item); + } + } + } + + self.super_operand(operand); + + fn is_function_or_method(item_kind: mir::ItemKind) -> bool { + match item_kind { + mir::ItemKind::Constant => false, + mir::ItemKind::Function | + mir::ItemKind::Method => true + } + } + + fn can_result_in_trans_item<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId) + -> bool { + if !match ccx.tcx().lookup_item_type(def_id).ty.sty { + ty::TyBareFn(Some(def_id), _) => { + // Some constructors also have type TyBareFn but they are + // always instantiated inline and don't result in + // translation item. + match ccx.tcx().map.get_if_local(def_id) { + Some(hir_map::NodeVariant(_)) | + Some(hir_map::NodeStructCtor(_)) => false, + Some(_) => true, + None => { + ccx.sess().cstore.variant_kind(def_id).is_none() + } + } + } + ty::TyClosure(..) => true, + _ => false + } { + return false; + } + + can_have_local_instance(ccx, def_id) + } + } +} + +fn can_have_local_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId) + -> bool { + // Take a look if we have the definition available. If not, we + // will not emit code for this item in the local crate, and thus + // don't create a translation item for it. + def_id.is_local() || ccx.sess().cstore.is_item_mir_available(def_id) +} + +fn create_drop_glue_trans_items<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + mono_ty: ty::Ty<'tcx>, + param_substs: &'tcx Substs<'tcx>, + output: &mut Vec>) +{ + visit_types_of_owned_components(ccx, + mono_ty, + &mut FnvHashSet(), + &mut |ty| { + debug!("create_drop_glue_trans_items: {}", type_to_string(ccx, ty)); + // Add a translation item for the drop glue, if even this type does not + // need to be dropped (in which case it has been mapped to i8) + output.push(TransItem::DropGlue(ty)); + + if glue::type_needs_drop(ccx.tcx(), ty) { + + // Make sure the exchange_free_fn() lang-item gets translated if + // there is a boxed value. + if let ty::TyBox(_) = ty.sty { + + let exchange_free_fn_def_id = ccx.tcx() + .lang_items + .require(ExchangeFreeFnLangItem) + .expect("Could not find ExchangeFreeFnLangItem"); + + assert!(can_have_local_instance(ccx, exchange_free_fn_def_id)); + let exchange_free_fn_trans_item = + create_fn_trans_item(ccx, + exchange_free_fn_def_id, + &Substs::trans_empty(), + param_substs); + + output.push(exchange_free_fn_trans_item); + } + + // If the type implements Drop, also add a translation item for the + // monomorphized Drop::drop() implementation. + let destructor_did = match ty.sty { + ty::TyStruct(def, _) | + ty::TyEnum(def, _) => def.destructor(), + _ => None + }; + + if let Some(destructor_did) = destructor_did { + use rustc::middle::ty::ToPolyTraitRef; + + let drop_trait_def_id = ccx.tcx() + .lang_items + .drop_trait() + .unwrap(); + + let self_type_substs = ccx.tcx().mk_substs( + Substs::trans_empty().with_self_ty(ty)); + + let trait_ref = ty::TraitRef { + def_id: drop_trait_def_id, + substs: self_type_substs, + }.to_poly_trait_ref(); + + let substs = match fulfill_obligation(ccx, DUMMY_SP, trait_ref) { + traits::VtableImpl(data) => data.substs, + _ => unreachable!() + }; + + if can_have_local_instance(ccx, destructor_did) { + let trans_item = create_fn_trans_item(ccx, + destructor_did, + ccx.tcx().mk_substs(substs), + param_substs); + output.push(trans_item); + } + } + + true + } else { + false + } + }); + + fn visit_types_of_owned_components<'a, 'tcx, F>(ccx: &CrateContext<'a, 'tcx>, + ty: ty::Ty<'tcx>, + visited: &mut FnvHashSet>, + mut f: &mut F) + where F: FnMut(ty::Ty<'tcx>) -> bool + { + let ty = glue::get_drop_glue_type(ccx, ty); + + if !visited.insert(ty) { + return; + } + + if !f(ty) { + // Don't recurse further + return; + } + + match ty.sty { + ty::TyBool | + ty::TyChar | + ty::TyInt(_) | + ty::TyUint(_) | + ty::TyStr | + ty::TyFloat(_) | + ty::TyRawPtr(_) | + ty::TyRef(..) | + ty::TyBareFn(..) | + ty::TySlice(_) | + ty::TyTrait(_) => { + /* nothing to do */ + } + ty::TyStruct(ref adt_def, substs) | + ty::TyEnum(ref adt_def, substs) => { + for field in adt_def.all_fields() { + let field_type = monomorphize::apply_param_substs(ccx.tcx(), + substs, + &field.unsubst_ty()); + visit_types_of_owned_components(ccx, field_type, visited, f); + } + } + ty::TyClosure(_, ref substs) => { + for upvar_ty in &substs.upvar_tys { + visit_types_of_owned_components(ccx, upvar_ty, visited, f); + } + } + ty::TyBox(inner_type) | + ty::TyArray(inner_type, _) => { + visit_types_of_owned_components(ccx, inner_type, visited, f); + } + ty::TyTuple(ref args) => { + for arg in args { + visit_types_of_owned_components(ccx, arg, visited, f); + } + } + ty::TyProjection(_) | + ty::TyParam(_) | + ty::TyInfer(_) | + ty::TyError => { + ccx.sess().bug("encountered unexpected type"); + } + } + } +} + +fn do_static_dispatch<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + fn_def_id: DefId, + fn_substs: &'tcx Substs<'tcx>, + param_substs: &'tcx Substs<'tcx>) + -> Option<(DefId, &'tcx Substs<'tcx>)> { + debug!("do_static_dispatch(fn_def_id={}, fn_substs={:?}, param_substs={:?})", + def_id_to_string(ccx, fn_def_id, None), + fn_substs, + param_substs); + + let is_trait_method = ccx.tcx().trait_of_item(fn_def_id).is_some(); + + if is_trait_method { + match ccx.tcx().impl_or_trait_item(fn_def_id) { + ty::MethodTraitItem(ref method) => { + match method.container { + ty::TraitContainer(trait_def_id) => { + debug!(" => trait method, attempting to find impl"); + do_static_trait_method_dispatch(ccx, + method, + trait_def_id, + fn_substs, + param_substs) + } + ty::ImplContainer(_) => { + // This is already a concrete implementation + debug!(" => impl method"); + Some((fn_def_id, fn_substs)) + } + } + } + _ => unreachable!() + } + } else { + debug!(" => regular function"); + // The function is not part of an impl or trait, no dispatching + // to be done + Some((fn_def_id, fn_substs)) + } +} + +// Given a trait-method and substitution information, find out the actual +// implementation of the trait method. +fn do_static_trait_method_dispatch<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + trait_method: &ty::Method, + trait_id: DefId, + callee_substs: &'tcx Substs<'tcx>, + param_substs: &'tcx Substs<'tcx>) + -> Option<(DefId, &'tcx Substs<'tcx>)> { + let tcx = ccx.tcx(); + debug!("do_static_trait_method_dispatch(trait_method={}, \ + trait_id={}, \ + callee_substs={:?}, \ + param_substs={:?}", + def_id_to_string(ccx, trait_method.def_id, None), + def_id_to_string(ccx, trait_id, None), + callee_substs, + param_substs); + + let rcvr_substs = monomorphize::apply_param_substs(tcx, + param_substs, + callee_substs); + + let trait_ref = ty::Binder(rcvr_substs.to_trait_ref(tcx, trait_id)); + let vtbl = fulfill_obligation(ccx, DUMMY_SP, trait_ref); + + // Now that we know which impl is being used, we can dispatch to + // the actual function: + match vtbl { + traits::VtableImpl(traits::VtableImplData { + impl_def_id: impl_did, + substs: impl_substs, + nested: _ }) => + { + let callee_substs = impl_substs.with_method_from(&rcvr_substs); + let impl_method = tcx.get_impl_method(impl_did, + callee_substs, + trait_method.name); + Some((impl_method.method.def_id, tcx.mk_substs(impl_method.substs))) + } + // If we have a closure or a function pointer, we will also encounter + // the concrete closure/function somewhere else (during closure or fn + // pointer construction). That's where we track those things. + traits::VtableClosure(..) | + traits::VtableFnPointer(..) | + traits::VtableObject(..) => { + None + } + _ => { + tcx.sess.bug(&format!("static call to invalid vtable: {:?}", vtbl)) + } + } +} + +/// For given pair of source and target type that occur in an unsizing coercion, +/// this function finds the pair of types that determines the vtable linking +/// them. +/// +/// For example, the source type might be `&SomeStruct` and the target type\ +/// might be `&SomeTrait` in a cast like: +/// +/// let src: &SomeStruct = ...; +/// let target = src as &SomeTrait; +/// +/// Then the output of this function would be (SomeStruct, SomeTrait) since for +/// constructing the `target` fat-pointer we need the vtable for that pair. +/// +/// Things can get more complicated though because there's also the case where +/// the unsized type occurs as a field: +/// +/// ```rust +/// struct ComplexStruct { +/// a: u32, +/// b: f64, +/// c: T +/// } +/// ``` +/// +/// In this case, if `T` is sized, `&ComplexStruct` is a thin pointer. If `T` +/// is unsized, `&SomeStruct` is a fat pointer, and the vtable it points to is +/// for the pair of `T` (which is a trait) and the concrete type that `T` was +/// originally coerced from: +/// +/// let src: &ComplexStruct = ...; +/// let target = src as &ComplexStruct; +/// +/// Again, we want this `find_vtable_types_for_unsizing()` to provide the pair +/// `(SomeStruct, SomeTrait)`. +/// +/// Finally, there is also the case of custom unsizing coercions, e.g. for +/// smart pointers such as `Rc` and `Arc`. +fn find_vtable_types_for_unsizing<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + source_ty: ty::Ty<'tcx>, + target_ty: ty::Ty<'tcx>) + -> (ty::Ty<'tcx>, ty::Ty<'tcx>) { + match (&source_ty.sty, &target_ty.sty) { + (&ty::TyBox(a), &ty::TyBox(b)) | + (&ty::TyRef(_, ty::TypeAndMut { ty: a, .. }), + &ty::TyRef(_, ty::TypeAndMut { ty: b, .. })) | + (&ty::TyRef(_, ty::TypeAndMut { ty: a, .. }), + &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) | + (&ty::TyRawPtr(ty::TypeAndMut { ty: a, .. }), + &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) => { + let (inner_source, inner_target) = (a, b); + + if !type_is_sized(ccx.tcx(), inner_source) { + (inner_source, inner_target) + } else { + ccx.tcx().struct_lockstep_tails(inner_source, inner_target) + } + } + + (&ty::TyStruct(source_adt_def, source_substs), + &ty::TyStruct(target_adt_def, target_substs)) => { + assert_eq!(source_adt_def, target_adt_def); + + let kind = custom_coerce_unsize_info(ccx, source_ty, target_ty); + + let coerce_index = match kind { + CustomCoerceUnsized::Struct(i) => i + }; + + let source_fields = &source_adt_def.struct_variant().fields; + let target_fields = &target_adt_def.struct_variant().fields; + + assert!(coerce_index < source_fields.len() && + source_fields.len() == target_fields.len()); + + find_vtable_types_for_unsizing(ccx, + source_fields[coerce_index].ty(ccx.tcx(), + source_substs), + target_fields[coerce_index].ty(ccx.tcx(), + target_substs)) + } + _ => ccx.sess() + .bug(&format!("find_vtable_types_for_unsizing: invalid coercion {:?} -> {:?}", + source_ty, + target_ty)) + } +} + +fn create_fn_trans_item<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId, + fn_substs: &Substs<'tcx>, + param_substs: &Substs<'tcx>) + -> TransItem<'tcx> +{ + debug!("create_fn_trans_item(def_id={}, fn_substs={:?}, param_substs={:?})", + def_id_to_string(ccx, def_id, None), + fn_substs, + param_substs); + + // We only get here, if fn_def_id either designates a local item or + // an inlineable external item. Non-inlineable external items are + // ignored because we don't want to generate any code for them. + let concrete_substs = monomorphize::apply_param_substs(ccx.tcx(), + param_substs, + fn_substs); + let concrete_substs = ccx.tcx().erase_regions(&concrete_substs); + + let trans_item = TransItem::Fn { + def_id: def_id, + substs: ccx.tcx().mk_substs(concrete_substs), + }; + + return trans_item; +} + +/// Creates a `TransItem` for each method that is referenced by the vtable for +/// the given trait/impl pair. +fn create_trans_items_for_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + trait_ty: ty::Ty<'tcx>, + impl_ty: ty::Ty<'tcx>, + output: &mut Vec>) { + assert!(!trait_ty.needs_subst() && !impl_ty.needs_subst()); + + if let ty::TyTrait(ref trait_ty) = trait_ty.sty { + let poly_trait_ref = trait_ty.principal_trait_ref_with_self_ty(ccx.tcx(), + impl_ty); + + // Walk all methods of the trait, including those of its supertraits + for trait_ref in traits::supertraits(ccx.tcx(), poly_trait_ref) { + let vtable = fulfill_obligation(ccx, DUMMY_SP, trait_ref); + match vtable { + traits::VtableImpl( + traits::VtableImplData { + impl_def_id, + substs, + nested: _ }) => { + let items = meth::get_vtable_methods(ccx, impl_def_id, substs) + .into_iter() + // filter out None values + .filter_map(|opt_impl_method| opt_impl_method) + // create translation items + .filter_map(|impl_method| { + if can_have_local_instance(ccx, impl_method.method.def_id) { + let substs = ccx.tcx().mk_substs(impl_method.substs); + Some(create_fn_trans_item(ccx, + impl_method.method.def_id, + substs, + &Substs::trans_empty())) + } else { + None + } + }) + .collect::>(); + + output.extend(items.into_iter()); + } + _ => { /* */ } + } + } + } +} + +//=----------------------------------------------------------------------------- +// Root Collection +//=----------------------------------------------------------------------------- + +struct RootCollector<'b, 'a: 'b, 'tcx: 'a + 'b> { + ccx: &'b CrateContext<'a, 'tcx>, + mode: TransItemCollectionMode, + output: &'b mut Vec>, + enclosing_item: Option<&'tcx hir::Item>, + trans_empty_substs: &'tcx Substs<'tcx> +} + +impl<'b, 'a, 'v> hir_visit::Visitor<'v> for RootCollector<'b, 'a, 'v> { + fn visit_item(&mut self, item: &'v hir::Item) { + let old_enclosing_item = self.enclosing_item; + self.enclosing_item = Some(item); + + match item.node { + hir::ItemExternCrate(..) | + hir::ItemUse(..) | + hir::ItemForeignMod(..) | + hir::ItemTy(..) | + hir::ItemDefaultImpl(..) | + hir::ItemTrait(..) | + hir::ItemConst(..) | + hir::ItemMod(..) => { + // Nothing to do, just keep recursing... + } + + hir::ItemImpl(..) => { + if self.mode == TransItemCollectionMode::Eager { + create_trans_items_for_default_impls(self.ccx, + item, + self.trans_empty_substs, + self.output); + } + } + + hir::ItemEnum(_, ref generics) | + hir::ItemStruct(_, ref generics) => { + if !generics.is_parameterized() { + let ty = { + let tables = self.ccx.tcx().tables.borrow(); + tables.node_types[&item.id] + }; + + if self.mode == TransItemCollectionMode::Eager { + debug!("RootCollector: ADT drop-glue for {}", + def_id_to_string(self.ccx, + self.ccx.tcx().map.local_def_id(item.id), + None)); + + create_drop_glue_trans_items(self.ccx, + ty, + self.trans_empty_substs, + self.output); + } + } + } + hir::ItemStatic(..) => { + debug!("RootCollector: ItemStatic({})", + def_id_to_string(self.ccx, + self.ccx.tcx().map.local_def_id(item.id), + None)); + self.output.push(TransItem::Static(item.id)); + } + hir::ItemFn(_, _, constness, _, ref generics, _) => { + if !generics.is_type_parameterized() && + constness == hir::Constness::NotConst { + let def_id = self.ccx.tcx().map.local_def_id(item.id); + + debug!("RootCollector: ItemFn({})", + def_id_to_string(self.ccx, def_id, None)); + + self.output.push(TransItem::Fn { + def_id: def_id, + substs: self.trans_empty_substs + }); + } + } + } + + hir_visit::walk_item(self, item); + self.enclosing_item = old_enclosing_item; + } + + fn visit_impl_item(&mut self, ii: &'v hir::ImplItem) { + match ii.node { + hir::ImplItemKind::Method(hir::MethodSig { + ref generics, + constness, + .. + }, _) if constness == hir::Constness::NotConst => { + let hir_map = &self.ccx.tcx().map; + let parent_node_id = hir_map.get_parent_node(ii.id); + let is_impl_generic = match hir_map.expect_item(parent_node_id) { + &hir::Item { + node: hir::ItemImpl(_, _, ref generics, _, _, _), + .. + } => { + generics.is_type_parameterized() + } + _ => { + unreachable!() + } + }; + + if !generics.is_type_parameterized() && !is_impl_generic { + let def_id = self.ccx.tcx().map.local_def_id(ii.id); + + debug!("RootCollector: MethodImplItem({})", + def_id_to_string(self.ccx, def_id, None)); + + self.output.push(TransItem::Fn { + def_id: def_id, + substs: self.trans_empty_substs + }); + } + } + _ => { /* Nothing to do here */ } + } + + hir_visit::walk_impl_item(self, ii) + } +} + +fn create_trans_items_for_default_impls<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + item: &'tcx hir::Item, + trans_empty_substs: &'tcx Substs<'tcx>, + output: &mut Vec>) { + match item.node { + hir::ItemImpl(_, + _, + ref generics, + _, + _, + ref items) => { + if generics.is_type_parameterized() { + return + } + + let tcx = ccx.tcx(); + let impl_def_id = tcx.map.local_def_id(item.id); + + debug!("create_trans_items_for_default_impls(item={})", + def_id_to_string(ccx, impl_def_id, None)); + + if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) { + let default_impls = tcx.provided_trait_methods(trait_ref.def_id); + let callee_substs = tcx.mk_substs(tcx.erase_regions(trait_ref.substs)); + let overridden_methods: FnvHashSet<_> = items.iter() + .map(|item| item.name) + .collect(); + for default_impl in default_impls { + if overridden_methods.contains(&default_impl.name) { + continue; + } + + if default_impl.generics.has_type_params(subst::FnSpace) { + continue; + } + + // The substitutions we have are on the impl, so we grab + // the method type from the impl to substitute into. + let mth = tcx.get_impl_method(impl_def_id, + callee_substs.clone(), + default_impl.name); + + assert!(mth.is_provided); + + let predicates = mth.method.predicates.predicates.subst(tcx, &mth.substs); + if !normalize_and_test_predicates(ccx, predicates.into_vec()) { + continue; + } + + if can_have_local_instance(ccx, default_impl.def_id) { + let item = create_fn_trans_item(ccx, + default_impl.def_id, + callee_substs, + trans_empty_substs); + output.push(item); + } + } + } + } + _ => { + unreachable!() + } + } +} + +//=----------------------------------------------------------------------------- +// TransItem String Keys +//=----------------------------------------------------------------------------- + +// The code below allows for producing a unique string key for a trans item. +// These keys are used by the handwritten auto-tests, so they need to be +// predictable and human-readable. +// +// Note: A lot of this could looks very similar to what's already in the +// ppaux module. It would be good to refactor things so we only have one +// parameterizable implementation for printing types. + +/// Same as `unique_type_name()` but with the result pushed onto the given +/// `output` parameter. +pub fn push_unique_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + t: ty::Ty<'tcx>, + output: &mut String) { + match t.sty { + ty::TyBool => output.push_str("bool"), + ty::TyChar => output.push_str("char"), + ty::TyStr => output.push_str("str"), + ty::TyInt(ast::TyIs) => output.push_str("isize"), + ty::TyInt(ast::TyI8) => output.push_str("i8"), + ty::TyInt(ast::TyI16) => output.push_str("i16"), + ty::TyInt(ast::TyI32) => output.push_str("i32"), + ty::TyInt(ast::TyI64) => output.push_str("i64"), + ty::TyUint(ast::TyUs) => output.push_str("usize"), + ty::TyUint(ast::TyU8) => output.push_str("u8"), + ty::TyUint(ast::TyU16) => output.push_str("u16"), + ty::TyUint(ast::TyU32) => output.push_str("u32"), + ty::TyUint(ast::TyU64) => output.push_str("u64"), + ty::TyFloat(ast::TyF32) => output.push_str("f32"), + ty::TyFloat(ast::TyF64) => output.push_str("f64"), + ty::TyStruct(adt_def, substs) | + ty::TyEnum(adt_def, substs) => { + push_item_name(cx, adt_def.did, output); + push_type_params(cx, substs, &[], output); + }, + ty::TyTuple(ref component_types) => { + output.push('('); + for &component_type in component_types { + push_unique_type_name(cx, component_type, output); + output.push_str(", "); + } + if !component_types.is_empty() { + output.pop(); + output.pop(); + } + output.push(')'); + }, + ty::TyBox(inner_type) => { + output.push_str("Box<"); + push_unique_type_name(cx, inner_type, output); + output.push('>'); + }, + ty::TyRawPtr(ty::TypeAndMut { ty: inner_type, mutbl } ) => { + output.push('*'); + match mutbl { + hir::MutImmutable => output.push_str("const "), + hir::MutMutable => output.push_str("mut "), + } + + push_unique_type_name(cx, inner_type, output); + }, + ty::TyRef(_, ty::TypeAndMut { ty: inner_type, mutbl }) => { + output.push('&'); + if mutbl == hir::MutMutable { + output.push_str("mut "); + } + + push_unique_type_name(cx, inner_type, output); + }, + ty::TyArray(inner_type, len) => { + output.push('['); + push_unique_type_name(cx, inner_type, output); + output.push_str(&format!("; {}", len)); + output.push(']'); + }, + ty::TySlice(inner_type) => { + output.push('['); + push_unique_type_name(cx, inner_type, output); + output.push(']'); + }, + ty::TyTrait(ref trait_data) => { + push_item_name(cx, trait_data.principal.skip_binder().def_id, output); + push_type_params(cx, + &trait_data.principal.skip_binder().substs, + &trait_data.bounds.projection_bounds, + output); + }, + ty::TyBareFn(_, &ty::BareFnTy{ unsafety, abi, ref sig } ) => { + if unsafety == hir::Unsafety::Unsafe { + output.push_str("unsafe "); + } + + if abi != ::syntax::abi::Rust { + output.push_str("extern \""); + output.push_str(abi.name()); + output.push_str("\" "); + } + + output.push_str("fn("); + + let sig = cx.tcx().erase_late_bound_regions(sig); + if !sig.inputs.is_empty() { + for ¶meter_type in &sig.inputs { + push_unique_type_name(cx, parameter_type, output); + output.push_str(", "); + } + output.pop(); + output.pop(); + } + + if sig.variadic { + if !sig.inputs.is_empty() { + output.push_str(", ..."); + } else { + output.push_str("..."); + } + } + + output.push(')'); + + match sig.output { + ty::FnConverging(result_type) if result_type.is_nil() => {} + ty::FnConverging(result_type) => { + output.push_str(" -> "); + push_unique_type_name(cx, result_type, output); + } + ty::FnDiverging => { + output.push_str(" -> !"); + } + } + }, + ty::TyClosure(def_id, ref closure_substs) => { + push_item_name(cx, def_id, output); + output.push_str("{"); + output.push_str(&format!("{}:{}", def_id.krate, def_id.index.as_usize())); + output.push_str("}"); + push_type_params(cx, closure_substs.func_substs, &[], output); + } + ty::TyError | + ty::TyInfer(_) | + ty::TyProjection(..) | + ty::TyParam(_) => { + cx.sess().bug(&format!("debuginfo: Trying to create type name for \ + unexpected type: {:?}", t)); + } + } +} + +fn push_item_name(ccx: &CrateContext, + def_id: DefId, + output: &mut String) { + if def_id.is_local() { + let node_id = ccx.tcx().map.as_local_node_id(def_id).unwrap(); + let inlined_from = ccx.external_srcs() + .borrow() + .get(&node_id) + .map(|def_id| *def_id); + + if let Some(extern_def_id) = inlined_from { + push_item_name(ccx, extern_def_id, output); + return; + } + + output.push_str(&ccx.link_meta().crate_name); + output.push_str("::"); + } + + for part in ccx.tcx().def_path(def_id) { + output.push_str(&format!("{}[{}]::", + part.data.as_interned_str(), + part.disambiguator)); + } + + output.pop(); + output.pop(); +} + +fn push_type_params<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + substs: &Substs<'tcx>, + projections: &[ty::PolyProjectionPredicate<'tcx>], + output: &mut String) { + if substs.types.is_empty() && projections.is_empty() { + return; + } + + output.push('<'); + + for &type_parameter in &substs.types { + push_unique_type_name(cx, type_parameter, output); + output.push_str(", "); + } + + for projection in projections { + let projection = projection.skip_binder(); + let name = token::get_ident_interner().get(projection.projection_ty.item_name); + output.push_str(&name[..]); + output.push_str("="); + push_unique_type_name(cx, projection.ty, output); + output.push_str(", "); + } + + output.pop(); + output.pop(); + + output.push('>'); +} + +fn push_def_id_as_string<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId, + substs: Option<&Substs<'tcx>>, + output: &mut String) { + push_item_name(ccx, def_id, output); + + if let Some(substs) = substs { + push_type_params(ccx, substs, &[], output); + } +} + +fn def_id_to_string<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId, + substs: Option<&Substs<'tcx>>) + -> String { + let mut output = String::new(); + push_def_id_as_string(ccx, def_id, substs, &mut output); + output +} + +fn type_to_string<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + ty: ty::Ty<'tcx>) + -> String { + let mut output = String::new(); + push_unique_type_name(ccx, ty, &mut output); + output +} + +impl<'tcx> TransItem<'tcx> { + + pub fn to_string<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> String { + let hir_map = &ccx.tcx().map; + + return match *self { + TransItem::DropGlue(t) => { + let mut s = String::with_capacity(32); + s.push_str("drop-glue "); + push_unique_type_name(ccx, t, &mut s); + s + } + TransItem::Fn { def_id, ref substs } => { + to_string_internal(ccx, "fn ", def_id, Some(substs)) + }, + TransItem::Static(node_id) => { + let def_id = hir_map.local_def_id(node_id); + to_string_internal(ccx, "static ", def_id, None) + }, + }; + + fn to_string_internal<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + prefix: &str, + def_id: DefId, + substs: Option<&Substs<'tcx>>) + -> String { + let mut result = String::with_capacity(32); + result.push_str(prefix); + push_def_id_as_string(ccx, def_id, substs, &mut result); + result + } + } + + fn to_raw_string(&self) -> String { + match *self { + TransItem::DropGlue(t) => { + format!("DropGlue({})", t as *const _ as usize) + } + TransItem::Fn { def_id, substs } => { + format!("Fn({:?}, {})", + def_id, + substs as *const _ as usize) + } + TransItem::Static(id) => { + format!("Static({:?})", id) + } + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum TransItemState { + PredictedAndGenerated, + PredictedButNotGenerated, + NotPredictedButGenerated, +} + +pub fn print_collection_results<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>) { + use std::hash::{Hash, SipHasher, Hasher}; + + if !cfg!(debug_assertions) { + return; + } + + if ccx.sess().opts.debugging_opts.print_trans_items.is_none() { + return; + } + + fn hash(t: &T) -> u64 { + let mut s = SipHasher::new(); + t.hash(&mut s); + s.finish() + } + + let trans_items = ccx.translation_items().borrow(); + + { + // Check for duplicate item keys + let mut item_keys = FnvHashMap(); + + for (item, item_state) in trans_items.iter() { + let k = item.to_string(&ccx); + + if item_keys.contains_key(&k) { + let prev: (TransItem, TransItemState) = item_keys[&k]; + debug!("DUPLICATE KEY: {}", k); + debug!(" (1) {:?}, {:?}, hash: {}, raw: {}", + prev.0, + prev.1, + hash(&prev.0), + prev.0.to_raw_string()); + + debug!(" (2) {:?}, {:?}, hash: {}, raw: {}", + *item, + *item_state, + hash(item), + item.to_raw_string()); + } else { + item_keys.insert(k, (*item, *item_state)); + } + } + } + + let mut predicted_but_not_generated = FnvHashSet(); + let mut not_predicted_but_generated = FnvHashSet(); + let mut predicted = FnvHashSet(); + let mut generated = FnvHashSet(); + + for (item, item_state) in trans_items.iter() { + let item_key = item.to_string(&ccx); + + match *item_state { + TransItemState::PredictedAndGenerated => { + predicted.insert(item_key.clone()); + generated.insert(item_key); + } + TransItemState::PredictedButNotGenerated => { + predicted_but_not_generated.insert(item_key.clone()); + predicted.insert(item_key); + } + TransItemState::NotPredictedButGenerated => { + not_predicted_but_generated.insert(item_key.clone()); + generated.insert(item_key); + } + } + } + + debug!("Total number of translation items predicted: {}", predicted.len()); + debug!("Total number of translation items generated: {}", generated.len()); + debug!("Total number of translation items predicted but not generated: {}", + predicted_but_not_generated.len()); + debug!("Total number of translation items not predicted but generated: {}", + not_predicted_but_generated.len()); + + if generated.len() > 0 { + debug!("Failed to predict {}% of translation items", + (100 * not_predicted_but_generated.len()) / generated.len()); + } + if generated.len() > 0 { + debug!("Predict {}% too many translation items", + (100 * predicted_but_not_generated.len()) / generated.len()); + } + + debug!(""); + debug!("Not predicted but generated:"); + debug!("============================"); + for item in not_predicted_but_generated { + debug!(" - {}", item); + } + + debug!(""); + debug!("Predicted but not generated:"); + debug!("============================"); + for item in predicted_but_not_generated { + debug!(" - {}", item); + } +} diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 0f5d8dbd94d..daf36bf8693 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -30,6 +30,7 @@ use middle::def_id::DefId; use trans::{adt, closure, debuginfo, expr, inline, machine}; use trans::base::{self, push_ctxt}; +use trans::collector::TransItem; use trans::common::{self, type_is_sized, ExprOrMethodCall, node_id_substs, C_nil, const_get_elt}; use trans::common::{CrateContext, C_integral, C_floating, C_bool, C_str_slice, C_bytes, val_ty}; use trans::common::{C_struct, C_undef, const_to_opt_int, const_to_opt_uint, VariantInfo, C_uint}; @@ -1016,6 +1017,9 @@ pub fn trans_static(ccx: &CrateContext, id: ast::NodeId, attrs: &[ast::Attribute]) -> Result { + + ccx.record_translation_item_as_generated(TransItem::Static(id)); + unsafe { let _icx = push_ctxt("trans_static"); let g = base::get_item_val(ccx, id); diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 7301afa46fc..e8868cdbcc7 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -24,6 +24,7 @@ use trans::declare; use trans::glue::DropGlueKind; use trans::monomorphize::MonoId; +use trans::collector::{TransItem, TransItemState}; use trans::type_::{Type, TypeNames}; use middle::subst::Substs; use middle::ty::{self, Ty}; @@ -77,6 +78,8 @@ pub struct SharedCrateContext<'a, 'tcx: 'a> { available_drop_glues: RefCell, String>>, use_dll_storage_attrs: bool, + + translation_items: RefCell, TransItemState>>, } /// The local portion of a `CrateContext`. There is one `LocalCrateContext` @@ -245,7 +248,6 @@ fn next(&mut self) -> Option<(CrateContext<'a, 'tcx>, bool)> { } } - unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (ContextRef, ModuleRef) { let llcx = llvm::LLVMContextCreate(); let mod_name = CString::new(mod_name).unwrap(); @@ -354,6 +356,7 @@ pub fn new(crate_name: &str, check_drop_flag_for_sanity: check_drop_flag_for_sanity, available_drop_glues: RefCell::new(FnvHashMap()), use_dll_storage_attrs: use_dll_storage_attrs, + translation_items: RefCell::new(FnvHashMap()), }; for i in 0..local_count { @@ -829,6 +832,24 @@ pub fn use_dll_storage_attrs(&self) -> bool { pub fn mir_map(&self) -> &'b MirMap<'tcx> { self.shared.mir_map } + + pub fn translation_items(&self) -> &RefCell, TransItemState>> { + &self.shared.translation_items + } + + pub fn record_translation_item_as_generated(&self, cgi: TransItem<'tcx>) { + if self.sess().opts.debugging_opts.print_trans_items.is_none() { + return; + } + + let mut codegen_items = self.translation_items().borrow_mut(); + + if codegen_items.contains_key(&cgi) { + codegen_items.insert(cgi, TransItemState::PredictedAndGenerated); + } else { + codegen_items.insert(cgi, TransItemState::NotPredictedButGenerated); + } + } } pub struct TypeOfDepthLock<'a, 'tcx: 'a>(&'a LocalCrateContext<'tcx>); diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index ada37c5d8df..3de6447f4ee 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -55,9 +55,7 @@ use llvm::{self, ValueRef, TypeKind}; use middle::const_qualif::ConstQualif; use middle::def::Def; -use middle::lang_items::CoerceUnsizedTraitLangItem; -use middle::subst::{Substs, VecPerParamSpace}; -use middle::traits; +use middle::subst::Substs; use trans::{_match, adt, asm, base, callee, closure, consts, controlflow}; use trans::base::*; use trans::build::*; @@ -500,24 +498,7 @@ fn coerce_unsized<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let source = unpack_datum!(bcx, source.to_ref_datum(bcx)); assert!(target.kind.is_by_ref()); - let trait_substs = Substs::erased(VecPerParamSpace::new(vec![target.ty], - vec![source.ty], - Vec::new())); - let trait_ref = ty::Binder(ty::TraitRef { - def_id: langcall(bcx, Some(span), "coercion", - CoerceUnsizedTraitLangItem), - substs: bcx.tcx().mk_substs(trait_substs) - }); - - let kind = match fulfill_obligation(bcx.ccx(), span, trait_ref) { - traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => { - bcx.tcx().custom_coerce_unsized_kind(impl_def_id) - } - vtable => { - bcx.sess().span_bug(span, &format!("invalid CoerceUnsized vtable: {:?}", - vtable)); - } - }; + let kind = custom_coerce_unsize_info(bcx.ccx(), source.ty, target.ty); let repr_source = adt::represent_type(bcx.ccx(), source.ty); let src_fields = match &*repr_source { diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs index a1165ffe171..9073846e2ae 100644 --- a/src/librustc_trans/trans/glue.rs +++ b/src/librustc_trans/trans/glue.rs @@ -28,6 +28,7 @@ use trans::callee; use trans::cleanup; use trans::cleanup::CleanupMethods; +use trans::collector::TransItem; use trans::common::*; use trans::debuginfo::DebugLoc; use trans::declare; @@ -88,7 +89,7 @@ pub fn trans_exchange_free_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } } -fn type_needs_drop<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool { +pub fn type_needs_drop<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool { tcx.type_needs_drop_given_env(ty, &tcx.empty_parameter_environment()) } @@ -496,6 +497,11 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, in fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueKind<'tcx>) -> Block<'blk, 'tcx> { let t = g.ty(); + + bcx.ccx() + .record_translation_item_as_generated(TransItem::DropGlue(bcx.tcx() + .erase_regions(&t))); + let skip_dtor = match g { DropGlueKind::Ty(_) => false, DropGlueKind::TyContents(_) => true }; // NB: v0 is an *alias* of type t here, not a direct value. let _icx = push_ctxt("make_drop_glue"); diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 4695595d16f..b8f577f654c 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -495,7 +495,23 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, impl_def_id: id, substs, nested: _ }) => { - emit_vtable_methods(ccx, id, substs, param_substs).into_iter() + let nullptr = C_null(Type::nil(ccx).ptr_to()); + get_vtable_methods(ccx, id, substs) + .into_iter() + .map(|opt_mth| { + match opt_mth { + Some(mth) => { + trans_fn_ref_with_substs(ccx, + mth.method.def_id, + ExprId(0), + param_substs, + mth.substs).val + } + None => nullptr + } + }) + .collect::>() + .into_iter() } traits::VtableClosure( traits::VtableClosureData { @@ -549,18 +565,14 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, vtable } -fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - impl_id: DefId, - substs: subst::Substs<'tcx>, - param_substs: &'tcx subst::Substs<'tcx>) - -> Vec +pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + impl_id: DefId, + substs: subst::Substs<'tcx>) + -> Vec>> { let tcx = ccx.tcx(); - debug!("emit_vtable_methods(impl_id={:?}, substs={:?}, param_substs={:?})", - impl_id, - substs, - param_substs); + debug!("get_vtable_methods(impl_id={:?}, substs={:?}", impl_id, substs); let trt_id = match tcx.impl_trait_ref(impl_id) { Some(t_id) => t_id.def_id, @@ -570,7 +582,6 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, tcx.populate_implementations_for_trait_if_necessary(trt_id); - let nullptr = C_null(Type::nil(ccx).ptr_to()); let trait_item_def_ids = tcx.trait_item_def_ids(trt_id); trait_item_def_ids .iter() @@ -587,7 +598,7 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // method could never be called from this object, just supply // null. .map(|trait_method_def_id| { - debug!("emit_vtable_methods: trait_method_def_id={:?}", + debug!("get_vtable_methods: trait_method_def_id={:?}", trait_method_def_id); let trait_method_type = match tcx.impl_or_trait_item(trait_method_def_id) { @@ -598,18 +609,18 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // Some methods cannot be called on an object; skip those. if !traits::is_vtable_safe_method(tcx, trt_id, &trait_method_type) { - debug!("emit_vtable_methods: not vtable safe"); - return nullptr; + debug!("get_vtable_methods: not vtable safe"); + return None; } - debug!("emit_vtable_methods: trait_method_type={:?}", + debug!("get_vtable_methods: trait_method_type={:?}", trait_method_type); // The substitutions we have are on the impl, so we grab // the method type from the impl to substitute into. let mth = tcx.get_impl_method(impl_id, substs.clone(), name); - debug!("emit_vtable_methods: mth={:?}", mth); + debug!("get_vtable_methods: mth={:?}", mth); // If this is a default method, it's possible that it // relies on where clauses that do not hold for this @@ -619,16 +630,12 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, if mth.is_provided { let predicates = mth.method.predicates.predicates.subst(tcx, &mth.substs); if !normalize_and_test_predicates(ccx, predicates.into_vec()) { - debug!("emit_vtable_methods: predicates do not hold"); - return nullptr; + debug!("get_vtable_methods: predicates do not hold"); + return None; } } - trans_fn_ref_with_substs(ccx, - mth.method.def_id, - ExprId(0), - param_substs, - mth.substs).val + Some(mth) }) .collect() } diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs index d87c17cbf88..898f260f8df 100644 --- a/src/librustc_trans/trans/mod.rs +++ b/src/librustc_trans/trans/mod.rs @@ -58,6 +58,7 @@ mod meth; mod mir; mod monomorphize; +mod collector; mod tvec; mod type_; mod type_of; diff --git a/src/test/auxiliary/cgu_export_trait_method.rs b/src/test/auxiliary/cgu_export_trait_method.rs new file mode 100644 index 00000000000..49b8e43836e --- /dev/null +++ b/src/test/auxiliary/cgu_export_trait_method.rs @@ -0,0 +1,34 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +pub trait Trait : Sized { + fn without_self() -> u32; + fn without_self_default() -> u32 { 0 } + + fn with_default_impl(self) -> Self { self } + fn with_default_impl_generic(self, x: T) -> (Self, T) { (self, x) } + + fn without_default_impl(x: u32) -> (Self, u32); + fn without_default_impl_generic(x: T) -> (Self, T); +} + +impl Trait for char { + fn without_self() -> u32 { 2 } + fn without_default_impl(x: u32) -> (Self, u32) { ('c', x) } + fn without_default_impl_generic(x: T) -> (Self, T) { ('c', x) } +} + +impl Trait for u32 { + fn without_self() -> u32 { 1 } + fn without_default_impl(x: u32) -> (Self, u32) { (0, x) } + fn without_default_impl_generic(x: T) -> (Self, T) { (0, x) } +} diff --git a/src/test/auxiliary/cgu_extern_closures.rs b/src/test/auxiliary/cgu_extern_closures.rs new file mode 100644 index 00000000000..944d85db508 --- /dev/null +++ b/src/test/auxiliary/cgu_extern_closures.rs @@ -0,0 +1,33 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +#[inline] +pub fn inlined_fn(x: i32, y: i32) -> i32 { + + let closure = |a, b| { a + b }; + + closure(x, y) +} + +pub fn inlined_fn_generic(x: i32, y: i32, z: T) -> (i32, T) { + + let closure = |a, b| { a + b }; + + (closure(x, y), z) +} + +pub fn non_inlined_fn(x: i32, y: i32) -> i32 { + + let closure = |a, b| { a + b }; + + closure(x, y) +} diff --git a/src/test/auxiliary/cgu_generic_function.rs b/src/test/auxiliary/cgu_generic_function.rs new file mode 100644 index 00000000000..83bb65bc2b7 --- /dev/null +++ b/src/test/auxiliary/cgu_generic_function.rs @@ -0,0 +1,36 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +struct Struct(u32); + +pub fn foo(x: T) -> (T, u32, i8) { + let (x, Struct(y)) = bar(x); + (x, y, 2) +} + + +fn bar(x: T) -> (T, Struct) { + let _ = not_exported_and_not_generic(0); + (x, Struct(1)) +} + +// These should not contribute to the codegen items of other crates. +#[inline(never)] +pub fn exported_but_not_generic(x: i32) -> i64 { + x as i64 +} + +#[inline(never)] +fn not_exported_and_not_generic(x: u32) -> u64 { + x as u64 +} + diff --git a/src/test/codegen-units/cross-crate-closures.rs b/src/test/codegen-units/cross-crate-closures.rs new file mode 100644 index 00000000000..32b07d42fec --- /dev/null +++ b/src/test/codegen-units/cross-crate-closures.rs @@ -0,0 +1,35 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +// aux-build:cgu_extern_closures.rs +extern crate cgu_extern_closures; + +//~ TRANS_ITEM fn cross_crate_closures::main[0] +fn main() { + + //~ TRANS_ITEM fn cgu_extern_closures[0]::inlined_fn[0] + //~ TRANS_ITEM fn cgu_extern_closures[0]::inlined_fn[0]::{{closure}}[0] + let _ = cgu_extern_closures::inlined_fn(1, 2); + + //~ TRANS_ITEM fn cgu_extern_closures[0]::inlined_fn_generic[0] + //~ TRANS_ITEM fn cgu_extern_closures[0]::inlined_fn_generic[0]::{{closure}}[0] + let _ = cgu_extern_closures::inlined_fn_generic(3, 4, 5i32); + + // Nothing should be generated for this call, we just link to the instance instance + // in the extern crate. + let _ = cgu_extern_closures::non_inlined_fn(6, 7); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/cross-crate-generic-functions.rs b/src/test/codegen-units/cross-crate-generic-functions.rs new file mode 100644 index 00000000000..82d940a1548 --- /dev/null +++ b/src/test/codegen-units/cross-crate-generic-functions.rs @@ -0,0 +1,34 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +// aux-build:cgu_generic_function.rs +extern crate cgu_generic_function; + +//~ TRANS_ITEM fn cross_crate_generic_functions::main[0] +fn main() +{ + //~ TRANS_ITEM fn cgu_generic_function[0]::bar[0] + //~ TRANS_ITEM fn cgu_generic_function[0]::foo[0] + let _ = cgu_generic_function::foo(1u32); + + //~ TRANS_ITEM fn cgu_generic_function[0]::bar[0] + //~ TRANS_ITEM fn cgu_generic_function[0]::foo[0] + let _ = cgu_generic_function::foo(2u64); + + // This should not introduce a codegen item + let _ = cgu_generic_function::exported_but_not_generic(3); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/cross-crate-trait-method.rs b/src/test/codegen-units/cross-crate-trait-method.rs new file mode 100644 index 00000000000..aa1f6b06c81 --- /dev/null +++ b/src/test/codegen-units/cross-crate-trait-method.rs @@ -0,0 +1,60 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +// aux-build:cgu_export_trait_method.rs +extern crate cgu_export_trait_method; + +use cgu_export_trait_method::Trait; + +//~ TRANS_ITEM fn cross_crate_trait_method::main[0] +fn main() +{ + // The object code of these methods is contained in the external crate, so + // calling them should *not* introduce codegen items in the current crate. + let _: (u32, u32) = Trait::without_default_impl(0); + let _: (char, u32) = Trait::without_default_impl(0); + + // Currently, no object code is generated for trait methods with default + // implemenations, unless they are actually called from somewhere. Therefore + // we cannot import the implementations and have to create our own inline. + //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl[0] + let _ = Trait::with_default_impl(0u32); + //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl[0] + let _ = Trait::with_default_impl('c'); + + + + //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl_generic[0] + let _ = Trait::with_default_impl_generic(0u32, "abc"); + //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl_generic[0] + let _ = Trait::with_default_impl_generic(0u32, false); + + //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl_generic[0] + let _ = Trait::with_default_impl_generic('x', 1i16); + //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl_generic[0] + let _ = Trait::with_default_impl_generic('y', 0i32); + + //~ TRANS_ITEM fn cgu_export_trait_method[0]::u32.Trait[0]::without_default_impl_generic[0] + let _: (u32, char) = Trait::without_default_impl_generic('c'); + //~ TRANS_ITEM fn cgu_export_trait_method[0]::u32.Trait[0]::without_default_impl_generic[0] + let _: (u32, bool) = Trait::without_default_impl_generic(false); + + //~ TRANS_ITEM fn cgu_export_trait_method[0]::char.Trait[0]::without_default_impl_generic[0] + let _: (char, char) = Trait::without_default_impl_generic('c'); + //~ TRANS_ITEM fn cgu_export_trait_method[0]::char.Trait[0]::without_default_impl_generic[0] + let _: (char, bool) = Trait::without_default_impl_generic(false); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/function-as-argument.rs b/src/test/codegen-units/function-as-argument.rs new file mode 100644 index 00000000000..3a9d56c2a8b --- /dev/null +++ b/src/test/codegen-units/function-as-argument.rs @@ -0,0 +1,46 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +fn take_fn_once(f: F, x: T1, y: T2) { + (f)(x, y) +} + +fn function(_: T1, _: T2) {} + +fn take_fn_pointer(f: fn(T1, T2), x: T1, y: T2) { + (f)(x, y) +} + +//~ TRANS_ITEM fn function_as_argument::main[0] +fn main() { + + //~ TRANS_ITEM fn function_as_argument::take_fn_once[0] + //~ TRANS_ITEM fn function_as_argument::function[0] + take_fn_once(function, 0u32, "abc"); + + //~ TRANS_ITEM fn function_as_argument::take_fn_once[0] + //~ TRANS_ITEM fn function_as_argument::function[0] + take_fn_once(function, 'c', 0f64); + + //~ TRANS_ITEM fn function_as_argument::take_fn_pointer[0] + //~ TRANS_ITEM fn function_as_argument::function[0] + take_fn_pointer(function, 0i32, ()); + + //~ TRANS_ITEM fn function_as_argument::take_fn_pointer[0] + //~ TRANS_ITEM fn function_as_argument::function[0] + take_fn_pointer(function, 0f32, 0i64); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/generic-drop-glue.rs b/src/test/codegen-units/generic-drop-glue.rs new file mode 100644 index 00000000000..f89d6e61bc5 --- /dev/null +++ b/src/test/codegen-units/generic-drop-glue.rs @@ -0,0 +1,98 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +struct StructWithDrop { + x: T1, + y: T2, +} + +impl Drop for StructWithDrop { + fn drop(&mut self) {} +} + +struct StructNoDrop { + x: T1, + y: T2, +} + +enum EnumWithDrop { + A(T1), + B(T2) +} + +impl Drop for EnumWithDrop { + fn drop(&mut self) {} +} + +enum EnumNoDrop { + A(T1), + B(T2) +} + + +struct NonGenericNoDrop(i32); + +struct NonGenericWithDrop(i32); +//~ TRANS_ITEM drop-glue generic_drop_glue::NonGenericWithDrop[0] + +impl Drop for NonGenericWithDrop { + fn drop(&mut self) {} +//~ TRANS_ITEM fn generic_drop_glue::NonGenericWithDrop.Drop[0]::drop[0] +} + +//~ TRANS_ITEM fn generic_drop_glue::main[0] +fn main() { + //~ TRANS_ITEM drop-glue generic_drop_glue::StructWithDrop[0] + //~ TRANS_ITEM fn generic_drop_glue::StructWithDrop.Drop[0]::drop[0] + let _ = StructWithDrop { x: 0i8, y: 'a' }.x; + + //~ TRANS_ITEM drop-glue generic_drop_glue::StructWithDrop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]> + //~ TRANS_ITEM fn generic_drop_glue::StructWithDrop.Drop[0]::drop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]> + let _ = StructWithDrop { x: "&str", y: NonGenericNoDrop(0) }.y; + + // Should produce no drop glue + let _ = StructNoDrop { x: 'a', y: 0u32 }.x; + + // This is supposed to generate drop-glue because it contains a field that + // needs to be dropped. + //~ TRANS_ITEM drop-glue generic_drop_glue::StructNoDrop[0] + let _ = StructNoDrop { x: NonGenericWithDrop(0), y: 0f64 }.y; + + //~ TRANS_ITEM drop-glue generic_drop_glue::EnumWithDrop[0] + //~ TRANS_ITEM fn generic_drop_glue::EnumWithDrop.Drop[0]::drop[0] + let _ = match EnumWithDrop::A::(0) { + EnumWithDrop::A(x) => x, + EnumWithDrop::B(x) => x as i32 + }; + + //~ TRANS_ITEM drop-glue generic_drop_glue::EnumWithDrop[0] + //~ TRANS_ITEM fn generic_drop_glue::EnumWithDrop.Drop[0]::drop[0] + let _ = match EnumWithDrop::B::(1.0) { + EnumWithDrop::A(x) => x, + EnumWithDrop::B(x) => x as f64 + }; + + let _ = match EnumNoDrop::A::(0) { + EnumNoDrop::A(x) => x, + EnumNoDrop::B(x) => x as i32 + }; + + let _ = match EnumNoDrop::B::(1.0) { + EnumNoDrop::A(x) => x, + EnumNoDrop::B(x) => x as f64 + }; +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/generic-functions.rs b/src/test/codegen-units/generic-functions.rs new file mode 100644 index 00000000000..5ec1f7fbc3c --- /dev/null +++ b/src/test/codegen-units/generic-functions.rs @@ -0,0 +1,64 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +fn foo1(a: T1) -> (T1, u32) { + (a, 1) +} + +fn foo2(a: T1, b: T2) -> (T1, T2) { + (a, b) +} + +fn foo3(a: T1, b: T2, c: T3) -> (T1, T2, T3) { + (a, b, c) +} + +// This function should be instantiated even if no used +//~ TRANS_ITEM fn generic_functions::lifetime_only[0] +pub fn lifetime_only<'a>(a: &'a u32) -> &'a u32 { + a +} + +//~ TRANS_ITEM fn generic_functions::main[0] +fn main() { + //~ TRANS_ITEM fn generic_functions::foo1[0] + let _ = foo1(2i32); + //~ TRANS_ITEM fn generic_functions::foo1[0] + let _ = foo1(2i64); + //~ TRANS_ITEM fn generic_functions::foo1[0]<&str> + let _ = foo1("abc"); + //~ TRANS_ITEM fn generic_functions::foo1[0] + let _ = foo1('v'); + + //~ TRANS_ITEM fn generic_functions::foo2[0] + let _ = foo2(2i32, 2i32); + //~ TRANS_ITEM fn generic_functions::foo2[0] + let _ = foo2(2i64, "abc"); + //~ TRANS_ITEM fn generic_functions::foo2[0]<&str, usize> + let _ = foo2("a", 2usize); + //~ TRANS_ITEM fn generic_functions::foo2[0] + let _ = foo2('v', ()); + + //~ TRANS_ITEM fn generic_functions::foo3[0] + let _ = foo3(2i32, 2i32, 2i32); + //~ TRANS_ITEM fn generic_functions::foo3[0] + let _ = foo3(2i64, "abc", 'c'); + //~ TRANS_ITEM fn generic_functions::foo3[0] + let _ = foo3(0i16, "a", 2usize); + //~ TRANS_ITEM fn generic_functions::foo3[0] + let _ = foo3('v', (), ()); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/generic-impl.rs b/src/test/codegen-units/generic-impl.rs new file mode 100644 index 00000000000..6e6bb5cbf53 --- /dev/null +++ b/src/test/codegen-units/generic-impl.rs @@ -0,0 +1,81 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +struct Struct { + x: T, + f: fn(x: T) -> T, +} + +fn id(x: T) -> T { x } + +impl Struct { + + fn new(x: T) -> Struct { + Struct { + x: x, + f: id + } + } + + fn get(self, x: T2) -> (T, T2) { + (self.x, x) + } +} + +pub struct LifeTimeOnly<'a> { + _a: &'a u32 +} + +impl<'a> LifeTimeOnly<'a> { + + //~ TRANS_ITEM fn generic_impl::LifeTimeOnly<'a>[0]::foo[0] + pub fn foo(&self) {} + //~ TRANS_ITEM fn generic_impl::LifeTimeOnly<'a>[0]::bar[0] + pub fn bar(&'a self) {} + //~ TRANS_ITEM fn generic_impl::LifeTimeOnly<'a>[0]::baz[0] + pub fn baz<'b>(&'b self) {} + + pub fn non_instantiated(&self) {} +} + + +//~ TRANS_ITEM fn generic_impl::main[0] +fn main() { + //~ TRANS_ITEM fn generic_impl::Struct[0]::new[0] + //~ TRANS_ITEM fn generic_impl::id[0] + //~ TRANS_ITEM fn generic_impl::Struct[0]::get[0] + let _ = Struct::new(0i32).get(0i16); + + //~ TRANS_ITEM fn generic_impl::Struct[0]::new[0] + //~ TRANS_ITEM fn generic_impl::id[0] + //~ TRANS_ITEM fn generic_impl::Struct[0]::get[0] + let _ = Struct::new(0i64).get(0i16); + + //~ TRANS_ITEM fn generic_impl::Struct[0]::new[0] + //~ TRANS_ITEM fn generic_impl::id[0] + //~ TRANS_ITEM fn generic_impl::Struct[0]::get[0] + let _ = Struct::new('c').get(0i16); + + //~ TRANS_ITEM fn generic_impl::Struct[0]::new[0]<&str> + //~ TRANS_ITEM fn generic_impl::id[0]<&str> + //~ TRANS_ITEM fn generic_impl::Struct[0]::get[0], i16> + let _ = Struct::new(Struct::new("str")).get(0i16); + + //~ TRANS_ITEM fn generic_impl::Struct[0]::new[0]> + //~ TRANS_ITEM fn generic_impl::id[0]> + let _ = (Struct::new(Struct::new("str")).f)(Struct::new("str")); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/impl-in-non-instantiated-generic.rs b/src/test/codegen-units/impl-in-non-instantiated-generic.rs new file mode 100644 index 00000000000..e17a1a7094f --- /dev/null +++ b/src/test/codegen-units/impl-in-non-instantiated-generic.rs @@ -0,0 +1,36 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +trait SomeTrait { + fn foo(&self); +} + +// This function is never instantiated but the contained impl must still be +// discovered. +pub fn generic_function(x: T) -> (T, i32) { + impl SomeTrait for i64 { + //~ TRANS_ITEM fn impl_in_non_instantiated_generic::generic_function[0]::i64.SomeTrait[0]::foo[0] + fn foo(&self) {} + } + + (x, 0) +} + +//~ TRANS_ITEM fn impl_in_non_instantiated_generic::main[0] +fn main() { + 0i64.foo(); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/instantiation-through-vtable.rs b/src/test/codegen-units/instantiation-through-vtable.rs new file mode 100644 index 00000000000..46587b2b0a1 --- /dev/null +++ b/src/test/codegen-units/instantiation-through-vtable.rs @@ -0,0 +1,42 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +trait Trait { + fn foo(&self) -> u32; + fn bar(&self); +} + +struct Struct { + _a: T +} + +impl Trait for Struct { + fn foo(&self) -> u32 { 0 } + fn bar(&self) {} +} + +//~ TRANS_ITEM fn instantiation_through_vtable::main[0] +fn main() { + let s1 = Struct { _a: 0u32 }; + + //~ TRANS_ITEM fn instantiation_through_vtable::Struct.Trait[0]::foo[0] + //~ TRANS_ITEM fn instantiation_through_vtable::Struct.Trait[0]::bar[0] + let _ = &s1 as &Trait; + + let s1 = Struct { _a: 0u64 }; + //~ TRANS_ITEM fn instantiation_through_vtable::Struct.Trait[0]::foo[0] + //~ TRANS_ITEM fn instantiation_through_vtable::Struct.Trait[0]::bar[0] + let _ = &s1 as &Trait; +} diff --git a/src/test/codegen-units/items-within-generic-items.rs b/src/test/codegen-units/items-within-generic-items.rs new file mode 100644 index 00000000000..a2dcd81b675 --- /dev/null +++ b/src/test/codegen-units/items-within-generic-items.rs @@ -0,0 +1,44 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +fn generic_fn(a: T) -> (T, i32) { + //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0]::nested_fn[0] + fn nested_fn(a: i32) -> i32 { + a + 1 + } + + let x = { + //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0]::nested_fn[1] + fn nested_fn(a: i32) -> i32 { + a + 2 + } + + 1 + nested_fn(1) + }; + + return (a, x + nested_fn(0)); +} + +//~ TRANS_ITEM fn items_within_generic_items::main[0] +fn main() { + //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0] + let _ = generic_fn(0i64); + //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0] + let _ = generic_fn(0u16); + //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0] + let _ = generic_fn(0i8); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/non-generic-closures.rs b/src/test/codegen-units/non-generic-closures.rs new file mode 100644 index 00000000000..bf8804e12ce --- /dev/null +++ b/src/test/codegen-units/non-generic-closures.rs @@ -0,0 +1,63 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +//~ TRANS_ITEM fn non_generic_closures::temporary[0] +fn temporary() { + //~ TRANS_ITEM fn non_generic_closures::temporary[0]::{{closure}}[0] + (|a: u32| { + let _ = a; + })(4); +} + +//~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_but_not_executed[0] +fn assigned_to_variable_but_not_executed() { + //~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_but_not_executed[0]::{{closure}}[0] + let _x = |a: i16| { + let _ = a + 1; + }; +} + +//~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_executed_directly[0] +fn assigned_to_variable_executed_indirectly() { + //~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_executed_directly[0]::{{closure}}[0] + let f = |a: i32| { + let _ = a + 2; + }; + run_closure(&f); +} + +//~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_executed_indirectly[0] +fn assigned_to_variable_executed_directly() { + //~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_executed_indirectly[0]::{{closure}}[0] + let f = |a: i64| { + let _ = a + 3; + }; + f(4); +} + +//~ TRANS_ITEM fn non_generic_closures::main[0] +fn main() { + temporary(); + assigned_to_variable_but_not_executed(); + assigned_to_variable_executed_directly(); + assigned_to_variable_executed_indirectly(); +} + +//~ TRANS_ITEM fn non_generic_closures::run_closure[0] +fn run_closure(f: &Fn(i32)) { + f(3); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/non-generic-drop-glue.rs b/src/test/codegen-units/non-generic-drop-glue.rs new file mode 100644 index 00000000000..a82e85b7a53 --- /dev/null +++ b/src/test/codegen-units/non-generic-drop-glue.rs @@ -0,0 +1,56 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +//~ TRANS_ITEM drop-glue non_generic_drop_glue::StructWithDrop[0] +struct StructWithDrop { + x: i32 +} + +impl Drop for StructWithDrop { + //~ TRANS_ITEM fn non_generic_drop_glue::StructWithDrop.Drop[0]::drop[0] + fn drop(&mut self) {} +} + +struct StructNoDrop { + x: i32 +} + +//~ TRANS_ITEM drop-glue non_generic_drop_glue::EnumWithDrop[0] +enum EnumWithDrop { + A(i32) +} + +impl Drop for EnumWithDrop { + //~ TRANS_ITEM fn non_generic_drop_glue::EnumWithDrop.Drop[0]::drop[0] + fn drop(&mut self) {} +} + +enum EnumNoDrop { + A(i32) +} + +//~ TRANS_ITEM fn non_generic_drop_glue::main[0] +fn main() { + let _ = StructWithDrop { x: 0 }.x; + let _ = StructNoDrop { x: 0 }.x; + let _ = match EnumWithDrop::A(0) { + EnumWithDrop::A(x) => x + }; + let _ = match EnumNoDrop::A(0) { + EnumNoDrop::A(x) => x + }; +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/non-generic-functions.rs b/src/test/codegen-units/non-generic-functions.rs new file mode 100644 index 00000000000..687ce7fa05c --- /dev/null +++ b/src/test/codegen-units/non-generic-functions.rs @@ -0,0 +1,81 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +//~ TRANS_ITEM fn non_generic_functions::foo[0] +fn foo() { + { + //~ TRANS_ITEM fn non_generic_functions::foo[0]::foo[0] + fn foo() {} + foo(); + } + + { + //~ TRANS_ITEM fn non_generic_functions::foo[0]::foo[1] + fn foo() {} + foo(); + } +} + +//~ TRANS_ITEM fn non_generic_functions::bar[0] +fn bar() { + //~ TRANS_ITEM fn non_generic_functions::bar[0]::baz[0] + fn baz() {} + baz(); +} + +struct Struct { _x: i32 } + +impl Struct { + //~ TRANS_ITEM fn non_generic_functions::Struct[0]::foo[0] + fn foo() { + { + //~ TRANS_ITEM fn non_generic_functions::Struct[0]::foo[0]::foo[0] + fn foo() {} + foo(); + } + + { + //~ TRANS_ITEM fn non_generic_functions::Struct[0]::foo[0]::foo[1] + fn foo() {} + foo(); + } + } + + //~ TRANS_ITEM fn non_generic_functions::Struct[0]::bar[0] + fn bar(&self) { + { + //~ TRANS_ITEM fn non_generic_functions::Struct[0]::bar[0]::foo[0] + fn foo() {} + foo(); + } + + { + //~ TRANS_ITEM fn non_generic_functions::Struct[0]::bar[0]::foo[1] + fn foo() {} + foo(); + } + } +} + +//~ TRANS_ITEM fn non_generic_functions::main[0] +fn main() { + foo(); + bar(); + Struct::foo(); + let x = Struct { _x: 0 }; + x.bar(); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/overloaded-operators.rs b/src/test/codegen-units/overloaded-operators.rs new file mode 100644 index 00000000000..134110222f3 --- /dev/null +++ b/src/test/codegen-units/overloaded-operators.rs @@ -0,0 +1,72 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] +#![crate_type="lib"] + +use std::ops::{Index, IndexMut, Add, Deref}; + +pub struct Indexable { + data: [u8; 3] +} + +impl Index for Indexable { + type Output = u8; + + //~ TRANS_ITEM fn overloaded_operators::Indexable.Index[0]::index[0] + fn index(&self, index: usize) -> &Self::Output { + if index >= 3 { + &self.data[0] + } else { + &self.data[index] + } + } +} + +impl IndexMut for Indexable { + //~ TRANS_ITEM fn overloaded_operators::Indexable.IndexMut[0]::index_mut[0] + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + if index >= 3 { + &mut self.data[0] + } else { + &mut self.data[index] + } + } +} + + +//~ TRANS_ITEM fn overloaded_operators::Equatable.::std::cmp::PartialEq[0]::eq[0] +//~ TRANS_ITEM fn overloaded_operators::Equatable.::std::cmp::PartialEq[0]::ne[0] +#[derive(PartialEq)] +pub struct Equatable(u32); + + +impl Add for Equatable { + type Output = u32; + + //~ TRANS_ITEM fn overloaded_operators::Equatable.Add[0]::add[0] + fn add(self, rhs: u32) -> u32 { + self.0 + rhs + } +} + +impl Deref for Equatable { + type Target = u32; + + //~ TRANS_ITEM fn overloaded_operators::Equatable.Deref[0]::deref[0] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/statics-and-consts.rs b/src/test/codegen-units/statics-and-consts.rs new file mode 100644 index 00000000000..7c8b2b117ef --- /dev/null +++ b/src/test/codegen-units/statics-and-consts.rs @@ -0,0 +1,64 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +static STATIC1: i64 = { + const STATIC1_CONST1: i64 = 2; + 1 + CONST1 as i64 + STATIC1_CONST1 +}; + +const CONST1: i64 = { + const CONST1_1: i64 = { + const CONST1_1_1: i64 = 2; + CONST1_1_1 + 1 + }; + 1 + CONST1_1 as i64 +}; + +fn foo() { + let _ = { + const CONST2: i64 = 0; + static STATIC2: i64 = CONST2; + + let x = { + const CONST2: i64 = 1; + static STATIC2: i64 = CONST2; + STATIC2 + }; + + x + STATIC2 + }; + + let _ = { + const CONST2: i64 = 0; + static STATIC2: i64 = CONST2; + STATIC2 + }; +} + +fn main() { + foo(); + let _ = STATIC1; +} + +//~ TRANS_ITEM static statics_and_consts::STATIC1[0] + +//~ TRANS_ITEM fn statics_and_consts::foo[0] +//~ TRANS_ITEM static statics_and_consts::foo[0]::STATIC2[0] +//~ TRANS_ITEM static statics_and_consts::foo[0]::STATIC2[1] +//~ TRANS_ITEM static statics_and_consts::foo[0]::STATIC2[2] + +//~ TRANS_ITEM fn statics_and_consts::main[0] + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/trait-implementations.rs b/src/test/codegen-units/trait-implementations.rs new file mode 100644 index 00000000000..590859f15a3 --- /dev/null +++ b/src/test/codegen-units/trait-implementations.rs @@ -0,0 +1,82 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +pub trait SomeTrait { + fn foo(&self); + fn bar(&self, x: T); +} + +impl SomeTrait for i64 { + + //~ TRANS_ITEM fn trait_implementations::i64.SomeTrait[0]::foo[0] + fn foo(&self) {} + + fn bar(&self, _: T) {} +} + +impl SomeTrait for i32 { + + //~ TRANS_ITEM fn trait_implementations::i32.SomeTrait[0]::foo[0] + fn foo(&self) {} + + fn bar(&self, _: T) {} +} + +pub trait SomeGenericTrait { + fn foo(&self, x: T); + fn bar(&self, x: T, y: T2); +} + +// Concrete impl of generic trait +impl SomeGenericTrait for f64 { + + //~ TRANS_ITEM fn trait_implementations::f64.SomeGenericTrait[0]::foo[0] + fn foo(&self, _: u32) {} + + fn bar(&self, _: u32, _: T2) {} +} + +// Generic impl of generic trait +impl SomeGenericTrait for f32 { + + fn foo(&self, _: T) {} + fn bar(&self, _: T, _: T2) {} +} + +//~ TRANS_ITEM fn trait_implementations::main[0] +fn main() { + //~ TRANS_ITEM fn trait_implementations::i32.SomeTrait[0]::bar[0] + 0i32.bar('x'); + + //~ TRANS_ITEM fn trait_implementations::f64.SomeGenericTrait[0]::bar[0]<&str> + 0f64.bar(0u32, "&str"); + + //~ TRANS_ITEM fn trait_implementations::f64.SomeGenericTrait[0]::bar[0]<()> + 0f64.bar(0u32, ()); + + //~ TRANS_ITEM fn trait_implementations::f32.SomeGenericTrait[0]::foo[0] + 0f32.foo('x'); + + //~ TRANS_ITEM fn trait_implementations::f32.SomeGenericTrait[0]::foo[0] + 0f32.foo(-1i64); + + //~ TRANS_ITEM fn trait_implementations::f32.SomeGenericTrait[0]::bar[0] + 0f32.bar(0u32, ()); + + //~ TRANS_ITEM fn trait_implementations::f32.SomeGenericTrait[0]::bar[0]<&str, &str> + 0f32.bar("&str", "&str"); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/trait-method-as-argument.rs b/src/test/codegen-units/trait-method-as-argument.rs new file mode 100644 index 00000000000..fdf63df5471 --- /dev/null +++ b/src/test/codegen-units/trait-method-as-argument.rs @@ -0,0 +1,62 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +trait Trait : Sized { + fn foo(self) -> Self { self } +} + +impl Trait for u32 { + fn foo(self) -> u32 { self } +} + +impl Trait for char { +} + +fn take_foo_once T>(f: F, arg: T) -> T { + (f)(arg) +} + +fn take_foo T>(f: F, arg: T) -> T { + (f)(arg) +} + +fn take_foo_mut T>(mut f: F, arg: T) -> T { + (f)(arg) +} + +//~ TRANS_ITEM fn trait_method_as_argument::main[0] +fn main() { + //~ TRANS_ITEM fn trait_method_as_argument::take_foo_once[0] u32> + //~ TRANS_ITEM fn trait_method_as_argument::u32.Trait[0]::foo[0] + take_foo_once(Trait::foo, 0u32); + + //~ TRANS_ITEM fn trait_method_as_argument::take_foo_once[0] char> + //~ TRANS_ITEM fn trait_method_as_argument::Trait[0]::foo[0] + take_foo_once(Trait::foo, 'c'); + + //~ TRANS_ITEM fn trait_method_as_argument::take_foo[0] u32> + take_foo(Trait::foo, 0u32); + + //~ TRANS_ITEM fn trait_method_as_argument::take_foo[0] char> + take_foo(Trait::foo, 'c'); + + //~ TRANS_ITEM fn trait_method_as_argument::take_foo_mut[0] u32> + take_foo_mut(Trait::foo, 0u32); + + //~ TRANS_ITEM fn trait_method_as_argument::take_foo_mut[0] char> + take_foo_mut(Trait::foo, 'c'); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/trait-method-default-impl.rs b/src/test/codegen-units/trait-method-default-impl.rs new file mode 100644 index 00000000000..2b3b83cb7ec --- /dev/null +++ b/src/test/codegen-units/trait-method-default-impl.rs @@ -0,0 +1,70 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +trait SomeTrait { + fn foo(&self) { } + fn bar(&self, x: T) -> T { x } +} + +impl SomeTrait for i8 { + // take the default implementations + + // For the non-generic foo(), we should generate a codegen-item even if it + // is not called anywhere + //~ TRANS_ITEM fn trait_method_default_impl::SomeTrait[0]::foo[0] +} + +trait SomeGenericTrait { + fn foo(&self) { } + fn bar(&self, x: T1, y: T2) {} +} + +// Non-generic impl of generic trait +impl SomeGenericTrait for i32 { + // take the default implementations + + // For the non-generic foo(), we should generate a codegen-item even if it + // is not called anywhere + //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::foo[0] +} + +// Non-generic impl of generic trait +impl SomeGenericTrait for u32 { + // take the default implementations + // since nothing is monomorphic here, nothing should be generated unless used somewhere. +} + +//~ TRANS_ITEM fn trait_method_default_impl::main[0] +fn main() { + //~ TRANS_ITEM fn trait_method_default_impl::SomeTrait[0]::bar[0] + let _ = 1i8.bar('c'); + + //~ TRANS_ITEM fn trait_method_default_impl::SomeTrait[0]::bar[0] + let _ = 2i8.bar("&str"); + + //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + 0i32.bar(0u64, 'c'); + + //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + 0i32.bar(0u64, "&str"); + + //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + 0u32.bar(0i8, &['c']); + + //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + 0u32.bar(0i16, ()); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/transitive-drop-glue.rs b/src/test/codegen-units/transitive-drop-glue.rs new file mode 100644 index 00000000000..6982cb9299a --- /dev/null +++ b/src/test/codegen-units/transitive-drop-glue.rs @@ -0,0 +1,55 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +//~ TRANS_ITEM drop-glue transitive_drop_glue::Root[0] +struct Root(Intermediate); +//~ TRANS_ITEM drop-glue transitive_drop_glue::Intermediate[0] +struct Intermediate(Leaf); +//~ TRANS_ITEM drop-glue transitive_drop_glue::Leaf[0] +struct Leaf; + +impl Drop for Leaf { + //~ TRANS_ITEM fn transitive_drop_glue::Leaf.Drop[0]::drop[0] + fn drop(&mut self) {} +} + +//~ TRANS_ITEM drop-glue transitive_drop_glue::Root[0] +struct RootGen(IntermediateGen); +//~ TRANS_ITEM drop-glue transitive_drop_glue::Root[0] +struct IntermediateGen(LeafGen); +//~ TRANS_ITEM drop-glue transitive_drop_glue::Root[0] +struct LeafGen(T); + +impl Drop for LeafGen { + fn drop(&mut self) {} +} + +//~ TRANS_ITEM fn transitive_drop_glue::main[0] +fn main() { + + let _ = Root(Intermediate(Leaf)); + + //~ TRANS_ITEM drop-glue transitive_drop_glue::RootGen[0] + //~ TRANS_ITEM drop-glue transitive_drop_glue::IntermediateGen[0] + //~ TRANS_ITEM drop-glue transitive_drop_glue::LeafGen[0] + //~ TRANS_ITEM fn transitive_drop_glue::LeafGen.Drop[0]::drop[0] + let _ = RootGen(IntermediateGen(LeafGen(0u32))); + + //~ TRANS_ITEM drop-glue transitive_drop_glue::RootGen[0] + //~ TRANS_ITEM drop-glue transitive_drop_glue::IntermediateGen[0] + //~ TRANS_ITEM drop-glue transitive_drop_glue::LeafGen[0] + //~ TRANS_ITEM fn transitive_drop_glue::LeafGen.Drop[0]::drop[0] + let _ = RootGen(IntermediateGen(LeafGen(0i16))); +} diff --git a/src/test/codegen-units/tuple-drop-glue.rs b/src/test/codegen-units/tuple-drop-glue.rs new file mode 100644 index 00000000000..87fcb00eab8 --- /dev/null +++ b/src/test/codegen-units/tuple-drop-glue.rs @@ -0,0 +1,32 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +//~ TRANS_ITEM drop-glue tuple_drop_glue::Dropped[0] +struct Dropped; + +impl Drop for Dropped { + //~ TRANS_ITEM fn tuple_drop_glue::Dropped.Drop[0]::drop[0] + fn drop(&mut self) {} +} + +//~ TRANS_ITEM fn tuple_drop_glue::main[0] +fn main() { + //~ TRANS_ITEM drop-glue (u32, tuple_drop_glue::Dropped[0]) + let x = (0u32, Dropped); + + //~ TRANS_ITEM drop-glue (i16, (tuple_drop_glue::Dropped[0], bool)) + //~ TRANS_ITEM drop-glue (tuple_drop_glue::Dropped[0], bool) + let x = (0i16, (Dropped, true)); +} diff --git a/src/test/codegen-units/unsizing.rs b/src/test/codegen-units/unsizing.rs new file mode 100644 index 00000000000..dd90d32858f --- /dev/null +++ b/src/test/codegen-units/unsizing.rs @@ -0,0 +1,80 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] +#![feature(coerce_unsized)] +#![feature(unsize)] + +use std::marker::Unsize; +use std::ops::CoerceUnsized; + +trait Trait { + fn foo(&self); +} + +// Simple Case +impl Trait for bool { + fn foo(&self) {} +} + +impl Trait for char { + fn foo(&self) {} +} + +// Struct Field Case +struct Struct { + _a: u32, + _b: i32, + _c: T +} + +impl Trait for f64 { + fn foo(&self) {} +} + +// Custom Coercion Case +impl Trait for u32 { + fn foo(&self) {} +} + +#[derive(Clone, Copy)] +struct Wrapper(*const T); + +impl, U: ?Sized> CoerceUnsized> for Wrapper {} + +//~ TRANS_ITEM fn unsizing::main[0] +fn main() +{ + // simple case + let bool_sized = &true; + //~ TRANS_ITEM fn unsizing::bool.Trait[0]::foo[0] + let _bool_unsized = bool_sized as &Trait; + + let char_sized = &true; + //~ TRANS_ITEM fn unsizing::char.Trait[0]::foo[0] + let _char_unsized = char_sized as &Trait; + + // struct field + let struct_sized = &Struct { + _a: 1, + _b: 2, + _c: 3.0f64 + }; + //~ TRANS_ITEM fn unsizing::f64.Trait[0]::foo[0] + let _struct_unsized = struct_sized as &Struct; + + // custom coercion + let wrapper_sized = Wrapper(&0u32); + //~ TRANS_ITEM fn unsizing::u32.Trait[0]::foo[0] + let _wrapper_sized = wrapper_sized as Wrapper; +} diff --git a/src/test/codegen-units/unused-traits-and-generics.rs b/src/test/codegen-units/unused-traits-and-generics.rs new file mode 100644 index 00000000000..a4c5099ab97 --- /dev/null +++ b/src/test/codegen-units/unused-traits-and-generics.rs @@ -0,0 +1,89 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![crate_type="lib"] +#![deny(dead_code)] + +// This test asserts that no codegen items are generated for generic items that +// are never instantiated in the local crate. + +pub trait Trait { + fn foo() {} + fn bar(&self) {} +} + +pub fn foo(x: T) -> (T, T) { + (x, x) +} + +pub struct Struct { + x: T +} + +impl Struct { + pub fn foo(self) -> T { + self.x + } + + pub fn bar() {} +} + +pub enum Enum { + A(T), + B { x: T } +} + +impl Enum { + pub fn foo(self) -> T { + match self { + Enum::A(x) => x, + Enum::B { x } => x, + } + } + + pub fn bar() {} +} + +pub struct TupleStruct(T); + +impl TupleStruct { + pub fn foo(self) -> T { + self.0 + } + + pub fn bar() {} +} + +pub type Pair = (T, T); + +pub struct NonGeneric { + x: i32 +} + +impl NonGeneric { + pub fn foo(self) -> i32 { + self.x + } + + pub fn generic_foo(&self, x: T) -> (T, i32) { + (x, self.x) + } + + pub fn generic_bar(x: T) -> (T, T) { + (x, x) + } +} + +// Only the non-generic methods should be instantiated: +//~ TRANS_ITEM fn unused_traits_and_generics::NonGeneric[0]::foo[0] +//~ TRANS_ITEM drop-glue i8 -- 2.44.0