diff --git a/configure b/configure index f284d13ee3bbd..2693e9bcd236a 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 b3f7278ad62cc..5ca27c489ed9a 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 eb6c29eefbe78..e66094dc3954b 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 @@ impl FromStr for Mode { "debuginfo-gdb" => Ok(DebugInfoGdb), "codegen" => Ok(Codegen), "rustdoc" => Ok(Rustdoc), + "codegen-units" => Ok(CodegenUnits), _ => Err(()), } } @@ -59,6 +61,7 @@ impl fmt::Display for Mode { DebugInfoLldb => "debuginfo-lldb", Codegen => "codegen", Rustdoc => "rustdoc", + CodegenUnits => "codegen-units", }, f) } } diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 459b43b4ffe5d..c7561248eb7fb 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 procsrv; 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 e903fcf6a56c2..10c1372cd86e4 100644 --- a/src/librustc/front/map/definitions.rs +++ b/src/librustc/front/map/definitions.rs @@ -196,33 +196,33 @@ impl DefPathData { 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 27745a85935ab..cb7c6a6432a02 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -223,6 +223,8 @@ pub trait CrateStore<'tcx> : Any { -> 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 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { -> 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 7010bae7ee5bf..0fd6b933ed528 100644 --- a/src/librustc/middle/ty/context.rs +++ b/src/librustc/middle/ty/context.rs @@ -563,7 +563,7 @@ impl<'tcx> ctxt<'tcx> { 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 7d5276f379ffe..8d8afc199f59e 100644 --- a/src/librustc/middle/ty/maps.rs +++ b/src/librustc/middle/ty/maps.rs @@ -13,7 +13,7 @@ use middle::def_id::DefId; 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 @@ dep_map_ty! { InherentImpls: InherentImpls(DefId) -> Rc> } 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 3e71d3ba9f397..de7a694a646cf 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 1a99aba591a8c..6956ee8eac2de 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -643,6 +643,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "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 e91c7e6ac4506..cdbb684470314 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 802629e8f3e8b..bc52921e1b8ca 100644 --- a/src/librustc_metadata/csearch.rs +++ b/src/librustc_metadata/csearch.rs @@ -445,6 +445,11 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { 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 bca8cb3995ab1..326f68561b09d 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 7460ef82ebee4..68f8d9d2adbb1 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -41,8 +41,10 @@ use middle::infer; 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::callee; 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::collections::{HashMap, HashSet}; 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 @@ impl<'a, 'tcx, 'v> Visitor<'v> for TransItemsWithinModVisitor<'a, 'tcx> { } } } + +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 0000000000000..574cba2f0eeee --- /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 0f5d8dbd94db0..daf36bf8693d1 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -30,6 +30,7 @@ use middle::def::Def; 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 7301afa46fcd1..e8868cdbcc759 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -24,6 +24,7 @@ use trans::debuginfo; 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 @@ impl<'a, 'tcx> Iterator for CrateContextMaybeIterator<'a, 'tcx> { } } - 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 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { 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 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { 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 ada37c5d8dfbe..3de6447f4ee8e 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -55,9 +55,7 @@ use back::abi; 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 a1165ffe171d0..9073846e2aeee 100644 --- a/src/librustc_trans/trans/glue.rs +++ b/src/librustc_trans/trans/glue.rs @@ -28,6 +28,7 @@ use trans::build::*; 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 4695595d16f30..b8f577f654c68 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 d87c17cbf88d4..898f260f8df43 100644 --- a/src/librustc_trans/trans/mod.rs +++ b/src/librustc_trans/trans/mod.rs @@ -58,6 +58,7 @@ mod _match; 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 0000000000000..49b8e43836e52 --- /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 0000000000000..944d85db50806 --- /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 0000000000000..83bb65bc2b7f0 --- /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 0000000000000..32b07d42fec44 --- /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 0000000000000..82d940a154852 --- /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 0000000000000..aa1f6b06c8135 --- /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 0000000000000..3a9d56c2a8bf7 --- /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 0000000000000..f89d6e61bc552 --- /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 0000000000000..5ec1f7fbc3ca3 --- /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 0000000000000..6e6bb5cbf53c8 --- /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 0000000000000..e17a1a7094f2f --- /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 0000000000000..46587b2b0a1b2 --- /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 0000000000000..a2dcd81b6750c --- /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 0000000000000..bf8804e12ce49 --- /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 0000000000000..a82e85b7a5315 --- /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 0000000000000..687ce7fa05cb4 --- /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 0000000000000..134110222f392 --- /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 0000000000000..7c8b2b117ef7c --- /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 0000000000000..590859f15a3e1 --- /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 0000000000000..fdf63df547111 --- /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 0000000000000..2b3b83cb7ec63 --- /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 0000000000000..6982cb9299a55 --- /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 0000000000000..87fcb00eab8c2 --- /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 0000000000000..dd90d32858f11 --- /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 0000000000000..a4c5099ab9751 --- /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