From c7db40f23256c14f2ef6d43e3091135791e5e153 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 9 Mar 2017 21:05:56 +0200 Subject: [PATCH 01/54] Rename expected_types_for_fn_args to expected_inputs_for_expected_output. --- src/librustc_typeck/check/callee.rs | 4 ++-- src/librustc_typeck/check/mod.rs | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 4b88f5acf42da..529ee107c46ce 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -254,7 +254,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Call the generic checker. let expected_arg_tys = - self.expected_types_for_fn_args(call_expr.span, + self.expected_inputs_for_expected_output(call_expr.span, expected, fn_sig.output(), fn_sig.inputs()); @@ -280,7 +280,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // do know the types expected for each argument and the return // type. - let expected_arg_tys = self.expected_types_for_fn_args(call_expr.span, + let expected_arg_tys = self.expected_inputs_for_expected_output(call_expr.span, expected, fn_sig.output().clone(), fn_sig.inputs()); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index e8957bad0986c..847aea553534d 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2321,7 +2321,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { match method_fn_ty.sty { ty::TyFnDef(def_id, .., ref fty) => { // HACK(eddyb) ignore self in the definition (see above). - let expected_arg_tys = self.expected_types_for_fn_args( + let expected_arg_tys = self.expected_inputs_for_expected_output( sp, expected, fty.0.output(), @@ -2674,14 +2674,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { TypeAndSubsts { substs: substs, ty: substd_ty } } - /// Unifies the return type with the expected type early, for more coercions - /// and forward type information on the argument expressions. - fn expected_types_for_fn_args(&self, - call_span: Span, - expected_ret: Expectation<'tcx>, - formal_ret: Ty<'tcx>, - formal_args: &[Ty<'tcx>]) - -> Vec> { + /// Unifies the output type with the expected type early, for more coercions + /// and forward type information on the input expressions. + fn expected_inputs_for_expected_output(&self, + call_span: Span, + expected_ret: Expectation<'tcx>, + formal_ret: Ty<'tcx>, + formal_args: &[Ty<'tcx>]) + -> Vec> { let expected_args = expected_ret.only_has_type(self).and_then(|ret_ty| { self.fudge_regions_if_ok(&RegionVariableOrigin::Coercion(call_span), || { // Attempt to apply a subtyping relationship between the formal @@ -2704,7 +2704,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }).collect()) }).ok() }).unwrap_or(vec![]); - debug!("expected_types_for_fn_args(formal={:?} -> {:?}, expected={:?} -> {:?})", + debug!("expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})", formal_args, formal_ret, expected_args, expected_ret); expected_args From 50aee36d26dd78ddc78670b2ad63d276c5faa646 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 9 Mar 2017 21:06:18 +0200 Subject: [PATCH 02/54] Propagate expected type hints through struct literals. --- src/librustc_typeck/check/mod.rs | 29 +++++++++++++++++++++-------- src/test/run-pass/issue-31260.rs | 20 ++++++++++++++++++++ 2 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 src/test/run-pass/issue-31260.rs diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 847aea553534d..f43dcefb84591 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3061,14 +3061,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn check_expr_struct_fields(&self, adt_ty: Ty<'tcx>, + expected: Expectation<'tcx>, expr_id: ast::NodeId, span: Span, variant: &'tcx ty::VariantDef, ast_fields: &'gcx [hir::Field], check_completeness: bool) { let tcx = self.tcx; - let (substs, adt_kind, kind_name) = match adt_ty.sty { - ty::TyAdt(adt, substs) => (substs, adt.adt_kind(), adt.variant_descr()), + + let adt_ty_hint = + self.expected_inputs_for_expected_output(span, expected, adt_ty, &[adt_ty]) + .get(0).cloned().unwrap_or(adt_ty); + + let (substs, hint_substs, adt_kind, kind_name) = match (&adt_ty.sty, &adt_ty_hint.sty) { + (&ty::TyAdt(adt, substs), &ty::TyAdt(_, hint_substs)) => { + (substs, hint_substs, adt.adt_kind(), adt.variant_descr()) + } _ => span_bug!(span, "non-ADT passed to check_expr_struct_fields") }; @@ -3083,10 +3091,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Typecheck each field. for field in ast_fields { - let expected_field_type; + let final_field_type; + let field_type_hint; if let Some(v_field) = remaining_fields.remove(&field.name.node) { - expected_field_type = self.field_ty(field.span, v_field, substs); + final_field_type = self.field_ty(field.span, v_field, substs); + field_type_hint = self.field_ty(field.span, v_field, hint_substs); seen_fields.insert(field.name.node, field.span); @@ -3098,7 +3108,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } else { error_happened = true; - expected_field_type = tcx.types.err; + final_field_type = tcx.types.err; + field_type_hint = tcx.types.err; if let Some(_) = variant.find_field_named(field.name.node) { let mut err = struct_span_err!(self.tcx.sess, field.name.span, @@ -3120,7 +3131,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Make sure to give a type to the field even if there's // an error, so we can continue typechecking - self.check_expr_coercable_to_type(&field.expr, expected_field_type); + let ty = self.check_expr_with_hint(&field.expr, field_type_hint); + self.demand_coerce(&field.expr, ty, final_field_type); } // Make sure the programmer specified correct number of fields. @@ -3230,6 +3242,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn check_expr_struct(&self, expr: &hir::Expr, + expected: Expectation<'tcx>, qpath: &hir::QPath, fields: &'gcx [hir::Field], base_expr: &'gcx Option>) -> Ty<'tcx> @@ -3248,7 +3261,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { hir::QPath::TypeRelative(ref qself, _) => qself.span }; - self.check_expr_struct_fields(struct_ty, expr.id, path_span, variant, fields, + self.check_expr_struct_fields(struct_ty, expected, expr.id, path_span, variant, fields, base_expr.is_none()); if let &Some(ref base_expr) = base_expr { self.check_expr_has_type(base_expr, struct_ty); @@ -3793,7 +3806,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } hir::ExprStruct(ref qpath, ref fields, ref base_expr) => { - self.check_expr_struct(expr, qpath, fields, base_expr) + self.check_expr_struct(expr, expected, qpath, fields, base_expr) } hir::ExprField(ref base, ref field) => { self.check_field(expr, lvalue_pref, &base, field) diff --git a/src/test/run-pass/issue-31260.rs b/src/test/run-pass/issue-31260.rs new file mode 100644 index 0000000000000..e771fc7464d00 --- /dev/null +++ b/src/test/run-pass/issue-31260.rs @@ -0,0 +1,20 @@ +// Copyright 2017 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. + +pub struct Struct { + pub field: K, +} + +// Partial fix for #31260, doesn't work without {...}. +static STRUCT: Struct<&'static [u8]> = Struct { + field: {&[1]} +}; + +fn main() {} From 6ba494b68bede3bd8d1288f53137c8895260bec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 11 Mar 2017 11:11:50 -0800 Subject: [PATCH 03/54] Point to let when modifying field of immutable variable Point at the immutable local variable when trying to modify one of its fields. Given a file: ```rust struct Foo { pub v: Vec } fn main() { let f = Foo { v: Vec::new() }; f.v.push("cat".to_string()); } ``` present the following output: ``` error: cannot borrow immutable field `f.v` as mutable --> file.rs:7:13 | 6 | let f = Foo { v: Vec::new() }; | - this should be `mut` 7 | f.v.push("cat".to_string()); | ^^^ error: aborting due to previous error ``` --- src/librustc/middle/mem_categorization.rs | 15 +++++++++++++++ src/librustc_borrowck/borrowck/mod.rs | 12 ++++++++++++ src/test/ui/did_you_mean/issue-39544.stderr | 2 ++ 3 files changed, 29 insertions(+) diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index b0c85e2ef4cd4..7db206296933b 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -195,6 +195,21 @@ pub struct cmt_<'tcx> { pub type cmt<'tcx> = Rc>; impl<'tcx> cmt_<'tcx> { + pub fn get_def(&self) -> Option { + match self.cat { + Categorization::Deref(ref cmt, ..) | + Categorization::Interior(ref cmt, _) | + Categorization::Downcast(ref cmt, _) => { + if let Categorization::Local(nid) = cmt.cat { + Some(nid) + } else { + None + } + } + _ => None + } + } + pub fn get_field(&self, name: ast::Name) -> Option { match self.cat { Categorization::Deref(ref cmt, ..) | diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 47b614a81ae25..04036d6c6b9e7 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -659,6 +659,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { pub fn bckerr_to_diag(&self, err: &BckError<'tcx>) -> DiagnosticBuilder<'a> { let span = err.span.clone(); let mut immutable_field = None; + let mut local_def = None; let msg = &match err.code { err_mutbl => { @@ -708,6 +709,14 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } None }); + local_def = err.cmt.get_def() + .and_then(|nid| { + if !self.tcx.hir.is_argument(nid) { + Some(self.tcx.hir.span(nid)) + } else { + None + } + }); format!("cannot borrow {} as mutable", descr) } @@ -738,6 +747,9 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { if let Some((span, msg)) = immutable_field { db.span_label(span, &msg); } + if let Some(span) = local_def { + db.span_label(span, &"this should be `mut`"); + } db } diff --git a/src/test/ui/did_you_mean/issue-39544.stderr b/src/test/ui/did_you_mean/issue-39544.stderr index c0088f39ad3e1..1cd322efab57d 100644 --- a/src/test/ui/did_you_mean/issue-39544.stderr +++ b/src/test/ui/did_you_mean/issue-39544.stderr @@ -1,6 +1,8 @@ error: cannot borrow immutable field `z.x` as mutable --> $DIR/issue-39544.rs:21:18 | +20 | let z = Z { x: X::Y }; + | - this should be `mut` 21 | let _ = &mut z.x; | ^^^ From 38b5b29c57fc86aae5a1bc8d1319cc08907d9ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 12 Mar 2017 13:49:28 -0700 Subject: [PATCH 04/54] Change label to "consider changing this to `mut f`" Change the wording of mutable borrow on immutable binding from "this should be `mut`" to "consider changing this to `mut f`". --- src/librustc_borrowck/borrowck/mod.rs | 4 +++- src/test/ui/did_you_mean/issue-39544.stderr | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 04036d6c6b9e7..073ebe24de924 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -748,7 +748,9 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { db.span_label(span, &msg); } if let Some(span) = local_def { - db.span_label(span, &"this should be `mut`"); + if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) { + db.span_label(span, &format!("consider changing this to `mut {}`", snippet)); + } } db } diff --git a/src/test/ui/did_you_mean/issue-39544.stderr b/src/test/ui/did_you_mean/issue-39544.stderr index 1cd322efab57d..3eb3e9a4c3b0a 100644 --- a/src/test/ui/did_you_mean/issue-39544.stderr +++ b/src/test/ui/did_you_mean/issue-39544.stderr @@ -2,7 +2,7 @@ error: cannot borrow immutable field `z.x` as mutable --> $DIR/issue-39544.rs:21:18 | 20 | let z = Z { x: X::Y }; - | - this should be `mut` + | - consider changing this to `mut z` 21 | let _ = &mut z.x; | ^^^ From 197e529c4425ba5d24cb3a3d59c1211093bbb82a Mon Sep 17 00:00:00 2001 From: Austin Hicks Date: Wed, 8 Mar 2017 16:28:47 -0500 Subject: [PATCH 05/54] Initial attempt at implementing optimization fuel and re-enabling struct field reordering. --- src/librustc/session/config.rs | 24 +++++++++ src/librustc/session/mod.rs | 52 ++++++++++++++++++++ src/librustc/ty/context.rs | 5 ++ src/librustc/ty/layout.rs | 9 +--- src/librustc/ty/mod.rs | 5 ++ src/librustc_driver/lib.rs | 8 +++ src/test/run-pass/type-sizes.rs | 13 +++++ src/test/ui/print_type_sizes/nullable.stdout | 35 ++++++------- src/test/ui/print_type_sizes/packed.stdout | 10 ++-- src/test/ui/print_type_sizes/padding.stdout | 16 +++--- 10 files changed, 138 insertions(+), 39 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 5fff03dabcece..53af7b9ac84c0 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -636,6 +636,8 @@ macro_rules! options { Some("either `panic` or `abort`"); pub const parse_sanitizer: Option<&'static str> = Some("one of: `address`, `leak`, `memory` or `thread`"); + pub const parse_optimization_fuel: Option<&'static str> = + Some("crate=integer"); } #[allow(dead_code)] @@ -772,6 +774,21 @@ macro_rules! options { } true } + + fn parse_optimization_fuel(slot: &mut Option<(String, u64)>, v: Option<&str>) -> bool { + match v { + None => false, + Some(s) => { + let parts = s.split('=').collect::>(); + if parts.len() != 2 { return false; } + let crate_name = parts[0].to_string(); + let fuel = parts[1].parse::(); + if fuel.is_err() { return false; } + *slot = Some((crate_name, fuel.unwrap())); + true + } + } + } } ) } @@ -974,6 +991,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "pass `-install_name @rpath/...` to the OSX linker"), sanitizer: Option = (None, parse_sanitizer, [TRACKED], "Use a sanitizer"), + fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED], + "Set the optimization fuel quota for a crate."), + print_fuel: Option = (None, parse_opt_string, [TRACKED], + "Make Rustc print the total optimization fuel used by a crate."), } pub fn default_lib_output() -> CrateType { @@ -1766,11 +1787,13 @@ mod dep_tracking { impl_dep_tracking_hash_via_hash!(bool); impl_dep_tracking_hash_via_hash!(usize); + impl_dep_tracking_hash_via_hash!(u64); impl_dep_tracking_hash_via_hash!(String); impl_dep_tracking_hash_via_hash!(lint::Level); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option<(String, u64)>); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); @@ -1792,6 +1815,7 @@ mod dep_tracking { impl_dep_tracking_hash_for_sortable_vec_of!((String, lint::Level)); impl_dep_tracking_hash_for_sortable_vec_of!((String, Option, Option)); + impl_dep_tracking_hash_for_sortable_vec_of!((String, u64)); impl DepTrackingHash for SearchPaths { fn hash(&self, hasher: &mut DefaultHasher, _: ErrorOutputType) { let mut elems: Vec<_> = self diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 3ba82f34c3266..021cf2b3af941 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -123,6 +123,20 @@ pub struct Session { pub code_stats: RefCell, next_node_id: Cell, + + /// If -zfuel=crate=n is specified, Some(crate). + optimization_fuel_crate: Option, + /// If -zfuel=crate=n is specified, initially set to n. Otherwise 0. + optimization_fuel_limit: Cell, + /// We're rejecting all further optimizations. + out_of_fuel: Cell, + + // The next two are public because the driver needs to read them. + + /// If -zprint-fuel=crate, Some(crate). + pub print_fuel_crate: Option, + /// Always set to zero and incremented so that we can print fuel expended by a crate. + pub print_fuel: Cell, } pub struct PerfStats { @@ -504,6 +518,33 @@ impl Session { println!("Total time spent decoding DefPath tables: {}", duration_to_secs_str(self.perf_stats.decode_def_path_tables_time.get())); } + + /// We want to know if we're allowed to do an optimization for crate crate. + /// This expends fuel if applicable, and records fuel if applicable. + pub fn consider_optimizing String>(&self, crate_name: &str, msg: T) -> bool { + let mut ret = true; + match self.optimization_fuel_crate { + Some(ref c) if c == crate_name => { + let fuel = self.optimization_fuel_limit.get(); + ret = fuel != 0; + if fuel == 0 && !self.out_of_fuel.get(){ + println!("optimization-fuel-exhausted: {}", msg()); + self.out_of_fuel.set(true); + } + else { + self.optimization_fuel_limit.set(fuel-1); + } + } + _ => {} + } + match self.print_fuel_crate { + Some(ref c) if c == crate_name=> { + self.print_fuel.set(self.print_fuel.get()+1); + }, + _ => {} + } + ret + } } pub fn build_session(sopts: config::Options, @@ -599,6 +640,12 @@ pub fn build_session_(sopts: config::Options, } ); + let optimization_fuel_crate = sopts.debugging_opts.fuel.as_ref().map(|i| i.0.clone()); + let optimization_fuel_limit = Cell::new(sopts.debugging_opts.fuel.as_ref() + .map(|i| i.1).unwrap_or(0)); + let print_fuel_crate = sopts.debugging_opts.print_fuel.clone(); + let print_fuel = Cell::new(0); + let sess = Session { dep_graph: dep_graph.clone(), target: target_cfg, @@ -640,6 +687,11 @@ pub fn build_session_(sopts: config::Options, decode_def_path_tables_time: Cell::new(Duration::from_secs(0)), }, code_stats: RefCell::new(CodeStats::new()), + optimization_fuel_crate: optimization_fuel_crate, + optimization_fuel_limit: optimization_fuel_limit, + print_fuel_crate: print_fuel_crate, + print_fuel: print_fuel, + out_of_fuel: Cell::new(false), }; init_llvm(&sess); diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index a0aeb4107c156..47801c3921d2a 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -728,6 +728,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { ast_ty_to_ty_cache: RefCell::new(NodeMap()), }, f) } + + pub fn consider_optimizing String>(&self, msg: T) -> bool { + let cname = self.crate_name(LOCAL_CRATE).as_str(); + self.sess.consider_optimizing(&cname, msg) + } } impl<'gcx: 'tcx, 'tcx> GlobalCtxt<'gcx> { diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 123db6e89476c..ade67e3efbe07 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -555,7 +555,6 @@ enum StructKind { } impl<'a, 'gcx, 'tcx> Struct { - // FIXME(camlorn): reprs need a better representation to deal with multiple reprs on one type. fn new(dl: &TargetDataLayout, fields: &Vec<&'a Layout>, repr: &ReprOptions, kind: StructKind, scapegoat: Ty<'gcx>) -> Result> { @@ -573,12 +572,8 @@ impl<'a, 'gcx, 'tcx> Struct { // Neither do 1-member and 2-member structs. // In addition, code in trans assume that 2-element structs can become pairs. // It's easier to just short-circuit here. - let mut can_optimize = (fields.len() > 2 || StructKind::EnumVariant == kind) - && ! (repr.c || repr.packed); - - // Disable field reordering until we can decide what to do. - // The odd pattern here avoids a warning about the value never being read. - if can_optimize { can_optimize = false; } + let can_optimize = (fields.len() > 2 || StructKind::EnumVariant == kind) + && ! (repr.c || repr.packed || repr.linear || repr.simd); let (optimize, sort_ascending) = match kind { StructKind::AlwaysSizedUnivariant => (can_optimize, false), diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 3c37c7353d683..c2d977a793bba 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1376,6 +1376,8 @@ pub struct ReprOptions { pub packed: bool, pub simd: bool, pub int: Option, + // Internal only for now. If true, don't reorder fields. + pub linear: bool, } impl ReprOptions { @@ -1398,6 +1400,9 @@ impl ReprOptions { ret.simd = true; } + // This is here instead of layout because the choice must make it into metadata. + ret.linear = !tcx.consider_optimizing(|| format!("Reorder fields of {:?}", + tcx.item_path_str(did))); ret } diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 62d7512655728..d30183f01f54b 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -514,6 +514,14 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { control.make_glob_map = resolve::MakeGlobMap::Yes; } + if sess.print_fuel_crate.is_some() { + control.compilation_done.callback = box |state| { + let sess = state.session; + println!("Fuel used by {}: {}", + sess.print_fuel_crate.as_ref().unwrap(), + sess.print_fuel.get()); + } + } control } } diff --git a/src/test/run-pass/type-sizes.rs b/src/test/run-pass/type-sizes.rs index bbb01eaaf46b9..9d1a3500a582a 100644 --- a/src/test/run-pass/type-sizes.rs +++ b/src/test/run-pass/type-sizes.rs @@ -31,6 +31,17 @@ enum e3 { a([u16; 0], u8), b } +struct ReorderedStruct { + a: u8, + b: u64, + c: u8 +} + +enum ReorderedEnum { + A(u8, u64, u8), + B(u8, u64, u8), +} + pub fn main() { assert_eq!(size_of::(), 1 as usize); assert_eq!(size_of::(), 4 as usize); @@ -54,4 +65,6 @@ pub fn main() { assert_eq!(size_of::(), 8 as usize); assert_eq!(size_of::(), 8 as usize); assert_eq!(size_of::(), 4 as usize); + assert_eq!(size_of::(), 16); + assert_eq!(size_of::(), 16); } diff --git a/src/test/ui/print_type_sizes/nullable.stdout b/src/test/ui/print_type_sizes/nullable.stdout index dd999c4a5e4c7..830678f174f88 100644 --- a/src/test/ui/print_type_sizes/nullable.stdout +++ b/src/test/ui/print_type_sizes/nullable.stdout @@ -1,25 +1,22 @@ -print-type-size type: `IndirectNonZero`: 20 bytes, alignment: 4 bytes -print-type-size field `.pre`: 1 bytes -print-type-size padding: 3 bytes -print-type-size field `.nested`: 12 bytes, alignment: 4 bytes +print-type-size type: `IndirectNonZero`: 12 bytes, alignment: 4 bytes +print-type-size field `.nested`: 8 bytes print-type-size field `.post`: 2 bytes -print-type-size end padding: 2 bytes -print-type-size type: `MyOption>`: 20 bytes, alignment: 4 bytes -print-type-size variant `Some`: 20 bytes -print-type-size field `.0`: 20 bytes -print-type-size type: `EmbeddedDiscr`: 12 bytes, alignment: 4 bytes -print-type-size variant `Record`: 10 bytes -print-type-size field `.pre`: 1 bytes -print-type-size padding: 3 bytes -print-type-size field `.val`: 4 bytes, alignment: 4 bytes -print-type-size field `.post`: 2 bytes -print-type-size end padding: 2 bytes -print-type-size type: `NestedNonZero`: 12 bytes, alignment: 4 bytes print-type-size field `.pre`: 1 bytes -print-type-size padding: 3 bytes -print-type-size field `.val`: 4 bytes, alignment: 4 bytes +print-type-size end padding: 1 bytes +print-type-size type: `MyOption>`: 12 bytes, alignment: 4 bytes +print-type-size variant `Some`: 12 bytes +print-type-size field `.0`: 12 bytes +print-type-size type: `EmbeddedDiscr`: 8 bytes, alignment: 4 bytes +print-type-size variant `Record`: 7 bytes +print-type-size field `.val`: 4 bytes +print-type-size field `.post`: 2 bytes +print-type-size field `.pre`: 1 bytes +print-type-size end padding: 1 bytes +print-type-size type: `NestedNonZero`: 8 bytes, alignment: 4 bytes +print-type-size field `.val`: 4 bytes print-type-size field `.post`: 2 bytes -print-type-size end padding: 2 bytes +print-type-size field `.pre`: 1 bytes +print-type-size end padding: 1 bytes print-type-size type: `MyOption>`: 4 bytes, alignment: 4 bytes print-type-size variant `Some`: 4 bytes print-type-size field `.0`: 4 bytes diff --git a/src/test/ui/print_type_sizes/packed.stdout b/src/test/ui/print_type_sizes/packed.stdout index 1278a7d7c92c6..83fd333c9c7fc 100644 --- a/src/test/ui/print_type_sizes/packed.stdout +++ b/src/test/ui/print_type_sizes/packed.stdout @@ -1,13 +1,11 @@ -print-type-size type: `Padded`: 16 bytes, alignment: 4 bytes +print-type-size type: `Padded`: 12 bytes, alignment: 4 bytes +print-type-size field `.g`: 4 bytes +print-type-size field `.h`: 2 bytes print-type-size field `.a`: 1 bytes print-type-size field `.b`: 1 bytes -print-type-size padding: 2 bytes -print-type-size field `.g`: 4 bytes, alignment: 4 bytes print-type-size field `.c`: 1 bytes -print-type-size padding: 1 bytes -print-type-size field `.h`: 2 bytes, alignment: 2 bytes print-type-size field `.d`: 1 bytes -print-type-size end padding: 3 bytes +print-type-size end padding: 2 bytes print-type-size type: `Packed`: 10 bytes, alignment: 1 bytes print-type-size field `.a`: 1 bytes print-type-size field `.b`: 1 bytes diff --git a/src/test/ui/print_type_sizes/padding.stdout b/src/test/ui/print_type_sizes/padding.stdout index bb95f790bd9e4..0eaff7118b35c 100644 --- a/src/test/ui/print_type_sizes/padding.stdout +++ b/src/test/ui/print_type_sizes/padding.stdout @@ -1,10 +1,12 @@ print-type-size type: `E1`: 12 bytes, alignment: 4 bytes -print-type-size discriminant: 4 bytes -print-type-size variant `A`: 5 bytes -print-type-size field `.0`: 4 bytes +print-type-size discriminant: 1 bytes +print-type-size variant `A`: 7 bytes print-type-size field `.1`: 1 bytes -print-type-size variant `B`: 8 bytes -print-type-size field `.0`: 8 bytes +print-type-size padding: 2 bytes +print-type-size field `.0`: 4 bytes, alignment: 4 bytes +print-type-size variant `B`: 11 bytes +print-type-size padding: 3 bytes +print-type-size field `.0`: 8 bytes, alignment: 4 bytes print-type-size type: `E2`: 12 bytes, alignment: 4 bytes print-type-size discriminant: 1 bytes print-type-size variant `A`: 7 bytes @@ -15,7 +17,7 @@ print-type-size variant `B`: 11 bytes print-type-size padding: 3 bytes print-type-size field `.0`: 8 bytes, alignment: 4 bytes print-type-size type: `S`: 8 bytes, alignment: 4 bytes +print-type-size field `.g`: 4 bytes print-type-size field `.a`: 1 bytes print-type-size field `.b`: 1 bytes -print-type-size padding: 2 bytes -print-type-size field `.g`: 4 bytes, alignment: 4 bytes +print-type-size end padding: 2 bytes From 8f36057f6aec8c257cec5ceed69c71bc6f8ba56d Mon Sep 17 00:00:00 2001 From: Austin Hicks Date: Wed, 8 Mar 2017 22:20:07 -0500 Subject: [PATCH 06/54] Make a comment better. --- src/librustc/session/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 021cf2b3af941..1dd615273b9dc 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -519,7 +519,7 @@ impl Session { duration_to_secs_str(self.perf_stats.decode_def_path_tables_time.get())); } - /// We want to know if we're allowed to do an optimization for crate crate. + /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. /// This expends fuel if applicable, and records fuel if applicable. pub fn consider_optimizing String>(&self, crate_name: &str, msg: T) -> bool { let mut ret = true; From c2d14fc4c74d43d42cc753ccbb8374cdf86ff41e Mon Sep 17 00:00:00 2001 From: Austin Hicks Date: Fri, 10 Mar 2017 14:13:59 -0500 Subject: [PATCH 07/54] Tests for -Z fuel=foo=n --- src/librustc/session/mod.rs | 2 +- src/test/run-pass/optimization-fuel-0.rs | 24 ++++++++++++++++++++++ src/test/run-pass/optimization-fuel-1.rs | 26 ++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/test/run-pass/optimization-fuel-0.rs create mode 100644 src/test/run-pass/optimization-fuel-1.rs diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 1dd615273b9dc..072301eeda5aa 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -531,7 +531,7 @@ impl Session { println!("optimization-fuel-exhausted: {}", msg()); self.out_of_fuel.set(true); } - else { + else if fuel > 0{ self.optimization_fuel_limit.set(fuel-1); } } diff --git a/src/test/run-pass/optimization-fuel-0.rs b/src/test/run-pass/optimization-fuel-0.rs new file mode 100644 index 0000000000000..3832c040108f8 --- /dev/null +++ b/src/test/run-pass/optimization-fuel-0.rs @@ -0,0 +1,24 @@ +// Copyright 2012 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_name="foo"] + +use std::mem::size_of; + +// compile-flags: -Z fuel=foo=0 + +struct S1(u8, u16, u8); +struct S2(u8, u16, u8); + +fn main() { + assert_eq!(size_of::(), 6); + assert_eq!(size_of::(), 6); +} + diff --git a/src/test/run-pass/optimization-fuel-1.rs b/src/test/run-pass/optimization-fuel-1.rs new file mode 100644 index 0000000000000..5f294e26aa53e --- /dev/null +++ b/src/test/run-pass/optimization-fuel-1.rs @@ -0,0 +1,26 @@ +// Copyright 2012 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_name="foo"] + +use std::mem::size_of; + +// compile-flags: -Z fuel=foo=1 + +struct S1(u8, u16, u8); +struct S2(u8, u16, u8); + +fn main() { + let optimized = (size_of::() == 4) as usize + +(size_of::() == 4) as usize; + assert_eq!(optimized, 1); +} + + From 3fb94b7ab064b23e62b018ef8b9dc47b2a59df2d Mon Sep 17 00:00:00 2001 From: Austin Hicks Date: Fri, 10 Mar 2017 14:24:50 -0500 Subject: [PATCH 08/54] UI test for -Z print-fuel=foo --- src/test/ui/print-fuel/print-fuel.rs | 21 +++++++++++++++++++++ src/test/ui/print-fuel/print-fuel.stdout | 1 + 2 files changed, 22 insertions(+) create mode 100644 src/test/ui/print-fuel/print-fuel.rs create mode 100644 src/test/ui/print-fuel/print-fuel.stdout diff --git a/src/test/ui/print-fuel/print-fuel.rs b/src/test/ui/print-fuel/print-fuel.rs new file mode 100644 index 0000000000000..0d9e243763f78 --- /dev/null +++ b/src/test/ui/print-fuel/print-fuel.rs @@ -0,0 +1,21 @@ +// Copyright 2016 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_name="foo"] +#![allow(dead_code)] + +// compile-flags: -Z print-fuel=foo + +struct S1(u8, u16, u8); +struct S2(u8, u16, u8); +struct S3(u8, u16, u8); + +fn main() { +} diff --git a/src/test/ui/print-fuel/print-fuel.stdout b/src/test/ui/print-fuel/print-fuel.stdout new file mode 100644 index 0000000000000..cc88cc077bb21 --- /dev/null +++ b/src/test/ui/print-fuel/print-fuel.stdout @@ -0,0 +1 @@ +Fuel used by foo: 3 From 9ac628d5e84bd3664e41d53bafa3d66b303b19c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 12 Mar 2017 21:28:49 -0700 Subject: [PATCH 09/54] Add label to primary span for mutable access of immutable struct error --- src/librustc_borrowck/borrowck/mod.rs | 11 ++++++++--- src/test/ui/did_you_mean/issue-39544.stderr | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 073ebe24de924..1b44ba1ec617e 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -747,9 +747,9 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { if let Some((span, msg)) = immutable_field { db.span_label(span, &msg); } - if let Some(span) = local_def { - if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) { - db.span_label(span, &format!("consider changing this to `mut {}`", snippet)); + if let Some(let_span) = local_def { + if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(let_span) { + db.span_label(let_span, &format!("consider changing this to `mut {}`", snippet)); } } db @@ -1120,6 +1120,11 @@ before rustc 1.16, this temporary lived longer - see issue #39283 \ } else { db.span_label(*error_span, &format!("cannot borrow mutably")); } + } else if let Categorization::Interior(ref cmt, _) = err.cmt.cat { + if let mc::MutabilityCategory::McImmutable = cmt.mutbl { + db.span_label(*error_span, + &"cannot mutably borrow immutable field"); + } } } } diff --git a/src/test/ui/did_you_mean/issue-39544.stderr b/src/test/ui/did_you_mean/issue-39544.stderr index 3eb3e9a4c3b0a..7f124e6d34d35 100644 --- a/src/test/ui/did_you_mean/issue-39544.stderr +++ b/src/test/ui/did_you_mean/issue-39544.stderr @@ -4,7 +4,7 @@ error: cannot borrow immutable field `z.x` as mutable 20 | let z = Z { x: X::Y }; | - consider changing this to `mut z` 21 | let _ = &mut z.x; - | ^^^ + | ^^^ cannot mutably borrow immutable field error: aborting due to previous error From 460bf55f8a649a7f19680df2ac67dbeb936f8700 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Mon, 6 Mar 2017 06:45:28 +0000 Subject: [PATCH 10/54] Cleanup. --- src/libsyntax/ext/tt/macro_parser.rs | 2 +- src/libsyntax/parse/parser.rs | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index b9cb3d82d4f7c..6385d206a0cb9 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -488,7 +488,7 @@ pub fn parse(sess: &ParseSess, tts: TokenStream, ms: &[TokenTree], directory: Op fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal { match name { "tt" => { - return token::NtTT(panictry!(p.parse_token_tree())); + return token::NtTT(p.parse_token_tree()); } _ => {} } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 6446d38e5ef70..9872afd27b7bc 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -891,7 +891,7 @@ impl<'a> Parser<'a> { self.parse_seq_to_before_tokens(kets, SeqSep::none(), - |p| p.parse_token_tree(), + |p| Ok(p.parse_token_tree()), |mut e| handler.cancel(&mut e)); } @@ -1267,7 +1267,7 @@ impl<'a> Parser<'a> { break; } token::OpenDelim(token::Brace) => { - self.parse_token_tree()?; + self.parse_token_tree(); break; } _ => self.bump(), @@ -2101,10 +2101,10 @@ impl<'a> Parser<'a> { fn expect_delimited_token_tree(&mut self) -> PResult<'a, (token::DelimToken, ThinTokenStream)> { match self.token { - token::OpenDelim(delim) => self.parse_token_tree().map(|tree| match tree { - TokenTree::Delimited(_, delimited) => (delim, delimited.stream().into()), + token::OpenDelim(delim) => match self.parse_token_tree() { + TokenTree::Delimited(_, delimited) => Ok((delim, delimited.stream().into())), _ => unreachable!(), - }), + }, _ => Err(self.fatal("expected open delimiter")), } } @@ -2643,24 +2643,23 @@ impl<'a> Parser<'a> { } /// parse a single token tree from the input. - pub fn parse_token_tree(&mut self) -> PResult<'a, TokenTree> { + pub fn parse_token_tree(&mut self) -> TokenTree { match self.token { token::OpenDelim(..) => { let frame = mem::replace(&mut self.token_cursor.frame, self.token_cursor.stack.pop().unwrap()); self.span = frame.span; self.bump(); - return Ok(TokenTree::Delimited(frame.span, Delimited { + TokenTree::Delimited(frame.span, Delimited { delim: frame.delim, tts: frame.tree_cursor.original_stream().into(), - })); + }) }, token::CloseDelim(_) | token::Eof => unreachable!(), _ => { let token = mem::replace(&mut self.token, token::Underscore); - let res = Ok(TokenTree::Token(self.span, token)); self.bump(); - res + TokenTree::Token(self.prev_span, token) } } } @@ -2670,7 +2669,7 @@ impl<'a> Parser<'a> { pub fn parse_all_token_trees(&mut self) -> PResult<'a, Vec> { let mut tts = Vec::new(); while self.token != token::Eof { - tts.push(self.parse_token_tree()?); + tts.push(self.parse_token_tree()); } Ok(tts) } From 68c1cc68b44bb987ec57251bc457a55292515d1d Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Fri, 3 Mar 2017 09:23:59 +0000 Subject: [PATCH 11/54] Refactor `Attribute` to use `Path` and `TokenStream` instead of `MetaItem`. --- src/librustc/hir/check_attr.rs | 11 +- src/librustc/hir/lowering.rs | 2 +- src/librustc/lint/context.rs | 4 +- src/librustc/middle/stability.rs | 4 +- src/librustc/traits/error_reporting.rs | 2 +- .../calculate_svh/svh_visitor.rs | 55 +--- .../persist/dirty_clean.rs | 8 +- src/librustc_lint/builtin.rs | 8 +- src/librustc_lint/lib.rs | 1 + src/librustc_lint/unused.rs | 5 +- src/librustc_metadata/creader.rs | 8 +- src/librustc_metadata/cstore.rs | 9 +- src/librustc_passes/ast_validation.rs | 4 +- src/librustc_resolve/lib.rs | 5 +- src/librustc_resolve/macros.rs | 43 ++- src/librustc_save_analysis/external_data.rs | 4 +- src/librustc_save_analysis/lib.rs | 5 +- src/librustc_trans/assert_module_sources.rs | 2 +- src/librustdoc/clean/mod.rs | 21 +- src/librustdoc/html/render.rs | 4 +- src/librustdoc/test.rs | 14 +- src/librustdoc/visit_ast.rs | 2 +- src/libsyntax/ast.rs | 9 +- src/libsyntax/attr.rs | 306 +++++++++++++++--- src/libsyntax/config.rs | 8 +- src/libsyntax/ext/derive.rs | 4 +- src/libsyntax/ext/expand.rs | 42 +-- src/libsyntax/ext/quote.rs | 14 +- src/libsyntax/feature_gate.rs | 28 +- src/libsyntax/fold.rs | 7 +- src/libsyntax/lib.rs | 10 + src/libsyntax/parse/attr.rs | 16 +- src/libsyntax/parse/mod.rs | 131 +++++--- src/libsyntax/parse/parser.rs | 44 +-- src/libsyntax/parse/token.rs | 4 +- src/libsyntax/print/pprust.rs | 100 +++--- src/libsyntax/std_inject.rs | 8 +- src/libsyntax/tokenstream.rs | 2 +- src/libsyntax_ext/deriving/custom.rs | 8 +- src/libsyntax_ext/deriving/generic/mod.rs | 2 +- src/libsyntax_ext/proc_macro_registrar.rs | 12 +- 41 files changed, 614 insertions(+), 362 deletions(-) diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs index 6f5f548aa7802..54ae947214091 100644 --- a/src/librustc/hir/check_attr.rs +++ b/src/librustc/hir/check_attr.rs @@ -120,11 +120,12 @@ impl<'a> CheckAttrVisitor<'a> { } fn check_attribute(&self, attr: &ast::Attribute, target: Target) { - let name: &str = &attr.name().as_str(); - match name { - "inline" => self.check_inline(attr, target), - "repr" => self.check_repr(attr, target), - _ => (), + if let Some(name) = attr.name() { + match &*name.as_str() { + "inline" => self.check_inline(attr, target), + "repr" => self.check_repr(attr, target), + _ => (), + } } } } diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index aa6614b0af4f7..a5c8213067550 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -1277,7 +1277,7 @@ impl<'a> LoweringContext<'a> { let attrs = self.lower_attrs(&i.attrs); let mut vis = self.lower_visibility(&i.vis); if let ItemKind::MacroDef(ref tts) = i.node { - if i.attrs.iter().any(|attr| attr.name() == "macro_export") { + if i.attrs.iter().any(|attr| attr.path == "macro_export") { self.exported_macros.push(hir::MacroDef { name: name, attrs: attrs, id: i.id, span: i.span, body: tts.clone().into(), }); diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 9279f24a57ab3..65e2fec0b8b09 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -402,14 +402,14 @@ pub fn gather_attrs(attrs: &[ast::Attribute]) -> Vec Vec> { let mut out = vec![]; - let level = match Level::from_str(&attr.name().as_str()) { + let level = match attr.name().and_then(|name| Level::from_str(&name.as_str())) { None => return out, Some(lvl) => lvl, }; + let meta = unwrap_or!(attr.meta(), return out); attr::mark_used(attr); - let meta = &attr.value; let metas = if let Some(metas) = meta.meta_item_list() { metas } else { diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index baa22d7061435..1fb5371402574 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -197,7 +197,7 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> { } else { // Emit errors for non-staged-api crates. for attr in attrs { - let tag = attr.name(); + let tag = unwrap_or!(attr.name(), continue); if tag == "unstable" || tag == "stable" || tag == "rustc_deprecated" { attr::mark_used(attr); self.tcx.sess.span_err(attr.span(), "stability attributes may not be used \ @@ -402,7 +402,7 @@ impl<'a, 'tcx> Index<'tcx> { let mut is_staged_api = false; for attr in &krate.attrs { - if attr.name() == "stable" || attr.name() == "unstable" { + if attr.path == "stable" || attr.path == "unstable" { is_staged_api = true; break } diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 0e5c786cd8dcf..27525d550ff20 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -274,7 +274,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { .filter(|a| a.check_name("rustc_on_unimplemented")) .next() { - let err_sp = item.meta().span.substitute_dummy(span); + let err_sp = item.span.substitute_dummy(span); let trait_str = self.tcx.item_path_str(trait_ref.def_id); if let Some(istring) = item.value_str() { let istring = &*istring.as_str(); diff --git a/src/librustc_incremental/calculate_svh/svh_visitor.rs b/src/librustc_incremental/calculate_svh/svh_visitor.rs index d0eedcac0c06a..fac49b29598aa 100644 --- a/src/librustc_incremental/calculate_svh/svh_visitor.rs +++ b/src/librustc_incremental/calculate_svh/svh_visitor.rs @@ -18,16 +18,15 @@ use syntax::abi::Abi; use syntax::ast::{self, Name, NodeId}; use syntax::attr; use syntax::parse::token; -use syntax::symbol::{Symbol, InternedString}; +use syntax::symbol::InternedString; use syntax_pos::{Span, NO_EXPANSION, COMMAND_LINE_EXPN, BytePos}; use syntax::tokenstream; use rustc::hir; use rustc::hir::*; use rustc::hir::def::Def; use rustc::hir::def_id::DefId; -use rustc::hir::intravisit as visit; +use rustc::hir::intravisit::{self as visit, Visitor}; use rustc::ty::TyCtxt; -use rustc_data_structures::fnv; use std::hash::{Hash, Hasher}; use super::def_path_hash::DefPathHashes; @@ -559,7 +558,7 @@ macro_rules! hash_span { }); } -impl<'a, 'hash, 'tcx> visit::Visitor<'tcx> for StrictVersionHashVisitor<'a, 'hash, 'tcx> { +impl<'a, 'hash, 'tcx> Visitor<'tcx> for StrictVersionHashVisitor<'a, 'hash, 'tcx> { fn nested_visit_map<'this>(&'this mut self) -> visit::NestedVisitorMap<'this, 'tcx> { if self.hash_bodies { visit::NestedVisitorMap::OnlyBodies(&self.tcx.hir) @@ -960,50 +959,24 @@ impl<'a, 'hash, 'tcx> StrictVersionHashVisitor<'a, 'hash, 'tcx> { } } - fn hash_meta_item(&mut self, meta_item: &ast::MetaItem) { - debug!("hash_meta_item: st={:?}", self.st); - - // ignoring span information, it doesn't matter here - self.hash_discriminant(&meta_item.node); - meta_item.name.as_str().len().hash(self.st); - meta_item.name.as_str().hash(self.st); - - match meta_item.node { - ast::MetaItemKind::Word => {} - ast::MetaItemKind::NameValue(ref lit) => saw_lit(lit).hash(self.st), - ast::MetaItemKind::List(ref items) => { - // Sort subitems so the hash does not depend on their order - let indices = self.indices_sorted_by(&items, |p| { - (p.name().map(Symbol::as_str), fnv::hash(&p.literal().map(saw_lit))) - }); - items.len().hash(self.st); - for (index, &item_index) in indices.iter().enumerate() { - index.hash(self.st); - let nested_meta_item: &ast::NestedMetaItemKind = &items[item_index].node; - self.hash_discriminant(nested_meta_item); - match *nested_meta_item { - ast::NestedMetaItemKind::MetaItem(ref meta_item) => { - self.hash_meta_item(meta_item); - } - ast::NestedMetaItemKind::Literal(ref lit) => { - saw_lit(lit).hash(self.st); - } - } - } - } - } - } - pub fn hash_attributes(&mut self, attributes: &[ast::Attribute]) { debug!("hash_attributes: st={:?}", self.st); let indices = self.indices_sorted_by(attributes, |attr| attr.name()); for i in indices { let attr = &attributes[i]; - if !attr.is_sugared_doc && - !IGNORED_ATTRIBUTES.contains(&&*attr.value.name().as_str()) { + match attr.name() { + Some(name) if IGNORED_ATTRIBUTES.contains(&&*name.as_str()) => continue, + _ => {} + }; + if !attr.is_sugared_doc { SawAttribute(attr.style).hash(self.st); - self.hash_meta_item(&attr.value); + for segment in &attr.path.segments { + SawIdent(segment.identifier.name.as_str()).hash(self.st); + } + for tt in attr.tokens.trees() { + self.hash_token_tree(&tt); + } } } } diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs index 156f8b9e7c489..929249df0b173 100644 --- a/src/librustc_incremental/persist/dirty_clean.rs +++ b/src/librustc_incremental/persist/dirty_clean.rs @@ -104,9 +104,9 @@ pub struct DirtyCleanVisitor<'a, 'tcx:'a> { impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> { fn dep_node(&self, attr: &Attribute, def_id: DefId) -> DepNode { - for item in attr.meta_item_list().unwrap_or(&[]) { + for item in attr.meta_item_list().unwrap_or_else(Vec::new) { if item.check_name(LABEL) { - let value = expect_associated_value(self.tcx, item); + let value = expect_associated_value(self.tcx, &item); match DepNode::from_label_string(&value.as_str(), def_id) { Ok(def_id) => return def_id, Err(()) => { @@ -331,9 +331,9 @@ fn check_config(tcx: TyCtxt, attr: &Attribute) -> bool { debug!("check_config(attr={:?})", attr); let config = &tcx.sess.parse_sess.config; debug!("check_config: config={:?}", config); - for item in attr.meta_item_list().unwrap_or(&[]) { + for item in attr.meta_item_list().unwrap_or_else(Vec::new) { if item.check_name(CFG) { - let value = expect_associated_value(tcx, item); + let value = expect_associated_value(tcx, &item); debug!("check_config: searching for cfg {:?}", value); return config.contains(&(value, None)); } diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 58336f939d122..f0276f90f274d 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -312,7 +312,7 @@ impl MissingDoc { } } - let has_doc = attrs.iter().any(|a| a.is_value_str() && a.name() == "doc"); + let has_doc = attrs.iter().any(|a| a.is_value_str() && a.check_name("doc")); if !has_doc { cx.span_lint(MISSING_DOCS, sp, @@ -635,7 +635,7 @@ impl LintPass for DeprecatedAttr { impl EarlyLintPass for DeprecatedAttr { fn check_attribute(&mut self, cx: &EarlyContext, attr: &ast::Attribute) { - let name = attr.name(); + let name = unwrap_or!(attr.name(), return); for &&(n, _, ref g) in &self.depr_attrs { if name == n { if let &AttributeGate::Gated(Stability::Deprecated(link), @@ -1121,8 +1121,8 @@ impl LintPass for UnstableFeatures { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnstableFeatures { fn check_attribute(&mut self, ctx: &LateContext, attr: &ast::Attribute) { - if attr.meta().check_name("feature") { - if let Some(items) = attr.meta().meta_item_list() { + if attr.check_name("feature") { + if let Some(items) = attr.meta_item_list() { for item in items { ctx.span_lint(UNSTABLE_FEATURES, item.span(), "unstable feature"); } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 443a219928f1c..05dbbc0987025 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -38,6 +38,7 @@ #![feature(slice_patterns)] #![feature(staged_api)] +#[macro_use] extern crate syntax; #[macro_use] extern crate rustc; diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index f9b7c68587678..abba8afd9da86 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -269,6 +269,7 @@ impl LintPass for UnusedAttributes { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes { fn check_attribute(&mut self, cx: &LateContext, attr: &ast::Attribute) { debug!("checking attribute: {:?}", attr); + let name = unwrap_or!(attr.name(), return); // Note that check_name() marks the attribute as used if it matches. for &(ref name, ty, _) in BUILTIN_ATTRIBUTES { @@ -294,13 +295,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes { cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute"); // Is it a builtin attribute that must be used at the crate level? let known_crate = BUILTIN_ATTRIBUTES.iter() - .find(|&&(name, ty, _)| attr.name() == name && ty == AttributeType::CrateLevel) + .find(|&&(builtin, ty, _)| name == builtin && ty == AttributeType::CrateLevel) .is_some(); // Has a plugin registered this attribute as one which must be used at // the crate level? let plugin_crate = plugin_attributes.iter() - .find(|&&(ref x, t)| attr.name() == &**x && AttributeType::CrateLevel == t) + .find(|&&(ref x, t)| name == &**x && AttributeType::CrateLevel == t) .is_some(); if known_crate || plugin_crate { let msg = match attr.style { diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 49dcffb4830a1..9f5ce00f40812 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -973,9 +973,11 @@ impl<'a> CrateLoader<'a> { impl<'a> CrateLoader<'a> { pub fn preprocess(&mut self, krate: &ast::Crate) { - for attr in krate.attrs.iter().filter(|m| m.name() == "link_args") { - if let Some(linkarg) = attr.value_str() { - self.cstore.add_used_link_args(&linkarg.as_str()); + for attr in &krate.attrs { + if attr.path == "link_args" { + if let Some(linkarg) = attr.value_str() { + self.cstore.add_used_link_args(&linkarg.as_str()); + } } } } diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index bb30245df5f56..17a6a706e0aaa 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -269,9 +269,12 @@ impl CrateMetadata { } pub fn is_staged_api(&self) -> bool { - self.get_item_attrs(CRATE_DEF_INDEX) - .iter() - .any(|attr| attr.name() == "stable" || attr.name() == "unstable") + for attr in self.get_item_attrs(CRATE_DEF_INDEX) { + if attr.path == "stable" || attr.path == "unstable" { + return true; + } + } + false } pub fn is_allocator(&self) -> bool { diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 0933fdfd357cd..8c45a66694533 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -241,12 +241,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ItemKind::Mod(_) => { // Ensure that `path` attributes on modules are recorded as used (c.f. #35584). attr::first_attr_value_str_by_name(&item.attrs, "path"); - if let Some(attr) = - item.attrs.iter().find(|attr| attr.name() == "warn_directory_ownership") { + if item.attrs.iter().any(|attr| attr.check_name("warn_directory_ownership")) { let lint = lint::builtin::LEGACY_DIRECTORY_OWNERSHIP; let msg = "cannot declare a new module at this location"; self.session.add_lint(lint, item.id, item.span, msg.to_string()); - attr::mark_used(attr); } } ItemKind::Union(ref vdata, _) => { diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 0958748ed092f..c3e471650a3e1 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -3360,8 +3360,9 @@ impl<'a> Resolver<'a> { if self.proc_macro_enabled { return; } for attr in attrs { - let maybe_binding = self.builtin_macros.get(&attr.name()).cloned().or_else(|| { - let ident = Ident::with_empty_ctxt(attr.name()); + let name = unwrap_or!(attr.name(), continue); + let maybe_binding = self.builtin_macros.get(&name).cloned().or_else(|| { + let ident = Ident::with_empty_ctxt(name); self.resolve_lexical_macro_path_segment(ident, MacroNS, None).ok() }); diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 7ad122d1c31d8..9e1dcd1bc35c4 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -30,6 +30,7 @@ use syntax::feature_gate::{self, emit_feature_err, GateIssue}; use syntax::fold::{self, Folder}; use syntax::ptr::P; use syntax::symbol::{Symbol, keywords}; +use syntax::tokenstream::TokenStream; use syntax::util::lev_distance::find_best_match_for_name; use syntax_pos::{Span, DUMMY_SP}; @@ -176,12 +177,14 @@ impl<'a> base::Resolver for Resolver<'a> { fn find_legacy_attr_invoc(&mut self, attrs: &mut Vec) -> Option { for i in 0..attrs.len() { + let name = unwrap_or!(attrs[i].name(), continue); + if self.session.plugin_attributes.borrow().iter() - .any(|&(ref attr_nm, _)| attrs[i].name() == &**attr_nm) { + .any(|&(ref attr_nm, _)| name == &**attr_nm) { attr::mark_known(&attrs[i]); } - match self.builtin_macros.get(&attrs[i].name()).cloned() { + match self.builtin_macros.get(&name).cloned() { Some(binding) => match *binding.get_macro(self) { MultiModifier(..) | MultiDecorator(..) | SyntaxExtension::AttrProcMacro(..) => { return Some(attrs.remove(i)) @@ -194,9 +197,11 @@ impl<'a> base::Resolver for Resolver<'a> { // Check for legacy derives for i in 0..attrs.len() { - if attrs[i].name() == "derive" { + let name = unwrap_or!(attrs[i].name(), continue); + + if name == "derive" { let mut traits = match attrs[i].meta_item_list() { - Some(traits) if !traits.is_empty() => traits.to_owned(), + Some(traits) => traits, _ => continue, }; @@ -213,18 +218,11 @@ impl<'a> base::Resolver for Resolver<'a> { if traits.is_empty() { attrs.remove(i); } else { - attrs[i].value = ast::MetaItem { - name: attrs[i].name(), - span: attrs[i].span, - node: ast::MetaItemKind::List(traits), - }; + attrs[i].tokens = ast::MetaItemKind::List(traits).tokens(attrs[i].span); } return Some(ast::Attribute { - value: ast::MetaItem { - name: legacy_name, - span: span, - node: ast::MetaItemKind::Word, - }, + path: ast::Path::from_ident(span, Ident::with_empty_ctxt(legacy_name)), + tokens: TokenStream::empty(), id: attr::mk_attr_id(), style: ast::AttrStyle::Outer, is_sugared_doc: false, @@ -270,19 +268,20 @@ impl<'a> Resolver<'a> { } }; - let (attr_name, path) = { - let attr = attr.as_ref().unwrap(); - (attr.name(), ast::Path::from_ident(attr.span, Ident::with_empty_ctxt(attr.name()))) - }; - let mut determined = true; + let path = attr.as_ref().unwrap().path.clone(); + let mut determinacy = Determinacy::Determined; match self.resolve_macro_to_def(scope, &path, MacroKind::Attr, force) { Ok(def) => return Ok(def), - Err(Determinacy::Undetermined) => determined = false, + Err(Determinacy::Undetermined) => determinacy = Determinacy::Undetermined, Err(Determinacy::Determined) if force => return Err(Determinacy::Determined), Err(Determinacy::Determined) => {} } + let attr_name = match path.segments.len() { + 1 => path.segments[0].identifier.name, + _ => return Err(determinacy), + }; for &(name, span) in traits { let path = ast::Path::from_ident(span, Ident::with_empty_ctxt(name)); match self.resolve_macro(scope, &path, MacroKind::Derive, force) { @@ -304,12 +303,12 @@ impl<'a> Resolver<'a> { } return Err(Determinacy::Undetermined); }, - Err(Determinacy::Undetermined) => determined = false, + Err(Determinacy::Undetermined) => determinacy = Determinacy::Undetermined, Err(Determinacy::Determined) => {} } } - Err(if determined { Determinacy::Determined } else { Determinacy::Undetermined }) + Err(determinacy) } fn resolve_macro_to_def(&mut self, scope: Mark, path: &ast::Path, kind: MacroKind, force: bool) diff --git a/src/librustc_save_analysis/external_data.rs b/src/librustc_save_analysis/external_data.rs index 41658dc5b1b48..f038c2dc298ad 100644 --- a/src/librustc_save_analysis/external_data.rs +++ b/src/librustc_save_analysis/external_data.rs @@ -14,7 +14,6 @@ use rustc::ty::TyCtxt; use syntax::ast::{self, NodeId}; use syntax::codemap::CodeMap; use syntax::print::pprust; -use syntax::symbol::Symbol; use syntax_pos::Span; use data::{self, Visibility, SigElement}; @@ -77,10 +76,9 @@ impl Lower for Vec { type Target = Vec; fn lower(self, tcx: TyCtxt) -> Vec { - let doc = Symbol::intern("doc"); self.into_iter() // Only retain real attributes. Doc comments are lowered separately. - .filter(|attr| attr.name() != doc) + .filter(|attr| attr.path != "doc") .map(|mut attr| { // Remove the surrounding '#[..]' or '#![..]' of the pretty printed // attribute. First normalize all inner attribute (#![..]) to outer diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 111c8370be2b1..90ee19198c939 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -54,7 +54,7 @@ use std::path::{Path, PathBuf}; use syntax::ast::{self, NodeId, PatKind, Attribute, CRATE_NODE_ID}; use syntax::parse::lexer::comments::strip_doc_comment_decoration; use syntax::parse::token; -use syntax::symbol::{Symbol, keywords}; +use syntax::symbol::keywords; use syntax::visit::{self, Visitor}; use syntax::print::pprust::{ty_to_string, arg_to_string}; use syntax::codemap::MacroAttribute; @@ -829,11 +829,10 @@ impl<'a> Visitor<'a> for PathCollector { } fn docs_for_attrs(attrs: &[Attribute]) -> String { - let doc = Symbol::intern("doc"); let mut result = String::new(); for attr in attrs { - if attr.name() == doc { + if attr.check_name("doc") { if let Some(val) = attr.value_str() { if attr.is_sugared_doc { result.push_str(&strip_doc_comment_decoration(&val.as_str())); diff --git a/src/librustc_trans/assert_module_sources.rs b/src/librustc_trans/assert_module_sources.rs index 7a41f8341099b..8528482c7856c 100644 --- a/src/librustc_trans/assert_module_sources.rs +++ b/src/librustc_trans/assert_module_sources.rs @@ -113,7 +113,7 @@ impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> { } fn field(&self, attr: &ast::Attribute, name: &str) -> ast::Name { - for item in attr.meta_item_list().unwrap_or(&[]) { + for item in attr.meta_item_list().unwrap_or_else(Vec::new) { if item.check_name(name) { if let Some(value) = item.value_str() { return value; diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1294296840ebd..660fa647882aa 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -39,12 +39,11 @@ use rustc::util::nodemap::{FxHashMap, FxHashSet}; use rustc::hir; +use std::{mem, slice, vec}; use std::path::PathBuf; use std::rc::Rc; -use std::slice; use std::sync::Arc; use std::u32; -use std::mem; use core::DocContext; use doctree; @@ -472,12 +471,12 @@ impl Clean for doctree::Module { pub struct ListAttributesIter<'a> { attrs: slice::Iter<'a, ast::Attribute>, - current_list: slice::Iter<'a, ast::NestedMetaItem>, + current_list: vec::IntoIter, name: &'a str } impl<'a> Iterator for ListAttributesIter<'a> { - type Item = &'a ast::NestedMetaItem; + type Item = ast::NestedMetaItem; fn next(&mut self) -> Option { if let Some(nested) = self.current_list.next() { @@ -485,9 +484,9 @@ impl<'a> Iterator for ListAttributesIter<'a> { } for attr in &mut self.attrs { - if let Some(ref list) = attr.meta_item_list() { + if let Some(list) = attr.meta_item_list() { if attr.check_name(self.name) { - self.current_list = list.iter(); + self.current_list = list.into_iter(); if let Some(nested) = self.current_list.next() { return Some(nested); } @@ -508,7 +507,7 @@ impl AttributesExt for [ast::Attribute] { fn lists<'a>(&'a self, name: &'a str) -> ListAttributesIter<'a> { ListAttributesIter { attrs: self.iter(), - current_list: [].iter(), + current_list: Vec::new().into_iter(), name: name } } @@ -519,7 +518,7 @@ pub trait NestedAttributesExt { fn has_word(self, &str) -> bool; } -impl<'a, I: IntoIterator> NestedAttributesExt for I { +impl> NestedAttributesExt for I { fn has_word(self, word: &str) -> bool { self.into_iter().any(|attr| attr.is_word() && attr.check_name(word)) } @@ -2596,9 +2595,9 @@ impl Clean> for doctree::Import { // #[doc(no_inline)] attribute is present. // Don't inline doc(hidden) imports so they can be stripped at a later stage. let denied = self.vis != hir::Public || self.attrs.iter().any(|a| { - a.name() == "doc" && match a.meta_item_list() { - Some(l) => attr::list_contains_name(l, "no_inline") || - attr::list_contains_name(l, "hidden"), + a.name().unwrap() == "doc" && match a.meta_item_list() { + Some(l) => attr::list_contains_name(&l, "no_inline") || + attr::list_contains_name(&l, "hidden"), None => false, } }); diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 44f71d8952985..130a4526bf705 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -2620,11 +2620,11 @@ fn render_attributes(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result { let mut attrs = String::new(); for attr in &it.attrs.other_attrs { - let name = attr.name(); + let name = attr.name().unwrap(); if !ATTRIBUTE_WHITELIST.contains(&&name.as_str()[..]) { continue; } - if let Some(s) = render_attribute(attr.meta()) { + if let Some(s) = render_attribute(&attr.meta().unwrap()) { attrs.push_str(&format!("#[{}]\n", s)); } } diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index c1ecc241b7b63..f6b7a07bdae01 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -137,13 +137,13 @@ fn scrape_test_config(krate: &::rustc::hir::Crate) -> TestOptions { attrs: Vec::new(), }; - let attrs = krate.attrs.iter() - .filter(|a| a.check_name("doc")) - .filter_map(|a| a.meta_item_list()) - .flat_map(|l| l) - .filter(|a| a.check_name("test")) - .filter_map(|a| a.meta_item_list()) - .flat_map(|l| l); + let test_attrs: Vec<_> = krate.attrs.iter() + .filter(|a| a.check_name("doc")) + .flat_map(|a| a.meta_item_list().unwrap_or_else(Vec::new)) + .filter(|a| a.check_name("test")) + .collect(); + let attrs = test_attrs.iter().flat_map(|a| a.meta_item_list().unwrap_or(&[])); + for attr in attrs { if attr.check_name("no_crate_inject") { opts.no_crate_inject = true; diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index b80de3cc50546..4a909f8e2a972 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -376,7 +376,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { if item.vis == hir::Public && self.inside_public_path { let please_inline = item.attrs.iter().any(|item| { match item.meta_item_list() { - Some(list) if item.check_name("doc") => { + Some(ref list) if item.check_name("doc") => { list.iter().any(|i| i.check_name("inline")) } _ => false, diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 981667337d59a..5deb91ef53aeb 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -116,6 +116,12 @@ pub struct Path { pub segments: Vec, } +impl<'a> PartialEq<&'a str> for Path { + fn eq(&self, string: &&'a str) -> bool { + self.segments.len() == 1 && self.segments[0].identifier.name == *string + } +} + impl fmt::Debug for Path { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "path({})", pprust::path_to_string(self)) @@ -1679,7 +1685,8 @@ pub struct AttrId(pub usize); pub struct Attribute { pub id: AttrId, pub style: AttrStyle, - pub value: MetaItem, + pub path: Path, + pub tokens: TokenStream, pub is_sugared_doc: bool, pub span: Span, } diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 096657a6e7ac8..68f1f690a62f0 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -15,20 +15,24 @@ pub use self::ReprAttr::*; pub use self::IntType::*; use ast; -use ast::{AttrId, Attribute, Name}; +use ast::{AttrId, Attribute, Name, Ident}; use ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind}; -use ast::{Lit, Expr, Item, Local, Stmt, StmtKind}; +use ast::{Lit, LitKind, Expr, Item, Local, Stmt, StmtKind}; use codemap::{Spanned, spanned, dummy_spanned, mk_sp}; use syntax_pos::{Span, BytePos, DUMMY_SP}; use errors::Handler; use feature_gate::{Features, GatedCfg}; use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration}; -use parse::ParseSess; +use parse::parser::Parser; +use parse::{self, ParseSess, PResult}; +use parse::token::{self, Token}; use ptr::P; use symbol::Symbol; +use tokenstream::{TokenStream, TokenTree, Delimited}; use util::ThinVec; use std::cell::{RefCell, Cell}; +use std::iter; thread_local! { static USED_ATTRS: RefCell> = RefCell::new(Vec::new()); @@ -185,26 +189,38 @@ impl NestedMetaItem { impl Attribute { pub fn check_name(&self, name: &str) -> bool { - let matches = self.name() == name; + let matches = self.path == name; if matches { mark_used(self); } matches } - pub fn name(&self) -> Name { self.meta().name() } + pub fn name(&self) -> Option { + match self.path.segments.len() { + 1 => Some(self.path.segments[0].identifier.name), + _ => None, + } + } pub fn value_str(&self) -> Option { - self.meta().value_str() + self.meta().and_then(|meta| meta.value_str()) } - pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> { - self.meta().meta_item_list() + pub fn meta_item_list(&self) -> Option> { + match self.meta() { + Some(MetaItem { node: MetaItemKind::List(list), .. }) => Some(list), + _ => None + } } - pub fn is_word(&self) -> bool { self.meta().is_word() } + pub fn is_word(&self) -> bool { + self.path.segments.len() == 1 && self.tokens.is_empty() + } - pub fn span(&self) -> Span { self.meta().span } + pub fn span(&self) -> Span { + self.span + } pub fn is_meta_item_list(&self) -> bool { self.meta_item_list().is_some() @@ -225,7 +241,7 @@ impl MetaItem { match self.node { MetaItemKind::NameValue(ref v) => { match v.node { - ast::LitKind::Str(ref s, _) => Some((*s).clone()), + LitKind::Str(ref s, _) => Some((*s).clone()), _ => None, } }, @@ -264,8 +280,35 @@ impl MetaItem { impl Attribute { /// Extract the MetaItem from inside this Attribute. - pub fn meta(&self) -> &MetaItem { - &self.value + pub fn meta(&self) -> Option { + let mut tokens = self.tokens.trees().peekable(); + Some(MetaItem { + name: match self.path.segments.len() { + 1 => self.path.segments[0].identifier.name, + _ => return None, + }, + node: if let Some(node) = MetaItemKind::from_tokens(&mut tokens) { + if tokens.peek().is_some() { + return None; + } + node + } else { + return None; + }, + span: self.span, + }) + } + + pub fn parse_meta<'a>(&self, sess: &'a ParseSess) -> PResult<'a, MetaItem> { + if self.path.segments.len() > 1 { + sess.span_diagnostic.span_err(self.path.span, "expected ident, found path"); + } + + Ok(MetaItem { + name: self.path.segments.last().unwrap().identifier.name, + node: Parser::new(sess, self.tokens.clone(), None, false).parse_meta_item_kind()?, + span: self.span, + }) } /// Convert self to a normal #[doc="foo"] comment, if it is a @@ -293,7 +336,7 @@ impl Attribute { /* Constructors */ pub fn mk_name_value_item_str(name: Name, value: Symbol) -> MetaItem { - let value_lit = dummy_spanned(ast::LitKind::Str(value, ast::StrStyle::Cooked)); + let value_lit = dummy_spanned(LitKind::Str(value, ast::StrStyle::Cooked)); mk_spanned_name_value_item(DUMMY_SP, name, value_lit) } @@ -348,7 +391,8 @@ pub fn mk_spanned_attr_inner(sp: Span, id: AttrId, item: MetaItem) -> Attribute Attribute { id: id, style: ast::AttrStyle::Inner, - value: item, + path: ast::Path::from_ident(item.span, ast::Ident::with_empty_ctxt(item.name)), + tokens: item.node.tokens(item.span), is_sugared_doc: false, span: sp, } @@ -365,7 +409,8 @@ pub fn mk_spanned_attr_outer(sp: Span, id: AttrId, item: MetaItem) -> Attribute Attribute { id: id, style: ast::AttrStyle::Outer, - value: item, + path: ast::Path::from_ident(item.span, ast::Ident::with_empty_ctxt(item.name)), + tokens: item.node.tokens(item.span), is_sugared_doc: false, span: sp, } @@ -374,32 +419,25 @@ pub fn mk_spanned_attr_outer(sp: Span, id: AttrId, item: MetaItem) -> Attribute pub fn mk_sugared_doc_attr(id: AttrId, text: Symbol, lo: BytePos, hi: BytePos) -> Attribute { let style = doc_comment_style(&text.as_str()); - let lit = spanned(lo, hi, ast::LitKind::Str(text, ast::StrStyle::Cooked)); + let lit = spanned(lo, hi, LitKind::Str(text, ast::StrStyle::Cooked)); Attribute { id: id, style: style, - value: MetaItem { - span: mk_sp(lo, hi), - name: Symbol::intern("doc"), - node: MetaItemKind::NameValue(lit), - }, + path: ast::Path::from_ident(mk_sp(lo, hi), ast::Ident::from_str("doc")), + tokens: MetaItemKind::NameValue(lit).tokens(mk_sp(lo, hi)), is_sugared_doc: true, span: mk_sp(lo, hi), } } pub fn list_contains_name(items: &[NestedMetaItem], name: &str) -> bool { - debug!("attr::list_contains_name (name={})", name); items.iter().any(|item| { - debug!(" testing: {:?}", item.name()); item.check_name(name) }) } pub fn contains_name(attrs: &[Attribute], name: &str) -> bool { - debug!("attr::contains_name (name={})", name); attrs.iter().any(|item| { - debug!(" testing: {}", item.name()); item.check_name(name) }) } @@ -452,8 +490,14 @@ pub enum InlineAttr { /// Determine what `#[inline]` attribute is present in `attrs`, if any. pub fn find_inline_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> InlineAttr { attrs.iter().fold(InlineAttr::None, |ia, attr| { - match attr.value.node { - _ if attr.value.name != "inline" => ia, + if attr.path != "inline" { + return ia; + } + let meta = match attr.meta() { + Some(meta) => meta.node, + None => return ia, + }; + match meta { MetaItemKind::Word => { mark_used(attr); InlineAttr::Hint @@ -574,14 +618,15 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler, let mut rustc_depr: Option = None; 'outer: for attr in attrs_iter { - let tag = attr.name(); - if tag != "rustc_deprecated" && tag != "unstable" && tag != "stable" { + if attr.path != "rustc_deprecated" && attr.path != "unstable" && attr.path != "stable" { continue // not a stability level } mark_used(attr); - if let Some(metas) = attr.meta_item_list() { + let meta = attr.meta(); + if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }) = meta { + let meta = meta.as_ref().unwrap(); let get = |meta: &MetaItem, item: &mut Option| { if item.is_some() { handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.name())); @@ -596,7 +641,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler, } }; - match &*tag.as_str() { + match &*meta.name.as_str() { "rustc_deprecated" => { if rustc_depr.is_some() { span_err!(diagnostic, item_sp, E0540, @@ -772,7 +817,7 @@ fn find_deprecation_generic<'a, I>(diagnostic: &Handler, let mut depr: Option = None; 'outer: for attr in attrs_iter { - if attr.name() != "deprecated" { + if attr.path != "deprecated" { continue } @@ -847,8 +892,8 @@ pub fn find_deprecation(diagnostic: &Handler, attrs: &[Attribute], /// structure layout, and `packed` to remove padding. pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec { let mut acc = Vec::new(); - match attr.value.node { - ast::MetaItemKind::List(ref items) if attr.value.name == "repr" => { + if attr.path == "repr" { + if let Some(items) = attr.meta_item_list() { mark_used(attr); for item in items { if !item.is_meta_item() { @@ -883,8 +928,6 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec } } } - // Not a "repr" hint: ignore. - _ => { } } acc } @@ -931,6 +974,195 @@ impl IntType { } } +impl MetaItem { + fn tokens(&self) -> TokenStream { + let ident = TokenTree::Token(self.span, Token::Ident(Ident::with_empty_ctxt(self.name))); + TokenStream::concat(vec![ident.into(), self.node.tokens(self.span)]) + } + + fn from_tokens(tokens: &mut iter::Peekable) -> Option + where I: Iterator, + { + let (mut span, name) = match tokens.next() { + Some(TokenTree::Token(span, Token::Ident(ident))) => (span, ident.name), + _ => return None, + }; + let node = match MetaItemKind::from_tokens(tokens) { + Some(node) => node, + _ => return None, + }; + if let Some(last_span) = node.last_span() { + span.hi = last_span.hi; + } + Some(MetaItem { name: name, span: span, node: node }) + } +} + +impl MetaItemKind { + fn last_span(&self) -> Option { + match *self { + MetaItemKind::Word => None, + MetaItemKind::List(ref list) => list.last().map(NestedMetaItem::span), + MetaItemKind::NameValue(ref lit) => Some(lit.span), + } + } + + pub fn tokens(&self, span: Span) -> TokenStream { + match *self { + MetaItemKind::Word => TokenStream::empty(), + MetaItemKind::NameValue(ref lit) => { + TokenStream::concat(vec![TokenTree::Token(span, Token::Eq).into(), lit.tokens()]) + } + MetaItemKind::List(ref list) => { + let mut tokens = Vec::new(); + for (i, item) in list.iter().enumerate() { + if i > 0 { + tokens.push(TokenTree::Token(span, Token::Comma).into()); + } + tokens.push(item.node.tokens()); + } + TokenTree::Delimited(span, Delimited { + delim: token::Paren, + tts: TokenStream::concat(tokens).into(), + }).into() + } + } + } + + fn from_tokens(tokens: &mut iter::Peekable) -> Option + where I: Iterator, + { + let delimited = match tokens.peek().cloned() { + Some(TokenTree::Token(_, token::Eq)) => { + tokens.next(); + return if let Some(TokenTree::Token(span, token)) = tokens.next() { + LitKind::from_token(token) + .map(|lit| MetaItemKind::NameValue(Spanned { node: lit, span: span })) + } else { + None + }; + } + Some(TokenTree::Delimited(_, ref delimited)) if delimited.delim == token::Paren => { + tokens.next(); + delimited.stream() + } + _ => return Some(MetaItemKind::Word), + }; + + let mut tokens = delimited.into_trees().peekable(); + let mut result = Vec::new(); + while let Some(..) = tokens.peek() { + match NestedMetaItemKind::from_tokens(&mut tokens) { + Some(item) => result.push(Spanned { span: item.span(), node: item }), + None => return None, + } + match tokens.next() { + None | Some(TokenTree::Token(_, Token::Comma)) => {} + _ => return None, + } + } + Some(MetaItemKind::List(result)) + } +} + +impl NestedMetaItemKind { + fn span(&self) -> Span { + match *self { + NestedMetaItemKind::MetaItem(ref item) => item.span, + NestedMetaItemKind::Literal(ref lit) => lit.span, + } + } + + fn tokens(&self) -> TokenStream { + match *self { + NestedMetaItemKind::MetaItem(ref item) => item.tokens(), + NestedMetaItemKind::Literal(ref lit) => lit.tokens(), + } + } + + fn from_tokens(tokens: &mut iter::Peekable) -> Option + where I: Iterator, + { + if let Some(TokenTree::Token(span, token)) = tokens.peek().cloned() { + if let Some(node) = LitKind::from_token(token) { + tokens.next(); + return Some(NestedMetaItemKind::Literal(Spanned { node: node, span: span })); + } + } + + MetaItem::from_tokens(tokens).map(NestedMetaItemKind::MetaItem) + } +} + +impl Lit { + fn tokens(&self) -> TokenStream { + TokenTree::Token(self.span, self.node.token()).into() + } +} + +impl LitKind { + fn token(&self) -> Token { + use std::ascii; + + match *self { + LitKind::Str(string, ast::StrStyle::Cooked) => { + let mut escaped = String::new(); + for ch in string.as_str().chars() { + escaped.extend(ch.escape_unicode()); + } + Token::Literal(token::Lit::Str_(Symbol::intern(&escaped)), None) + } + LitKind::Str(string, ast::StrStyle::Raw(n)) => { + Token::Literal(token::Lit::StrRaw(string, n), None) + } + LitKind::ByteStr(ref bytes) => { + let string = bytes.iter().cloned().flat_map(ascii::escape_default) + .map(Into::::into).collect::(); + Token::Literal(token::Lit::ByteStr(Symbol::intern(&string)), None) + } + LitKind::Byte(byte) => { + let string: String = ascii::escape_default(byte).map(Into::::into).collect(); + Token::Literal(token::Lit::Byte(Symbol::intern(&string)), None) + } + LitKind::Char(ch) => { + let string: String = ch.escape_default().map(Into::::into).collect(); + Token::Literal(token::Lit::Char(Symbol::intern(&string)), None) + } + LitKind::Int(n, ty) => { + let suffix = match ty { + ast::LitIntType::Unsigned(ty) => Some(Symbol::intern(ty.ty_to_string())), + ast::LitIntType::Signed(ty) => Some(Symbol::intern(ty.ty_to_string())), + ast::LitIntType::Unsuffixed => None, + }; + Token::Literal(token::Lit::Integer(Symbol::intern(&n.to_string())), suffix) + } + LitKind::Float(symbol, ty) => { + Token::Literal(token::Lit::Float(symbol), Some(Symbol::intern(ty.ty_to_string()))) + } + LitKind::FloatUnsuffixed(symbol) => Token::Literal(token::Lit::Float(symbol), None), + LitKind::Bool(value) => Token::Ident(Ident::with_empty_ctxt(Symbol::intern(match value { + true => "true", + false => "false", + }))), + } + } + + fn from_token(token: Token) -> Option { + match token { + Token::Ident(ident) if ident.name == "true" => Some(LitKind::Bool(true)), + Token::Ident(ident) if ident.name == "false" => Some(LitKind::Bool(false)), + Token::Literal(lit, suf) => { + let (suffix_illegal, result) = parse::lit_token(lit, suf, None); + if suffix_illegal && suf.is_some() { + return None; + } + result + } + _ => None, + } + } +} + pub trait HasAttrs: Sized { fn attrs(&self) -> &[ast::Attribute]; fn map_attrs) -> Vec>(self, f: F) -> Self; diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index ea12a31770fc0..2591a5766693f 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -109,7 +109,8 @@ impl<'a> StripUnconfigured<'a> { self.process_cfg_attr(ast::Attribute { id: attr::mk_attr_id(), style: attr.style, - value: mi.clone(), + path: ast::Path::from_ident(mi.span, ast::Ident::with_empty_ctxt(mi.name)), + tokens: mi.node.tokens(mi.span), is_sugared_doc: false, span: mi.span, }) @@ -132,8 +133,9 @@ impl<'a> StripUnconfigured<'a> { return false; } - let mis = match attr.value.node { - ast::MetaItemKind::List(ref mis) if is_cfg(&attr) => mis, + let mis = attr.meta_item_list(); + let mis = match mis { + Some(ref mis) if is_cfg(&attr) => mis, _ => return true }; diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index 77cc7bab0315a..5b253635f257e 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -18,7 +18,7 @@ use syntax_pos::Span; pub fn collect_derives(cx: &mut ExtCtxt, attrs: &mut Vec) -> Vec<(Symbol, Span)> { let mut result = Vec::new(); attrs.retain(|attr| { - if attr.name() != "derive" { + if attr.path != "derive" { return true; } @@ -27,7 +27,7 @@ pub fn collect_derives(cx: &mut ExtCtxt, attrs: &mut Vec) -> Vec return false; } - let traits = attr.meta_item_list().unwrap_or(&[]).to_owned(); + let traits = attr.meta_item_list().unwrap_or_else(Vec::new); if traits.is_empty() { cx.span_warn(attr.span, "empty trait list in `derive`"); return false; diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 10168f010a077..c1095d3445682 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -272,7 +272,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.collect_invocations(expansion, &[]) } else if let InvocationKind::Attr { attr: None, traits, item } = invoc.kind { let item = item - .map_attrs(|mut attrs| { attrs.retain(|a| a.name() != "derive"); attrs }); + .map_attrs(|mut attrs| { attrs.retain(|a| a.path != "derive"); attrs }); let item_with_markers = add_derived_markers(&mut self.cx, &traits, item.clone()); let derives = derives.entry(invoc.expansion_data.mark).or_insert_with(Vec::new); @@ -380,7 +380,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }; attr::mark_used(&attr); - let name = attr.name(); + let name = attr.path.segments[0].identifier.name; self.cx.bt_push(ExpnInfo { call_site: attr.span, callee: NameAndSpan { @@ -392,25 +392,25 @@ impl<'a, 'b> MacroExpander<'a, 'b> { match *ext { MultiModifier(ref mac) => { - let item = mac.expand(self.cx, attr.span, &attr.value, item); + let meta = panictry!(attr.parse_meta(&self.cx.parse_sess)); + let item = mac.expand(self.cx, attr.span, &meta, item); kind.expect_from_annotatables(item) } MultiDecorator(ref mac) => { let mut items = Vec::new(); - mac.expand(self.cx, attr.span, &attr.value, &item, - &mut |item| items.push(item)); + let meta = panictry!(attr.parse_meta(&self.cx.parse_sess)); + mac.expand(self.cx, attr.span, &meta, &item, &mut |item| items.push(item)); items.push(item); kind.expect_from_annotatables(items) } SyntaxExtension::AttrProcMacro(ref mac) => { - let attr_toks = stream_for_attr_args(&attr, &self.cx.parse_sess); let item_toks = stream_for_item(&item, &self.cx.parse_sess); let span = Span { expn_id: self.cx.codemap().record_expansion(ExpnInfo { call_site: attr.span, callee: NameAndSpan { - format: MacroAttribute(name), + format: MacroAttribute(Symbol::intern(&format!("{}", attr.path))), span: None, allow_internal_unstable: false, }, @@ -418,7 +418,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { ..attr.span }; - let tok_result = mac.expand(self.cx, attr.span, attr_toks, item_toks); + let tok_result = mac.expand(self.cx, attr.span, attr.tokens.clone(), item_toks); self.parse_expansion(tok_result, kind, name, span) } SyntaxExtension::ProcMacroDerive(..) | SyntaxExtension::BuiltinDerive(..) => { @@ -784,32 +784,6 @@ fn stream_for_item(item: &Annotatable, parse_sess: &ParseSess) -> TokenStream { string_to_stream(text, parse_sess) } -fn stream_for_attr_args(attr: &ast::Attribute, parse_sess: &ParseSess) -> TokenStream { - use ast::MetaItemKind::*; - use print::pp::Breaks; - use print::pprust::PrintState; - - let token_string = match attr.value.node { - // For `#[foo]`, an empty token - Word => return TokenStream::empty(), - // For `#[foo(bar, baz)]`, returns `(bar, baz)` - List(ref items) => pprust::to_string(|s| { - s.popen()?; - s.commasep(Breaks::Consistent, - &items[..], - |s, i| s.print_meta_list_item(&i))?; - s.pclose() - }), - // For `#[foo = "bar"]`, returns `= "bar"` - NameValue(ref lit) => pprust::to_string(|s| { - s.word_space("=")?; - s.print_literal(lit) - }), - }; - - string_to_stream(token_string, parse_sess) -} - fn string_to_stream(text: String, parse_sess: &ParseSess) -> TokenStream { let filename = String::from(""); filemap_to_stream(parse_sess, parse_sess.codemap().new_filemap(filename, None, text)) diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index 69ff726e719a9..10b7249743b8c 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -220,16 +220,24 @@ pub mod rt { } impl ToTokens for ast::Attribute { - fn to_tokens(&self, cx: &ExtCtxt) -> Vec { + fn to_tokens(&self, _cx: &ExtCtxt) -> Vec { let mut r = vec![]; // FIXME: The spans could be better r.push(TokenTree::Token(self.span, token::Pound)); if self.style == ast::AttrStyle::Inner { r.push(TokenTree::Token(self.span, token::Not)); } + let mut inner = Vec::new(); + for (i, segment) in self.path.segments.iter().enumerate() { + if i > 0 { + inner.push(TokenTree::Token(self.span, token::Colon).into()); + } + inner.push(TokenTree::Token(self.span, token::Ident(segment.identifier)).into()); + } + inner.push(self.tokens.clone()); + r.push(TokenTree::Delimited(self.span, tokenstream::Delimited { - delim: token::Bracket, - tts: self.value.to_tokens(cx).into_iter().collect::().into(), + delim: token::Bracket, tts: TokenStream::concat(inner).into() })); r } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index e7bf16eae9ee6..2c3ad98a6be63 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -859,35 +859,34 @@ macro_rules! gate_feature { impl<'a> Context<'a> { fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) { debug!("check_attribute(attr = {:?})", attr); - let name = &*attr.name().as_str(); + let name = unwrap_or!(attr.name(), return); + for &(n, ty, ref gateage) in BUILTIN_ATTRIBUTES { - if n == name { + if name == n { if let &Gated(_, ref name, ref desc, ref has_feature) = gateage { gate_feature_fn!(self, has_feature, attr.span, name, desc); } - debug!("check_attribute: {:?} is builtin, {:?}, {:?}", name, ty, gateage); + debug!("check_attribute: {:?} is builtin, {:?}, {:?}", attr.path, ty, gateage); return; } } for &(ref n, ref ty) in self.plugin_attributes { - if n == name { + if attr.path == &**n { // Plugins can't gate attributes, so we don't check for it // unlike the code above; we only use this loop to // short-circuit to avoid the checks below - debug!("check_attribute: {:?} is registered by a plugin, {:?}", name, ty); + debug!("check_attribute: {:?} is registered by a plugin, {:?}", attr.path, ty); return; } } - if name.starts_with("rustc_") { + if name.as_str().starts_with("rustc_") { gate_feature!(self, rustc_attrs, attr.span, "unless otherwise specified, attributes \ with the prefix `rustc_` \ are reserved for internal compiler diagnostics"); - } else if name.starts_with("derive_") { + } else if name.as_str().starts_with("derive_") { gate_feature!(self, custom_derive, attr.span, EXPLAIN_DERIVE_UNDERSCORE); - } else if attr::is_known(attr) { - debug!("check_attribute: {:?} is known", name); - } else { + } else if !attr::is_known(attr) { // Only run the custom attribute lint during regular // feature gate checking. Macro gating runs // before the plugin attributes are registered @@ -898,7 +897,7 @@ impl<'a> Context<'a> { unknown to the compiler and \ may have meaning \ added to it in the future", - name)); + attr.path)); } } } @@ -1097,7 +1096,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { self.context.check_attribute(attr, false); } - if contains_novel_literal(&attr.value) { + let meta = panictry!(attr.parse_meta(&self.context.parse_sess)); + if contains_novel_literal(&meta) { gate_feature_post!(&self, attr_literals, attr.span, "non-string literals in attributes, or string \ literals in top-level positions, are experimental"); @@ -1160,8 +1160,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { `#[repr(simd)]` instead"); } for attr in &i.attrs { - if attr.name() == "repr" { - for item in attr.meta_item_list().unwrap_or(&[]) { + if attr.path == "repr" { + for item in attr.meta_item_list().unwrap_or_else(Vec::new) { if item.check_name("simd") { gate_feature_post!(&self, repr_simd, i.span, "SIMD types are experimental \ diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index fb4eb19be2b15..903dac1f379ca 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -488,7 +488,8 @@ pub fn noop_fold_attribute(attr: Attribute, fld: &mut T) -> Option(nt: token::Nonterminal, fld: &mut T) token::NtExpr(expr) => token::NtExpr(fld.fold_expr(expr)), token::NtTy(ty) => token::NtTy(fld.fold_ty(ty)), token::NtIdent(id) => token::NtIdent(Spanned::{node: fld.fold_ident(id.node), ..id}), - token::NtMeta(meta_item) => token::NtMeta(fld.fold_meta_item(meta_item)), + token::NtMeta(meta) => token::NtMeta(fld.fold_meta_item(meta)), token::NtPath(path) => token::NtPath(fld.fold_path(path)), token::NtTT(tt) => token::NtTT(fld.fold_tt(tt)), token::NtArm(arm) => token::NtArm(fld.fold_arm(arm)), @@ -1369,7 +1370,7 @@ mod tests { matches_codepattern, "matches_codepattern", pprust::to_string(|s| fake_print_crate(s, &folded_crate)), - "#[a]mod zz{fn zz(zz:zz,zz:zz){zz!(zz,zz,zz);zz;zz}}".to_string()); + "#[zz]mod zz{fn zz(zz:zz,zz:zz){zz!(zz,zz,zz);zz;zz}}".to_string()); } // even inside macro defs.... diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 39a9aff48bf27..4c9a5d512af02 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -65,6 +65,16 @@ macro_rules! panictry { }) } +#[macro_export] +macro_rules! unwrap_or { + ($opt:expr, $default:expr) => { + match $opt { + Some(x) => x, + None => $default, + } + } +} + #[macro_use] pub mod diagnostics { #[macro_use] diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index ded676da3c676..272cff7ad34b2 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -143,7 +143,8 @@ impl<'a> Parser<'a> { Ok(ast::Attribute { id: attr::mk_attr_id(), style: style, - value: value, + path: ast::Path::from_ident(value.span, ast::Ident::with_empty_ctxt(value.name)), + tokens: value.node.tokens(value.span), is_sugared_doc: false, span: span, }) @@ -221,15 +222,20 @@ impl<'a> Parser<'a> { let lo = self.span.lo; let ident = self.parse_ident()?; - let node = if self.eat(&token::Eq) { + let node = self.parse_meta_item_kind()?; + let hi = self.prev_span.hi; + Ok(ast::MetaItem { name: ident.name, node: node, span: mk_sp(lo, hi) }) + } + + pub fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> { + Ok(if self.eat(&token::Eq) { ast::MetaItemKind::NameValue(self.parse_unsuffixed_lit()?) } else if self.token == token::OpenDelim(token::Paren) { ast::MetaItemKind::List(self.parse_meta_seq()?) } else { + self.eat(&token::OpenDelim(token::Paren)); ast::MetaItemKind::Word - }; - let hi = self.prev_span.hi; - Ok(ast::MetaItem { name: ident.name, node: node, span: mk_sp(lo, hi) }) + }) } /// matches meta_item_inner : (meta_item | UNSUFFIXED_LIT) ; diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index c00d2952b3b42..2bdd3938d6bd5 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -374,38 +374,80 @@ fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { s[1..].chars().all(|c| '0' <= c && c <= '9') } -fn filtered_float_lit(data: Symbol, suffix: Option, sd: &Handler, sp: Span) - -> ast::LitKind { +macro_rules! err { + ($opt_diag:expr, |$span:ident, $diag:ident| $($body:tt)*) => { + match $opt_diag { + Some(($span, $diag)) => { $($body)* } + None => return None, + } + } +} + +pub fn lit_token(lit: token::Lit, suf: Option, diag: Option<(Span, &Handler)>) + -> (bool /* suffix illegal? */, Option) { + use ast::LitKind; + + match lit { + token::Byte(i) => (true, Some(LitKind::Byte(byte_lit(&i.as_str()).0))), + token::Char(i) => (true, Some(LitKind::Char(char_lit(&i.as_str()).0))), + + // There are some valid suffixes for integer and float literals, + // so all the handling is done internally. + token::Integer(s) => (false, integer_lit(&s.as_str(), suf, diag)), + token::Float(s) => (false, float_lit(&s.as_str(), suf, diag)), + + token::Str_(s) => { + let s = Symbol::intern(&str_lit(&s.as_str())); + (true, Some(LitKind::Str(s, ast::StrStyle::Cooked))) + } + token::StrRaw(s, n) => { + let s = Symbol::intern(&raw_str_lit(&s.as_str())); + (true, Some(LitKind::Str(s, ast::StrStyle::Raw(n)))) + } + token::ByteStr(i) => { + (true, Some(LitKind::ByteStr(byte_str_lit(&i.as_str())))) + } + token::ByteStrRaw(i, _) => { + (true, Some(LitKind::ByteStr(Rc::new(i.to_string().into_bytes())))) + } + } +} + +fn filtered_float_lit(data: Symbol, suffix: Option, diag: Option<(Span, &Handler)>) + -> Option { debug!("filtered_float_lit: {}, {:?}", data, suffix); let suffix = match suffix { Some(suffix) => suffix, - None => return ast::LitKind::FloatUnsuffixed(data), + None => return Some(ast::LitKind::FloatUnsuffixed(data)), }; - match &*suffix.as_str() { + Some(match &*suffix.as_str() { "f32" => ast::LitKind::Float(data, ast::FloatTy::F32), "f64" => ast::LitKind::Float(data, ast::FloatTy::F64), suf => { - if suf.len() >= 2 && looks_like_width_suffix(&['f'], suf) { - // if it looks like a width, lets try to be helpful. - sd.struct_span_err(sp, &format!("invalid width `{}` for float literal", &suf[1..])) - .help("valid widths are 32 and 64") - .emit(); - } else { - sd.struct_span_err(sp, &format!("invalid suffix `{}` for float literal", suf)) - .help("valid suffixes are `f32` and `f64`") - .emit(); - } + err!(diag, |span, diag| { + if suf.len() >= 2 && looks_like_width_suffix(&['f'], suf) { + // if it looks like a width, lets try to be helpful. + let msg = format!("invalid width `{}` for float literal", &suf[1..]); + diag.struct_span_err(span, &msg).help("valid widths are 32 and 64").emit() + } else { + let msg = format!("invalid suffix `{}` for float literal", suf); + diag.struct_span_err(span, &msg) + .help("valid suffixes are `f32` and `f64`") + .emit(); + } + }); ast::LitKind::FloatUnsuffixed(data) } - } + }) } -pub fn float_lit(s: &str, suffix: Option, sd: &Handler, sp: Span) -> ast::LitKind { +pub fn float_lit(s: &str, suffix: Option, diag: Option<(Span, &Handler)>) + -> Option { debug!("float_lit: {:?}, {:?}", s, suffix); // FIXME #2252: bounds checking float literals is deferred until trans let s = s.chars().filter(|&c| c != '_').collect::(); - filtered_float_lit(Symbol::intern(&s), suffix, sd, sp) + filtered_float_lit(Symbol::intern(&s), suffix, diag) } /// Parse a string representing a byte literal into its final form. Similar to `char_lit` @@ -500,7 +542,8 @@ pub fn byte_str_lit(lit: &str) -> Rc> { Rc::new(res) } -pub fn integer_lit(s: &str, suffix: Option, sd: &Handler, sp: Span) -> ast::LitKind { +pub fn integer_lit(s: &str, suffix: Option, diag: Option<(Span, &Handler)>) + -> Option { // s can only be ascii, byte indexing is fine let s2 = s.chars().filter(|&c| c != '_').collect::(); @@ -524,13 +567,16 @@ pub fn integer_lit(s: &str, suffix: Option, sd: &Handler, sp: Span) -> a // 1f64 and 2f32 etc. are valid float literals. if let Some(suf) = suffix { if looks_like_width_suffix(&['f'], &suf.as_str()) { - match base { - 16 => sd.span_err(sp, "hexadecimal float literal is not supported"), - 8 => sd.span_err(sp, "octal float literal is not supported"), - 2 => sd.span_err(sp, "binary float literal is not supported"), - _ => () + let err = match base { + 16 => Some("hexadecimal float literal is not supported"), + 8 => Some("octal float literal is not supported"), + 2 => Some("binary float literal is not supported"), + _ => None, + }; + if let Some(err) = err { + err!(diag, |span, diag| diag.span_err(span, err)); } - return filtered_float_lit(Symbol::intern(&s), Some(suf), sd, sp) + return filtered_float_lit(Symbol::intern(&s), Some(suf), diag) } } @@ -539,7 +585,9 @@ pub fn integer_lit(s: &str, suffix: Option, sd: &Handler, sp: Span) -> a } if let Some(suf) = suffix { - if suf.as_str().is_empty() { sd.span_bug(sp, "found empty literal suffix in Some")} + if suf.as_str().is_empty() { + err!(diag, |span, diag| diag.span_bug(span, "found empty literal suffix in Some")); + } ty = match &*suf.as_str() { "isize" => ast::LitIntType::Signed(ast::IntTy::Is), "i8" => ast::LitIntType::Signed(ast::IntTy::I8), @@ -556,17 +604,20 @@ pub fn integer_lit(s: &str, suffix: Option, sd: &Handler, sp: Span) -> a suf => { // i and u look like widths, so lets // give an error message along those lines - if looks_like_width_suffix(&['i', 'u'], suf) { - sd.struct_span_err(sp, &format!("invalid width `{}` for integer literal", - &suf[1..])) - .help("valid widths are 8, 16, 32, 64 and 128") - .emit(); - } else { - sd.struct_span_err(sp, &format!("invalid suffix `{}` for numeric literal", suf)) - .help("the suffix must be one of the integral types \ - (`u32`, `isize`, etc)") - .emit(); - } + err!(diag, |span, diag| { + if looks_like_width_suffix(&['i', 'u'], suf) { + let msg = format!("invalid width `{}` for integer literal", &suf[1..]); + diag.struct_span_err(span, &msg) + .help("valid widths are 8, 16, 32, 64 and 128") + .emit(); + } else { + let msg = format!("invalid suffix `{}` for numeric literal", suf); + diag.struct_span_err(span, &msg) + .help("the suffix must be one of the integral types \ + (`u32`, `isize`, etc)") + .emit(); + } + }); ty } @@ -576,7 +627,7 @@ pub fn integer_lit(s: &str, suffix: Option, sd: &Handler, sp: Span) -> a debug!("integer_lit: the type is {:?}, base {:?}, the new string is {:?}, the original \ string was {:?}, the original suffix was {:?}", ty, base, s, orig, suffix); - match u128::from_str_radix(s, base) { + Some(match u128::from_str_radix(s, base) { Ok(r) => ast::LitKind::Int(r, ty), Err(_) => { // small bases are lexed as if they were base 10, e.g, the string @@ -588,11 +639,11 @@ pub fn integer_lit(s: &str, suffix: Option, sd: &Handler, sp: Span) -> a s.chars().any(|c| c.to_digit(10).map_or(false, |d| d >= base)); if !already_errored { - sd.span_err(sp, "int literal is too large"); + err!(diag, |span, diag| diag.span_err(span, "int literal is too large")); } ast::LitKind::Int(0, ty) } - } + }) } #[cfg(test)] @@ -957,7 +1008,7 @@ mod tests { let source = "/// doc comment\r\n/// line 2\r\nfn foo() {}".to_string(); let item = parse_item_from_source_str(name.clone(), source, &sess) .unwrap().unwrap(); - let docs = item.attrs.iter().filter(|a| a.name() == "doc") + let docs = item.attrs.iter().filter(|a| a.path == "doc") .map(|a| a.value_str().unwrap().to_string()).collect::>(); let b: &[_] = &["/// doc comment".to_string(), "/// line 2".to_string()]; assert_eq!(&docs[..], b); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 9872afd27b7bc..ed512b899877d 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -60,7 +60,6 @@ use util::ThinVec; use std::collections::HashSet; use std::{cmp, mem, slice}; use std::path::{Path, PathBuf}; -use std::rc::Rc; bitflags! { flags Restrictions: u8 { @@ -1643,44 +1642,15 @@ impl<'a> Parser<'a> { _ => { return self.unexpected_last(&self.token); } }, token::Literal(lit, suf) => { - let (suffix_illegal, out) = match lit { - token::Byte(i) => (true, LitKind::Byte(parse::byte_lit(&i.as_str()).0)), - token::Char(i) => (true, LitKind::Char(parse::char_lit(&i.as_str()).0)), - - // there are some valid suffixes for integer and - // float literals, so all the handling is done - // internally. - token::Integer(s) => { - let diag = &self.sess.span_diagnostic; - (false, parse::integer_lit(&s.as_str(), suf, diag, self.span)) - } - token::Float(s) => { - let diag = &self.sess.span_diagnostic; - (false, parse::float_lit(&s.as_str(), suf, diag, self.span)) - } - - token::Str_(s) => { - let s = Symbol::intern(&parse::str_lit(&s.as_str())); - (true, LitKind::Str(s, ast::StrStyle::Cooked)) - } - token::StrRaw(s, n) => { - let s = Symbol::intern(&parse::raw_str_lit(&s.as_str())); - (true, LitKind::Str(s, ast::StrStyle::Raw(n))) - } - token::ByteStr(i) => { - (true, LitKind::ByteStr(parse::byte_str_lit(&i.as_str()))) - } - token::ByteStrRaw(i, _) => { - (true, LitKind::ByteStr(Rc::new(i.to_string().into_bytes()))) - } - }; + let diag = Some((self.span, &self.sess.span_diagnostic)); + let (suffix_illegal, result) = parse::lit_token(lit, suf, diag); if suffix_illegal { let sp = self.span; self.expect_no_suffix(sp, &format!("{} literal", lit.short_name()), suf) } - out + result.unwrap() } _ => { return self.unexpected_last(&self.token); } }; @@ -5135,11 +5105,9 @@ impl<'a> Parser<'a> { let attr = ast::Attribute { id: attr::mk_attr_id(), style: ast::AttrStyle::Outer, - value: ast::MetaItem { - name: Symbol::intern("warn_directory_ownership"), - node: ast::MetaItemKind::Word, - span: syntax_pos::DUMMY_SP, - }, + path: ast::Path::from_ident(syntax_pos::DUMMY_SP, + Ident::from_str("warn_directory_ownership")), + tokens: TokenStream::empty(), is_sugared_doc: false, span: syntax_pos::DUMMY_SP, }; diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 5b65aac92b81c..3837700457290 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -17,7 +17,7 @@ pub use self::Token::*; use ast::{self}; use ptr::P; use symbol::keywords; -use tokenstream; +use tokenstream::TokenTree; use std::fmt; use std::rc::Rc; @@ -348,7 +348,7 @@ pub enum Nonterminal { /// Stuff inside brackets for attributes NtMeta(ast::MetaItem), NtPath(ast::Path), - NtTT(tokenstream::TokenTree), + NtTT(TokenTree), // These are not exposed to macros, but are used by quasiquote. NtArm(ast::Arm), NtImplItem(ast::ImplItem), diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 3efadbd00d1e0..d8af95d8d3062 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -28,7 +28,7 @@ use ptr::P; use std_inject; use symbol::{Symbol, keywords}; use syntax_pos::DUMMY_SP; -use tokenstream::{self, TokenTree}; +use tokenstream::{self, TokenStream, TokenTree}; use std::ascii; use std::io::{self, Write, Read}; @@ -329,6 +329,10 @@ pub fn tts_to_string(tts: &[tokenstream::TokenTree]) -> String { to_string(|s| s.print_tts(tts.iter().cloned().collect())) } +pub fn tokens_to_string(tokens: TokenStream) -> String { + to_string(|s| s.print_tts(tokens)) +} + pub fn stmt_to_string(stmt: &ast::Stmt) -> String { to_string(|s| s.print_stmt(stmt)) } @@ -750,7 +754,21 @@ pub trait PrintState<'a> { ast::AttrStyle::Inner => word(self.writer(), "#![")?, ast::AttrStyle::Outer => word(self.writer(), "#[")?, } - self.print_meta_item(&attr.meta())?; + if let Some(mi) = attr.meta() { + self.print_meta_item(&mi)? + } else { + for (i, segment) in attr.path.segments.iter().enumerate() { + if i > 0 { + word(self.writer(), "::")? + } + if segment.identifier.name != keywords::CrateRoot.name() && + segment.identifier.name != "$crate" { + word(self.writer(), &segment.identifier.name.as_str())?; + } + } + space(self.writer())?; + self.print_tts(attr.tokens.clone())?; + } word(self.writer(), "]") } } @@ -789,6 +807,45 @@ pub trait PrintState<'a> { self.end() } + /// This doesn't deserve to be called "pretty" printing, but it should be + /// meaning-preserving. A quick hack that might help would be to look at the + /// spans embedded in the TTs to decide where to put spaces and newlines. + /// But it'd be better to parse these according to the grammar of the + /// appropriate macro, transcribe back into the grammar we just parsed from, + /// and then pretty-print the resulting AST nodes (so, e.g., we print + /// expression arguments as expressions). It can be done! I think. + fn print_tt(&mut self, tt: tokenstream::TokenTree) -> io::Result<()> { + match tt { + TokenTree::Token(_, ref tk) => { + word(self.writer(), &token_to_string(tk))?; + match *tk { + parse::token::DocComment(..) => { + hardbreak(self.writer()) + } + _ => Ok(()) + } + } + TokenTree::Delimited(_, ref delimed) => { + word(self.writer(), &token_to_string(&delimed.open_token()))?; + space(self.writer())?; + self.print_tts(delimed.stream())?; + space(self.writer())?; + word(self.writer(), &token_to_string(&delimed.close_token())) + }, + } + } + + fn print_tts(&mut self, tts: tokenstream::TokenStream) -> io::Result<()> { + self.ibox(0)?; + for (i, tt) in tts.into_trees().enumerate() { + if i != 0 { + space(self.writer())?; + } + self.print_tt(tt)?; + } + self.end() + } + fn space_if_not_bol(&mut self) -> io::Result<()> { if !self.is_bol() { space(self.writer())?; } Ok(()) @@ -1458,45 +1515,6 @@ impl<'a> State<'a> { } } - /// This doesn't deserve to be called "pretty" printing, but it should be - /// meaning-preserving. A quick hack that might help would be to look at the - /// spans embedded in the TTs to decide where to put spaces and newlines. - /// But it'd be better to parse these according to the grammar of the - /// appropriate macro, transcribe back into the grammar we just parsed from, - /// and then pretty-print the resulting AST nodes (so, e.g., we print - /// expression arguments as expressions). It can be done! I think. - pub fn print_tt(&mut self, tt: tokenstream::TokenTree) -> io::Result<()> { - match tt { - TokenTree::Token(_, ref tk) => { - word(&mut self.s, &token_to_string(tk))?; - match *tk { - parse::token::DocComment(..) => { - hardbreak(&mut self.s) - } - _ => Ok(()) - } - } - TokenTree::Delimited(_, ref delimed) => { - word(&mut self.s, &token_to_string(&delimed.open_token()))?; - space(&mut self.s)?; - self.print_tts(delimed.stream())?; - space(&mut self.s)?; - word(&mut self.s, &token_to_string(&delimed.close_token())) - }, - } - } - - pub fn print_tts(&mut self, tts: tokenstream::TokenStream) -> io::Result<()> { - self.ibox(0)?; - for (i, tt) in tts.into_trees().enumerate() { - if i != 0 { - space(&mut self.s)?; - } - self.print_tt(tt)?; - } - self.end() - } - pub fn print_variant(&mut self, v: &ast::Variant) -> io::Result<()> { self.head("")?; let generics = ast::Generics::default(); diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index 4a2dfaf61247c..94954e2c42980 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -15,6 +15,7 @@ use syntax_pos::{DUMMY_SP, Span}; use codemap::{self, ExpnInfo, NameAndSpan, MacroAttribute}; use parse::ParseSess; use ptr::P; +use tokenstream::TokenStream; /// Craft a span that will be ignored by the stability lint's /// call to codemap's is_internal check. @@ -70,11 +71,8 @@ pub fn maybe_inject_crates_ref(sess: &ParseSess, krate.module.items.insert(0, P(ast::Item { attrs: vec![ast::Attribute { style: ast::AttrStyle::Outer, - value: ast::MetaItem { - name: Symbol::intern("prelude_import"), - node: ast::MetaItemKind::Word, - span: span, - }, + path: ast::Path::from_ident(span, ast::Ident::from_str("prelude_import")), + tokens: TokenStream::empty(), id: attr::mk_attr_id(), is_sugared_doc: false, span: span, diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs index 2da442a1a53da..35e4d9eb68aea 100644 --- a/src/libsyntax/tokenstream.rs +++ b/src/libsyntax/tokenstream.rs @@ -360,7 +360,7 @@ impl PartialEq for ThinTokenStream { impl fmt::Display for TokenStream { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(&pprust::tts_to_string(&self.trees().collect::>())) + f.write_str(&pprust::tokens_to_string(self.clone())) } } diff --git a/src/libsyntax_ext/deriving/custom.rs b/src/libsyntax_ext/deriving/custom.rs index a7e2d82bb978f..b01ef65e5fe5e 100644 --- a/src/libsyntax_ext/deriving/custom.rs +++ b/src/libsyntax_ext/deriving/custom.rs @@ -23,9 +23,11 @@ struct MarkAttrs<'a>(&'a [ast::Name]); impl<'a> Visitor<'a> for MarkAttrs<'a> { fn visit_attribute(&mut self, attr: &Attribute) { - if self.0.contains(&attr.name()) { - mark_used(attr); - mark_known(attr); + if let Some(name) = attr.name() { + if self.0.contains(&name) { + mark_used(attr); + mark_known(attr); + } } } diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index fe492bd7fc849..48e7ff0d24370 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -439,7 +439,7 @@ impl<'a> TraitDef<'a> { attrs.extend(item.attrs .iter() .filter(|a| { - match &*a.name().as_str() { + a.name().is_some() && match &*a.name().unwrap().as_str() { "allow" | "warn" | "deny" | "forbid" | "stable" | "unstable" => true, _ => false, } diff --git a/src/libsyntax_ext/proc_macro_registrar.rs b/src/libsyntax_ext/proc_macro_registrar.rs index 5adaf470f2374..2d815b3f1bb7d 100644 --- a/src/libsyntax_ext/proc_macro_registrar.rs +++ b/src/libsyntax_ext/proc_macro_registrar.rs @@ -248,7 +248,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { fn visit_item(&mut self, item: &'a ast::Item) { if let ast::ItemKind::MacroDef(..) = item.node { if self.is_proc_macro_crate && - item.attrs.iter().any(|attr| attr.name() == "macro_export") { + item.attrs.iter().any(|attr| attr.path == "macro_export") { let msg = "cannot export macro_rules! macros from a `proc-macro` crate type currently"; self.handler.span_err(item.span, msg); @@ -270,12 +270,12 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { for attr in &item.attrs { if is_proc_macro_attr(&attr) { if let Some(prev_attr) = found_attr { - let msg = if attr.name() == prev_attr.name() { + let msg = if attr.path == prev_attr.path { format!("Only one `#[{}]` attribute is allowed on any given function", - attr.name()) + attr.path) } else { format!("`#[{}]` and `#[{}]` attributes cannot both be applied \ - to the same function", attr.name(), prev_attr.name()) + to the same function", attr.path, prev_attr.path) }; self.handler.struct_span_err(attr.span(), &msg) @@ -299,7 +299,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { if !is_fn { let msg = format!("the `#[{}]` attribute may only be used on bare functions", - attr.name()); + attr.path); self.handler.span_err(attr.span(), &msg); return; @@ -311,7 +311,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { if !self.is_proc_macro_crate { let msg = format!("the `#[{}]` attribute is only usable with crates of the \ - `proc-macro` crate type", attr.name()); + `proc-macro` crate type", attr.path); self.handler.span_err(attr.span(), &msg); return; From 839c2860ccb7cd3d381abf2838dfba566f52618e Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Wed, 8 Mar 2017 23:13:35 +0000 Subject: [PATCH 12/54] Liberalize attributes. --- src/librustc_resolve/lib.rs | 2 + src/librustc_resolve/macros.rs | 54 +++++++---- src/libsyntax/attr.rs | 46 ++++++++- src/libsyntax/config.rs | 72 +++++++------- src/libsyntax/ext/derive.rs | 45 ++++----- src/libsyntax/ext/expand.rs | 94 ++++++++++--------- src/libsyntax/ext/tt/macro_rules.rs | 3 +- src/libsyntax/feature_gate.rs | 4 + src/libsyntax/parse/attr.rs | 32 +++++-- src/libsyntax/parse/parser.rs | 11 +++ .../macro-attribute.rs | 2 - .../compile-fail/malformed-derive-entry.rs | 4 +- .../compile-fail/suffixed-literal-meta.rs | 25 +++++ src/test/parse-fail/attr-bad-meta.rs | 6 +- src/test/parse-fail/suffixed-literal-meta.rs | 25 ----- src/test/ui/span/E0536.stderr | 2 +- src/test/ui/span/E0537.stderr | 2 +- 17 files changed, 257 insertions(+), 172 deletions(-) rename src/test/{parse-fail => compile-fail}/macro-attribute.rs (94%) create mode 100644 src/test/compile-fail/suffixed-literal-meta.rs delete mode 100644 src/test/parse-fail/suffixed-literal-meta.rs diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index c3e471650a3e1..bf7115abd4edd 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1165,6 +1165,7 @@ pub struct Resolver<'a> { privacy_errors: Vec>, ambiguity_errors: Vec>, + gated_errors: FxHashSet, disallowed_shadowing: Vec<&'a LegacyBinding<'a>>, arenas: &'a ResolverArenas<'a>, @@ -1355,6 +1356,7 @@ impl<'a> Resolver<'a> { privacy_errors: Vec::new(), ambiguity_errors: Vec::new(), + gated_errors: FxHashSet(), disallowed_shadowing: Vec::new(), arenas: arenas, diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 9e1dcd1bc35c4..67ce24efb3b1f 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -28,9 +28,11 @@ use syntax::ext::placeholders::placeholder; use syntax::ext::tt::macro_rules; use syntax::feature_gate::{self, emit_feature_err, GateIssue}; use syntax::fold::{self, Folder}; +use syntax::parse::parser::PathStyle; +use syntax::parse::token::{self, Token}; use syntax::ptr::P; use syntax::symbol::{Symbol, keywords}; -use syntax::tokenstream::TokenStream; +use syntax::tokenstream::{TokenStream, TokenTree, Delimited}; use syntax::util::lev_distance::find_best_match_for_name; use syntax_pos::{Span, DUMMY_SP}; @@ -200,16 +202,22 @@ impl<'a> base::Resolver for Resolver<'a> { let name = unwrap_or!(attrs[i].name(), continue); if name == "derive" { - let mut traits = match attrs[i].meta_item_list() { - Some(traits) => traits, - _ => continue, + let result = attrs[i].parse_list(&self.session.parse_sess, + |parser| parser.parse_path(PathStyle::Mod)); + let mut traits = match result { + Ok(traits) => traits, + Err(mut e) => { + e.cancel(); + continue + } }; for j in 0..traits.len() { - let legacy_name = Symbol::intern(&match traits[j].word() { - Some(..) => format!("derive_{}", traits[j].name().unwrap()), - None => continue, - }); + if traits[j].segments.len() > 1 { + continue + } + let trait_name = traits[j].segments[0].identifier.name; + let legacy_name = Symbol::intern(&format!("derive_{}", trait_name)); if !self.builtin_macros.contains_key(&legacy_name) { continue } @@ -218,7 +226,23 @@ impl<'a> base::Resolver for Resolver<'a> { if traits.is_empty() { attrs.remove(i); } else { - attrs[i].tokens = ast::MetaItemKind::List(traits).tokens(attrs[i].span); + let mut tokens = Vec::new(); + for (i, path) in traits.iter().enumerate() { + if i > 0 { + tokens.push(TokenTree::Token(attrs[i].span, Token::Comma).into()); + } + for (j, segment) in path.segments.iter().enumerate() { + if j > 0 { + tokens.push(TokenTree::Token(path.span, Token::ModSep).into()); + } + let tok = Token::Ident(segment.identifier); + tokens.push(TokenTree::Token(path.span, tok).into()); + } + } + attrs[i].tokens = TokenTree::Delimited(attrs[i].span, Delimited { + delim: token::Paren, + tts: TokenStream::concat(tokens).into(), + }).into(); } return Some(ast::Attribute { path: ast::Path::from_ident(span, Ident::with_empty_ctxt(legacy_name)), @@ -262,9 +286,8 @@ impl<'a> Resolver<'a> { InvocationKind::Bang { ref mac, .. } => { return self.resolve_macro_to_def(scope, &mac.node.path, MacroKind::Bang, force); } - InvocationKind::Derive { name, span, .. } => { - let path = ast::Path::from_ident(span, Ident::with_empty_ctxt(name)); - return self.resolve_macro_to_def(scope, &path, MacroKind::Derive, force); + InvocationKind::Derive { ref path, .. } => { + return self.resolve_macro_to_def(scope, path, MacroKind::Derive, force); } }; @@ -282,9 +305,8 @@ impl<'a> Resolver<'a> { 1 => path.segments[0].identifier.name, _ => return Err(determinacy), }; - for &(name, span) in traits { - let path = ast::Path::from_ident(span, Ident::with_empty_ctxt(name)); - match self.resolve_macro(scope, &path, MacroKind::Derive, force) { + for path in traits { + match self.resolve_macro(scope, path, MacroKind::Derive, force) { Ok(ext) => if let SyntaxExtension::ProcMacroDerive(_, ref inert_attrs) = *ext { if inert_attrs.contains(&attr_name) { // FIXME(jseyfried) Avoid `mem::replace` here. @@ -327,7 +349,7 @@ impl<'a> Resolver<'a> { self.current_module = invocation.module.get(); if path.len() > 1 { - if !self.use_extern_macros { + if !self.use_extern_macros && self.gated_errors.insert(span) { let msg = "non-ident macro paths are experimental"; let feature = "use_extern_macros"; emit_feature_err(&self.session.parse_sess, feature, span, GateIssue::Language, msg); diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 68f1f690a62f0..2f1efd6ad00ee 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -17,7 +17,7 @@ pub use self::IntType::*; use ast; use ast::{AttrId, Attribute, Name, Ident}; use ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind}; -use ast::{Lit, LitKind, Expr, Item, Local, Stmt, StmtKind}; +use ast::{Lit, LitKind, Expr, ExprKind, Item, Local, Stmt, StmtKind}; use codemap::{Spanned, spanned, dummy_spanned, mk_sp}; use syntax_pos::{Span, BytePos, DUMMY_SP}; use errors::Handler; @@ -299,6 +299,37 @@ impl Attribute { }) } + pub fn parse<'a, T, F>(&self, sess: &'a ParseSess, mut f: F) -> PResult<'a, T> + where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, + { + let mut parser = Parser::new(sess, self.tokens.clone(), None, false); + let result = f(&mut parser)?; + if parser.token != token::Eof { + parser.unexpected()?; + } + Ok(result) + } + + pub fn parse_list<'a, T, F>(&self, sess: &'a ParseSess, mut f: F) -> PResult<'a, Vec> + where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, + { + if self.tokens.is_empty() { + return Ok(Vec::new()); + } + self.parse(sess, |parser| { + parser.expect(&token::OpenDelim(token::Paren))?; + let mut list = Vec::new(); + while !parser.eat(&token::CloseDelim(token::Paren)) { + list.push(f(parser)?); + if !parser.eat(&token::Comma) { + parser.expect(&token::CloseDelim(token::Paren))?; + break + } + } + Ok(list) + }) + } + pub fn parse_meta<'a>(&self, sess: &'a ParseSess) -> PResult<'a, MetaItem> { if self.path.segments.len() > 1 { sess.span_diagnostic.span_err(self.path.span, "expected ident, found path"); @@ -306,7 +337,7 @@ impl Attribute { Ok(MetaItem { name: self.path.segments.last().unwrap().identifier.name, - node: Parser::new(sess, self.tokens.clone(), None, false).parse_meta_item_kind()?, + node: self.parse(sess, |parser| parser.parse_meta_item_kind())?, span: self.span, }) } @@ -985,6 +1016,10 @@ impl MetaItem { { let (mut span, name) = match tokens.next() { Some(TokenTree::Token(span, Token::Ident(ident))) => (span, ident.name), + Some(TokenTree::Token(_, Token::Interpolated(ref nt))) => return match **nt { + token::Nonterminal::NtMeta(ref meta) => Some(meta.clone()), + _ => None, + }, _ => return None, }; let node = match MetaItemKind::from_tokens(tokens) { @@ -1151,6 +1186,13 @@ impl LitKind { match token { Token::Ident(ident) if ident.name == "true" => Some(LitKind::Bool(true)), Token::Ident(ident) if ident.name == "false" => Some(LitKind::Bool(false)), + Token::Interpolated(ref nt) => match **nt { + token::NtExpr(ref v) => match v.node { + ExprKind::Lit(ref lit) => Some(lit.node.clone()), + _ => None, + }, + _ => None, + }, Token::Literal(lit, suf) => { let (suffix_illegal, result) = parse::lit_token(lit, suf, None); if suffix_illegal && suf.is_some() { diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 2591a5766693f..ede8a33df6546 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -13,9 +13,10 @@ use feature_gate::{feature_err, EXPLAIN_STMT_ATTR_SYNTAX, Features, get_features use {fold, attr}; use ast; use codemap::Spanned; -use parse::ParseSess; -use ptr::P; +use parse::{token, ParseSess}; +use syntax_pos::Span; +use ptr::P; use util::small_vector::SmallVector; /// A folder that strips out items that do not belong in the current configuration. @@ -84,44 +85,33 @@ impl<'a> StripUnconfigured<'a> { return Some(attr); } - let attr_list = match attr.meta_item_list() { - Some(attr_list) => attr_list, - None => { - let msg = "expected `#[cfg_attr(, )]`"; - self.sess.span_diagnostic.span_err(attr.span, msg); - return None; - } - }; - - let (cfg, mi) = match (attr_list.len(), attr_list.get(0), attr_list.get(1)) { - (2, Some(cfg), Some(mi)) => (cfg, mi), - _ => { - let msg = "expected `#[cfg_attr(, )]`"; - self.sess.span_diagnostic.span_err(attr.span, msg); + let (cfg, path, tokens, span) = match attr.parse(self.sess, |parser| { + parser.expect(&token::OpenDelim(token::Paren))?; + let cfg = parser.parse_meta_item()?; + parser.expect(&token::Comma)?; + let lo = parser.span.lo; + let (path, tokens) = parser.parse_path_and_tokens()?; + parser.expect(&token::CloseDelim(token::Paren))?; + Ok((cfg, path, tokens, Span { lo: lo, ..parser.prev_span })) + }) { + Ok(result) => result, + Err(mut e) => { + e.emit(); return None; } }; - use attr::cfg_matches; - match (cfg.meta_item(), mi.meta_item()) { - (Some(cfg), Some(mi)) => - if cfg_matches(&cfg, self.sess, self.features) { - self.process_cfg_attr(ast::Attribute { - id: attr::mk_attr_id(), - style: attr.style, - path: ast::Path::from_ident(mi.span, ast::Ident::with_empty_ctxt(mi.name)), - tokens: mi.node.tokens(mi.span), - is_sugared_doc: false, - span: mi.span, - }) - } else { - None - }, - _ => { - let msg = "unexpected literal(s) in `#[cfg_attr(, )]`"; - self.sess.span_diagnostic.span_err(attr.span, msg); - None - } + if attr::cfg_matches(&cfg, self.sess, self.features) { + self.process_cfg_attr(ast::Attribute { + id: attr::mk_attr_id(), + style: attr.style, + path: path, + tokens: tokens, + is_sugared_doc: false, + span: span, + }) + } else { + None } } @@ -133,10 +123,12 @@ impl<'a> StripUnconfigured<'a> { return false; } - let mis = attr.meta_item_list(); - let mis = match mis { - Some(ref mis) if is_cfg(&attr) => mis, - _ => return true + let mis = if !is_cfg(&attr) { + return true; + } else if let Some(mis) = attr.meta_item_list() { + mis + } else { + return true; }; if mis.len() != 1 { diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index 5b253635f257e..1569d9f540b8e 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -12,36 +12,31 @@ use attr::HasAttrs; use {ast, codemap}; use ext::base::ExtCtxt; use ext::build::AstBuilder; +use parse::parser::PathStyle; use symbol::Symbol; use syntax_pos::Span; -pub fn collect_derives(cx: &mut ExtCtxt, attrs: &mut Vec) -> Vec<(Symbol, Span)> { +pub fn collect_derives(cx: &mut ExtCtxt, attrs: &mut Vec) -> Vec { let mut result = Vec::new(); attrs.retain(|attr| { if attr.path != "derive" { return true; } - if attr.value_str().is_some() { - cx.span_err(attr.span, "unexpected value in `derive`"); - return false; - } - - let traits = attr.meta_item_list().unwrap_or_else(Vec::new); - if traits.is_empty() { - cx.span_warn(attr.span, "empty trait list in `derive`"); - return false; - } - - for titem in traits { - if titem.word().is_none() { - cx.span_err(titem.span, "malformed `derive` entry"); - return false; + match attr.parse_list(cx.parse_sess, |parser| parser.parse_path(PathStyle::Mod)) { + Ok(ref traits) if traits.is_empty() => { + cx.span_warn(attr.span, "empty trait list in `derive`"); + false + } + Ok(traits) => { + result.extend(traits); + true + } + Err(mut e) => { + e.emit(); + false } - result.push((titem.name().unwrap(), titem.span)); } - - true }); result } @@ -60,21 +55,21 @@ fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span { } } -pub fn add_derived_markers(cx: &mut ExtCtxt, traits: &[(Symbol, Span)], item: T) -> T { +pub fn add_derived_markers(cx: &mut ExtCtxt, traits: &[ast::Path], item: T) -> T { let span = match traits.get(0) { - Some(&(_, span)) => span, + Some(path) => path.span, None => return item, }; item.map_attrs(|mut attrs| { - if traits.iter().any(|&(name, _)| name == "PartialEq") && - traits.iter().any(|&(name, _)| name == "Eq") { + if traits.iter().any(|path| *path == "PartialEq") && + traits.iter().any(|path| *path == "Eq") { let span = allow_unstable(cx, span, "derive(PartialEq, Eq)"); let meta = cx.meta_word(span, Symbol::intern("structural_match")); attrs.push(cx.attribute(span, meta)); } - if traits.iter().any(|&(name, _)| name == "Copy") && - traits.iter().any(|&(name, _)| name == "Clone") { + if traits.iter().any(|path| *path == "Copy") && + traits.iter().any(|path| *path == "Clone") { let span = allow_unstable(cx, span, "derive(Copy, Clone)"); let meta = cx.meta_word(span, Symbol::intern("rustc_copy_clone_marker")); attrs.push(cx.attribute(span, meta)); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index c1095d3445682..c1816582bc6ca 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ast::{self, Block, Ident, PatKind}; -use ast::{Name, MacStmtStyle, StmtKind, ItemKind}; +use ast::{self, Block, Ident, PatKind, Path}; +use ast::{MacStmtStyle, StmtKind, ItemKind}; use attr::{self, HasAttrs}; use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use config::{is_test_or_bench, StripUnconfigured}; @@ -27,7 +27,7 @@ use ptr::P; use std_inject; use symbol::Symbol; use symbol::keywords; -use syntax_pos::{self, Span, ExpnId}; +use syntax_pos::{Span, ExpnId, DUMMY_SP}; use tokenstream::TokenStream; use util::small_vector::SmallVector; use visit::Visitor; @@ -165,12 +165,11 @@ pub enum InvocationKind { }, Attr { attr: Option, - traits: Vec<(Symbol, Span)>, + traits: Vec, item: Annotatable, }, Derive { - name: Symbol, - span: Span, + path: Path, item: Annotatable, }, } @@ -180,8 +179,8 @@ impl Invocation { match self.kind { InvocationKind::Bang { span, .. } => span, InvocationKind::Attr { attr: Some(ref attr), .. } => attr.span, - InvocationKind::Attr { attr: None, .. } => syntax_pos::DUMMY_SP, - InvocationKind::Derive { span, .. } => span, + InvocationKind::Attr { attr: None, .. } => DUMMY_SP, + InvocationKind::Derive { ref path, .. } => path.span, } } } @@ -277,12 +276,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> { add_derived_markers(&mut self.cx, &traits, item.clone()); let derives = derives.entry(invoc.expansion_data.mark).or_insert_with(Vec::new); - for &(name, span) in &traits { + for path in &traits { let mark = Mark::fresh(); derives.push(mark); - let path = ast::Path::from_ident(span, Ident::with_empty_ctxt(name)); let item = match self.cx.resolver.resolve_macro( - Mark::root(), &path, MacroKind::Derive, false) { + Mark::root(), path, MacroKind::Derive, false) { Ok(ext) => match *ext { SyntaxExtension::BuiltinDerive(..) => item_with_markers.clone(), _ => item.clone(), @@ -290,7 +288,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { _ => item.clone(), }; invocations.push(Invocation { - kind: InvocationKind::Derive { name: name, span: span, item: item }, + kind: InvocationKind::Derive { path: path.clone(), item: item }, expansion_kind: invoc.expansion_kind, expansion_data: ExpansionData { mark: mark, @@ -380,11 +378,10 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }; attr::mark_used(&attr); - let name = attr.path.segments[0].identifier.name; self.cx.bt_push(ExpnInfo { call_site: attr.span, callee: NameAndSpan { - format: MacroAttribute(name), + format: MacroAttribute(Symbol::intern(&format!("{}", attr.path))), span: Some(attr.span), allow_internal_unstable: false, } @@ -419,14 +416,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }; let tok_result = mac.expand(self.cx, attr.span, attr.tokens.clone(), item_toks); - self.parse_expansion(tok_result, kind, name, span) + self.parse_expansion(tok_result, kind, &attr.path, span) } SyntaxExtension::ProcMacroDerive(..) | SyntaxExtension::BuiltinDerive(..) => { - self.cx.span_err(attr.span, &format!("`{}` is a derive mode", name)); + self.cx.span_err(attr.span, &format!("`{}` is a derive mode", attr.path)); kind.dummy(attr.span) } _ => { - let msg = &format!("macro `{}` may not be used in attributes", name); + let msg = &format!("macro `{}` may not be used in attributes", attr.path); self.cx.span_err(attr.span, &msg); kind.dummy(attr.span) } @@ -442,7 +439,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }; let path = &mac.node.path; - let extname = path.segments.last().unwrap().identifier.name; let ident = ident.unwrap_or(keywords::Invalid.ident()); let marked_tts = noop_fold_tts(mac.node.stream(), &mut Marker { mark: mark, expn_id: None }); @@ -450,7 +446,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { NormalTT(ref expandfun, exp_span, allow_internal_unstable) => { if ident.name != keywords::Invalid.name() { let msg = - format!("macro {}! expects no ident argument, given '{}'", extname, ident); + format!("macro {}! expects no ident argument, given '{}'", path, ident); self.cx.span_err(path.span, &msg); return kind.dummy(span); } @@ -458,7 +454,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx.bt_push(ExpnInfo { call_site: span, callee: NameAndSpan { - format: MacroBang(extname), + format: MacroBang(Symbol::intern(&format!("{}", path))), span: exp_span, allow_internal_unstable: allow_internal_unstable, }, @@ -470,14 +466,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> { IdentTT(ref expander, tt_span, allow_internal_unstable) => { if ident.name == keywords::Invalid.name() { self.cx.span_err(path.span, - &format!("macro {}! expects an ident argument", extname)); + &format!("macro {}! expects an ident argument", path)); return kind.dummy(span); }; self.cx.bt_push(ExpnInfo { call_site: span, callee: NameAndSpan { - format: MacroBang(extname), + format: MacroBang(Symbol::intern(&format!("{}", path))), span: tt_span, allow_internal_unstable: allow_internal_unstable, } @@ -489,19 +485,19 @@ impl<'a, 'b> MacroExpander<'a, 'b> { MultiDecorator(..) | MultiModifier(..) | SyntaxExtension::AttrProcMacro(..) => { self.cx.span_err(path.span, - &format!("`{}` can only be used in attributes", extname)); + &format!("`{}` can only be used in attributes", path)); return kind.dummy(span); } SyntaxExtension::ProcMacroDerive(..) | SyntaxExtension::BuiltinDerive(..) => { - self.cx.span_err(path.span, &format!("`{}` is a derive mode", extname)); + self.cx.span_err(path.span, &format!("`{}` is a derive mode", path)); return kind.dummy(span); } SyntaxExtension::ProcMacro(ref expandfun) => { if ident.name != keywords::Invalid.name() { let msg = - format!("macro {}! expects no ident argument, given '{}'", extname, ident); + format!("macro {}! expects no ident argument, given '{}'", path, ident); self.cx.span_err(path.span, &msg); return kind.dummy(span); } @@ -509,7 +505,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx.bt_push(ExpnInfo { call_site: span, callee: NameAndSpan { - format: MacroBang(extname), + format: MacroBang(Symbol::intern(&format!("{}", path))), // FIXME procedural macros do not have proper span info // yet, when they do, we should use it here. span: None, @@ -519,7 +515,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }); let tok_result = expandfun.expand(self.cx, span, marked_tts); - Some(self.parse_expansion(tok_result, kind, extname, span)) + Some(self.parse_expansion(tok_result, kind, path, span)) } }; @@ -541,19 +537,24 @@ impl<'a, 'b> MacroExpander<'a, 'b> { /// Expand a derive invocation. Returns the result of expansion. fn expand_derive_invoc(&mut self, invoc: Invocation, ext: Rc) -> Expansion { let Invocation { expansion_kind: kind, .. } = invoc; - let (name, span, item) = match invoc.kind { - InvocationKind::Derive { name, span, item } => (name, span, item), + let (path, item) = match invoc.kind { + InvocationKind::Derive { path, item } => (path, item), _ => unreachable!(), }; - let mitem = ast::MetaItem { name: name, span: span, node: ast::MetaItemKind::Word }; - let pretty_name = Symbol::intern(&format!("derive({})", name)); + let pretty_name = Symbol::intern(&format!("derive({})", path)); + let span = path.span; + let attr = ast::Attribute { + path: path, tokens: TokenStream::empty(), span: span, + // irrelevant: + id: ast::AttrId(0), style: ast::AttrStyle::Outer, is_sugared_doc: false, + }; self.cx.bt_push(ExpnInfo { call_site: span, callee: NameAndSpan { format: MacroAttribute(pretty_name), - span: Some(span), + span: None, allow_internal_unstable: false, } }); @@ -571,7 +572,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }), ..span }; - return kind.expect_from_annotatables(ext.expand(self.cx, span, &mitem, item)); + let dummy = ast::MetaItem { // FIXME(jseyfried) avoid this + name: keywords::Invalid.name(), + span: DUMMY_SP, + node: ast::MetaItemKind::Word, + }; + return kind.expect_from_annotatables(ext.expand(self.cx, span, &dummy, item)); } SyntaxExtension::BuiltinDerive(func) => { let span = Span { @@ -586,20 +592,18 @@ impl<'a, 'b> MacroExpander<'a, 'b> { ..span }; let mut items = Vec::new(); - func(self.cx, span, &mitem, &item, &mut |a| { - items.push(a) - }); + func(self.cx, span, &attr.meta().unwrap(), &item, &mut |a| items.push(a)); return kind.expect_from_annotatables(items); } _ => { - let msg = &format!("macro `{}` may not be used for derive attributes", name); + let msg = &format!("macro `{}` may not be used for derive attributes", attr.path); self.cx.span_err(span, &msg); kind.dummy(span) } } } - fn parse_expansion(&mut self, toks: TokenStream, kind: ExpansionKind, name: Name, span: Span) + fn parse_expansion(&mut self, toks: TokenStream, kind: ExpansionKind, path: &Path, span: Span) -> Expansion { let mut parser = self.cx.new_parser_from_tts(&toks.into_trees().collect::>()); let expansion = match parser.parse_expansion(kind, false) { @@ -609,7 +613,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { return kind.dummy(span); } }; - parser.ensure_complete_parse(name, kind.name(), span); + parser.ensure_complete_parse(path, kind.name(), span); // FIXME better span info expansion.fold_with(&mut ChangeSpan { span: span }) } @@ -658,14 +662,14 @@ impl<'a> Parser<'a> { }) } - pub fn ensure_complete_parse(&mut self, macro_name: ast::Name, kind_name: &str, span: Span) { + pub fn ensure_complete_parse(&mut self, macro_path: &Path, kind_name: &str, span: Span) { if self.token != token::Eof { let msg = format!("macro expansion ignores token `{}` and any following", self.this_token_to_string()); let mut err = self.diagnostic().struct_span_err(self.span, &msg); let msg = format!("caused by the macro expansion here; the usage \ of `{}!` is likely invalid in {} context", - macro_name, kind_name); + macro_path, kind_name); err.span_note(span, &msg).emit(); } } @@ -708,20 +712,20 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn collect_attr(&mut self, attr: Option, - traits: Vec<(Symbol, Span)>, + traits: Vec, item: Annotatable, kind: ExpansionKind) -> Expansion { if !traits.is_empty() && (kind == ExpansionKind::TraitItems || kind == ExpansionKind::ImplItems) { - self.cx.span_err(traits[0].1, "`derive` can be only be applied to items"); + self.cx.span_err(traits[0].span, "`derive` can be only be applied to items"); return kind.expect_from_annotatables(::std::iter::once(item)); } self.collect(kind, InvocationKind::Attr { attr: attr, traits: traits, item: item }) } // If `item` is an attr invocation, remove and return the macro attribute. - fn classify_item(&mut self, mut item: T) -> (Option, Vec<(Symbol, Span)>, T) + fn classify_item(&mut self, mut item: T) -> (Option, Vec, T) where T: HasAttrs, { let (mut attr, mut traits) = (None, Vec::new()); @@ -900,7 +904,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { // Detect if this is an inline module (`mod m { ... }` as opposed to `mod m;`). // In the non-inline case, `inner` is never the dummy span (c.f. `parse_item_mod`). // Thus, if `inner` is the dummy span, we know the module is inline. - let inline_module = item.span.contains(inner) || inner == syntax_pos::DUMMY_SP; + let inline_module = item.span.contains(inner) || inner == DUMMY_SP; if inline_module { if let Some(path) = attr::first_attr_value_str_by_name(&item.attrs, "path") { diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 7aa1230f9aeea..021c5398a4200 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -51,7 +51,8 @@ impl<'a> ParserAnyMacro<'a> { } // Make sure we don't have any tokens left to parse so we don't silently drop anything. - parser.ensure_complete_parse(macro_ident.name, kind.name(), site_span); + let path = ast::Path::from_ident(site_span, macro_ident); + parser.ensure_complete_parse(&path, kind.name(), site_span); expansion } } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 2c3ad98a6be63..05e7b0f9aa4da 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -1096,6 +1096,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { self.context.check_attribute(attr, false); } + if self.context.features.proc_macro && attr::is_known(attr) { + return + } + let meta = panictry!(attr.parse_meta(&self.context.parse_sess)); if contains_novel_literal(&meta) { gate_feature_post!(&self, attr_literals, attr.span, diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index 272cff7ad34b2..53106214fa310 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -14,8 +14,9 @@ use syntax_pos::{mk_sp, Span}; use codemap::spanned; use parse::common::SeqSep; use parse::PResult; -use parse::token; -use parse::parser::{Parser, TokenType}; +use parse::token::{self, Nonterminal}; +use parse::parser::{Parser, TokenType, PathStyle}; +use tokenstream::TokenStream; #[derive(PartialEq, Eq, Debug)] enum InnerAttributeParsePolicy<'a> { @@ -91,7 +92,7 @@ impl<'a> Parser<'a> { debug!("parse_attribute_with_inner_parse_policy: inner_parse_policy={:?} self.token={:?}", inner_parse_policy, self.token); - let (span, value, mut style) = match self.token { + let (span, path, tokens, mut style) = match self.token { token::Pound => { let lo = self.span.lo; self.bump(); @@ -119,11 +120,11 @@ impl<'a> Parser<'a> { }; self.expect(&token::OpenDelim(token::Bracket))?; - let meta_item = self.parse_meta_item()?; + let (path, tokens) = self.parse_path_and_tokens()?; self.expect(&token::CloseDelim(token::Bracket))?; let hi = self.prev_span.hi; - (mk_sp(lo, hi), meta_item, style) + (mk_sp(lo, hi), path, tokens, style) } _ => { let token_str = self.this_token_to_string(); @@ -143,13 +144,30 @@ impl<'a> Parser<'a> { Ok(ast::Attribute { id: attr::mk_attr_id(), style: style, - path: ast::Path::from_ident(value.span, ast::Ident::with_empty_ctxt(value.name)), - tokens: value.node.tokens(value.span), + path: path, + tokens: tokens, is_sugared_doc: false, span: span, }) } + pub fn parse_path_and_tokens(&mut self) -> PResult<'a, (ast::Path, TokenStream)> { + let meta = match self.token { + token::Interpolated(ref nt) => match **nt { + Nonterminal::NtMeta(ref meta) => Some(meta.clone()), + _ => None, + }, + _ => None, + }; + Ok(if let Some(meta) = meta { + self.bump(); + (ast::Path::from_ident(meta.span, ast::Ident::with_empty_ctxt(meta.name)), + meta.node.tokens(meta.span)) + } else { + (self.parse_path(PathStyle::Mod)?, self.parse_tokens()) + }) + } + /// Parse attributes that appear after the opening of an item. These should /// be preceded by an exclamation mark, but we accept and warn about one /// terminated by a semicolon. diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index ed512b899877d..308876fed56dc 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2644,6 +2644,17 @@ impl<'a> Parser<'a> { Ok(tts) } + pub fn parse_tokens(&mut self) -> TokenStream { + let mut result = Vec::new(); + loop { + match self.token { + token::Eof | token::CloseDelim(..) => break, + _ => result.push(self.parse_token_tree().into()), + } + } + TokenStream::concat(result) + } + /// Parse a prefix-unary-operator expr pub fn parse_prefix_expr(&mut self, already_parsed_attrs: Option>) diff --git a/src/test/parse-fail/macro-attribute.rs b/src/test/compile-fail/macro-attribute.rs similarity index 94% rename from src/test/parse-fail/macro-attribute.rs rename to src/test/compile-fail/macro-attribute.rs index 18add7d011cba..52f867fe913b8 100644 --- a/src/test/parse-fail/macro-attribute.rs +++ b/src/test/compile-fail/macro-attribute.rs @@ -8,7 +8,5 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only - #[doc = $not_there] //~ error: unexpected token: `$` fn main() { } diff --git a/src/test/compile-fail/malformed-derive-entry.rs b/src/test/compile-fail/malformed-derive-entry.rs index 62dbc21495a54..ac000628f2b04 100644 --- a/src/test/compile-fail/malformed-derive-entry.rs +++ b/src/test/compile-fail/malformed-derive-entry.rs @@ -9,11 +9,11 @@ // except according to those terms. #[derive(Copy(Bad))] -//~^ ERROR malformed `derive` entry +//~^ ERROR expected one of `)`, `,`, or `::`, found `(` struct Test1; #[derive(Copy="bad")] -//~^ ERROR malformed `derive` entry +//~^ ERROR expected one of `)`, `,`, or `::`, found `=` struct Test2; #[derive()] diff --git a/src/test/compile-fail/suffixed-literal-meta.rs b/src/test/compile-fail/suffixed-literal-meta.rs new file mode 100644 index 0000000000000..bf55b7bdcb1de --- /dev/null +++ b/src/test/compile-fail/suffixed-literal-meta.rs @@ -0,0 +1,25 @@ +// Copyright 2012 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. + +#![feature(attr_literals)] + +#[path = 1usize] //~ ERROR: suffixed literals are not allowed in attributes +#[path = 1u8] //~ ERROR: suffixed literals are not allowed in attributes +#[path = 1u16] //~ ERROR: suffixed literals are not allowed in attributes +#[path = 1u32] //~ ERROR: suffixed literals are not allowed in attributes +#[path = 1u64] //~ ERROR: suffixed literals are not allowed in attributes +#[path = 1isize] //~ ERROR: suffixed literals are not allowed in attributes +#[path = 1i8] //~ ERROR: suffixed literals are not allowed in attributes +#[path = 1i16] //~ ERROR: suffixed literals are not allowed in attributes +#[path = 1i32] //~ ERROR: suffixed literals are not allowed in attributes +#[path = 1i64] //~ ERROR: suffixed literals are not allowed in attributes +#[path = 1.0f32] //~ ERROR: suffixed literals are not allowed in attributes +#[path = 1.0f64] //~ ERROR: suffixed literals are not allowed in attributes +fn main() { } diff --git a/src/test/parse-fail/attr-bad-meta.rs b/src/test/parse-fail/attr-bad-meta.rs index 092adbf29e340..d57a813311b5a 100644 --- a/src/test/parse-fail/attr-bad-meta.rs +++ b/src/test/parse-fail/attr-bad-meta.rs @@ -8,10 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only - -// error-pattern:expected one of `=` or `]` - // asterisk is bogus -#[attr*] +#[path*] //~ ERROR expected one of `(` or `=` mod m {} diff --git a/src/test/parse-fail/suffixed-literal-meta.rs b/src/test/parse-fail/suffixed-literal-meta.rs deleted file mode 100644 index 0e2840c69d364..0000000000000 --- a/src/test/parse-fail/suffixed-literal-meta.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2012 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. - -// compile-flags: -Z parse-only - -#[foo = 1usize] //~ ERROR: suffixed literals are not allowed in attributes -#[foo = 1u8] //~ ERROR: suffixed literals are not allowed in attributes -#[foo = 1u16] //~ ERROR: suffixed literals are not allowed in attributes -#[foo = 1u32] //~ ERROR: suffixed literals are not allowed in attributes -#[foo = 1u64] //~ ERROR: suffixed literals are not allowed in attributes -#[foo = 1isize] //~ ERROR: suffixed literals are not allowed in attributes -#[foo = 1i8] //~ ERROR: suffixed literals are not allowed in attributes -#[foo = 1i16] //~ ERROR: suffixed literals are not allowed in attributes -#[foo = 1i32] //~ ERROR: suffixed literals are not allowed in attributes -#[foo = 1i64] //~ ERROR: suffixed literals are not allowed in attributes -#[foo = 1.0f32] //~ ERROR: suffixed literals are not allowed in attributes -#[foo = 1.0f64] //~ ERROR: suffixed literals are not allowed in attributes -fn main() { } diff --git a/src/test/ui/span/E0536.stderr b/src/test/ui/span/E0536.stderr index c33b89953e274..b2da0c6a296d8 100644 --- a/src/test/ui/span/E0536.stderr +++ b/src/test/ui/span/E0536.stderr @@ -2,7 +2,7 @@ error[E0536]: expected 1 cfg-pattern --> $DIR/E0536.rs:11:7 | 11 | #[cfg(not())] //~ ERROR E0536 - | ^^^^^ + | ^^^ error: aborting due to previous error diff --git a/src/test/ui/span/E0537.stderr b/src/test/ui/span/E0537.stderr index 9d66ddbaae317..29873943f444d 100644 --- a/src/test/ui/span/E0537.stderr +++ b/src/test/ui/span/E0537.stderr @@ -2,7 +2,7 @@ error[E0537]: invalid predicate `unknown` --> $DIR/E0537.rs:11:7 | 11 | #[cfg(unknown())] //~ ERROR E0537 - | ^^^^^^^^^ + | ^^^^^^^ error: aborting due to previous error From 85e02bdbfcfd0e38def7656a8295a5260640fd4a Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Wed, 8 Mar 2017 23:13:43 +0000 Subject: [PATCH 13/54] Add tests. --- .../proc-macro/proc-macro-attributes.rs | 1 + src/test/compile-fail/macro-with-seps-err-msg.rs | 1 + src/test/run-pass-fulldeps/proc-macro/attr-args.rs | 6 +++--- .../run-pass-fulldeps/proc-macro/auxiliary/attr-args.rs | 5 +++++ .../run-pass-fulldeps/proc-macro/auxiliary/derive-b.rs | 2 +- src/test/run-pass-fulldeps/proc-macro/derive-b.rs | 7 ++++--- 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/test/compile-fail-fulldeps/proc-macro/proc-macro-attributes.rs b/src/test/compile-fail-fulldeps/proc-macro/proc-macro-attributes.rs index 4ad1cf79d61c6..df881bedec1bb 100644 --- a/src/test/compile-fail-fulldeps/proc-macro/proc-macro-attributes.rs +++ b/src/test/compile-fail-fulldeps/proc-macro/proc-macro-attributes.rs @@ -20,6 +20,7 @@ extern crate derive_b; #[C] //~ ERROR: The attribute `C` is currently unknown to the compiler #[B(D)] #[B(E = "foo")] +#[B arbitrary tokens] //~ expected one of `(` or `=`, found `arbitrary` struct B; fn main() {} diff --git a/src/test/compile-fail/macro-with-seps-err-msg.rs b/src/test/compile-fail/macro-with-seps-err-msg.rs index 6cc682bde997f..c28e22d58f9db 100644 --- a/src/test/compile-fail/macro-with-seps-err-msg.rs +++ b/src/test/compile-fail/macro-with-seps-err-msg.rs @@ -14,4 +14,5 @@ fn main() { globnar::brotz!(); //~ ERROR non-ident macro paths are experimental ::foo!(); //~ ERROR non-ident macro paths are experimental foo::!(); //~ ERROR type parameters are not allowed on macros + #[derive(foo::Bar)] struct T; //~ ERROR non-ident macro paths are experimental } diff --git a/src/test/run-pass-fulldeps/proc-macro/attr-args.rs b/src/test/run-pass-fulldeps/proc-macro/attr-args.rs index d28d75d81a2fb..8a9fdd7536770 100644 --- a/src/test/run-pass-fulldeps/proc-macro/attr-args.rs +++ b/src/test/run-pass-fulldeps/proc-macro/attr-args.rs @@ -19,6 +19,6 @@ use attr_args::attr_with_args; #[attr_with_args(text = "Hello, world!")] fn foo() {} -fn main() { - assert_eq!(foo(), "Hello, world!"); -} +#[::attr_args::identity + fn main() { assert_eq!(foo(), "Hello, world!"); }] +struct Dummy; diff --git a/src/test/run-pass-fulldeps/proc-macro/auxiliary/attr-args.rs b/src/test/run-pass-fulldeps/proc-macro/auxiliary/attr-args.rs index 6e1eb395a0a19..989c77f1089cf 100644 --- a/src/test/run-pass-fulldeps/proc-macro/auxiliary/attr-args.rs +++ b/src/test/run-pass-fulldeps/proc-macro/auxiliary/attr-args.rs @@ -30,3 +30,8 @@ pub fn attr_with_args(args: TokenStream, input: TokenStream) -> TokenStream { fn foo() -> &'static str { "Hello, world!" } "#.parse().unwrap() } + +#[proc_macro_attribute] +pub fn identity(attr_args: TokenStream, _: TokenStream) -> TokenStream { + attr_args +} diff --git a/src/test/run-pass-fulldeps/proc-macro/auxiliary/derive-b.rs b/src/test/run-pass-fulldeps/proc-macro/auxiliary/derive-b.rs index bf793534d50c9..7b521f2b9138a 100644 --- a/src/test/run-pass-fulldeps/proc-macro/auxiliary/derive-b.rs +++ b/src/test/run-pass-fulldeps/proc-macro/auxiliary/derive-b.rs @@ -19,7 +19,7 @@ use proc_macro::TokenStream; #[proc_macro_derive(B, attributes(B, C))] pub fn derive(input: TokenStream) -> TokenStream { let input = input.to_string(); - assert!(input.contains("#[B]")); + assert!(input.contains("#[B arbitrary tokens]")); assert!(input.contains("struct B {")); assert!(input.contains("#[C]")); "".parse().unwrap() diff --git a/src/test/run-pass-fulldeps/proc-macro/derive-b.rs b/src/test/run-pass-fulldeps/proc-macro/derive-b.rs index f1e1626ddf8ca..995dc65729a50 100644 --- a/src/test/run-pass-fulldeps/proc-macro/derive-b.rs +++ b/src/test/run-pass-fulldeps/proc-macro/derive-b.rs @@ -11,11 +11,12 @@ // aux-build:derive-b.rs // ignore-stage1 -#[macro_use] +#![feature(proc_macro)] + extern crate derive_b; -#[derive(Debug, PartialEq, B, Eq, Copy, Clone)] -#[B] +#[derive(Debug, PartialEq, derive_b::B, Eq, Copy, Clone)] +#[cfg_attr(all(), B arbitrary tokens)] struct B { #[C] a: u64 From b43c744318b76934cdbcc0c8a4fdbc7d0091bbd8 Mon Sep 17 00:00:00 2001 From: Tobias Schottdorf Date: Sat, 11 Mar 2017 10:54:45 -0500 Subject: [PATCH 14/54] Add feature toggle for rvalue-static-promotion RFC See https://github.com/rust-lang/rfcs/pull/1414. Updates #38865. --- src/doc/unstable-book/src/SUMMARY.md | 1 + .../src/rvalue-static-promotion.md | 5 +++++ src/librustc/middle/mem_categorization.rs | 5 ++--- src/libsyntax/feature_gate.rs | 3 +++ .../feature-gate-rvalue_static_promotion.rs | 15 +++++++++++++++ src/test/run-pass/rvalue-static-promotion.rs | 17 +++++++++++++++++ 6 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/doc/unstable-book/src/rvalue-static-promotion.md create mode 100644 src/test/compile-fail/feature-gate-rvalue_static_promotion.rs create mode 100644 src/test/run-pass/rvalue-static-promotion.rs diff --git a/src/doc/unstable-book/src/SUMMARY.md b/src/doc/unstable-book/src/SUMMARY.md index 5fb323d6ce909..034f9f4eb2523 100644 --- a/src/doc/unstable-book/src/SUMMARY.md +++ b/src/doc/unstable-book/src/SUMMARY.md @@ -71,6 +71,7 @@ - [repr_simd](repr-simd.md) - [rustc_attrs](rustc-attrs.md) - [rustc_diagnostic_macros](rustc-diagnostic-macros.md) +- [rvalue_static_promotion](rvalue-static-promotion.md) - [sanitizer_runtime](sanitizer-runtime.md) - [simd](simd.md) - [simd_ffi](simd-ffi.md) diff --git a/src/doc/unstable-book/src/rvalue-static-promotion.md b/src/doc/unstable-book/src/rvalue-static-promotion.md new file mode 100644 index 0000000000000..3b654960c8e3c --- /dev/null +++ b/src/doc/unstable-book/src/rvalue-static-promotion.md @@ -0,0 +1,5 @@ +# `rvalue_static_promotion` + +The tracking issue for this feature is: [#38865] + +------------------------ diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index b0c85e2ef4cd4..a669ac3a69b98 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -843,11 +843,10 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { let promotable = self.tcx().rvalue_promotable_to_static.borrow().get(&id).cloned() .unwrap_or(false); - // Only promote `[T; 0]` before an RFC for rvalue promotions - // is accepted. + // When the corresponding feature isn't toggled, only promote `[T; 0]`. let promotable = match expr_ty.sty { ty::TyArray(_, 0) => true, - _ => promotable & false + _ => promotable & self.tcx().sess.features.borrow().rvalue_static_promotion, }; // Compute maximum lifetime of this rvalue. This is 'static if diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 15913d56d162f..59e9ab30e0e8c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -342,6 +342,9 @@ declare_features! ( // Allows the `catch {...}` expression (active, catch_expr, "1.17.0", Some(31436)), + + // See rust-lang/rfcs#1414. Allows code like `let x: &'static u32 = &42` to work. + (active, rvalue_static_promotion, "1.15.1", Some(38865)), ); declare_features! ( diff --git a/src/test/compile-fail/feature-gate-rvalue_static_promotion.rs b/src/test/compile-fail/feature-gate-rvalue_static_promotion.rs new file mode 100644 index 0000000000000..41dc282be41f0 --- /dev/null +++ b/src/test/compile-fail/feature-gate-rvalue_static_promotion.rs @@ -0,0 +1,15 @@ +// Copyright 2017 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. + +#[allow(unused_variables)] +fn main() { + let x: &'static u32 = &42; //~ error: does not live long enough + let y: &'static Option = &None; //~ error: does not live long enough +} \ No newline at end of file diff --git a/src/test/run-pass/rvalue-static-promotion.rs b/src/test/run-pass/rvalue-static-promotion.rs new file mode 100644 index 0000000000000..f8d93783877bb --- /dev/null +++ b/src/test/run-pass/rvalue-static-promotion.rs @@ -0,0 +1,17 @@ +// Copyright 2017 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. + +#![feature(rvalue_static_promotion)] + +#[allow(unused_variables)] +fn main() { + let x: &'static u32 = &42; + let y: &'static Option = &None; +} \ No newline at end of file From 20c0f323fc26fb913256301e242f34bccc788e31 Mon Sep 17 00:00:00 2001 From: Tobias Schottdorf Date: Sun, 12 Mar 2017 00:24:11 -0500 Subject: [PATCH 15/54] Use `&&` instead of `&` It does not seem valuable to always evaluate the right-hand side here. --- src/librustc/middle/mem_categorization.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index a669ac3a69b98..a1deda0ab3ec1 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -846,7 +846,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { // When the corresponding feature isn't toggled, only promote `[T; 0]`. let promotable = match expr_ty.sty { ty::TyArray(_, 0) => true, - _ => promotable & self.tcx().sess.features.borrow().rvalue_static_promotion, + _ => promotable && self.tcx().sess.features.borrow().rvalue_static_promotion, }; // Compute maximum lifetime of this rvalue. This is 'static if From f06b04949f7944bfe31405d3735240bb02ee9bd1 Mon Sep 17 00:00:00 2001 From: Tobias Schottdorf Date: Sun, 12 Mar 2017 18:31:17 -0400 Subject: [PATCH 16/54] Improve the documentation for `rvalue_static_promotion` --- .../src/rvalue-static-promotion.md | 18 ++++++++++++++++++ .../feature-gate-rvalue_static_promotion.rs | 2 +- src/test/run-pass/rvalue-static-promotion.rs | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/doc/unstable-book/src/rvalue-static-promotion.md b/src/doc/unstable-book/src/rvalue-static-promotion.md index 3b654960c8e3c..2583d350ebe11 100644 --- a/src/doc/unstable-book/src/rvalue-static-promotion.md +++ b/src/doc/unstable-book/src/rvalue-static-promotion.md @@ -2,4 +2,22 @@ The tracking issue for this feature is: [#38865] +[#38865]: https://github.com/rust-lang/rust/issues/38865 + ------------------------ + +The `rvalue_static_promotion` feature allows directly creating `'static` references to +constant `rvalue`s, which in particular allowing for more concise code in the common case +in which a `'static` reference is all that's needed. + + +## Examples + +```rust +#![feature(rvalue_static_promotion)] + +fn main() { + let DEFAULT_VALUE: &'static u32 = &42; + assert_eq!(*DEFAULT_VALUE, 42); +} +``` diff --git a/src/test/compile-fail/feature-gate-rvalue_static_promotion.rs b/src/test/compile-fail/feature-gate-rvalue_static_promotion.rs index 41dc282be41f0..f33d0a71481d2 100644 --- a/src/test/compile-fail/feature-gate-rvalue_static_promotion.rs +++ b/src/test/compile-fail/feature-gate-rvalue_static_promotion.rs @@ -12,4 +12,4 @@ fn main() { let x: &'static u32 = &42; //~ error: does not live long enough let y: &'static Option = &None; //~ error: does not live long enough -} \ No newline at end of file +} diff --git a/src/test/run-pass/rvalue-static-promotion.rs b/src/test/run-pass/rvalue-static-promotion.rs index f8d93783877bb..30643cfc3eb7d 100644 --- a/src/test/run-pass/rvalue-static-promotion.rs +++ b/src/test/run-pass/rvalue-static-promotion.rs @@ -14,4 +14,4 @@ fn main() { let x: &'static u32 = &42; let y: &'static Option = &None; -} \ No newline at end of file +} From ff63866edb511a73eed657a8a4f5c81f1ee5a9bb Mon Sep 17 00:00:00 2001 From: Piotr Jawniak Date: Fri, 3 Mar 2017 15:42:30 +0100 Subject: [PATCH 17/54] Change how the `0` flag works in format! Now it always implies right-alignment, so that padding zeroes are placed after the sign (if any) and before the digits. In other words, it always takes precedence over explicitly specified `[[fill]align]`. This also affects the '#' flag: zeroes are placed after the prefix (0b, 0o, 0x) and before the digits. :05 :<05 :>05 :^05 before |-0001| |-1000| |-0001| |-0100| after |-0001| |-0001| |-0001| |-0001| :#05x :<#05x :>#05x :^#05x before |0x001| |0x100| |000x1| |0x010| after |0x001| |0x001| |0x001| |0x001| Fixes #39997 [breaking-change] --- src/libcollections/fmt.rs | 4 ++++ src/libcore/fmt/mod.rs | 1 + src/test/run-pass/ifmt.rs | 16 ++++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/src/libcollections/fmt.rs b/src/libcollections/fmt.rs index dfd292176d2f9..2735293528e3f 100644 --- a/src/libcollections/fmt.rs +++ b/src/libcollections/fmt.rs @@ -366,6 +366,10 @@ //! like `{:08}` would yield `00000001` for the integer `1`, while the //! same format would yield `-0000001` for the integer `-1`. Notice that //! the negative version has one fewer zero than the positive version. +//! Note that padding zeroes are always placed after the sign (if any) +//! and before the digits. When used together with the `#` flag, a similar +//! rule applies: padding zeroes are inserted after the prefix but before +//! the digits. //! //! ## Width //! diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 1657342ff6ac6..fd77201317f63 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -1045,6 +1045,7 @@ impl<'a> Formatter<'a> { // is zero Some(min) if self.sign_aware_zero_pad() => { self.fill = '0'; + self.align = rt::v1::Alignment::Right; write_prefix(self)?; self.with_padding(min - width, rt::v1::Alignment::Right, |f| { f.buf.write_str(buf) diff --git a/src/test/run-pass/ifmt.rs b/src/test/run-pass/ifmt.rs index 2a7a593d26800..dcd3920ffd9ad 100644 --- a/src/test/run-pass/ifmt.rs +++ b/src/test/run-pass/ifmt.rs @@ -160,6 +160,22 @@ pub fn main() { t!(format!("{:?}", -0.0), "-0"); t!(format!("{:?}", 0.0), "0"); + // sign aware zero padding + t!(format!("{:<3}", 1), "1 "); + t!(format!("{:>3}", 1), " 1"); + t!(format!("{:^3}", 1), " 1 "); + t!(format!("{:03}", 1), "001"); + t!(format!("{:<03}", 1), "001"); + t!(format!("{:>03}", 1), "001"); + t!(format!("{:^03}", 1), "001"); + t!(format!("{:+03}", 1), "+01"); + t!(format!("{:<+03}", 1), "+01"); + t!(format!("{:>+03}", 1), "+01"); + t!(format!("{:^+03}", 1), "+01"); + t!(format!("{:#05x}", 1), "0x001"); + t!(format!("{:<#05x}", 1), "0x001"); + t!(format!("{:>#05x}", 1), "0x001"); + t!(format!("{:^#05x}", 1), "0x001"); // Ergonomic format_args! t!(format!("{0:x} {0:X}", 15), "f F"); From 80654862831e27f249f05bcb50552510f1b5f643 Mon Sep 17 00:00:00 2001 From: Piotr Jawniak Date: Sat, 4 Mar 2017 21:29:17 +0100 Subject: [PATCH 18/54] Change how the 0 flag works in format! for floats Now it always implies right-alignment, so that padding zeroes are placed after the sign (if any) and before the digits. In other words, it always takes precedence over explicitly specified `[[fill]align]`. :06 :<06 :>06 :^06 before |-001.2| |-1.200| |-001.2| |-01.20| after |-001.2| |-001.2| |-001.2| |-001.2| --- src/libcore/fmt/mod.rs | 5 ++++- src/test/run-pass/ifmt.rs | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index fd77201317f63..0bfab92fa5d51 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -1154,8 +1154,9 @@ impl<'a> Formatter<'a> { // for the sign-aware zero padding, we render the sign first and // behave as if we had no sign from the beginning. let mut formatted = formatted.clone(); - let mut align = self.align; let old_fill = self.fill; + let old_align = self.align; + let mut align = old_align; if self.sign_aware_zero_pad() { // a sign always goes first let sign = unsafe { str::from_utf8_unchecked(formatted.sign) }; @@ -1166,6 +1167,7 @@ impl<'a> Formatter<'a> { width = if width < sign.len() { 0 } else { width - sign.len() }; align = rt::v1::Alignment::Right; self.fill = '0'; + self.align = rt::v1::Alignment::Right; } // remaining parts go through the ordinary padding process. @@ -1178,6 +1180,7 @@ impl<'a> Formatter<'a> { }) }; self.fill = old_fill; + self.align = old_align; ret } else { // this is the common case and we take a shortcut diff --git a/src/test/run-pass/ifmt.rs b/src/test/run-pass/ifmt.rs index dcd3920ffd9ad..cef2f879f9cd7 100644 --- a/src/test/run-pass/ifmt.rs +++ b/src/test/run-pass/ifmt.rs @@ -176,6 +176,18 @@ pub fn main() { t!(format!("{:<#05x}", 1), "0x001"); t!(format!("{:>#05x}", 1), "0x001"); t!(format!("{:^#05x}", 1), "0x001"); + t!(format!("{:05}", 1.2), "001.2"); + t!(format!("{:<05}", 1.2), "001.2"); + t!(format!("{:>05}", 1.2), "001.2"); + t!(format!("{:^05}", 1.2), "001.2"); + t!(format!("{:05}", -1.2), "-01.2"); + t!(format!("{:<05}", -1.2), "-01.2"); + t!(format!("{:>05}", -1.2), "-01.2"); + t!(format!("{:^05}", -1.2), "-01.2"); + t!(format!("{:+05}", 1.2), "+01.2"); + t!(format!("{:<+05}", 1.2), "+01.2"); + t!(format!("{:>+05}", 1.2), "+01.2"); + t!(format!("{:^+05}", 1.2), "+01.2"); // Ergonomic format_args! t!(format!("{0:x} {0:X}", 15), "f F"); From 2561dcddf9e61f5c52a65f1a42641e01bfabe3e2 Mon Sep 17 00:00:00 2001 From: Jimmy Cuadra Date: Sun, 5 Mar 2017 13:00:32 -0800 Subject: [PATCH 19/54] Rename TryFrom's associated type and implement str::parse using TryFrom. Per discussion on the tracking issue, naming `TryFrom`'s associated type `Error` is generally more consistent with similar traits in the Rust ecosystem, and what people seem to assume it should be called. It also helps disambiguate from `Result::Err`, the most common "Err". See https://github.com/rust-lang/rust/issues/33417#issuecomment-269108968. TryFrom<&str> and FromStr are equivalent, so have the latter provide the former to ensure that. Using TryFrom in the implementation of `str::parse` means types that implement either trait can use it. When we're ready to stabilize `TryFrom`, we should update `FromStr` to suggest implementing `TryFrom<&str>` instead for new code. See https://github.com/rust-lang/rust/issues/33417#issuecomment-277175994 and https://github.com/rust-lang/rust/issues/33417#issuecomment-277253827. Refs #33417. --- src/libcore/char.rs | 4 ++-- src/libcore/convert.rs | 24 ++++++++++++++++++------ src/libcore/num/mod.rs | 6 +++--- src/libcore/str/mod.rs | 7 +++++-- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/libcore/char.rs b/src/libcore/char.rs index 78764091cf032..a582180838f40 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -209,10 +209,10 @@ impl From for char { #[unstable(feature = "try_from", issue = "33417")] impl TryFrom for char { - type Err = CharTryFromError; + type Error = CharTryFromError; #[inline] - fn try_from(i: u32) -> Result { + fn try_from(i: u32) -> Result { if (i > MAX as u32) || (i >= 0xD800 && i <= 0xDFFF) { Err(CharTryFromError(())) } else { diff --git a/src/libcore/convert.rs b/src/libcore/convert.rs index 4e170794c1d6e..a9ac9a7f77184 100644 --- a/src/libcore/convert.rs +++ b/src/libcore/convert.rs @@ -48,6 +48,8 @@ #![stable(feature = "rust1", since = "1.0.0")] +use str::FromStr; + /// A cheap, reference-to-reference conversion. /// /// `AsRef` is very similar to, but different than, [`Borrow`]. See @@ -212,20 +214,20 @@ pub trait From: Sized { #[unstable(feature = "try_from", issue = "33417")] pub trait TryInto: Sized { /// The type returned in the event of a conversion error. - type Err; + type Error; /// Performs the conversion. - fn try_into(self) -> Result; + fn try_into(self) -> Result; } /// Attempt to construct `Self` via a conversion. #[unstable(feature = "try_from", issue = "33417")] pub trait TryFrom: Sized { /// The type returned in the event of a conversion error. - type Err; + type Error; /// Performs the conversion. - fn try_from(value: T) -> Result; + fn try_from(value: T) -> Result; } //////////////////////////////////////////////////////////////////////////////// @@ -290,9 +292,9 @@ impl From for T { // TryFrom implies TryInto #[unstable(feature = "try_from", issue = "33417")] impl TryInto for T where U: TryFrom { - type Err = U::Err; + type Error = U::Error; - fn try_into(self) -> Result { + fn try_into(self) -> Result { U::try_from(self) } } @@ -322,3 +324,13 @@ impl AsRef for str { self } } + +// FromStr implies TryFrom<&str> +#[unstable(feature = "try_from", issue = "33417")] +impl<'a, T> TryFrom<&'a str> for T where T: FromStr { + type Error = ::Err; + + fn try_from(s: &'a str) -> Result { + FromStr::from_str(s) + } +} diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 97ea6bb347b54..8edf690e7b521 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -2591,7 +2591,7 @@ macro_rules! same_sign_try_from_int_impl { ($storage:ty, $target:ty, $($source:ty),*) => {$( #[unstable(feature = "try_from", issue = "33417")] impl TryFrom<$source> for $target { - type Err = TryFromIntError; + type Error = TryFromIntError; fn try_from(u: $source) -> Result<$target, TryFromIntError> { let min = <$target as FromStrRadixHelper>::min_value() as $storage; @@ -2623,7 +2623,7 @@ macro_rules! cross_sign_from_int_impl { ($unsigned:ty, $($signed:ty),*) => {$( #[unstable(feature = "try_from", issue = "33417")] impl TryFrom<$unsigned> for $signed { - type Err = TryFromIntError; + type Error = TryFromIntError; fn try_from(u: $unsigned) -> Result<$signed, TryFromIntError> { let max = <$signed as FromStrRadixHelper>::max_value() as u128; @@ -2637,7 +2637,7 @@ macro_rules! cross_sign_from_int_impl { #[unstable(feature = "try_from", issue = "33417")] impl TryFrom<$signed> for $unsigned { - type Err = TryFromIntError; + type Error = TryFromIntError; fn try_from(u: $signed) -> Result<$unsigned, TryFromIntError> { let max = <$unsigned as FromStrRadixHelper>::max_value() as u128; diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 52e3301631052..9d48a8787079e 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -18,6 +18,7 @@ use self::pattern::Pattern; use self::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher}; use char; +use convert::TryFrom; use fmt; use iter::{Map, Cloned, FusedIterator}; use mem; @@ -1746,7 +1747,7 @@ pub trait StrExt { #[stable(feature = "core", since = "1.6.0")] fn is_empty(&self) -> bool; #[stable(feature = "core", since = "1.6.0")] - fn parse(&self) -> Result; + fn parse<'a, T: TryFrom<&'a str>>(&'a self) -> Result; } // truncate `&str` to length at most equal to `max` @@ -2045,7 +2046,9 @@ impl StrExt for str { fn is_empty(&self) -> bool { self.len() == 0 } #[inline] - fn parse(&self) -> Result { FromStr::from_str(self) } + fn parse<'a, T>(&'a self) -> Result where T: TryFrom<&'a str> { + T::try_from(self) + } } #[stable(feature = "rust1", since = "1.0.0")] From c11ab21b279f1ebcdd9c0b261dec5925f33c9259 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Wed, 8 Mar 2017 16:30:31 +1300 Subject: [PATCH 20/54] save-analysis: only index path references once --- src/librustc_save_analysis/dump_visitor.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index 61956e5cd9d66..11cf58dcafb92 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -1375,15 +1375,6 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, debug!("visit_expr {:?}", ex.node); self.process_macro_use(ex.span, ex.id); match ex.node { - ast::ExprKind::Call(ref _f, ref _args) => { - // Don't need to do anything for function calls, - // because just walking the callee path does what we want. - visit::walk_expr(self, ex); - } - ast::ExprKind::Path(_, ref path) => { - self.process_path(ex.id, path, None); - visit::walk_expr(self, ex); - } ast::ExprKind::Struct(ref path, ref fields, ref base) => { let hir_expr = self.save_ctxt.tcx.hir.expect_expr(ex.id); let adt = match self.save_ctxt.tables.expr_ty_opt(&hir_expr) { @@ -1481,6 +1472,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, self.visit_expr(element); self.nest_tables(count.id, |v| v.visit_expr(count)); } + // In particular, we take this branch for call and path expressions, + // where we'll index the idents involved just by continuing to walk. _ => { visit::walk_expr(self, ex) } From 310f5bd7c6ec82e67d18390fc9c0128ef131f299 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Wed, 8 Mar 2017 18:04:30 +1300 Subject: [PATCH 21/54] save-analysis: index extern blocks --- src/librustc_save_analysis/dump_visitor.rs | 35 +++++++++++++ src/librustc_save_analysis/lib.rs | 55 +++++++++++++++++++++ src/test/run-make/save-analysis-fail/foo.rs | 5 ++ 3 files changed, 95 insertions(+) diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index 11cf58dcafb92..6437533e8cd8a 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -1573,4 +1573,39 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, walk_list!(self, visit_ty, &l.ty); walk_list!(self, visit_expr, &l.init); } + + fn visit_foreign_item(&mut self, item: &'l ast::ForeignItem) { + match item.node { + ast::ForeignItemKind::Fn(ref decl, ref generics) => { + if let Some(fn_data) = self.save_ctxt.get_extern_item_data(item) { + down_cast_data!(fn_data, FunctionData, item.span); + if !self.span.filter_generated(Some(fn_data.span), item.span) { + self.dumper.function(fn_data.clone().lower(self.tcx)); + } + + self.nest_tables(item.id, |v| v.process_formals(&decl.inputs, + &fn_data.qualname)); + self.process_generic_params(generics, item.span, &fn_data.qualname, item.id); + } + + for arg in &decl.inputs { + self.visit_ty(&arg.ty); + } + + if let ast::FunctionRetTy::Ty(ref ret_ty) = decl.output { + self.visit_ty(&ret_ty); + } + } + ast::ForeignItemKind::Static(ref ty, _) => { + if let Some(var_data) = self.save_ctxt.get_extern_item_data(item) { + down_cast_data!(var_data, VariableData, item.span); + if !self.span.filter_generated(Some(var_data.span), item.span) { + self.dumper.variable(var_data.lower(self.tcx)); + } + } + + self.visit_ty(ty); + } + } + } } diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 111c8370be2b1..6bb1f3b5bf1eb 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -116,6 +116,46 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { result } + pub fn get_extern_item_data(&self, item: &ast::ForeignItem) -> Option { + let qualname = format!("::{}", self.tcx.node_path_str(item.id)); + match item.node { + ast::ForeignItemKind::Fn(ref decl, ref generics) => { + let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn); + Some(Data::FunctionData(FunctionData { + id: item.id, + name: item.ident.to_string(), + qualname: qualname, + declaration: None, + span: sub_span.unwrap(), + scope: self.enclosing_scope(item.id), + value: make_signature(decl, generics), + visibility: From::from(&item.vis), + parent: None, + docs: docs_for_attrs(&item.attrs), + sig: self.sig_base_extern(item), + })) + } + ast::ForeignItemKind::Static(ref ty, m) => { + let keyword = if m { keywords::Mut } else { keywords::Static }; + let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword); + Some(Data::VariableData(VariableData { + id: item.id, + kind: VariableKind::Static, + name: item.ident.to_string(), + qualname: qualname, + span: sub_span.unwrap(), + scope: self.enclosing_scope(item.id), + parent: None, + value: String::new(), + type_value: ty_to_string(ty), + visibility: From::from(&item.vis), + docs: docs_for_attrs(&item.attrs), + sig: Some(self.sig_base_extern(item)), + })) + } + } + } + pub fn get_item_data(&self, item: &ast::Item) -> Option { match item.node { ast::ItemKind::Fn(ref decl, .., ref generics, _) => { @@ -748,6 +788,21 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } } + fn sig_base_extern(&self, item: &ast::ForeignItem) -> Signature { + let text = self.span_utils.signature_string_for_span(item.span); + let name = item.ident.to_string(); + let ident_start = text.find(&name).expect("Name not in signature?"); + let ident_end = ident_start + name.len(); + Signature { + span: mk_sp(item.span.lo, item.span.lo + BytePos(text.len() as u32)), + text: text, + ident_start: ident_start, + ident_end: ident_end, + defs: vec![], + refs: vec![], + } + } + #[inline] pub fn enclosing_scope(&self, id: NodeId) -> NodeId { self.tcx.hir.get_enclosing_scope(id).unwrap_or(CRATE_NODE_ID) diff --git a/src/test/run-make/save-analysis-fail/foo.rs b/src/test/run-make/save-analysis-fail/foo.rs index e331f65abb7b3..a996aa4fad5a7 100644 --- a/src/test/run-make/save-analysis-fail/foo.rs +++ b/src/test/run-make/save-analysis-fail/foo.rs @@ -448,3 +448,8 @@ fn test_format_args() { print!("{0} + {} = {}", x, y); print!("x is {}, y is {1}, name is {n}", x, y, n = name); } + +extern { + static EXTERN_FOO: u8; + fn extern_foo(a: u8, b: i32) -> String; +} From 4cf22cb1c7ef48379b37560ccc060bf3386e4d74 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Thu, 16 Mar 2017 10:43:11 +1300 Subject: [PATCH 22/54] rebased --- src/librustc_save_analysis/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 6bb1f3b5bf1eb..4390ff07f4c6c 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -133,6 +133,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { parent: None, docs: docs_for_attrs(&item.attrs), sig: self.sig_base_extern(item), + attributes: item.attrs.clone(), })) } ast::ForeignItemKind::Static(ref ty, m) => { @@ -151,6 +152,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { visibility: From::from(&item.vis), docs: docs_for_attrs(&item.attrs), sig: Some(self.sig_base_extern(item)), + attributes: item.attrs.clone(), })) } } From befeb0437018820ee651ed6096a341ba1fd64a28 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Wed, 15 Mar 2017 16:32:30 -0700 Subject: [PATCH 23/54] Remove unused param from bootstrap::clean::rm_rf --- src/bootstrap/clean.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bootstrap/clean.rs b/src/bootstrap/clean.rs index a66ed46fe464f..e9547ee42d077 100644 --- a/src/bootstrap/clean.rs +++ b/src/bootstrap/clean.rs @@ -22,9 +22,9 @@ use std::path::Path; use Build; pub fn clean(build: &Build) { - rm_rf(build, "tmp".as_ref()); - rm_rf(build, &build.out.join("tmp")); - rm_rf(build, &build.out.join("dist")); + rm_rf("tmp".as_ref()); + rm_rf(&build.out.join("tmp")); + rm_rf(&build.out.join("dist")); for host in build.config.host.iter() { let entries = match build.out.join(host).read_dir() { @@ -38,12 +38,12 @@ pub fn clean(build: &Build) { continue } let path = t!(entry.path().canonicalize()); - rm_rf(build, &path); + rm_rf(&path); } } } -fn rm_rf(build: &Build, path: &Path) { +fn rm_rf(path: &Path) { if !path.exists() { return } @@ -55,7 +55,7 @@ fn rm_rf(build: &Build, path: &Path) { let file = t!(file).path(); if file.is_dir() { - rm_rf(build, &file); + rm_rf(&file); } else { // On windows we can't remove a readonly file, and git will // often clone files as readonly. As a result, we have some From 9b892745ad1da5ce9fc374da10f0f06174f13e48 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 16 Mar 2017 02:15:10 +0100 Subject: [PATCH 24/54] Fix const not displayed in rustdoc --- src/librustdoc/clean/mod.rs | 2 +- src/librustdoc/html/render.rs | 8 +++++--- src/test/rustdoc/const.rs | 22 ++++++++++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 src/test/rustdoc/const.rs diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1294296840ebd..b51a298c72dd5 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1388,7 +1388,7 @@ impl<'tcx> Clean for ty::AssociatedItem { decl: decl, abi: sig.abi(), - // trait methods canot (currently, at least) be const + // trait methods cannot (currently, at least) be const constness: hir::Constness::NotConst, }) } else { diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index c571bcb08e4b7..8f65c30602c5a 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -60,6 +60,7 @@ use rustc::middle::privacy::AccessLevels; use rustc::middle::stability; use rustc::hir; use rustc::util::nodemap::{FxHashMap, FxHashSet}; +use rustc::session::config::nightly_options::is_nightly_build; use rustc_data_structures::flock; use clean::{self, AttributesExt, GetDefId, SelfTy, Mutability}; @@ -2316,9 +2317,10 @@ fn render_assoc_item(w: &mut fmt::Formatter, } }; // FIXME(#24111): remove when `const_fn` is stabilized - let vis_constness = match UnstableFeatures::from_environment() { - UnstableFeatures::Allow => constness, - _ => hir::Constness::NotConst + let vis_constness = if is_nightly_build() { + constness + } else { + hir::Constness::NotConst }; let prefix = format!("{}{}{:#}fn {}{:#}", ConstnessSpace(vis_constness), diff --git a/src/test/rustdoc/const.rs b/src/test/rustdoc/const.rs new file mode 100644 index 0000000000000..380feb941d6fe --- /dev/null +++ b/src/test/rustdoc/const.rs @@ -0,0 +1,22 @@ +// Copyright 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"] + +#![feature(const_fn)] + +pub struct Foo; + +impl Foo { + // @has const/struct.Foo.html '//*[@id="new.v"]//code' 'const unsafe fn new' + pub const unsafe fn new() -> Foo { + Foo + } +} From 449219ab2bf65f465b18c0841d7f8d9d3b958943 Mon Sep 17 00:00:00 2001 From: Tim Neumann Date: Thu, 16 Mar 2017 21:01:05 +0100 Subject: [PATCH 25/54] isolate llvm 4.0 code path --- src/rustllvm/RustWrapper.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index e89f48b4105d3..714fd2459da10 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -606,26 +606,20 @@ extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreateStaticVariable( InitExpr = Builder->createConstantValueExpression( FPVal->getValueAPF().bitcastToAPInt().getZExtValue()); } -#endif -#if LLVM_VERSION_GE(4, 0) return wrap(Builder->createGlobalVariableExpression( -#else - return wrap(Builder->createGlobalVariable( -#endif unwrapDI(Context), Name, LinkageName, unwrapDI(File), LineNo, unwrapDI(Ty), IsLocalToUnit, -#if LLVM_VERSION_GE(4, 0) InitExpr, + unwrapDIPtr(Decl), + AlignInBits)); #else + return wrap(Builder->createGlobalVariable( + unwrapDI(Context), Name, LinkageName, + unwrapDI(File), LineNo, unwrapDI(Ty), IsLocalToUnit, InitVal, + unwrapDIPtr(Decl))); #endif - unwrapDIPtr(Decl) -#if LLVM_VERSION_GE(4, 0) - , - AlignInBits -#endif - )); } extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreateVariable( From 222ca3c4a550e001e0b1bb957f0035d6bd753d4a Mon Sep 17 00:00:00 2001 From: Tim Neumann Date: Thu, 16 Mar 2017 21:03:22 +0100 Subject: [PATCH 26/54] clang-format --- src/rustllvm/RustWrapper.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 714fd2459da10..bb0a44cef672b 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -610,15 +610,12 @@ extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreateStaticVariable( return wrap(Builder->createGlobalVariableExpression( unwrapDI(Context), Name, LinkageName, unwrapDI(File), LineNo, unwrapDI(Ty), IsLocalToUnit, - InitExpr, - unwrapDIPtr(Decl), - AlignInBits)); + InitExpr, unwrapDIPtr(Decl), AlignInBits)); #else return wrap(Builder->createGlobalVariable( unwrapDI(Context), Name, LinkageName, unwrapDI(File), LineNo, unwrapDI(Ty), IsLocalToUnit, - InitVal, - unwrapDIPtr(Decl))); + InitVal, unwrapDIPtr(Decl))); #endif } From 95bd7f2e013ad79d857ac54b42b362b36ae8806d Mon Sep 17 00:00:00 2001 From: Tim Neumann Date: Thu, 16 Mar 2017 21:10:04 +0100 Subject: [PATCH 27/54] add missing global metadata --- src/rustllvm/RustWrapper.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index bb0a44cef672b..5ab786f40b933 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -9,6 +9,7 @@ // except according to those terms. #include "rustllvm.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/Instructions.h" @@ -594,7 +595,7 @@ extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreateStaticVariable( const char *LinkageName, LLVMRustMetadataRef File, unsigned LineNo, LLVMRustMetadataRef Ty, bool IsLocalToUnit, LLVMValueRef V, LLVMRustMetadataRef Decl = nullptr, uint32_t AlignInBits = 0) { - Constant *InitVal = cast(unwrap(V)); + llvm::GlobalVariable *InitVal = cast(unwrap(V)); #if LLVM_VERSION_GE(4, 0) llvm::DIExpression *InitExpr = nullptr; @@ -607,10 +608,14 @@ extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreateStaticVariable( FPVal->getValueAPF().bitcastToAPInt().getZExtValue()); } - return wrap(Builder->createGlobalVariableExpression( + llvm::DIGlobalVariableExpression *VarExpr = Builder->createGlobalVariableExpression( unwrapDI(Context), Name, LinkageName, unwrapDI(File), LineNo, unwrapDI(Ty), IsLocalToUnit, - InitExpr, unwrapDIPtr(Decl), AlignInBits)); + InitExpr, unwrapDIPtr(Decl), AlignInBits); + + InitVal->setMetadata("dbg", VarExpr); + + return wrap(VarExpr); #else return wrap(Builder->createGlobalVariable( unwrapDI(Context), Name, LinkageName, From 50cede0d31e74d271d94eb6df85988bc9e05c120 Mon Sep 17 00:00:00 2001 From: z1mvader Date: Thu, 16 Mar 2017 19:59:36 -0500 Subject: [PATCH 28/54] documented order of conversion between u32 an ipv4addr --- src/libstd/net/ip.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index 5d6e8d319d7b2..24e0e6f3fa659 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -636,6 +636,7 @@ impl FromInner for Ipv4Addr { #[stable(feature = "ip_u32", since = "1.1.0")] impl From for u32 { + /// It performs the conversion in network order (big-endian). fn from(ip: Ipv4Addr) -> u32 { let ip = ip.octets(); ((ip[0] as u32) << 24) + ((ip[1] as u32) << 16) + ((ip[2] as u32) << 8) + (ip[3] as u32) @@ -644,6 +645,7 @@ impl From for u32 { #[stable(feature = "ip_u32", since = "1.1.0")] impl From for Ipv4Addr { + /// It performs the conversion in network order (big-endian). fn from(ip: u32) -> Ipv4Addr { Ipv4Addr::new((ip >> 24) as u8, (ip >> 16) as u8, (ip >> 8) as u8, ip as u8) } From 963d4dfddeb302326432eb089771c4aed29f7b5c Mon Sep 17 00:00:00 2001 From: QuietMisdreavus Date: Fri, 17 Mar 2017 10:50:30 -0500 Subject: [PATCH 29/54] minor wording tweak to slice::{as_ptr, as_mut_ptr} --- src/libcollections/slice.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libcollections/slice.rs b/src/libcollections/slice.rs index 2ea953df87357..0723be828e5b7 100644 --- a/src/libcollections/slice.rs +++ b/src/libcollections/slice.rs @@ -437,8 +437,8 @@ impl [T] { /// The caller must ensure that the slice outlives the pointer this /// function returns, or else it will end up pointing to garbage. /// - /// Modifying the slice may cause its buffer to be reallocated, which - /// would also make any pointers to it invalid. + /// Modifying the container referenced by this slice may cause its buffer + /// to be reallocated, which would also make any pointers to it invalid. /// /// # Examples /// @@ -463,8 +463,8 @@ impl [T] { /// The caller must ensure that the slice outlives the pointer this /// function returns, or else it will end up pointing to garbage. /// - /// Modifying the slice may cause its buffer to be reallocated, which - /// would also make any pointers to it invalid. + /// Modifying the container referenced by this slice may cause its buffer + /// to be reallocated, which would also make any pointers to it invalid. /// /// # Examples /// From ec8ecf4f9d06f7e034180a6c56f33a6f800dd1e2 Mon Sep 17 00:00:00 2001 From: ScottAbbey Date: Fri, 17 Mar 2017 13:27:13 -0500 Subject: [PATCH 30/54] Fix typo in mutex.rs docs This seems to match other uses of "be accessed" in the document. --- src/libstd/sync/mutex.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs index 97b84d59218ac..483d0ef752ddb 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -132,7 +132,7 @@ unsafe impl Sync for Mutex { } /// An RAII implementation of a "scoped lock" of a mutex. When this structure is /// dropped (falls out of scope), the lock will be unlocked. /// -/// The data protected by the mutex can be access through this guard via its +/// The data protected by the mutex can be accessed through this guard via its /// [`Deref`] and [`DerefMut`] implementations. /// /// This structure is created by the [`lock()`] and [`try_lock()`] methods on From 33a56659886a2afc3207de39000a9d74f3dddadc Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 14 Mar 2017 20:46:56 -0700 Subject: [PATCH 31/54] Stabilize move_cell feature, closes #39264 --- src/libcore/cell.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index 736797d162b1d..0186d9727828d 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -394,7 +394,6 @@ impl Cell { /// # Examples /// /// ``` - /// #![feature(move_cell)] /// use std::cell::Cell; /// /// let c1 = Cell::new(5i32); @@ -404,7 +403,7 @@ impl Cell { /// assert_eq!(5, c2.get()); /// ``` #[inline] - #[unstable(feature = "move_cell", issue = "39264")] + #[stable(feature = "move_cell", since = "1.17.0")] pub fn swap(&self, other: &Self) { if ptr::eq(self, other) { return; @@ -419,7 +418,6 @@ impl Cell { /// # Examples /// /// ``` - /// #![feature(move_cell)] /// use std::cell::Cell; /// /// let c = Cell::new(5); @@ -427,7 +425,7 @@ impl Cell { /// /// assert_eq!(5, old); /// ``` - #[unstable(feature = "move_cell", issue = "39264")] + #[stable(feature = "move_cell", since = "1.17.0")] pub fn replace(&self, val: T) -> T { mem::replace(unsafe { &mut *self.value.get() }, val) } @@ -437,7 +435,6 @@ impl Cell { /// # Examples /// /// ``` - /// #![feature(move_cell)] /// use std::cell::Cell; /// /// let c = Cell::new(5); @@ -445,7 +442,7 @@ impl Cell { /// /// assert_eq!(five, 5); /// ``` - #[unstable(feature = "move_cell", issue = "39264")] + #[stable(feature = "move_cell", since = "1.17.0")] pub fn into_inner(self) -> T { unsafe { self.value.into_inner() } } @@ -457,7 +454,6 @@ impl Cell { /// # Examples /// /// ``` - /// #![feature(move_cell)] /// use std::cell::Cell; /// /// let c = Cell::new(5); @@ -466,7 +462,7 @@ impl Cell { /// assert_eq!(five, 5); /// assert_eq!(c.into_inner(), 0); /// ``` - #[unstable(feature = "move_cell", issue = "39264")] + #[stable(feature = "move_cell", since = "1.17.0")] pub fn take(&self) -> T { self.replace(Default::default()) } From 65b7c4ed31ae8601c39deab8ca18235b0bea520b Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 14 Mar 2017 20:49:18 -0700 Subject: [PATCH 32/54] Stabilize expect_err feature, closes #39041 --- src/libcore/result.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libcore/result.rs b/src/libcore/result.rs index a05db9b489ca1..00ff2fd2ce5ef 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -803,12 +803,11 @@ impl Result { /// Basic usage: /// /// ```{.should_panic} - /// # #![feature(result_expect_err)] /// let x: Result = Ok(10); /// x.expect_err("Testing expect_err"); // panics with `Testing expect_err: 10` /// ``` #[inline] - #[unstable(feature = "result_expect_err", issue = "39041")] + #[stable(feature = "result_expect_err", since = "1.17.0")] pub fn expect_err(self, msg: &str) -> E { match self { Ok(t) => unwrap_failed(msg, t), From d38ea8b371f781aa4b0689c46ede75b5385fedba Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 14 Mar 2017 20:51:29 -0700 Subject: [PATCH 33/54] Stabilize ptr_unaligned feature, closes #37955 --- src/libcore/ptr.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 0b7aa4fa9117c..e4ad8cfd25654 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -161,8 +161,6 @@ pub unsafe fn read(src: *const T) -> T { /// Basic usage: /// /// ``` -/// #![feature(ptr_unaligned)] -/// /// let x = 12; /// let y = &x as *const i32; /// @@ -171,7 +169,7 @@ pub unsafe fn read(src: *const T) -> T { /// } /// ``` #[inline(always)] -#[unstable(feature = "ptr_unaligned", issue = "37955")] +#[stable(feature = "ptr_unaligned", since = "1.17.0")] pub unsafe fn read_unaligned(src: *const T) -> T { let mut tmp: T = mem::uninitialized(); copy_nonoverlapping(src as *const u8, @@ -243,8 +241,6 @@ pub unsafe fn write(dst: *mut T, src: T) { /// Basic usage: /// /// ``` -/// #![feature(ptr_unaligned)] -/// /// let mut x = 0; /// let y = &mut x as *mut i32; /// let z = 12; @@ -255,7 +251,7 @@ pub unsafe fn write(dst: *mut T, src: T) { /// } /// ``` #[inline] -#[unstable(feature = "ptr_unaligned", issue = "37955")] +#[stable(feature = "ptr_unaligned", since = "1.17.0")] pub unsafe fn write_unaligned(dst: *mut T, src: T) { copy_nonoverlapping(&src as *const T as *const u8, dst as *mut u8, From 9511fe60ce8c7498958662c2bf2c34da0778120d Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 14 Mar 2017 20:52:20 -0700 Subject: [PATCH 34/54] Stabilize process_abort feature, closes #37838 --- src/libstd/process.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/process.rs b/src/libstd/process.rs index f846ef3e69e09..97c48ee590341 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -1032,7 +1032,7 @@ pub fn exit(code: i32) -> ! { /// will be run. If a clean shutdown is needed it is recommended to only call /// this function at a known point where there are no more destructors left /// to run. -#[unstable(feature = "process_abort", issue = "37838")] +#[stable(feature = "process_abort", since = "1.17.0")] pub fn abort() -> ! { unsafe { ::sys::abort_internal() }; } From 10510aefb10aaad9e9c382acf973a40938d091ad Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 14 Mar 2017 21:01:18 -0700 Subject: [PATCH 35/54] Stabilize ptr_eq feature, closes #36497 --- src/liballoc/arc.rs | 6 +----- src/liballoc/rc.rs | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 38d843263ffda..b904d818feb39 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -461,17 +461,13 @@ impl Arc { } #[inline] - #[unstable(feature = "ptr_eq", - reason = "newly added", - issue = "36497")] + #[stable(feature = "ptr_eq", since = "1.17.0")] /// Returns true if the two `Arc`s point to the same value (not /// just values that compare as equal). /// /// # Examples /// /// ``` - /// #![feature(ptr_eq)] - /// /// use std::sync::Arc; /// /// let five = Arc::new(5); diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index eb449b2660679..a5d1381260bb6 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -551,17 +551,13 @@ impl Rc { } #[inline] - #[unstable(feature = "ptr_eq", - reason = "newly added", - issue = "36497")] + #[stable(feature = "ptr_eq", since = "1.17.0")] /// Returns true if the two `Rc`s point to the same value (not /// just values that compare as equal). /// /// # Examples /// /// ``` - /// #![feature(ptr_eq)] - /// /// use std::rc::Rc; /// /// let five = Rc::new(5); From 37b38a2f750c95aa653ced2a33c13c9060129800 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 14 Mar 2017 21:04:54 -0700 Subject: [PATCH 36/54] Stabilize btree_range, closes #27787 --- src/libcollections/btree/map.rs | 12 ++---------- src/libcollections/lib.rs | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/libcollections/btree/map.rs b/src/libcollections/btree/map.rs index 7218d15ded5f8..a746175a5e982 100644 --- a/src/libcollections/btree/map.rs +++ b/src/libcollections/btree/map.rs @@ -724,8 +724,6 @@ impl BTreeMap { /// Basic usage: /// /// ``` - /// #![feature(btree_range, collections_bound)] - /// /// use std::collections::BTreeMap; /// use std::collections::Bound::Included; /// @@ -738,9 +736,7 @@ impl BTreeMap { /// } /// assert_eq!(Some((&5, &"b")), map.range(4..).next()); /// ``` - #[unstable(feature = "btree_range", - reason = "matches collection reform specification, waiting for dust to settle", - issue = "27787")] + #[stable(feature = "btree_range", since = "1.17.0")] pub fn range(&self, range: R) -> Range where T: Ord, K: Borrow, R: RangeArgument { @@ -768,8 +764,6 @@ impl BTreeMap { /// Basic usage: /// /// ``` - /// #![feature(btree_range)] - /// /// use std::collections::BTreeMap; /// /// let mut map: BTreeMap<&str, i32> = ["Alice", "Bob", "Carol", "Cheryl"].iter() @@ -782,9 +776,7 @@ impl BTreeMap { /// println!("{} => {}", name, balance); /// } /// ``` - #[unstable(feature = "btree_range", - reason = "matches collection reform specification, waiting for dust to settle", - issue = "27787")] + #[stable(feature = "btree_range", since = "1.17.0")] pub fn range_mut(&mut self, range: R) -> RangeMut where T: Ord, K: Borrow, R: RangeArgument { diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index f88bdd0ecf382..a64fffab45c5a 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -129,7 +129,7 @@ mod std { } /// An endpoint of a range of keys. -#[unstable(feature = "collections_bound", issue = "27787")] +#[stable(feature = "collections_bound", since = "1.17.0")] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub enum Bound { /// An inclusive bound. From 48890d497163bec75d40198b365b3ca670cc3454 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 14 Mar 2017 21:06:29 -0700 Subject: [PATCH 37/54] Stabilize ordering_chaining, closes #37053 --- src/libcore/cmp.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index b43fe757d8473..cb39796eecd7d 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -255,8 +255,6 @@ impl Ordering { /// # Examples /// /// ``` - /// #![feature(ordering_chaining)] - /// /// use std::cmp::Ordering; /// /// let result = Ordering::Equal.then(Ordering::Less); @@ -278,7 +276,7 @@ impl Ordering { /// assert_eq!(result, Ordering::Less); /// ``` #[inline] - #[unstable(feature = "ordering_chaining", issue = "37053")] + #[stable(feature = "ordering_chaining", since = "1.17.0")] pub fn then(self, other: Ordering) -> Ordering { match self { Equal => other, @@ -294,8 +292,6 @@ impl Ordering { /// # Examples /// /// ``` - /// #![feature(ordering_chaining)] - /// /// use std::cmp::Ordering; /// /// let result = Ordering::Equal.then_with(|| Ordering::Less); @@ -317,7 +313,7 @@ impl Ordering { /// assert_eq!(result, Ordering::Less); /// ``` #[inline] - #[unstable(feature = "ordering_chaining", issue = "37053")] + #[stable(feature = "ordering_chaining", since = "1.17.0")] pub fn then_with Ordering>(self, f: F) -> Ordering { match self { Equal => f(), From a8f4a1bd984091ffb8f87f9440e2483f94b44a20 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 14 Mar 2017 21:10:02 -0700 Subject: [PATCH 38/54] Stabilize rc_raw feature, closes #37197 --- src/liballoc/arc.rs | 22 ++++++--------- src/liballoc/rc.rs | 22 ++++++--------- src/libcollections/lib.rs | 3 ++ src/libcollections/linked_list.rs | 34 +++++++++++------------ src/libcollections/vec.rs | 4 +-- src/libcollections/vec_deque.rs | 2 +- src/libcore/ptr.rs | 14 ++++++++-- src/librustc_data_structures/array_vec.rs | 3 +- src/librustc_data_structures/lib.rs | 1 - src/libstd/collections/hash/table.rs | 2 +- src/libstd/lib.rs | 1 - 11 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index b904d818feb39..b6191c4d43e8f 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -287,17 +287,15 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(rc_raw)] - /// /// use std::sync::Arc; /// /// let x = Arc::new(10); /// let x_ptr = Arc::into_raw(x); /// assert_eq!(unsafe { *x_ptr }, 10); /// ``` - #[unstable(feature = "rc_raw", issue = "37197")] - pub fn into_raw(this: Self) -> *mut T { - let ptr = unsafe { &mut (**this.ptr).data as *mut _ }; + #[stable(feature = "rc_raw", since = "1.17.0")] + pub fn into_raw(this: Self) -> *const T { + let ptr = unsafe { &(**this.ptr).data as *const _ }; mem::forget(this); ptr } @@ -315,8 +313,6 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(rc_raw)] - /// /// use std::sync::Arc; /// /// let x = Arc::new(10); @@ -332,11 +328,11 @@ impl Arc { /// /// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling! /// ``` - #[unstable(feature = "rc_raw", issue = "37197")] - pub unsafe fn from_raw(ptr: *mut T) -> Self { + #[stable(feature = "rc_raw", since = "1.17.0")] + pub unsafe fn from_raw(ptr: *const T) -> Self { // To find the corresponding pointer to the `ArcInner` we need to subtract the offset of the // `data` field from the pointer. - Arc { ptr: Shared::new((ptr as *mut u8).offset(-offset_of!(ArcInner, data)) as *mut _) } + Arc { ptr: Shared::new((ptr as *const u8).offset(-offset_of!(ArcInner, data)) as *const _) } } } @@ -448,7 +444,7 @@ impl Arc { // Non-inlined part of `drop`. #[inline(never)] unsafe fn drop_slow(&mut self) { - let ptr = *self.ptr; + let ptr = self.ptr.as_mut_ptr(); // Destroy the data at this time, even though we may not free the box // allocation itself (there may still be weak pointers lying around). @@ -624,7 +620,7 @@ impl Arc { // As with `get_mut()`, the unsafety is ok because our reference was // either unique to begin with, or became one upon cloning the contents. unsafe { - let inner = &mut **this.ptr; + let inner = &mut *this.ptr.as_mut_ptr(); &mut inner.data } } @@ -667,7 +663,7 @@ impl Arc { // the Arc itself to be `mut`, so we're returning the only possible // reference to the inner data. unsafe { - let inner = &mut **this.ptr; + let inner = &mut *this.ptr.as_mut_ptr(); Some(&mut inner.data) } } else { diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index a5d1381260bb6..e9b59017692eb 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -364,17 +364,15 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(rc_raw)] - /// /// use std::rc::Rc; /// /// let x = Rc::new(10); /// let x_ptr = Rc::into_raw(x); /// assert_eq!(unsafe { *x_ptr }, 10); /// ``` - #[unstable(feature = "rc_raw", issue = "37197")] - pub fn into_raw(this: Self) -> *mut T { - let ptr = unsafe { &mut (**this.ptr).value as *mut _ }; + #[stable(feature = "rc_raw", since = "1.17.0")] + pub fn into_raw(this: Self) -> *const T { + let ptr = unsafe { &mut (*this.ptr.as_mut_ptr()).value as *const _ }; mem::forget(this); ptr } @@ -392,8 +390,6 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(rc_raw)] - /// /// use std::rc::Rc; /// /// let x = Rc::new(10); @@ -409,11 +405,11 @@ impl Rc { /// /// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling! /// ``` - #[unstable(feature = "rc_raw", issue = "37197")] - pub unsafe fn from_raw(ptr: *mut T) -> Self { + #[stable(feature = "rc_raw", since = "1.17.0")] + pub unsafe fn from_raw(ptr: *const T) -> Self { // To find the corresponding pointer to the `RcBox` we need to subtract the offset of the // `value` field from the pointer. - Rc { ptr: Shared::new((ptr as *mut u8).offset(-offset_of!(RcBox, value)) as *mut _) } + Rc { ptr: Shared::new((ptr as *const u8).offset(-offset_of!(RcBox, value)) as *const _) } } } @@ -543,7 +539,7 @@ impl Rc { #[stable(feature = "rc_unique", since = "1.4.0")] pub fn get_mut(this: &mut Self) -> Option<&mut T> { if Rc::is_unique(this) { - let inner = unsafe { &mut **this.ptr }; + let inner = unsafe { &mut *this.ptr.as_mut_ptr() }; Some(&mut inner.value) } else { None @@ -627,7 +623,7 @@ impl Rc { // reference count is guaranteed to be 1 at this point, and we required // the `Rc` itself to be `mut`, so we're returning the only possible // reference to the inner value. - let inner = unsafe { &mut **this.ptr }; + let inner = unsafe { &mut *this.ptr.as_mut_ptr() }; &mut inner.value } } @@ -673,7 +669,7 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc { /// ``` fn drop(&mut self) { unsafe { - let ptr = *self.ptr; + let ptr = self.ptr.as_mut_ptr(); self.dec_strong(); if self.strong() == 0 { diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index a64fffab45c5a..10650dab583c3 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -133,10 +133,13 @@ mod std { #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub enum Bound { /// An inclusive bound. + #[stable(feature = "collections_bound", since = "1.17.0")] Included(T), /// An exclusive bound. + #[stable(feature = "collections_bound", since = "1.17.0")] Excluded(T), /// An infinite endpoint. Indicates that there is no bound in this direction. + #[stable(feature = "collections_bound", since = "1.17.0")] Unbounded, } diff --git a/src/libcollections/linked_list.rs b/src/libcollections/linked_list.rs index d4f77d625b361..f58c87b801f55 100644 --- a/src/libcollections/linked_list.rs +++ b/src/libcollections/linked_list.rs @@ -142,7 +142,7 @@ impl LinkedList { match self.head { None => self.tail = node, - Some(head) => (**head).prev = node, + Some(head) => (*head.as_mut_ptr()).prev = node, } self.head = node; @@ -154,12 +154,12 @@ impl LinkedList { #[inline] fn pop_front_node(&mut self) -> Option>> { self.head.map(|node| unsafe { - let node = Box::from_raw(*node); + let node = Box::from_raw(node.as_mut_ptr()); self.head = node.next; match self.head { None => self.tail = None, - Some(head) => (**head).prev = None, + Some(head) => (*head.as_mut_ptr()).prev = None, } self.len -= 1; @@ -177,7 +177,7 @@ impl LinkedList { match self.tail { None => self.head = node, - Some(tail) => (**tail).next = node, + Some(tail) => (*tail.as_mut_ptr()).next = node, } self.tail = node; @@ -189,12 +189,12 @@ impl LinkedList { #[inline] fn pop_back_node(&mut self) -> Option>> { self.tail.map(|node| unsafe { - let node = Box::from_raw(*node); + let node = Box::from_raw(node.as_mut_ptr()); self.tail = node.prev; match self.tail { None => self.head = None, - Some(tail) => (**tail).next = None, + Some(tail) => (*tail.as_mut_ptr()).next = None, } self.len -= 1; @@ -269,8 +269,8 @@ impl LinkedList { Some(tail) => { if let Some(other_head) = other.head.take() { unsafe { - (**tail).next = Some(other_head); - (**other_head).prev = Some(tail); + (*tail.as_mut_ptr()).next = Some(other_head); + (*other_head.as_mut_ptr()).prev = Some(tail); } self.tail = other.tail.take(); @@ -484,7 +484,7 @@ impl LinkedList { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn front_mut(&mut self) -> Option<&mut T> { - self.head.map(|node| unsafe { &mut (**node).element }) + self.head.map(|node| unsafe { &mut (*node.as_mut_ptr()).element }) } /// Provides a reference to the back element, or `None` if the list is @@ -530,7 +530,7 @@ impl LinkedList { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn back_mut(&mut self) -> Option<&mut T> { - self.tail.map(|node| unsafe { &mut (**node).element }) + self.tail.map(|node| unsafe { &mut (*node.as_mut_ptr()).element }) } /// Adds an element first in the list. @@ -675,9 +675,9 @@ impl LinkedList { let second_part_head; unsafe { - second_part_head = (**split_node.unwrap()).next.take(); + second_part_head = (*split_node.unwrap().as_mut_ptr()).next.take(); if let Some(head) = second_part_head { - (**head).prev = None; + (*head.as_mut_ptr()).prev = None; } } @@ -816,7 +816,7 @@ impl<'a, T> Iterator for IterMut<'a, T> { None } else { self.head.map(|node| unsafe { - let node = &mut **node; + let node = &mut *node.as_mut_ptr(); self.len -= 1; self.head = node.next; &mut node.element @@ -838,7 +838,7 @@ impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { None } else { self.tail.map(|node| unsafe { - let node = &mut **node; + let node = &mut *node.as_mut_ptr(); self.len -= 1; self.tail = node.prev; &mut node.element @@ -896,8 +896,8 @@ impl<'a, T> IterMut<'a, T> { element: element, }))); - (**prev).next = node; - (**head).prev = node; + (*prev.as_mut_ptr()).next = node; + (*head.as_mut_ptr()).prev = node; self.list.len += 1; }, @@ -929,7 +929,7 @@ impl<'a, T> IterMut<'a, T> { if self.len == 0 { None } else { - self.head.map(|node| unsafe { &mut (**node).element }) + self.head.map(|node| unsafe { &mut (*node.as_mut_ptr()).element }) } } } diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index a717163f45ef5..7b408af13aa2f 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -2120,7 +2120,7 @@ unsafe impl<#[may_dangle] T> Drop for IntoIter { for _x in self.by_ref() {} // RawVec handles deallocation - let _ = unsafe { RawVec::from_raw_parts(*self.buf, self.cap) }; + let _ = unsafe { RawVec::from_raw_parts(self.buf.as_mut_ptr(), self.cap) }; } } @@ -2185,7 +2185,7 @@ impl<'a, T> Drop for Drain<'a, T> { if self.tail_len > 0 { unsafe { - let source_vec = &mut **self.vec; + let source_vec = &mut *self.vec.as_mut_ptr(); // memmove back untouched tail, update to new length let start = source_vec.len(); let tail = self.tail_start; diff --git a/src/libcollections/vec_deque.rs b/src/libcollections/vec_deque.rs index 1985be7f901c6..6a04d47a345e8 100644 --- a/src/libcollections/vec_deque.rs +++ b/src/libcollections/vec_deque.rs @@ -2125,7 +2125,7 @@ impl<'a, T: 'a> Drop for Drain<'a, T> { fn drop(&mut self) { for _ in self.by_ref() {} - let source_deque = unsafe { &mut **self.deque }; + let source_deque = unsafe { &mut *self.deque.as_mut_ptr() }; // T = source_deque_tail; H = source_deque_head; t = drain_tail; h = drain_head // diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index e4ad8cfd25654..15174e72795ac 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -968,11 +968,19 @@ impl Shared { /// # Safety /// /// `ptr` must be non-null. - pub unsafe fn new(ptr: *mut T) -> Self { + pub unsafe fn new(ptr: *const T) -> Self { Shared { pointer: NonZero::new(ptr), _marker: PhantomData } } } +#[unstable(feature = "shared", issue = "27730")] +impl Shared { + /// Acquires the underlying pointer as a `*mut` pointer. + pub unsafe fn as_mut_ptr(&self) -> *mut T { + **self as _ + } +} + #[unstable(feature = "shared", issue = "27730")] impl Clone for Shared { fn clone(&self) -> Self { @@ -988,10 +996,10 @@ impl CoerceUnsized> for Shared where T: Unsiz #[unstable(feature = "shared", issue = "27730")] impl Deref for Shared { - type Target = *mut T; + type Target = *const T; #[inline] - fn deref(&self) -> &*mut T { + fn deref(&self) -> &*const T { unsafe { mem::transmute(&*self.pointer) } } } diff --git a/src/librustc_data_structures/array_vec.rs b/src/librustc_data_structures/array_vec.rs index 51e6e09ab5003..29fbcb70756ba 100644 --- a/src/librustc_data_structures/array_vec.rs +++ b/src/librustc_data_structures/array_vec.rs @@ -248,7 +248,7 @@ impl<'a, A: Array> Drop for Drain<'a, A> { if self.tail_len > 0 { unsafe { - let source_array_vec = &mut **self.array_vec; + let source_array_vec = &mut *self.array_vec.as_mut_ptr(); // memmove back untouched tail, update to new length let start = source_array_vec.len(); let tail = self.tail_start; @@ -317,4 +317,3 @@ impl Default for ManuallyDrop { ManuallyDrop::new() } } - diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index f278325ebec74..8ecfd75dc95a9 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -27,7 +27,6 @@ #![feature(shared)] #![feature(collections_range)] -#![feature(collections_bound)] #![cfg_attr(stage0,feature(field_init_shorthand))] #![feature(nonzero)] #![feature(rustc_private)] diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 2c8bb433e8aef..211605bef1ee0 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -1154,7 +1154,7 @@ impl<'a, K, V> Iterator for Drain<'a, K, V> { fn next(&mut self) -> Option<(SafeHash, K, V)> { self.iter.next().map(|bucket| { unsafe { - (**self.table).size -= 1; + (*self.table.as_mut_ptr()).size -= 1; let (k, v) = ptr::read(bucket.pair); (SafeHash { hash: ptr::replace(bucket.hash, EMPTY_BUCKET) }, k, v) } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 2c83518d38880..206a37b8e5db8 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -245,7 +245,6 @@ #![feature(char_escape_debug)] #![feature(char_internals)] #![feature(collections)] -#![feature(collections_bound)] #![feature(collections_range)] #![feature(compiler_builtins_lib)] #![feature(const_fn)] From 1241a88fa9ddf5e645d1e6e93e04c435bbf15cd4 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 15 Mar 2017 07:58:27 -0700 Subject: [PATCH 39/54] Minor fixups to fix tidy errors --- src/liballoc/arc.rs | 5 ++++- src/libcollections/btree/map.rs | 2 ++ src/libcollections/btree/set.rs | 7 ++----- src/libcollections/range.rs | 2 -- src/libcollectionstest/lib.rs | 2 -- src/libcore/ptr.rs | 3 +-- src/libcoretest/lib.rs | 4 ---- 7 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index b6191c4d43e8f..1d616233881b4 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -332,7 +332,10 @@ impl Arc { pub unsafe fn from_raw(ptr: *const T) -> Self { // To find the corresponding pointer to the `ArcInner` we need to subtract the offset of the // `data` field from the pointer. - Arc { ptr: Shared::new((ptr as *const u8).offset(-offset_of!(ArcInner, data)) as *const _) } + let ptr = (ptr as *const u8).offset(-offset_of!(ArcInner, data)); + Arc { + ptr: Shared::new(ptr as *const _), + } } } diff --git a/src/libcollections/btree/map.rs b/src/libcollections/btree/map.rs index a746175a5e982..53fe6b4bc9f4f 100644 --- a/src/libcollections/btree/map.rs +++ b/src/libcollections/btree/map.rs @@ -338,6 +338,7 @@ pub struct ValuesMut<'a, K: 'a, V: 'a> { } /// An iterator over a sub-range of BTreeMap's entries. +#[stable(feature = "btree_range", since = "1.17.0")] pub struct Range<'a, K: 'a, V: 'a> { front: Handle, K, V, marker::Leaf>, marker::Edge>, back: Handle, K, V, marker::Leaf>, marker::Edge>, @@ -351,6 +352,7 @@ impl<'a, K: 'a + fmt::Debug, V: 'a + fmt::Debug> fmt::Debug for Range<'a, K, V> } /// A mutable iterator over a sub-range of BTreeMap's entries. +#[stable(feature = "btree_range", since = "1.17.0")] pub struct RangeMut<'a, K: 'a, V: 'a> { front: Handle, K, V, marker::Leaf>, marker::Edge>, back: Handle, K, V, marker::Leaf>, marker::Edge>, diff --git a/src/libcollections/btree/set.rs b/src/libcollections/btree/set.rs index e3c990c80decf..72d25f87bca95 100644 --- a/src/libcollections/btree/set.rs +++ b/src/libcollections/btree/set.rs @@ -113,6 +113,7 @@ pub struct IntoIter { /// [`BTreeSet`]: struct.BTreeSet.html /// [`range`]: struct.BTreeSet.html#method.range #[derive(Debug)] +#[stable(feature = "btree_range", since = "1.17.0")] pub struct Range<'a, T: 'a> { iter: ::btree_map::Range<'a, T, ()>, } @@ -264,8 +265,6 @@ impl BTreeSet { /// # Examples /// /// ``` - /// #![feature(btree_range, collections_bound)] - /// /// use std::collections::BTreeSet; /// use std::collections::Bound::Included; /// @@ -278,9 +277,7 @@ impl BTreeSet { /// } /// assert_eq!(Some(&5), set.range(4..).next()); /// ``` - #[unstable(feature = "btree_range", - reason = "matches collection reform specification, waiting for dust to settle", - issue = "27787")] + #[stable(feature = "btree_range", since = "1.17.0")] pub fn range(&self, range: R) -> Range where K: Ord, T: Borrow, R: RangeArgument { diff --git a/src/libcollections/range.rs b/src/libcollections/range.rs index e4b94a1d70ee4..31e4d001397bf 100644 --- a/src/libcollections/range.rs +++ b/src/libcollections/range.rs @@ -29,7 +29,6 @@ pub trait RangeArgument { /// ``` /// #![feature(collections)] /// #![feature(collections_range)] - /// #![feature(collections_bound)] /// /// extern crate collections; /// @@ -52,7 +51,6 @@ pub trait RangeArgument { /// ``` /// #![feature(collections)] /// #![feature(collections_range)] - /// #![feature(collections_bound)] /// /// extern crate collections; /// diff --git a/src/libcollectionstest/lib.rs b/src/libcollectionstest/lib.rs index 98d0b1c8e1565..618eb386c0f4c 100644 --- a/src/libcollectionstest/lib.rs +++ b/src/libcollectionstest/lib.rs @@ -13,11 +13,9 @@ #![feature(binary_heap_extras)] #![feature(binary_heap_peek_mut_pop)] #![feature(box_syntax)] -#![feature(btree_range)] #![feature(inclusive_range_syntax)] #![feature(collection_placement)] #![feature(collections)] -#![feature(collections_bound)] #![feature(const_fn)] #![feature(exact_size_is_empty)] #![feature(pattern)] diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 15174e72795ac..909e44df20abb 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -658,7 +658,6 @@ impl Eq for *mut T {} /// # Examples /// /// ``` -/// #![feature(ptr_eq)] /// use std::ptr; /// /// let five = 5; @@ -673,7 +672,7 @@ impl Eq for *mut T {} /// assert!(ptr::eq(five_ref, same_five_ref)); /// assert!(!ptr::eq(five_ref, other_five_ref)); /// ``` -#[unstable(feature = "ptr_eq", reason = "newly added", issue = "36497")] +#[stable(feature = "ptr_eq", since = "1.17.0")] #[inline] pub fn eq(a: *const T, b: *const T) -> bool { a == b diff --git a/src/libcoretest/lib.rs b/src/libcoretest/lib.rs index e06b757691e5a..d84a1e227560e 100644 --- a/src/libcoretest/lib.rs +++ b/src/libcoretest/lib.rs @@ -23,7 +23,6 @@ #![feature(nonzero)] #![feature(rand)] #![feature(raw)] -#![feature(result_expect_err)] #![feature(sip_hash_13)] #![feature(slice_patterns)] #![feature(step_by)] @@ -31,9 +30,6 @@ #![feature(try_from)] #![feature(unicode)] #![feature(unique)] -#![feature(ordering_chaining)] -#![feature(ptr_unaligned)] -#![feature(move_cell)] #![feature(fmt_internals)] extern crate core; From 27fcdb86e0e3dd9e04f9536eca64dcebecfddc7f Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Thu, 9 Mar 2017 17:53:01 -0800 Subject: [PATCH 40/54] Specialize Vec::from_elem to use calloc or memset Fixes #38723. --- src/doc/unstable-book/src/allocator.md | 5 ++++ src/liballoc/heap.rs | 32 +++++++++++++++++++++++ src/liballoc/raw_vec.rs | 16 +++++++++++- src/liballoc_jemalloc/lib.rs | 21 +++++++++++++++ src/liballoc_system/lib.rs | 36 +++++++++++++++++++++++--- src/libcollections/vec.rs | 35 ++++++++++++++++++++++--- 6 files changed, 137 insertions(+), 8 deletions(-) diff --git a/src/doc/unstable-book/src/allocator.md b/src/doc/unstable-book/src/allocator.md index 7261641698f48..cfcf8e22d7088 100644 --- a/src/doc/unstable-book/src/allocator.md +++ b/src/doc/unstable-book/src/allocator.md @@ -51,6 +51,11 @@ pub extern fn __rust_allocate(size: usize, _align: usize) -> *mut u8 { unsafe { libc::malloc(size as libc::size_t) as *mut u8 } } +#[no_mangle] +pub extern fn __rust_allocate_zeroed(size: usize, _align: usize) -> *mut u8 { + unsafe { libc::calloc(size as libc::size_t, 1) as *mut u8 } +} + #[no_mangle] pub extern fn __rust_deallocate(ptr: *mut u8, _old_size: usize, _align: usize) { unsafe { libc::free(ptr as *mut libc::c_void) } diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 51e6f2f8bd7a6..9ec3535881341 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -23,6 +23,7 @@ use core::intrinsics::{min_align_of_val, size_of_val}; extern "C" { #[allocator] fn __rust_allocate(size: usize, align: usize) -> *mut u8; + fn __rust_allocate_zeroed(size: usize, align: usize) -> *mut u8; fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize); fn __rust_reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8; fn __rust_reallocate_inplace(ptr: *mut u8, @@ -59,6 +60,20 @@ pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { __rust_allocate(size, align) } +/// Return a pointer to `size` bytes of memory aligned to `align` and +/// initialized to zeroes. +/// +/// On failure, return a null pointer. +/// +/// Behavior is undefined if the requested size is 0 or the alignment is not a +/// power of 2. The alignment must be no larger than the largest supported page +/// size on the platform. +#[inline] +pub unsafe fn allocate_zeroed(size: usize, align: usize) -> *mut u8 { + check_size_and_alignment(size, align); + __rust_allocate_zeroed(size, align) +} + /// Resize the allocation referenced by `ptr` to `size` bytes. /// /// On failure, return a null pointer and leave the original allocation intact. @@ -162,6 +177,23 @@ mod tests { use boxed::Box; use heap; + #[test] + fn allocate_zeroed() { + unsafe { + let size = 1024; + let mut ptr = heap::allocate_zeroed(size, 1); + if ptr.is_null() { + ::oom() + } + let end = ptr.offset(size as isize); + while ptr < end { + assert_eq!(*ptr, 0); + ptr = ptr.offset(1); + } + heap::deallocate(ptr, size, 1); + } + } + #[test] fn basic_reallocate_inplace_noop() { unsafe { diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 357a2724e0020..54da527976715 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -82,6 +82,16 @@ impl RawVec { /// /// Aborts on OOM pub fn with_capacity(cap: usize) -> Self { + RawVec::allocate(cap, false) + } + + /// Like `with_capacity` but guarantees the buffer is zeroed. + pub fn with_capacity_zeroed(cap: usize) -> Self { + RawVec::allocate(cap, true) + } + + #[inline] + fn allocate(cap: usize, zeroed: bool) -> Self { unsafe { let elem_size = mem::size_of::(); @@ -93,7 +103,11 @@ impl RawVec { heap::EMPTY as *mut u8 } else { let align = mem::align_of::(); - let ptr = heap::allocate(alloc_size, align); + let ptr = if zeroed { + heap::allocate_zeroed(alloc_size, align) + } else { + heap::allocate(alloc_size, align) + }; if ptr.is_null() { oom() } diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index a7a67ef76d4f7..5b0aa176799c2 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -38,6 +38,10 @@ mod imp { target_os = "dragonfly", target_os = "windows"), link_name = "je_mallocx")] fn mallocx(size: size_t, flags: c_int) -> *mut c_void; + #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios", + target_os = "dragonfly", target_os = "windows"), + link_name = "je_calloc")] + fn calloc(size: size_t, flags: c_int) -> *mut c_void; #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios", target_os = "dragonfly", target_os = "windows"), link_name = "je_rallocx")] @@ -56,6 +60,8 @@ mod imp { fn nallocx(size: size_t, flags: c_int) -> size_t; } + const MALLOCX_ZERO: c_int = 0x40; + // The minimum alignment guaranteed by the architecture. This value is used to // add fast paths for low alignment values. In practice, the alignment is a // constant at the call site and the branch will be optimized out. @@ -91,6 +97,16 @@ mod imp { unsafe { mallocx(size as size_t, flags) as *mut u8 } } + #[no_mangle] + pub extern "C" fn __rust_allocate_zeroed(size: usize, align: usize) -> *mut u8 { + if align <= MIN_ALIGN { + unsafe { calloc(size as size_t, 1) as *mut u8 } + } else { + let flags = align_to_flags(align) | MALLOCX_ZERO; + unsafe { mallocx(size as size_t, flags) as *mut u8 } + } + } + #[no_mangle] pub extern "C" fn __rust_reallocate(ptr: *mut u8, _old_size: usize, @@ -135,6 +151,11 @@ mod imp { bogus() } + #[no_mangle] + pub extern "C" fn __rust_allocate_zeroed(_size: usize, _align: usize) -> *mut u8 { + bogus() + } + #[no_mangle] pub extern "C" fn __rust_reallocate(_ptr: *mut u8, _old_size: usize, diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index de2b75f62b68a..e012caf870a97 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -44,6 +44,11 @@ pub extern "C" fn __rust_allocate(size: usize, align: usize) -> *mut u8 { unsafe { imp::allocate(size, align) } } +#[no_mangle] +pub extern "C" fn __rust_allocate_zeroed(size: usize, align: usize) -> *mut u8 { + unsafe { imp::allocate_zeroed(size, align) } +} + #[no_mangle] pub extern "C" fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) { unsafe { imp::deallocate(ptr, old_size, align) } @@ -121,6 +126,18 @@ mod imp { } } + pub unsafe fn allocate_zeroed(size: usize, align: usize) -> *mut u8 { + if align <= MIN_ALIGN { + libc::calloc(size as libc::size_t, 1) as *mut u8 + } else { + let ptr = aligned_malloc(size, align); + if !ptr.is_null() { + ptr::write_bytes(ptr, 0, size); + } + ptr + } + } + pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 { if align <= MIN_ALIGN { libc::realloc(ptr as *mut libc::c_void, size as libc::size_t) as *mut u8 @@ -173,6 +190,8 @@ mod imp { #[repr(C)] struct Header(*mut u8); + + const HEAP_ZERO_MEMORY: DWORD = 0x00000008; const HEAP_REALLOC_IN_PLACE_ONLY: DWORD = 0x00000010; unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header { @@ -185,11 +204,12 @@ mod imp { aligned } - pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { + #[inline] + unsafe fn allocate_with_flags(size: usize, align: usize, flags: DWORD) -> *mut u8 { if align <= MIN_ALIGN { - HeapAlloc(GetProcessHeap(), 0, size as SIZE_T) as *mut u8 + HeapAlloc(GetProcessHeap(), flags, size as SIZE_T) as *mut u8 } else { - let ptr = HeapAlloc(GetProcessHeap(), 0, (size + align) as SIZE_T) as *mut u8; + let ptr = HeapAlloc(GetProcessHeap(), flags, (size + align) as SIZE_T) as *mut u8; if ptr.is_null() { return ptr; } @@ -197,6 +217,14 @@ mod imp { } } + pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { + allocate_with_flags(size, align, 0) + } + + pub unsafe fn allocate_zeroed(size: usize, align: usize) -> *mut u8 { + allocate_with_flags(size, align, HEAP_ZERO_MEMORY) + } + pub unsafe fn reallocate(ptr: *mut u8, _old_size: usize, size: usize, align: usize) -> *mut u8 { if align <= MIN_ALIGN { HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, size as SIZE_T) as *mut u8 diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index a717163f45ef5..a8514ece5c585 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -1369,9 +1369,38 @@ impl Vec { #[doc(hidden)] #[stable(feature = "rust1", since = "1.0.0")] pub fn from_elem(elem: T, n: usize) -> Vec { - let mut v = Vec::with_capacity(n); - v.extend_with_element(n, elem); - v + ::from_elem(elem, n) +} + +// Specialization trait used for Vec::from_elem +trait SpecFromElem: Sized { + fn from_elem(elem: Self, n: usize) -> Vec; +} + +impl SpecFromElem for T { + default fn from_elem(elem: Self, n: usize) -> Vec { + let mut v = Vec::with_capacity(n); + v.extend_with_element(n, elem); + v + } +} + +impl SpecFromElem for u8 { + #[inline] + fn from_elem(elem: u8, n: usize) -> Vec { + if elem == 0 { + return Vec { + buf: RawVec::with_capacity_zeroed(n), + len: n, + } + } + unsafe { + let mut v = Vec::with_capacity(n); + ptr::write_bytes(v.as_mut_ptr(), elem, n); + v.set_len(n); + v + } + } } //////////////////////////////////////////////////////////////////////////////// From ae630ca35e5cd62004dcbb464bdfa28b926fd04d Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Fri, 10 Mar 2017 08:19:42 -0800 Subject: [PATCH 41/54] Specialize Vec::from_elem for other integer types --- src/libcollections/lib.rs | 1 + src/libcollections/vec.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index f88bdd0ecf382..81e5ba6a604db 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -42,6 +42,7 @@ #![feature(fused)] #![feature(generic_param_attrs)] #![feature(heap_api)] +#![feature(i128_type)] #![feature(inclusive_range)] #![feature(lang_items)] #![feature(nonzero)] diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index a8514ece5c585..09bbf354eae79 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -1403,6 +1403,38 @@ impl SpecFromElem for u8 { } } +macro_rules! impl_spec_from_elem_int { + ($t: ty) => { + impl SpecFromElem for $t { + #[inline] + fn from_elem(elem: $t, n: usize) -> Vec<$t> { + if elem == 0 { + return Vec { + buf: RawVec::with_capacity_zeroed(n), + len: n, + } + } + let mut v = Vec::with_capacity(n); + v.extend_with_element(n, elem); + v + } + } + } +} + +impl_spec_from_elem_int!(i8); +impl_spec_from_elem_int!(i16); +impl_spec_from_elem_int!(i32); +impl_spec_from_elem_int!(i64); +impl_spec_from_elem_int!(i128); +impl_spec_from_elem_int!(isize); + +impl_spec_from_elem_int!(u16); +impl_spec_from_elem_int!(u32); +impl_spec_from_elem_int!(u64); +impl_spec_from_elem_int!(u128); +impl_spec_from_elem_int!(usize); + //////////////////////////////////////////////////////////////////////////////// // Common trait implementations for Vec //////////////////////////////////////////////////////////////////////////////// From e4af5fd825db387d79bf1a1885ad4e63ac964661 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 15 Feb 2017 07:57:59 -0800 Subject: [PATCH 42/54] Remove internal liblog This commit deletes the internal liblog in favor of the implementation that lives on crates.io. Similarly it's also setting a convention for adding crates to the compiler. The main restriction right now is that we want compiler implementation details to be unreachable from normal Rust code (e.g. requires a feature), and by default everything in the sysroot is reachable via `extern crate`. The proposal here is to require that crates pulled in have these lines in their `src/lib.rs`: #![cfg_attr(rustbuild, feature(staged_api, rustc_private))] #![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))] This'll mean that by default they're not using these attributes but when compiled as part of the compiler they do a few things: * Mark themselves as entirely unstable via the `staged_api` feature and the `#![unstable]` attribute. * Allow usage of other unstable crates via `feature(rustc_private)` which is required if the crate relies on any other crates to compile (other than std). --- src/Cargo.lock | 53 +- src/bootstrap/bin/rustc.rs | 4 + src/bootstrap/lib.rs | 13 + src/liblog/Cargo.toml | 9 - src/liblog/directive.rs | 193 ------- src/liblog/lib.rs | 506 ------------------ src/liblog/macros.rs | 205 ------- src/librustc/Cargo.toml | 2 +- src/librustc/hir/map/mod.rs | 2 +- src/librustc_back/Cargo.toml | 2 +- src/librustc_borrowck/Cargo.toml | 2 +- src/librustc_const_eval/Cargo.toml | 2 +- src/librustc_data_structures/Cargo.toml | 2 +- src/librustc_driver/Cargo.toml | 3 +- src/librustc_driver/driver.rs | 4 +- src/librustc_driver/lib.rs | 2 + src/librustc_incremental/Cargo.toml | 2 +- src/librustc_lint/Cargo.toml | 2 +- src/librustc_metadata/Cargo.toml | 2 +- src/librustc_metadata/creader.rs | 2 +- src/librustc_mir/Cargo.toml | 2 +- src/librustc_passes/Cargo.toml | 4 +- src/librustc_resolve/Cargo.toml | 2 +- src/librustc_save_analysis/Cargo.toml | 4 +- src/librustc_trans/Cargo.toml | 2 +- src/librustc_typeck/Cargo.toml | 2 +- src/librustdoc/Cargo.toml | 5 +- src/librustdoc/lib.rs | 2 + src/libsyntax/Cargo.toml | 2 +- src/libsyntax_ext/Cargo.toml | 2 +- .../auxiliary/logging_right_crate.rs | 18 - .../logging-enabled-debug.rs | 24 - src/test/run-pass-fulldeps/logging-enabled.rs | 27 - .../run-pass-fulldeps/logging-right-crate.rs | 31 -- .../logging-separate-lines.rs | 40 -- src/test/run-pass-fulldeps/rust-log-filter.rs | 58 -- src/tools/compiletest/Cargo.toml | 2 +- 37 files changed, 70 insertions(+), 1169 deletions(-) delete mode 100644 src/liblog/Cargo.toml delete mode 100644 src/liblog/directive.rs delete mode 100644 src/liblog/lib.rs delete mode 100644 src/liblog/macros.rs delete mode 100644 src/test/run-pass-fulldeps/auxiliary/logging_right_crate.rs delete mode 100644 src/test/run-pass-fulldeps/logging-enabled-debug.rs delete mode 100644 src/test/run-pass-fulldeps/logging-enabled.rs delete mode 100644 src/test/run-pass-fulldeps/logging-right-crate.rs delete mode 100644 src/test/run-pass-fulldeps/logging-separate-lines.rs delete mode 100644 src/test/run-pass-fulldeps/rust-log-filter.rs diff --git a/src/Cargo.lock b/src/Cargo.lock index b34007db8ac7a..89f455319c335 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -137,7 +137,7 @@ dependencies = [ name = "compiletest" version = "0.0.0" dependencies = [ - "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", @@ -152,14 +152,6 @@ name = "dtoa" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "env_logger" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "env_logger" version = "0.4.2" @@ -260,10 +252,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "linkchecker" version = "0.1.0" -[[package]] -name = "log" -version = "0.0.0" - [[package]] name = "log" version = "0.3.7" @@ -412,7 +400,7 @@ dependencies = [ "arena 0.0.0", "fmt_macros 0.0.0", "graphviz 0.0.0", - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_back 0.0.0", "rustc_bitflags 0.0.0", "rustc_const_math 0.0.0", @@ -452,7 +440,7 @@ dependencies = [ name = "rustc_back" version = "0.0.0" dependencies = [ - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "serialize 0.0.0", "syntax 0.0.0", ] @@ -466,7 +454,7 @@ name = "rustc_borrowck" version = "0.0.0" dependencies = [ "graphviz 0.0.0", - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", @@ -481,7 +469,7 @@ version = "0.0.0" dependencies = [ "arena 0.0.0", "graphviz 0.0.0", - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_back 0.0.0", "rustc_const_math 0.0.0", @@ -503,7 +491,7 @@ dependencies = [ name = "rustc_data_structures" version = "0.0.0" dependencies = [ - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "serialize 0.0.0", ] @@ -512,8 +500,9 @@ name = "rustc_driver" version = "0.0.0" dependencies = [ "arena 0.0.0", + "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "graphviz 0.0.0", - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "proc_macro_plugin 0.0.0", "rustc 0.0.0", "rustc_back 0.0.0", @@ -552,7 +541,7 @@ name = "rustc_incremental" version = "0.0.0" dependencies = [ "graphviz 0.0.0", - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_data_structures 0.0.0", "serialize 0.0.0", @@ -564,7 +553,7 @@ dependencies = [ name = "rustc_lint" version = "0.0.0" dependencies = [ - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_back 0.0.0", "rustc_const_eval 0.0.0", @@ -596,7 +585,7 @@ name = "rustc_metadata" version = "0.0.0" dependencies = [ "flate 0.0.0", - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "proc_macro 0.0.0", "rustc 0.0.0", "rustc_back 0.0.0", @@ -615,7 +604,7 @@ name = "rustc_mir" version = "0.0.0" dependencies = [ "graphviz 0.0.0", - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_bitflags 0.0.0", "rustc_const_eval 0.0.0", @@ -639,7 +628,7 @@ dependencies = [ name = "rustc_passes" version = "0.0.0" dependencies = [ - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_const_eval 0.0.0", "rustc_const_math 0.0.0", @@ -678,7 +667,7 @@ name = "rustc_resolve" version = "0.0.0" dependencies = [ "arena 0.0.0", - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_errors 0.0.0", "syntax 0.0.0", @@ -689,7 +678,7 @@ dependencies = [ name = "rustc_save_analysis" version = "0.0.0" dependencies = [ - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "serialize 0.0.0", "syntax 0.0.0", @@ -701,7 +690,7 @@ name = "rustc_trans" version = "0.0.0" dependencies = [ "flate 0.0.0", - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_back 0.0.0", "rustc_bitflags 0.0.0", @@ -733,7 +722,7 @@ version = "0.0.0" dependencies = [ "arena 0.0.0", "fmt_macros 0.0.0", - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_back 0.0.0", "rustc_const_eval 0.0.0", @@ -751,8 +740,9 @@ version = "0.0.0" dependencies = [ "arena 0.0.0", "build_helper 0.1.0", + "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_back 0.0.0", "rustc_const_eval 0.0.0", @@ -828,7 +818,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "syntax" version = "0.0.0" dependencies = [ - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_bitflags 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", @@ -841,7 +831,7 @@ name = "syntax_ext" version = "0.0.0" dependencies = [ "fmt_macros 0.0.0", - "log 0.0.0", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "proc_macro 0.0.0", "rustc_errors 0.0.0", "syntax 0.0.0", @@ -966,7 +956,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum clap 2.20.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7db281b0520e97fbd15cd615dcd8f8bcad0c26f5f7d5effe705f090f39e9a758" "checksum cmake 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "e1acc68a3f714627af38f9f5d09706a28584ba60dfe2cca68f40bf779f941b25" "checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90" -"checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" "checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83" "checksum filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5363ab8e4139b8568a6237db5248646e5a8a2f89bd5ccb02092182b11fd3e922" "checksum gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)" = "c07c758b972368e703a562686adb39125707cc1ef3399da8c019fc6c2498a75d" diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index ba85e81ff4fc4..a0facf5f0f46c 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -94,6 +94,10 @@ fn main() { cmd.arg("-Cprefer-dynamic"); } + if env::var_os("RUSTC_PASS_RUSTBUILD_FLAG").is_some() { + cmd.arg("--cfg").arg("rustbuild"); + } + // Help the libc crate compile by assisting it in finding the MUSL // native libraries. if let Some(s) = env::var_os("MUSL_ROOT") { diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 270cb8490d9a7..8e773102b9c42 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -499,6 +499,19 @@ impl Build { .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_libdir(compiler)); } + // Most of the time we want to pass `--cfg rustbuild` which will flag + // upstream crates.io crates to compile themselves as unstable as we're + // going to put them in the sysroot. If we're compiling tools, however, + // we don't want to do that as the upstream crates.io deps may be in the + // crate graph and we don't want to compile them with their unstable + // versions. + match mode { + Mode::Tool => {} + _ => { + cargo.env("RUSTC_PASS_RUSTBUILD_FLAG", "1"); + } + } + // Ignore incremental modes except for stage0, since we're // not guaranteeing correctness acros builds if the compiler // is changing under your feet.` diff --git a/src/liblog/Cargo.toml b/src/liblog/Cargo.toml deleted file mode 100644 index 31a862478d034..0000000000000 --- a/src/liblog/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "log" -version = "0.0.0" - -[lib] -name = "log" -path = "lib.rs" -crate-type = ["dylib", "rlib"] diff --git a/src/liblog/directive.rs b/src/liblog/directive.rs deleted file mode 100644 index eb50d6e6135ef..0000000000000 --- a/src/liblog/directive.rs +++ /dev/null @@ -1,193 +0,0 @@ -// 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. - -use std::ascii::AsciiExt; -use std::cmp; - -#[derive(Debug, Clone)] -pub struct LogDirective { - pub name: Option, - pub level: u32, -} - -pub const LOG_LEVEL_NAMES: [&'static str; 5] = ["ERROR", "WARN", "INFO", "DEBUG", "TRACE"]; - -/// Parse an individual log level that is either a number or a symbolic log level -fn parse_log_level(level: &str) -> Option { - level.parse::() - .ok() - .or_else(|| { - let pos = LOG_LEVEL_NAMES.iter().position(|&name| name.eq_ignore_ascii_case(level)); - pos.map(|p| p as u32 + 1) - }) - .map(|p| cmp::min(p, ::MAX_LOG_LEVEL)) -} - -/// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1/foo") -/// and return a vector with log directives. -/// -/// Valid log levels are 0-255, with the most likely ones being 1-4 (defined in -/// std::). Also supports string log levels of error, warn, info, and debug -pub fn parse_logging_spec(spec: &str) -> (Vec, Option) { - let mut dirs = Vec::new(); - - let mut parts = spec.split('/'); - let mods = parts.next(); - let filter = parts.next(); - if parts.next().is_some() { - println!("warning: invalid logging spec '{}', ignoring it (too many '/'s)", - spec); - return (dirs, None); - } - if let Some(m) = mods { - for s in m.split(',') { - if s.is_empty() { - continue; - } - let mut parts = s.split('='); - let (log_level, name) = - match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) { - (Some(part0), None, None) => { - // if the single argument is a log-level string or number, - // treat that as a global fallback - match parse_log_level(part0) { - Some(num) => (num, None), - None => (::MAX_LOG_LEVEL, Some(part0)), - } - } - (Some(part0), Some(""), None) => (::MAX_LOG_LEVEL, Some(part0)), - (Some(part0), Some(part1), None) => { - match parse_log_level(part1) { - Some(num) => (num, Some(part0)), - _ => { - println!("warning: invalid logging spec '{}', ignoring it", part1); - continue; - } - } - } - _ => { - println!("warning: invalid logging spec '{}', ignoring it", s); - continue; - } - }; - dirs.push(LogDirective { - name: name.map(str::to_owned), - level: log_level, - }); - } - } - - (dirs, filter.map(str::to_owned)) -} - -#[cfg(test)] -mod tests { - use super::parse_logging_spec; - - #[test] - fn parse_logging_spec_valid() { - let (dirs, filter) = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4"); - assert_eq!(dirs.len(), 3); - assert_eq!(dirs[0].name, Some("crate1::mod1".to_owned())); - assert_eq!(dirs[0].level, 1); - - assert_eq!(dirs[1].name, Some("crate1::mod2".to_owned())); - assert_eq!(dirs[1].level, ::MAX_LOG_LEVEL); - - assert_eq!(dirs[2].name, Some("crate2".to_owned())); - assert_eq!(dirs[2].level, 4); - assert!(filter.is_none()); - } - - #[test] - fn parse_logging_spec_invalid_crate() { - // test parse_logging_spec with multiple = in specification - let (dirs, filter) = parse_logging_spec("crate1::mod1=1=2,crate2=4"); - assert_eq!(dirs.len(), 1); - assert_eq!(dirs[0].name, Some("crate2".to_owned())); - assert_eq!(dirs[0].level, 4); - assert!(filter.is_none()); - } - - #[test] - fn parse_logging_spec_invalid_log_level() { - // test parse_logging_spec with 'noNumber' as log level - let (dirs, filter) = parse_logging_spec("crate1::mod1=noNumber,crate2=4"); - assert_eq!(dirs.len(), 1); - assert_eq!(dirs[0].name, Some("crate2".to_owned())); - assert_eq!(dirs[0].level, 4); - assert!(filter.is_none()); - } - - #[test] - fn parse_logging_spec_string_log_level() { - // test parse_logging_spec with 'warn' as log level - let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2=warn"); - assert_eq!(dirs.len(), 1); - assert_eq!(dirs[0].name, Some("crate2".to_owned())); - assert_eq!(dirs[0].level, ::WARN); - assert!(filter.is_none()); - } - - #[test] - fn parse_logging_spec_empty_log_level() { - // test parse_logging_spec with '' as log level - let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2="); - assert_eq!(dirs.len(), 1); - assert_eq!(dirs[0].name, Some("crate2".to_owned())); - assert_eq!(dirs[0].level, ::MAX_LOG_LEVEL); - assert!(filter.is_none()); - } - - #[test] - fn parse_logging_spec_global() { - // test parse_logging_spec with no crate - let (dirs, filter) = parse_logging_spec("warn,crate2=4"); - assert_eq!(dirs.len(), 2); - assert_eq!(dirs[0].name, None); - assert_eq!(dirs[0].level, 2); - assert_eq!(dirs[1].name, Some("crate2".to_owned())); - assert_eq!(dirs[1].level, 4); - assert!(filter.is_none()); - } - - #[test] - fn parse_logging_spec_valid_filter() { - let (dirs, filter) = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4/abc"); - assert_eq!(dirs.len(), 3); - assert_eq!(dirs[0].name, Some("crate1::mod1".to_owned())); - assert_eq!(dirs[0].level, 1); - - assert_eq!(dirs[1].name, Some("crate1::mod2".to_owned())); - assert_eq!(dirs[1].level, ::MAX_LOG_LEVEL); - - assert_eq!(dirs[2].name, Some("crate2".to_owned())); - assert_eq!(dirs[2].level, 4); - assert!(filter.is_some() && filter.unwrap().to_owned() == "abc"); - } - - #[test] - fn parse_logging_spec_invalid_crate_filter() { - let (dirs, filter) = parse_logging_spec("crate1::mod1=1=2,crate2=4/a.c"); - assert_eq!(dirs.len(), 1); - assert_eq!(dirs[0].name, Some("crate2".to_owned())); - assert_eq!(dirs[0].level, 4); - assert!(filter.is_some() && filter.unwrap().to_owned() == "a.c"); - } - - #[test] - fn parse_logging_spec_empty_with_filter() { - let (dirs, filter) = parse_logging_spec("crate1/a*c"); - assert_eq!(dirs.len(), 1); - assert_eq!(dirs[0].name, Some("crate1".to_owned())); - assert_eq!(dirs[0].level, ::MAX_LOG_LEVEL); - assert!(filter.is_some() && filter.unwrap().to_owned() == "a*c"); - } -} diff --git a/src/liblog/lib.rs b/src/liblog/lib.rs deleted file mode 100644 index 057df647c7257..0000000000000 --- a/src/liblog/lib.rs +++ /dev/null @@ -1,506 +0,0 @@ -// 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. - -//! Utilities for program-wide and customizable logging -//! -//! # Examples -//! -//! ``` -//! # #![feature(rustc_private)] -//! #[macro_use] extern crate log; -//! -//! fn main() { -//! debug!("this is a debug {:?}", "message"); -//! error!("this is printed by default"); -//! -//! if log_enabled!(log::INFO) { -//! let x = 3 * 4; // expensive computation -//! info!("the answer was: {:?}", x); -//! } -//! } -//! ``` -//! -//! Assumes the binary is `main`: -//! -//! ```{.bash} -//! $ RUST_LOG=error ./main -//! ERROR:main: this is printed by default -//! ``` -//! -//! ```{.bash} -//! $ RUST_LOG=info ./main -//! ERROR:main: this is printed by default -//! INFO:main: the answer was: 12 -//! ``` -//! -//! ```{.bash} -//! $ RUST_LOG=debug ./main -//! DEBUG:main: this is a debug message -//! ERROR:main: this is printed by default -//! INFO:main: the answer was: 12 -//! ``` -//! -//! You can also set the log level on a per module basis: -//! -//! ```{.bash} -//! $ RUST_LOG=main=info ./main -//! ERROR:main: this is printed by default -//! INFO:main: the answer was: 12 -//! ``` -//! -//! And enable all logging: -//! -//! ```{.bash} -//! $ RUST_LOG=main ./main -//! DEBUG:main: this is a debug message -//! ERROR:main: this is printed by default -//! INFO:main: the answer was: 12 -//! ``` -//! -//! # Logging Macros -//! -//! There are five macros that the logging subsystem uses: -//! -//! * `log!(level, ...)` - the generic logging macro, takes a level as a u32 and any -//! related `format!` arguments -//! * `debug!(...)` - a macro hard-wired to the log level of `DEBUG` -//! * `info!(...)` - a macro hard-wired to the log level of `INFO` -//! * `warn!(...)` - a macro hard-wired to the log level of `WARN` -//! * `error!(...)` - a macro hard-wired to the log level of `ERROR` -//! -//! All of these macros use the same style of syntax as the `format!` syntax -//! extension. Details about the syntax can be found in the documentation of -//! `std::fmt` along with the Rust tutorial/manual. -//! -//! If you want to check at runtime if a given logging level is enabled (e.g. if the -//! information you would want to log is expensive to produce), you can use the -//! following macro: -//! -//! * `log_enabled!(level)` - returns true if logging of the given level is enabled -//! -//! # Enabling logging -//! -//! Log levels are controlled on a per-module basis, and by default all logging is -//! disabled except for `error!` (a log level of 1). Logging is controlled via the -//! `RUST_LOG` environment variable. The value of this environment variable is a -//! comma-separated list of logging directives. A logging directive is of the form: -//! -//! ```text -//! path::to::module=log_level -//! ``` -//! -//! The path to the module is rooted in the name of the crate it was compiled for, -//! so if your program is contained in a file `hello.rs`, for example, to turn on -//! logging for this file you would use a value of `RUST_LOG=hello`. -//! Furthermore, this path is a prefix-search, so all modules nested in the -//! specified module will also have logging enabled. -//! -//! The actual `log_level` is optional to specify. If omitted, all logging will be -//! enabled. If specified, the it must be either a numeric in the range of 1-255, or -//! it must be one of the strings `debug`, `error`, `info`, or `warn`. If a numeric -//! is specified, then all logging less than or equal to that numeral is enabled. -//! For example, if logging level 3 is active, error, warn, and info logs will be -//! printed, but debug will be omitted. -//! -//! As the log level for a module is optional, the module to enable logging for is -//! also optional. If only a `log_level` is provided, then the global log level for -//! all modules is set to this value. -//! -//! Some examples of valid values of `RUST_LOG` are: -//! -//! * `hello` turns on all logging for the 'hello' module -//! * `info` turns on all info logging -//! * `hello=debug` turns on debug logging for 'hello' -//! * `hello=3` turns on info logging for 'hello' -//! * `hello,std::option` turns on hello, and std's option logging -//! * `error,hello=warn` turn on global error logging and also warn for hello -//! -//! # Filtering results -//! -//! A RUST_LOG directive may include a string filter. The syntax is to append -//! `/` followed by a string. Each message is checked against the string and is -//! only logged if it contains the string. Note that the matching is done after -//! formatting the log string but before adding any logging meta-data. There is -//! a single filter for all modules. -//! -//! Some examples: -//! -//! * `hello/foo` turns on all logging for the 'hello' module where the log message -//! includes 'foo'. -//! * `info/f.o` turns on all info logging where the log message includes 'foo', -//! 'f1o', 'fao', etc. -//! * `hello=debug/foo*foo` turns on debug logging for 'hello' where the log -//! message includes 'foofoo' or 'fofoo' or 'fooooooofoo', etc. -//! * `error,hello=warn/[0-9] scopes` turn on global error logging and also warn for -//! hello. In both cases the log message must include a single digit number -//! followed by 'scopes' -//! -//! # Performance and Side Effects -//! -//! Each of these macros will expand to code similar to: -//! -//! ```rust,ignore -//! if log_level <= my_module_log_level() { -//! ::log::log(log_level, format!(...)); -//! } -//! ``` -//! -//! What this means is that each of these macros are very cheap at runtime if -//! they're turned off (just a load and an integer comparison). This also means that -//! if logging is disabled, none of the components of the log will be executed. - -#![crate_name = "log"] -#![unstable(feature = "rustc_private", - reason = "use the crates.io `log` library instead", - issue = "27812")] -#![crate_type = "rlib"] -#![crate_type = "dylib"] -#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", - html_favicon_url = "https://doc.rust-lang.org/favicon.ico", - html_root_url = "https://doc.rust-lang.org/nightly/", - html_playground_url = "https://play.rust-lang.org/", - test(attr(deny(warnings))))] -#![deny(missing_docs)] -#![deny(warnings)] - -#![feature(staged_api)] - -use std::cell::RefCell; -use std::fmt; -use std::io::{self, Stderr}; -use std::io::prelude::*; -use std::mem; -use std::env; -use std::slice; -use std::sync::{Mutex, ONCE_INIT, Once}; - -use directive::LOG_LEVEL_NAMES; - -#[macro_use] -pub mod macros; - -mod directive; - -/// Maximum logging level of a module that can be specified. Common logging -/// levels are found in the DEBUG/INFO/WARN/ERROR constants. -pub const MAX_LOG_LEVEL: u32 = 255; - -/// The default logging level of a crate if no other is specified. -const DEFAULT_LOG_LEVEL: u32 = 1; - -static mut LOCK: *mut Mutex<(Vec, Option)> = 0 as *mut _; - -/// An unsafe constant that is the maximum logging level of any module -/// specified. This is the first line of defense to determining whether a -/// logging statement should be run. -static mut LOG_LEVEL: u32 = MAX_LOG_LEVEL; - -/// Debug log level -pub const DEBUG: u32 = 4; -/// Info log level -pub const INFO: u32 = 3; -/// Warn log level -pub const WARN: u32 = 2; -/// Error log level -pub const ERROR: u32 = 1; - -thread_local! { - static LOCAL_LOGGER: RefCell>> = { - RefCell::new(None) - } -} - -/// A trait used to represent an interface to a thread-local logger. Each thread -/// can have its own custom logger which can respond to logging messages -/// however it likes. -pub trait Logger { - /// Logs a single message described by the `record`. - fn log(&mut self, record: &LogRecord); -} - -struct DefaultLogger { - handle: Stderr, -} - -/// Wraps the log level with fmt implementations. -#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] -pub struct LogLevel(pub u32); - -impl fmt::Display for LogLevel { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let LogLevel(level) = *self; - match LOG_LEVEL_NAMES.get(level as usize - 1) { - Some(ref name) => fmt::Display::fmt(name, fmt), - None => fmt::Display::fmt(&level, fmt), - } - } -} - -impl Logger for DefaultLogger { - fn log(&mut self, record: &LogRecord) { - match writeln!(&mut self.handle, - "{}:{}: {}", - record.level, - record.module_path, - record.args) { - Err(e) => panic!("failed to log: {:?}", e), - Ok(()) => {} - } - } -} - -impl Drop for DefaultLogger { - fn drop(&mut self) { - // FIXME(#12628): is panicking the right thing to do? - match self.handle.flush() { - Err(e) => panic!("failed to flush a logger: {:?}", e), - Ok(()) => {} - } - } -} - -/// This function is called directly by the compiler when using the logging -/// macros. This function does not take into account whether the log level -/// specified is active or not, it will always log something if this method is -/// called. -/// -/// It is not recommended to call this function directly, rather it should be -/// invoked through the logging family of macros. -#[doc(hidden)] -pub fn log(level: u32, loc: &'static LogLocation, args: fmt::Arguments) { - // Test the literal string from args against the current filter, if there - // is one. - unsafe { - let filter = (*LOCK).lock().unwrap(); - if let Some(ref filter) = filter.1 { - if !args.to_string().contains(filter) { - return; - } - } - } - - // Completely remove the local logger from TLS in case anyone attempts to - // frob the slot while we're doing the logging. This will destroy any logger - // set during logging. - let logger = LOCAL_LOGGER.with(|s| s.borrow_mut().take()); - let mut logger = logger.unwrap_or_else(|| Box::new(DefaultLogger { handle: io::stderr() })); - logger.log(&LogRecord { - level: LogLevel(level), - args: args, - file: loc.file, - module_path: loc.module_path, - line: loc.line, - }); - set_logger(logger); -} - -/// Getter for the global log level. This is a function so that it can be called -/// safely -#[doc(hidden)] -#[inline(always)] -pub fn log_level() -> u32 { - unsafe { LOG_LEVEL } -} - -/// Replaces the thread-local logger with the specified logger, returning the old -/// logger. -pub fn set_logger(logger: Box) -> Option> { - LOCAL_LOGGER.with(|slot| mem::replace(&mut *slot.borrow_mut(), Some(logger))) -} - -/// A LogRecord is created by the logging macros, and passed as the only -/// argument to Loggers. -#[derive(Debug)] -pub struct LogRecord<'a> { - /// The module path of where the LogRecord originated. - pub module_path: &'a str, - - /// The LogLevel of this record. - pub level: LogLevel, - - /// The arguments from the log line. - pub args: fmt::Arguments<'a>, - - /// The file of where the LogRecord originated. - pub file: &'a str, - - /// The line number of where the LogRecord originated. - pub line: u32, -} - -#[doc(hidden)] -#[derive(Copy, Clone)] -pub struct LogLocation { - pub module_path: &'static str, - pub file: &'static str, - pub line: u32, -} - -/// Tests whether a given module's name is enabled for a particular level of -/// logging. This is the second layer of defense about determining whether a -/// module's log statement should be emitted or not. -#[doc(hidden)] -pub fn mod_enabled(level: u32, module: &str) -> bool { - static INIT: Once = ONCE_INIT; - INIT.call_once(init); - - // It's possible for many threads are in this function, only one of them - // will perform the global initialization, but all of them will need to check - // again to whether they should really be here or not. Hence, despite this - // check being expanded manually in the logging macro, this function checks - // the log level again. - if level > unsafe { LOG_LEVEL } { - return false; - } - - // This assertion should never get tripped unless we're in an at_exit - // handler after logging has been torn down and a logging attempt was made. - - unsafe { - let directives = (*LOCK).lock().unwrap(); - enabled(level, module, directives.0.iter()) - } -} - -fn enabled(level: u32, module: &str, iter: slice::Iter) -> bool { - // Search for the longest match, the vector is assumed to be pre-sorted. - for directive in iter.rev() { - match directive.name { - Some(ref name) if !module.starts_with(&name[..]) => {} - Some(..) | None => return level <= directive.level, - } - } - level <= DEFAULT_LOG_LEVEL -} - -/// Initialize logging for the current process. -/// -/// This is not threadsafe at all, so initialization is performed through a -/// `Once` primitive (and this function is called from that primitive). -fn init() { - let (mut directives, filter) = match env::var("RUST_LOG") { - Ok(spec) => directive::parse_logging_spec(&spec[..]), - Err(..) => (Vec::new(), None), - }; - - // Sort the provided directives by length of their name, this allows a - // little more efficient lookup at runtime. - directives.sort_by(|a, b| { - let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0); - let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0); - alen.cmp(&blen) - }); - - let max_level = { - let max = directives.iter().max_by_key(|d| d.level); - max.map(|d| d.level).unwrap_or(DEFAULT_LOG_LEVEL) - }; - - unsafe { - LOG_LEVEL = max_level; - - assert!(LOCK.is_null()); - LOCK = Box::into_raw(Box::new(Mutex::new((directives, filter)))); - } -} - -#[cfg(test)] -mod tests { - use super::enabled; - use directive::LogDirective; - - #[test] - fn match_full_path() { - let dirs = [LogDirective { - name: Some("crate2".to_string()), - level: 3, - }, - LogDirective { - name: Some("crate1::mod1".to_string()), - level: 2, - }]; - assert!(enabled(2, "crate1::mod1", dirs.iter())); - assert!(!enabled(3, "crate1::mod1", dirs.iter())); - assert!(enabled(3, "crate2", dirs.iter())); - assert!(!enabled(4, "crate2", dirs.iter())); - } - - #[test] - fn no_match() { - let dirs = [LogDirective { - name: Some("crate2".to_string()), - level: 3, - }, - LogDirective { - name: Some("crate1::mod1".to_string()), - level: 2, - }]; - assert!(!enabled(2, "crate3", dirs.iter())); - } - - #[test] - fn match_beginning() { - let dirs = [LogDirective { - name: Some("crate2".to_string()), - level: 3, - }, - LogDirective { - name: Some("crate1::mod1".to_string()), - level: 2, - }]; - assert!(enabled(3, "crate2::mod1", dirs.iter())); - } - - #[test] - fn match_beginning_longest_match() { - let dirs = [LogDirective { - name: Some("crate2".to_string()), - level: 3, - }, - LogDirective { - name: Some("crate2::mod".to_string()), - level: 4, - }, - LogDirective { - name: Some("crate1::mod1".to_string()), - level: 2, - }]; - assert!(enabled(4, "crate2::mod1", dirs.iter())); - assert!(!enabled(4, "crate2", dirs.iter())); - } - - #[test] - fn match_default() { - let dirs = [LogDirective { - name: None, - level: 3, - }, - LogDirective { - name: Some("crate1::mod1".to_string()), - level: 2, - }]; - assert!(enabled(2, "crate1::mod1", dirs.iter())); - assert!(enabled(3, "crate2::mod2", dirs.iter())); - } - - #[test] - fn zero_level() { - let dirs = [LogDirective { - name: None, - level: 3, - }, - LogDirective { - name: Some("crate1::mod1".to_string()), - level: 0, - }]; - assert!(!enabled(1, "crate1::mod1", dirs.iter())); - assert!(enabled(3, "crate2::mod2", dirs.iter())); - } -} diff --git a/src/liblog/macros.rs b/src/liblog/macros.rs deleted file mode 100644 index 803a2df9ccc8b..0000000000000 --- a/src/liblog/macros.rs +++ /dev/null @@ -1,205 +0,0 @@ -// 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. - -//! Logging macros - -/// The standard logging macro -/// -/// This macro will generically log over a provided level (of type u32) with a -/// format!-based argument list. See documentation in `std::fmt` for details on -/// how to use the syntax. -/// -/// # Examples -/// -/// ``` -/// # #![feature(rustc_private)] -/// #[macro_use] extern crate log; -/// -/// fn main() { -/// log!(log::WARN, "this is a warning {}", "message"); -/// log!(log::DEBUG, "this is a debug message"); -/// log!(6, "this is a custom logging level: {level}", level=6); -/// } -/// ``` -/// -/// Assumes the binary is `main`: -/// -/// ```{.bash} -/// $ RUST_LOG=warn ./main -/// WARN:main: this is a warning message -/// ``` -/// -/// ```{.bash} -/// $ RUST_LOG=debug ./main -/// DEBUG:main: this is a debug message -/// WARN:main: this is a warning message -/// ``` -/// -/// ```{.bash} -/// $ RUST_LOG=6 ./main -/// DEBUG:main: this is a debug message -/// WARN:main: this is a warning message -/// 6:main: this is a custom logging level: 6 -/// ``` -#[macro_export] -macro_rules! log { - ($lvl:expr, $($arg:tt)+) => ({ - static LOC: ::log::LogLocation = ::log::LogLocation { - line: line!(), - file: file!(), - module_path: module_path!(), - }; - let lvl = $lvl; - if log_enabled!(lvl) { - ::log::log(lvl, &LOC, format_args!($($arg)+)) - } - }) -} - -/// A convenience macro for logging at the error log level. -/// -/// # Examples -/// -/// ``` -/// # #![feature(rustc_private)] -/// #[macro_use] extern crate log; -/// -/// fn main() { -/// let error = 3; -/// error!("the build has failed with error code: {}", error); -/// } -/// ``` -/// -/// Assumes the binary is `main`: -/// -/// ```{.bash} -/// $ RUST_LOG=error ./main -/// ERROR:main: the build has failed with error code: 3 -/// ``` -/// -#[macro_export] -macro_rules! error { - ($($arg:tt)*) => (log!(::log::ERROR, $($arg)*)) -} - -/// A convenience macro for logging at the warning log level. -/// -/// # Examples -/// -/// ``` -/// # #![feature(rustc_private)] -/// #[macro_use] extern crate log; -/// -/// fn main() { -/// let code = 3; -/// warn!("you may like to know that a process exited with: {}", code); -/// } -/// ``` -/// -/// Assumes the binary is `main`: -/// -/// ```{.bash} -/// $ RUST_LOG=warn ./main -/// WARN:main: you may like to know that a process exited with: 3 -/// ``` -#[macro_export] -macro_rules! warn { - ($($arg:tt)*) => (log!(::log::WARN, $($arg)*)) -} - -/// A convenience macro for logging at the info log level. -/// -/// # Examples -/// -/// ``` -/// # #![feature(rustc_private)] -/// #[macro_use] extern crate log; -/// -/// fn main() { -/// let ret = 3; -/// info!("this function is about to return: {}", ret); -/// } -/// ``` -/// -/// Assumes the binary is `main`: -/// -/// ```{.bash} -/// $ RUST_LOG=info ./main -/// INFO:main: this function is about to return: 3 -/// ``` -#[macro_export] -macro_rules! info { - ($($arg:tt)*) => (log!(::log::INFO, $($arg)*)) -} - -/// A convenience macro for logging at the debug log level. This macro will -/// be omitted at compile time in an optimized build unless `-C debug-assertions` -/// is passed to the compiler. -/// -/// # Examples -/// -/// ``` -/// # #![feature(rustc_private)] -/// #[macro_use] extern crate log; -/// -/// fn main() { -/// debug!("x = {x}, y = {y}", x=10, y=20); -/// } -/// ``` -/// -/// Assumes the binary is `main`: -/// -/// ```{.bash} -/// $ RUST_LOG=debug ./main -/// DEBUG:main: x = 10, y = 20 -/// ``` -#[macro_export] -macro_rules! debug { - ($($arg:tt)*) => (if cfg!(debug_assertions) { log!(::log::DEBUG, $($arg)*) }) -} - -/// A macro to test whether a log level is enabled for the current module. -/// -/// # Examples -/// -/// ``` -/// # #![feature(rustc_private)] -/// #[macro_use] extern crate log; -/// -/// struct Point { x: i32, y: i32 } -/// fn some_expensive_computation() -> Point { Point { x: 1, y: 2 } } -/// -/// fn main() { -/// if log_enabled!(log::DEBUG) { -/// let x = some_expensive_computation(); -/// debug!("x.x = {}, x.y = {}", x.x, x.y); -/// } -/// } -/// ``` -/// -/// Assumes the binary is `main`: -/// -/// ```{.bash} -/// $ RUST_LOG=error ./main -/// ``` -/// -/// ```{.bash} -/// $ RUST_LOG=debug ./main -/// DEBUG:main: x.x = 1, x.y = 2 -/// ``` -#[macro_export] -macro_rules! log_enabled { - ($lvl:expr) => ({ - let lvl = $lvl; - (lvl != ::log::DEBUG || cfg!(debug_assertions)) && - lvl <= ::log::log_level() && - ::log::mod_enabled(lvl, module_path!()) - }) -} diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml index 5d53c60ad7fdc..fa217acd9f9bf 100644 --- a/src/librustc/Cargo.toml +++ b/src/librustc/Cargo.toml @@ -12,7 +12,7 @@ crate-type = ["dylib"] arena = { path = "../libarena" } fmt_macros = { path = "../libfmt_macros" } graphviz = { path = "../libgraphviz" } -log = { path = "../liblog" } +log = "0.3" rustc_back = { path = "../librustc_back" } rustc_bitflags = { path = "../librustc_bitflags" } rustc_const_math = { path = "../librustc_const_math" } diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index 5d074903b2b99..2a938dadd28dc 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -948,7 +948,7 @@ pub fn map_crate<'hir>(forest: &'hir mut Forest, intravisit::walk_crate(&mut collector, &forest.krate); let map = collector.map; - if log_enabled!(::log::DEBUG) { + if log_enabled!(::log::LogLevel::Debug) { // This only makes sense for ordered stores; note the // enumerate to count the number of entries. let (entries_less_1, _) = map.iter().filter(|&x| { diff --git a/src/librustc_back/Cargo.toml b/src/librustc_back/Cargo.toml index 85e861b405a9f..730abc54568e1 100644 --- a/src/librustc_back/Cargo.toml +++ b/src/librustc_back/Cargo.toml @@ -11,7 +11,7 @@ crate-type = ["dylib"] [dependencies] syntax = { path = "../libsyntax" } serialize = { path = "../libserialize" } -log = { path = "../liblog" } +log = "0.3" [features] jemalloc = [] diff --git a/src/librustc_borrowck/Cargo.toml b/src/librustc_borrowck/Cargo.toml index d53318f176848..af99c0e938724 100644 --- a/src/librustc_borrowck/Cargo.toml +++ b/src/librustc_borrowck/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["dylib"] test = false [dependencies] -log = { path = "../liblog" } +log = "0.3" syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } graphviz = { path = "../libgraphviz" } diff --git a/src/librustc_const_eval/Cargo.toml b/src/librustc_const_eval/Cargo.toml index 780b2c16a32ec..907410f74dca4 100644 --- a/src/librustc_const_eval/Cargo.toml +++ b/src/librustc_const_eval/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["dylib"] [dependencies] arena = { path = "../libarena" } -log = { path = "../liblog" } +log = "0.3" rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } rustc_const_math = { path = "../librustc_const_math" } diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml index e2e16059d9871..343b1ed68b804 100644 --- a/src/librustc_data_structures/Cargo.toml +++ b/src/librustc_data_structures/Cargo.toml @@ -9,5 +9,5 @@ path = "lib.rs" crate-type = ["dylib"] [dependencies] -log = { path = "../liblog" } +log = "0.3" serialize = { path = "../libserialize" } diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index caa5c8b7e0058..5b5113caa8e8c 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -11,7 +11,8 @@ crate-type = ["dylib"] [dependencies] arena = { path = "../libarena" } graphviz = { path = "../libgraphviz" } -log = { path = "../liblog" } +log = { version = "0.3", features = ["release_max_level_info"] } +env_logger = { version = "0.4", default-features = false } proc_macro_plugin = { path = "../libproc_macro_plugin" } rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 2126a5a7c71bd..834240cb69797 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -198,13 +198,13 @@ pub fn compile_input(sess: &Session, result?; - if log_enabled!(::log::INFO) { + if log_enabled!(::log::LogLevel::Info) { println!("Pre-trans"); tcx.print_debug_stats(); } let trans = phase_4_translate_to_llvm(tcx, analysis, &incremental_hashes_map); - if log_enabled!(::log::INFO) { + if log_enabled!(::log::LogLevel::Info) { println!("Post-trans"); tcx.print_debug_stats(); } diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 62d7512655728..68b9f85721ad5 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -35,6 +35,7 @@ extern crate arena; extern crate getopts; extern crate graphviz; +extern crate env_logger; extern crate libc; extern crate rustc; extern crate rustc_back; @@ -1127,6 +1128,7 @@ pub fn diagnostics_registry() -> errors::registry::Registry { } pub fn main() { + env_logger::init().unwrap(); let result = run(|| run_compiler(&env::args().collect::>(), &mut RustcDefaultCalls, None, diff --git a/src/librustc_incremental/Cargo.toml b/src/librustc_incremental/Cargo.toml index e3ee752754504..7bf2efa4b885f 100644 --- a/src/librustc_incremental/Cargo.toml +++ b/src/librustc_incremental/Cargo.toml @@ -13,6 +13,6 @@ graphviz = { path = "../libgraphviz" } rustc = { path = "../librustc" } rustc_data_structures = { path = "../librustc_data_structures" } serialize = { path = "../libserialize" } -log = { path = "../liblog" } +log = "0.3" syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_lint/Cargo.toml b/src/librustc_lint/Cargo.toml index 4d5c0d7ba0ae1..c3c5461ff7c50 100644 --- a/src/librustc_lint/Cargo.toml +++ b/src/librustc_lint/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["dylib"] test = false [dependencies] -log = { path = "../liblog" } +log = "0.3" rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } rustc_const_eval = { path = "../librustc_const_eval" } diff --git a/src/librustc_metadata/Cargo.toml b/src/librustc_metadata/Cargo.toml index 6f7f03ca216b9..e8b906092730e 100644 --- a/src/librustc_metadata/Cargo.toml +++ b/src/librustc_metadata/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["dylib"] [dependencies] flate = { path = "../libflate" } -log = { path = "../liblog" } +log = "0.3" proc_macro = { path = "../libproc_macro" } rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 63c14a0035f1a..ad9da4047ed65 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -1055,7 +1055,7 @@ impl<'a> middle::cstore::CrateLoader for CrateLoader<'a> { self.inject_allocator_crate(); self.inject_panic_runtime(krate); - if log_enabled!(log::INFO) { + if log_enabled!(log::LogLevel::Info) { dump_crates(&self.cstore); } diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index 531be0b6ae9f5..6e42e02d5109b 100644 --- a/src/librustc_mir/Cargo.toml +++ b/src/librustc_mir/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["dylib"] [dependencies] graphviz = { path = "../libgraphviz" } -log = { path = "../liblog" } +log = "0.3" rustc = { path = "../librustc" } rustc_const_eval = { path = "../librustc_const_eval" } rustc_const_math = { path = "../librustc_const_math" } diff --git a/src/librustc_passes/Cargo.toml b/src/librustc_passes/Cargo.toml index cc710e0ac3563..d2560c2f8203f 100644 --- a/src/librustc_passes/Cargo.toml +++ b/src/librustc_passes/Cargo.toml @@ -9,10 +9,10 @@ path = "lib.rs" crate-type = ["dylib"] [dependencies] -log = { path = "../liblog" } +log = "0.3" rustc = { path = "../librustc" } rustc_const_eval = { path = "../librustc_const_eval" } rustc_const_math = { path = "../librustc_const_math" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } -rustc_errors = { path = "../librustc_errors" } \ No newline at end of file +rustc_errors = { path = "../librustc_errors" } diff --git a/src/librustc_resolve/Cargo.toml b/src/librustc_resolve/Cargo.toml index 5ce4c74e735fd..0968ea31b754f 100644 --- a/src/librustc_resolve/Cargo.toml +++ b/src/librustc_resolve/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["dylib"] test = false [dependencies] -log = { path = "../liblog" } +log = "0.3" syntax = { path = "../libsyntax" } rustc = { path = "../librustc" } arena = { path = "../libarena" } diff --git a/src/librustc_save_analysis/Cargo.toml b/src/librustc_save_analysis/Cargo.toml index 3d66e5a300787..3d12df19acbe2 100644 --- a/src/librustc_save_analysis/Cargo.toml +++ b/src/librustc_save_analysis/Cargo.toml @@ -9,8 +9,8 @@ path = "lib.rs" crate-type = ["dylib"] [dependencies] -log = { path = "../liblog" } +log = "0.3" rustc = { path = "../librustc" } syntax = { path = "../libsyntax" } serialize = { path = "../libserialize" } -syntax_pos = { path = "../libsyntax_pos" } \ No newline at end of file +syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_trans/Cargo.toml b/src/librustc_trans/Cargo.toml index b5c67ad998b69..07dcb2fc29dc6 100644 --- a/src/librustc_trans/Cargo.toml +++ b/src/librustc_trans/Cargo.toml @@ -11,7 +11,7 @@ test = false [dependencies] flate = { path = "../libflate" } -log = { path = "../liblog" } +log = "0.3" rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } rustc_bitflags = { path = "../librustc_bitflags" } diff --git a/src/librustc_typeck/Cargo.toml b/src/librustc_typeck/Cargo.toml index f08d26373e50e..07998aa4a30ea 100644 --- a/src/librustc_typeck/Cargo.toml +++ b/src/librustc_typeck/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["dylib"] test = false [dependencies] -log = { path = "../liblog" } +log = "0.3" syntax = { path = "../libsyntax" } arena = { path = "../libarena" } fmt_macros = { path = "../libfmt_macros" } diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 93c0bd6d6d836..1c479ce1d0157 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -11,11 +11,13 @@ crate-type = ["dylib"] [dependencies] arena = { path = "../libarena" } +env_logger = { version = "0.4", default-features = false } +log = "0.3" rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } rustc_const_eval = { path = "../librustc_const_eval" } -rustc_driver = { path = "../librustc_driver" } rustc_data_structures = { path = "../librustc_data_structures" } +rustc_driver = { path = "../librustc_driver" } rustc_errors = { path = "../librustc_errors" } rustc_lint = { path = "../librustc_lint" } rustc_metadata = { path = "../librustc_metadata" } @@ -24,7 +26,6 @@ rustc_trans = { path = "../librustc_trans" } serialize = { path = "../libserialize" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } -log = { path = "../liblog" } [build-dependencies] build_helper = { path = "../build_helper" } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 84f69cd35045c..8dd03f6edc4d5 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -30,6 +30,7 @@ extern crate arena; extern crate getopts; +extern crate env_logger; extern crate libc; extern crate rustc; extern crate rustc_const_eval; @@ -99,6 +100,7 @@ struct Output { pub fn main() { const STACK_SIZE: usize = 32_000_000; // 32MB + env_logger::init().unwrap(); let res = std::thread::Builder::new().stack_size(STACK_SIZE).spawn(move || { let s = env::args().collect::>(); main_args(&s) diff --git a/src/libsyntax/Cargo.toml b/src/libsyntax/Cargo.toml index 0b38f5450b63f..97d37266130af 100644 --- a/src/libsyntax/Cargo.toml +++ b/src/libsyntax/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["dylib"] [dependencies] serialize = { path = "../libserialize" } -log = { path = "../liblog" } +log = "0.3" rustc_bitflags = { path = "../librustc_bitflags" } syntax_pos = { path = "../libsyntax_pos" } rustc_errors = { path = "../librustc_errors" } diff --git a/src/libsyntax_ext/Cargo.toml b/src/libsyntax_ext/Cargo.toml index 960db792a623e..bdcec26cb838b 100644 --- a/src/libsyntax_ext/Cargo.toml +++ b/src/libsyntax_ext/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["dylib"] [dependencies] fmt_macros = { path = "../libfmt_macros" } -log = { path = "../liblog" } +log = "0.3" proc_macro = { path = "../libproc_macro" } rustc_errors = { path = "../librustc_errors" } syntax = { path = "../libsyntax" } diff --git a/src/test/run-pass-fulldeps/auxiliary/logging_right_crate.rs b/src/test/run-pass-fulldeps/auxiliary/logging_right_crate.rs deleted file mode 100644 index db26b10fc67cb..0000000000000 --- a/src/test/run-pass-fulldeps/auxiliary/logging_right_crate.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2013-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. - -#![feature(rustc_private)] - -#[macro_use] extern crate log; - -pub fn foo() { - fn death() -> isize { panic!() } - debug!("{}", (||{ death() })()); -} diff --git a/src/test/run-pass-fulldeps/logging-enabled-debug.rs b/src/test/run-pass-fulldeps/logging-enabled-debug.rs deleted file mode 100644 index 3ae4884ce47fc..0000000000000 --- a/src/test/run-pass-fulldeps/logging-enabled-debug.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2013-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. - -// compile-flags:-C debug-assertions=no -// exec-env:RUST_LOG=logging-enabled-debug=debug - - -#![feature(rustc_private)] - -#[macro_use] -extern crate log; - -pub fn main() { - if log_enabled!(log::DEBUG) { - panic!("what?! debugging?"); - } -} diff --git a/src/test/run-pass-fulldeps/logging-enabled.rs b/src/test/run-pass-fulldeps/logging-enabled.rs deleted file mode 100644 index 26261348020f8..0000000000000 --- a/src/test/run-pass-fulldeps/logging-enabled.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2013-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. - -// exec-env:RUST_LOG=logging_enabled=info -// ignore-emscripten: FIXME(#31622) - - -#![feature(rustc_private)] - -#[macro_use] -extern crate log; - -pub fn main() { - if log_enabled!(log::DEBUG) { - panic!("what?! debugging?"); - } - if !log_enabled!(log::INFO) { - panic!("what?! no info?"); - } -} diff --git a/src/test/run-pass-fulldeps/logging-right-crate.rs b/src/test/run-pass-fulldeps/logging-right-crate.rs deleted file mode 100644 index 7caeeb401244b..0000000000000 --- a/src/test/run-pass-fulldeps/logging-right-crate.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2013-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. - -// aux-build:logging_right_crate.rs -// exec-env:RUST_LOG=logging-right-crate=debug - -// This is a test for issue #3046 to make sure that when we monomorphize a -// function from one crate to another the right top-level logging name is -// preserved. -// -// It used to be the case that if logging were turned on for this crate, all -// monomorphized functions from other crates had logging turned on (their -// logging module names were all incorrect). This test ensures that this no -// longer happens by enabling logging for *this* crate and then invoking a -// function in an external crate which will panic when logging is enabled. - -// pretty-expanded FIXME #23616 - -extern crate logging_right_crate; - -pub fn main() { - // this function panicks if logging is turned on - logging_right_crate::foo::(); -} diff --git a/src/test/run-pass-fulldeps/logging-separate-lines.rs b/src/test/run-pass-fulldeps/logging-separate-lines.rs deleted file mode 100644 index 183a522bba749..0000000000000 --- a/src/test/run-pass-fulldeps/logging-separate-lines.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2013-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. - -// ignore-windows -// exec-env:RUST_LOG=debug -// compile-flags:-C debug-assertions=y -// ignore-emscripten: FIXME(#31622) - -#![feature(rustc_private)] - -#[macro_use] -extern crate log; - -use std::process::Command; -use std::env; -use std::str; - -fn main() { - let args: Vec = env::args().collect(); - if args.len() > 1 && args[1] == "child" { - debug!("foo"); - debug!("bar"); - return - } - - let p = Command::new(&args[0]) - .arg("child") - .output().unwrap(); - assert!(p.status.success()); - let mut lines = str::from_utf8(&p.stderr).unwrap().lines(); - assert!(lines.next().unwrap().contains("foo")); - assert!(lines.next().unwrap().contains("bar")); -} diff --git a/src/test/run-pass-fulldeps/rust-log-filter.rs b/src/test/run-pass-fulldeps/rust-log-filter.rs deleted file mode 100644 index 306d24e317754..0000000000000 --- a/src/test/run-pass-fulldeps/rust-log-filter.rs +++ /dev/null @@ -1,58 +0,0 @@ -// 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. - -// exec-env:RUST_LOG=rust_log_filter/foo -// ignore-emscripten no threads support - -#![allow(unknown_features)] -#![feature(box_syntax, std_misc, rustc_private)] - -#[macro_use] -extern crate log; - -use std::sync::mpsc::{channel, Sender, Receiver}; -use std::thread; - -pub struct ChannelLogger { - tx: Sender -} - -impl ChannelLogger { - pub fn new() -> (Box, Receiver) { - let (tx, rx) = channel(); - (box ChannelLogger { tx: tx }, rx) - } -} - -impl log::Logger for ChannelLogger { - fn log(&mut self, record: &log::LogRecord) { - self.tx.send(format!("{}", record.args)).unwrap(); - } -} - -pub fn main() { - let (logger, rx) = ChannelLogger::new(); - - let t = thread::spawn(move|| { - log::set_logger(logger); - - info!("foo"); - info!("bar"); - info!("foo bar"); - info!("bar foo"); - }); - - assert_eq!(rx.recv().unwrap(), "foo"); - assert_eq!(rx.recv().unwrap(), "foo bar"); - assert_eq!(rx.recv().unwrap(), "bar foo"); - assert!(rx.recv().is_err()); - - t.join(); -} diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index 1fc98a78a7c47..7530b65a9b7c4 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -5,6 +5,6 @@ version = "0.0.0" [dependencies] log = "0.3" -env_logger = { version = "0.3.5", default-features = false } +env_logger = { version = "0.4", default-features = false } rustc-serialize = "0.3" filetime = "0.1" From cb96adea15f15b13adca6f13b285a4fff33f5be3 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 16 Mar 2017 20:39:41 +0000 Subject: [PATCH 43/54] Fix regression when `include!()`ing a `macro_rules!` containing a `$crate::` path. --- src/librustc_resolve/build_reduced_graph.rs | 5 ++++- src/test/run-pass/auxiliary/issue_40469.rs | 11 +++++++++++ src/test/run-pass/issue-40469.rs | 18 ++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/test/run-pass/auxiliary/issue_40469.rs create mode 100644 src/test/run-pass/issue-40469.rs diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 03c61067d64c2..be905a9d0f94a 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -23,7 +23,7 @@ use {resolve_error, resolve_struct_error, ResolutionError}; use rustc::middle::cstore::LoadedMacro; use rustc::hir::def::*; -use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId}; +use rustc::hir::def_id::{CrateNum, BUILTIN_MACROS_CRATE, CRATE_DEF_INDEX, DefId}; use rustc::ty; use std::cell::Cell; @@ -496,6 +496,9 @@ impl<'a> Resolver<'a> { let def_id = self.macro_defs[&expansion]; if let Some(id) = self.definitions.as_local_node_id(def_id) { self.local_macro_def_scopes[&id] + } else if def_id.krate == BUILTIN_MACROS_CRATE { + // FIXME(jseyfried): This happens when `include!()`ing a `$crate::` path, c.f, #40469. + self.graph_root } else { let module_def_id = ty::DefIdTree::parent(&*self, def_id).unwrap(); self.get_extern_crate_root(module_def_id.krate) diff --git a/src/test/run-pass/auxiliary/issue_40469.rs b/src/test/run-pass/auxiliary/issue_40469.rs new file mode 100644 index 0000000000000..4970bba431a84 --- /dev/null +++ b/src/test/run-pass/auxiliary/issue_40469.rs @@ -0,0 +1,11 @@ +// Copyright 2017 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. + +macro_rules! m { () => { $crate::main(); } } diff --git a/src/test/run-pass/issue-40469.rs b/src/test/run-pass/issue-40469.rs new file mode 100644 index 0000000000000..30055e532cd45 --- /dev/null +++ b/src/test/run-pass/issue-40469.rs @@ -0,0 +1,18 @@ +// Copyright 2017 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-pretty issue #37195 + +#![allow(dead_code)] + +include!("auxiliary/issue_40469.rs"); +fn f() { m!(); } + +fn main() {} From 2976ddbb1522397e3a9d91aa5ed9ae8e5cdbf97a Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Fri, 17 Mar 2017 17:15:01 -0700 Subject: [PATCH 44/54] Fix a spelling error in HashMap documentation, and slightly reword it to be more precise. --- src/libstd/collections/hash/map.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 3ca8b41347a26..5733217008146 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -222,8 +222,8 @@ const DISPLACEMENT_THRESHOLD: usize = 128; /// resistance against HashDoS attacks. The algorithm is randomly seeded, and a /// reasonable best-effort is made to generate this seed from a high quality, /// secure source of randomness provided by the host without blocking the -/// program. Because of this, the randomness of the seed is dependant on the -/// quality of the system's random number generator at the time it is created. +/// program. Because of this, the randomness of the seed depends on the output +/// quality of the system's random number generator when the seed is created. /// In particular, seeds generated when the system's entropy pool is abnormally /// low such as during system boot may be of a lower quality. /// From 0af3775dd2c93cdaf8902f83eb21037e474e058f Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 7 Feb 2017 22:46:21 +0100 Subject: [PATCH 45/54] translate tuple-variant constructors using MIR --- src/librustc/ty/mod.rs | 13 +++- src/librustc_metadata/encoder.rs | 4 +- src/librustc_mir/lib.rs | 2 + src/librustc_mir/mir_map.rs | 63 ++++++++++++++++++ src/librustc_mir/shim.rs | 109 +++++++++++++++++++++++++++++++ src/librustc_trans/base.rs | 78 ++-------------------- src/librustc_trans/callee.rs | 33 ---------- src/librustc_trans/collector.rs | 19 ++---- src/librustc_trans/common.rs | 10 ++- src/librustc_trans/mir/block.rs | 6 +- src/librustc_trans/trans_item.rs | 16 +++-- 11 files changed, 215 insertions(+), 138 deletions(-) create mode 100644 src/librustc_mir/shim.rs diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 3c37c7353d683..360fa24bf3687 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1264,10 +1264,17 @@ impl<'a, 'tcx> ParameterEnvironment<'tcx> { def_id, ROOT_CODE_EXTENT) } - _ => { + Some(hir_map::NodeStructCtor(..)) | + Some(hir_map::NodeVariant(..)) => { + let def_id = tcx.hir.local_def_id(id); + tcx.construct_parameter_environment(tcx.hir.span(id), + def_id, + ROOT_CODE_EXTENT) + } + it => { bug!("ParameterEnvironment::from_item(): \ - `{}` is not an item", - tcx.hir.node_to_string(id)) + `{}` = {:?} is unsupported", + tcx.hir.node_to_string(id), it) } } } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 0c31e30671dc2..044ed529ef74c 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -293,7 +293,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { predicates: Some(self.encode_predicates(def_id)), ast: None, - mir: None, + mir: self.encode_mir(def_id), } } @@ -426,7 +426,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { predicates: Some(self.encode_predicates(def_id)), ast: None, - mir: None, + mir: self.encode_mir(def_id), } } diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index f21f1881c832e..19028bfa531be 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -22,6 +22,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(associated_consts)] #![feature(box_patterns)] +#![feature(box_syntax)] #![feature(i128_type)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] @@ -50,6 +51,7 @@ pub mod callgraph; pub mod def_use; pub mod graphviz; mod hair; +mod shim; pub mod mir_map; pub mod pretty; pub mod transform; diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 58f23a5c81bd7..3fa7131a2b6b0 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -22,6 +22,7 @@ use rustc::dep_graph::DepNode; use rustc::mir::Mir; use rustc::mir::transform::MirSource; use rustc::mir::visit::MutVisitor; +use shim; use pretty; use hair::cx::Cx; @@ -30,6 +31,7 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::maps::Providers; use rustc::ty::subst::Substs; use rustc::hir; +use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use syntax::abi::Abi; use syntax::ast; use syntax_pos::Span; @@ -44,6 +46,31 @@ pub fn build_mir_for_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { tcx.visit_all_bodies_in_krate(|body_owner_def_id, _body_id| { tcx.item_mir(body_owner_def_id); }); + + // Tuple struct/variant constructors don't have a BodyId, so we need + // to build them separately. + struct GatherCtors<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx> + } + impl<'a, 'tcx> Visitor<'tcx> for GatherCtors<'a, 'tcx> { + fn visit_variant_data(&mut self, + v: &'tcx hir::VariantData, + _: ast::Name, + _: &'tcx hir::Generics, + _: ast::NodeId, + _: Span) { + if let hir::VariantData::Tuple(_, node_id) = *v { + self.tcx.item_mir(self.tcx.hir.local_def_id(node_id)); + } + intravisit::walk_struct_def(self, v) + } + fn nested_visit_map<'b>(&'b mut self) -> NestedVisitorMap<'b, 'tcx> { + NestedVisitorMap::None + } + } + tcx.visit_all_item_likes_in_krate(DepNode::Mir, &mut GatherCtors { + tcx: tcx + }.as_deep_visitor()); } } @@ -95,6 +122,10 @@ fn build_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) _ => hir::BodyId { node_id: expr.id } } } + hir::map::NodeVariant(variant) => + return create_constructor_shim(tcx, id, &variant.node.data), + hir::map::NodeStructCtor(ctor) => + return create_constructor_shim(tcx, id, ctor), _ => unsupported() }; @@ -180,6 +211,38 @@ impl<'a, 'gcx: 'tcx, 'tcx> MutVisitor<'tcx> for GlobalizeMir<'a, 'gcx> { } } +fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + ctor_id: ast::NodeId, + v: &'tcx hir::VariantData) + -> &'tcx RefCell> +{ + let span = tcx.hir.span(ctor_id); + if let hir::VariantData::Tuple(ref fields, ctor_id) = *v { + let pe = ty::ParameterEnvironment::for_item(tcx, ctor_id); + tcx.infer_ctxt(pe, Reveal::UserFacing).enter(|infcx| { + let (mut mir, src) = + shim::build_adt_ctor(&infcx, ctor_id, fields, span); + + // Convert the Mir to global types. + let tcx = infcx.tcx.global_tcx(); + let mut globalizer = GlobalizeMir { + tcx: tcx, + span: mir.span + }; + globalizer.visit_mir(&mut mir); + let mir = unsafe { + mem::transmute::>(mir) + }; + + pretty::dump_mir(tcx, "mir_map", &0, src, &mir); + + tcx.alloc_mir(mir) + }) + } else { + span_bug!(span, "attempting to create MIR for non-tuple variant {:?}", v); + } +} + /////////////////////////////////////////////////////////////////////////// // BuildMir -- walks a crate, looking for fn items and methods to build MIR from diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs new file mode 100644 index 0000000000000..3705a317715f4 --- /dev/null +++ b/src/librustc_mir/shim.rs @@ -0,0 +1,109 @@ +// Copyright 2016 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. + +use rustc::hir; +use rustc::infer; +use rustc::mir::*; +use rustc::mir::transform::MirSource; +use rustc::ty; + +use rustc_data_structures::indexed_vec::{IndexVec, Idx}; + +use syntax::ast; +use syntax_pos::Span; + +use std::iter; + +fn local_decls_for_sig<'tcx>(sig: &ty::FnSig<'tcx>) + -> IndexVec> +{ + iter::once(LocalDecl { + mutability: Mutability::Mut, + ty: sig.output(), + name: None, + source_info: None + }).chain(sig.inputs().iter().map(|ity| LocalDecl { + mutability: Mutability::Not, + ty: *ity, + name: None, + source_info: None, + })).collect() +} + +pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, + ctor_id: ast::NodeId, + fields: &[hir::StructField], + span: Span) + -> (Mir<'tcx>, MirSource) +{ + let tcx = infcx.tcx; + let def_id = tcx.hir.local_def_id(ctor_id); + let sig = match tcx.item_type(def_id).sty { + ty::TyFnDef(_, _, fty) => tcx.no_late_bound_regions(&fty) + .expect("LBR in ADT constructor signature"), + _ => bug!("unexpected type for ctor {:?}", def_id) + }; + let sig = tcx.erase_regions(&sig); + + let (adt_def, substs) = match sig.output().sty { + ty::TyAdt(adt_def, substs) => (adt_def, substs), + _ => bug!("unexpected type for ADT ctor {:?}", sig.output()) + }; + + debug!("build_ctor: def_id={:?} sig={:?} fields={:?}", def_id, sig, fields); + + let local_decls = local_decls_for_sig(&sig); + + let source_info = SourceInfo { + span: span, + scope: ARGUMENT_VISIBILITY_SCOPE + }; + + let variant_no = if adt_def.is_enum() { + adt_def.variant_index_with_id(def_id) + } else { + 0 + }; + + // return = ADT(arg0, arg1, ...); return + let start_block = BasicBlockData { + statements: vec![Statement { + source_info: source_info, + kind: StatementKind::Assign( + Lvalue::Local(RETURN_POINTER), + Rvalue::Aggregate( + AggregateKind::Adt(adt_def, variant_no, substs, None), + (1..sig.inputs().len()+1).map(|i| { + Operand::Consume(Lvalue::Local(Local::new(i))) + }).collect() + ) + ) + }], + terminator: Some(Terminator { + source_info: source_info, + kind: TerminatorKind::Return, + }), + is_cleanup: false + }; + + let mir = Mir::new( + IndexVec::from_elem_n(start_block, 1), + IndexVec::from_elem_n( + VisibilityScopeData { span: span, parent_scope: None }, 1 + ), + IndexVec::new(), + sig.output(), + local_decls, + sig.inputs().len(), + vec![], + span + ); + (mir, MirSource::Fn(ctor_id)) +} diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 1b43491e73c8f..fe2b21895cce6 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -34,10 +34,8 @@ use back::linker::LinkerInfo; use back::symbol_export::{self, ExportedSymbols}; use llvm::{Linkage, ValueRef, Vector, get_param}; use llvm; -use rustc::hir::def_id::{DefId, LOCAL_CRATE}; +use rustc::hir::def_id::LOCAL_CRATE; use middle::lang_items::StartFnLangItem; -use rustc::ty::subst::Substs; -use rustc::mir::tcx::LvalueTy; use rustc::traits; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::adjustment::CustomCoerceUnsized; @@ -47,9 +45,8 @@ use rustc::util::common::time; use session::config::{self, NoDebugInfo}; use rustc_incremental::IncrementalHashesMap; use session::{self, DataTypeKind, Session}; -use abi::{self, FnType}; +use abi; use mir::lvalue::LvalueRef; -use adt; use attributes; use builder::Builder; use callee::{Callee}; @@ -65,7 +62,7 @@ use context::{SharedCrateContext, CrateContextList}; use debuginfo; use declare; use machine; -use machine::{llalign_of_min, llsize_of}; +use machine::llsize_of; use meth; use mir; use monomorphize::{self, Instance}; @@ -76,7 +73,6 @@ use trans_item::{TransItem, DefPathBasedNames}; use type_::Type; use type_of; use value::Value; -use Disr; use util::nodemap::{NodeSet, FxHashMap, FxHashSet}; use libc::c_uint; @@ -615,72 +611,6 @@ pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance mir::trans_mir(ccx, lldecl, &mir, instance, sig); } -pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx>, - disr: Disr, - llfn: ValueRef) { - attributes::inline(llfn, attributes::InlineAttr::Hint); - attributes::set_frame_pointer_elimination(ccx, llfn); - - let ctor_ty = common::def_ty(ccx.shared(), def_id, substs); - let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&ctor_ty.fn_sig()); - let fn_ty = FnType::new(ccx, sig, &[]); - - let bcx = Builder::new_block(ccx, llfn, "entry-block"); - if !fn_ty.ret.is_ignore() { - // But if there are no nested returns, we skip the indirection - // and have a single retslot - let dest = if fn_ty.ret.is_indirect() { - get_param(llfn, 0) - } else { - // We create an alloca to hold a pointer of type `ret.original_ty` - // which will hold the pointer to the right alloca which has the - // final ret value - bcx.alloca(fn_ty.ret.memory_ty(ccx), "sret_slot") - }; - // Can return unsized value - let mut dest_val = LvalueRef::new_sized_ty(dest, sig.output(), Alignment::AbiAligned); - dest_val.ty = LvalueTy::Downcast { - adt_def: sig.output().ty_adt_def().unwrap(), - substs: substs, - variant_index: disr.0 as usize, - }; - let mut llarg_idx = fn_ty.ret.is_indirect() as usize; - let mut arg_idx = 0; - for (i, arg_ty) in sig.inputs().iter().enumerate() { - let (lldestptr, _) = dest_val.trans_field_ptr(&bcx, i); - let arg = &fn_ty.args[arg_idx]; - arg_idx += 1; - if common::type_is_fat_ptr(bcx.ccx, arg_ty) { - let meta = &fn_ty.args[arg_idx]; - arg_idx += 1; - arg.store_fn_arg(&bcx, &mut llarg_idx, get_dataptr(&bcx, lldestptr)); - meta.store_fn_arg(&bcx, &mut llarg_idx, get_meta(&bcx, lldestptr)); - } else { - arg.store_fn_arg(&bcx, &mut llarg_idx, lldestptr); - } - } - adt::trans_set_discr(&bcx, sig.output(), dest, disr); - - if fn_ty.ret.is_indirect() { - bcx.ret_void(); - return; - } - - if let Some(cast_ty) = fn_ty.ret.cast { - bcx.ret(bcx.load( - bcx.pointercast(dest, cast_ty.ptr_to()), - Some(llalign_of_min(ccx, fn_ty.ret.ty)) - )); - } else { - bcx.ret(bcx.load(dest, None)) - } - } else { - bcx.ret_void(); - } -} - pub fn llvm_linkage_by_name(name: &str) -> Option { // Use the names from src/llvm/docs/LangRef.rst here. Most types are only // applicable to variable declarations and may not really make sense for @@ -721,7 +651,7 @@ pub fn set_link_section(ccx: &CrateContext, } /// Create the `main` function which will initialise the rust runtime and call -/// users’ main function. +/// users main function. pub fn maybe_create_entry_wrapper(ccx: &CrateContext) { let (main_def_id, span) = match *ccx.sess().entry_fn.borrow() { Some((id, span)) => { diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 762aaf1ce1d1b..a5b42a973cf27 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -22,7 +22,6 @@ use rustc::ty::subst::{Substs, Subst}; use rustc::traits; use abi::{Abi, FnType}; use attributes; -use base; use builder::Builder; use common::{self, CrateContext}; use cleanup::CleanupScope; @@ -35,7 +34,6 @@ use meth; use monomorphize::Instance; use trans_item::TransItem; use type_of; -use Disr; use rustc::ty::{self, Ty, TypeFoldable}; use rustc::hir; use std::iter; @@ -46,9 +44,6 @@ use mir::lvalue::Alignment; #[derive(Debug)] pub enum CalleeData { - /// Constructor for enum variant/tuple-like-struct. - NamedTupleConstructor(Disr), - /// Function pointer. Fn(ValueRef), @@ -92,16 +87,6 @@ impl<'tcx> Callee<'tcx> { } } - // FIXME(eddyb) Detect ADT constructors more efficiently. - if let Some(adt_def) = fn_ty.fn_ret().skip_binder().ty_adt_def() { - if let Some(i) = adt_def.variants.iter().position(|v| def_id == v.did) { - return Callee { - data: NamedTupleConstructor(Disr::for_variant(tcx, adt_def, i)), - ty: fn_ty - }; - } - } - let (llfn, ty) = get_fn(ccx, def_id, substs); Callee::ptr(llfn, ty) } @@ -185,24 +170,6 @@ impl<'tcx> Callee<'tcx> { match self.data { Fn(llfn) => llfn, Virtual(_) => meth::trans_object_shim(ccx, self), - NamedTupleConstructor(disr) => match self.ty.sty { - ty::TyFnDef(def_id, substs, _) => { - let instance = Instance::new(def_id, substs); - if let Some(&llfn) = ccx.instances().borrow().get(&instance) { - return llfn; - } - - let sym = ccx.symbol_map().get_or_compute(ccx.shared(), - TransItem::Fn(instance)); - assert!(!ccx.codegen_unit().contains_item(&TransItem::Fn(instance))); - let lldecl = declare::define_internal_fn(ccx, &sym, self.ty); - base::trans_ctor_shim(ccx, def_id, substs, disr, lldecl); - ccx.instances().borrow_mut().insert(instance, lldecl); - - lldecl - } - _ => bug!("expected fn item type, found {}", self.ty) - }, Intrinsic => bug!("intrinsic {} getting reified", self.ty) } } diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 8d1db38999c6b..5e6b10f826ce0 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -630,14 +630,15 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { -> bool { match tcx.item_type(def_id).sty { ty::TyFnDef(def_id, _, _) => { - // Some constructors also have type TyFnDef but they are - // always instantiated inline and don't result in a - // translation item. Same for FFI functions. + // foreign items are linked from another library, not + // translated locally. if let Some(hir_map::NodeForeignItem(_)) = tcx.hir.get_if_local(def_id) { return false; } } - ty::TyClosure(..) => {} + ty::TyClosure(..) => { + // TODO: trans items for closures + } _ => return false } @@ -697,16 +698,6 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool { - if let ty::TyFnDef(_, _, sig) = tcx.item_type(def_id).sty { - if let Some(adt_def) = sig.output().skip_binder().ty_adt_def() { - if adt_def.variants.iter().any(|v| def_id == v.did) { - // HACK: ADT constructors are translated in-place and - // do not have a trans-item. - return false; - } - } - } - if def_id.is_local() { true } else { diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index a509587f80fd0..0e536d58a56fb 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -37,6 +37,7 @@ use libc::{c_uint, c_char}; use std::iter; use syntax::ast; +use syntax::attr; use syntax::symbol::InternedString; use syntax_pos::Span; @@ -601,8 +602,13 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } } -pub fn is_closure(tcx: TyCtxt, def_id: DefId) -> bool { - tcx.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr +pub fn requests_inline(tcx: TyCtxt, def_id: DefId) -> bool { + match tcx.def_key(def_id).disambiguated_data.data { + DefPathData::StructCtor | + DefPathData::EnumVariant(..) | + DefPathData::ClosureExpr => true, + _ => attr::requests_inline(&tcx.get_attrs(def_id)[..]), + } } /// Given a DefId and some Substs, produces the monomorphic item type. diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 9d40419d338b8..2f1a2c9134c39 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -16,7 +16,7 @@ use rustc::ty::{self, layout, TypeFoldable}; use rustc::mir; use abi::{Abi, FnType, ArgType}; use base::{self, Lifetime}; -use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual}; +use callee::{Callee, CalleeData, Fn, Intrinsic, Virtual}; use builder::Builder; use common::{self, Funclet}; use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef}; @@ -491,10 +491,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } let fn_ptr = match callee.data { - NamedTupleConstructor(_) => { - // FIXME translate this like mir::Rvalue::Aggregate. - callee.reify(bcx.ccx) - } Intrinsic => { use intrinsic::trans_intrinsic_call; diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs index d19f04b9554fb..5ec9c2a59957d 100644 --- a/src/librustc_trans/trans_item.rs +++ b/src/librustc_trans/trans_item.rs @@ -26,6 +26,7 @@ use monomorphize::Instance; use rustc::dep_graph::DepNode; use rustc::hir; use rustc::hir::def_id::DefId; +use rustc::hir::map::definitions::DefPathData; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::subst::Substs; use rustc_const_eval::fatal_const_eval_err; @@ -178,9 +179,14 @@ impl<'a, 'tcx> TransItem<'tcx> { llvm::SetUniqueComdat(ccx.llmod(), lldecl); } - if let ty::TyClosure(..) = mono_ty.sty { - // set an inline hint for all closures - attributes::inline(lldecl, attributes::InlineAttr::Hint); + debug!("predefine_fn: mono_ty = {:?} instance = {:?}", mono_ty, instance); + match ccx.tcx().def_key(instance.def).disambiguated_data.data { + DefPathData::StructCtor | + DefPathData::EnumVariant(..) | + DefPathData::ClosureExpr => { + attributes::inline(lldecl, attributes::InlineAttr::Hint); + } + _ => {} } attributes::from_fn_attrs(ccx, &attrs, lldecl); @@ -252,8 +258,8 @@ impl<'a, 'tcx> TransItem<'tcx> { match *self { TransItem::Fn(ref instance) => { if self.explicit_linkage(tcx).is_none() && - (common::is_closure(tcx, instance.def) || - attr::requests_inline(&tcx.get_attrs(instance.def)[..])) { + common::requests_inline(tcx, instance.def) + { InstantiationMode::LocalCopy } else { InstantiationMode::GloballyShared From ffee9566bbd7728e6411e6094105d6905373255d Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 8 Feb 2017 18:31:03 +0100 Subject: [PATCH 46/54] move Instance to rustc and use it in the collector --- src/librustc/dep_graph/dep_node.rs | 5 + src/librustc/ty/instance.rs | 105 ++++++++ src/librustc/ty/maps.rs | 45 +++- src/librustc/ty/mod.rs | 13 + src/librustc/ty/util.rs | 12 + src/librustc_mir/lib.rs | 1 + src/librustc_mir/shim.rs | 17 ++ src/librustc_trans/back/symbol_export.rs | 7 +- src/librustc_trans/back/symbol_names.rs | 160 ++++++------ src/librustc_trans/base.rs | 18 +- src/librustc_trans/callee.rs | 46 ++-- src/librustc_trans/collector.rs | 300 +++++++++-------------- src/librustc_trans/common.rs | 30 ++- src/librustc_trans/consts.rs | 13 +- src/librustc_trans/context.rs | 12 +- src/librustc_trans/debuginfo/mod.rs | 16 +- src/librustc_trans/meth.rs | 3 +- src/librustc_trans/mir/constant.rs | 30 ++- src/librustc_trans/mir/rvalue.rs | 4 +- src/librustc_trans/monomorphize.rs | 59 ++--- src/librustc_trans/partitioning.rs | 20 +- src/librustc_trans/symbol_map.rs | 2 +- src/librustc_trans/symbol_names_test.rs | 6 +- src/librustc_trans/trans_item.rs | 30 +-- 24 files changed, 522 insertions(+), 432 deletions(-) create mode 100644 src/librustc/ty/instance.rs diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 254cae61152b9..399af258e9251 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -89,6 +89,7 @@ pub enum DepNode { // things read/modify that MIR. MirKrate, Mir(D), + MirShim(Vec), BorrowCheckKrate, BorrowCheck(D), @@ -258,6 +259,10 @@ impl DepNode { IntrinsicCheck(ref d) => op(d).map(IntrinsicCheck), MatchCheck(ref d) => op(d).map(MatchCheck), Mir(ref d) => op(d).map(Mir), + MirShim(ref def_ids) => { + let def_ids: Option> = def_ids.iter().map(op).collect(); + def_ids.map(MirShim) + } BorrowCheck(ref d) => op(d).map(BorrowCheck), RvalueCheck(ref d) => op(d).map(RvalueCheck), StabilityCheck(ref d) => op(d).map(StabilityCheck), diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs new file mode 100644 index 0000000000000..fdcfb3ebd3ce5 --- /dev/null +++ b/src/librustc/ty/instance.rs @@ -0,0 +1,105 @@ +// Copyright 2016 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. + +use dep_graph::DepNode; +use hir::def_id::DefId; +use ty::{self, Ty, TypeFoldable, Substs}; +use util::ppaux; + +use std::borrow::Cow; +use std::fmt; +use syntax::ast; + + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Instance<'tcx> { + pub def: InstanceDef<'tcx>, + pub substs: &'tcx Substs<'tcx>, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum InstanceDef<'tcx> { + Item(DefId), + // ::call_* + FnPtrShim(DefId, Ty<'tcx>), +} + +impl<'tcx> InstanceDef<'tcx> { + #[inline] + pub fn def_id(&self) -> DefId { + match *self { + InstanceDef::Item(def_id) | + InstanceDef::FnPtrShim(def_id, _) + => def_id + } + } + + #[inline] + pub fn def_ty<'a>(&self, tcx: ty::TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx> { + tcx.item_type(self.def_id()) + } + + #[inline] + pub fn attrs<'a>(&self, tcx: ty::TyCtxt<'a, 'tcx, 'tcx>) -> Cow<'tcx, [ast::Attribute]> { + tcx.get_attrs(self.def_id()) + } + + pub(crate) fn dep_node(&self) -> DepNode { + // HACK: def-id binning, project-style; someone replace this with + // real on-demand. + let ty = match self { + &InstanceDef::FnPtrShim(_, ty) => Some(ty), + _ => None + }.into_iter(); + + DepNode::MirShim( + Some(self.def_id()).into_iter().chain( + ty.flat_map(|t| t.walk()).flat_map(|t| match t.sty { + ty::TyAdt(adt_def, _) => Some(adt_def.did), + ty::TyProjection(ref proj) => Some(proj.trait_ref.def_id), + _ => None, + }) + ).collect() + ) + } +} + +impl<'tcx> fmt::Display for Instance<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.def { + InstanceDef::Item(def) => { + ppaux::parameterized(f, self.substs, def, &[]) + } + InstanceDef::FnPtrShim(def, ty) => { + ppaux::parameterized(f, self.substs, def, &[])?; + write!(f, " - shim({:?})", ty) + } + } + } +} + +impl<'a, 'b, 'tcx> Instance<'tcx> { + pub fn new(def_id: DefId, substs: &'tcx Substs<'tcx>) + -> Instance<'tcx> { + assert!(substs.is_normalized_for_trans() && !substs.has_escaping_regions(), + "substs of instance {:?} not normalized for trans: {:?}", + def_id, substs); + Instance { def: InstanceDef::Item(def_id), substs: substs } + } + + pub fn mono(tcx: ty::TyCtxt<'a, 'tcx, 'b>, def_id: DefId) -> Instance<'tcx> { + Instance::new(def_id, tcx.global_tcx().empty_substs_for_def_id(def_id)) + } + + #[inline] + pub fn def_id(&self) -> DefId { + self.def.def_id() + } +} diff --git a/src/librustc/ty/maps.rs b/src/librustc/ty/maps.rs index af05c0c43113b..ac8c38c7d5856 100644 --- a/src/librustc/ty/maps.rs +++ b/src/librustc/ty/maps.rs @@ -9,7 +9,7 @@ // except according to those terms. use dep_graph::{DepGraph, DepNode, DepTrackingMap, DepTrackingMapConfig}; -use hir::def_id::{CrateNum, DefId}; +use hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use middle::const_val::ConstVal; use mir; use ty::{self, Ty, TyCtxt}; @@ -24,6 +24,16 @@ trait Key { fn default_span(&self, tcx: TyCtxt) -> Span; } +impl<'tcx> Key for ty::InstanceDef<'tcx> { + fn map_crate(&self) -> CrateNum { + LOCAL_CRATE + } + + fn default_span(&self, tcx: TyCtxt) -> Span { + tcx.def_span(self.def_id()) + } +} + impl Key for CrateNum { fn map_crate(&self) -> CrateNum { *self @@ -83,9 +93,9 @@ impl<'tcx> Value<'tcx> for Ty<'tcx> { } } -pub struct CycleError<'a> { +pub struct CycleError<'a, 'tcx: 'a> { span: Span, - cycle: RefMut<'a, [(Span, Query)]>, + cycle: RefMut<'a, [(Span, Query<'tcx>)]>, } impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { @@ -110,8 +120,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { err.emit(); } - fn cycle_check(self, span: Span, query: Query, compute: F) - -> Result> + fn cycle_check(self, span: Span, query: Query<'gcx>, compute: F) + -> Result> where F: FnOnce() -> R { { @@ -172,13 +182,20 @@ impl<'tcx> QueryDescription for queries::coherent_inherent_impls<'tcx> { } } +impl<'tcx> QueryDescription for queries::mir_shims<'tcx> { + fn describe(tcx: TyCtxt, def: ty::InstanceDef<'tcx>) -> String { + format!("generating MIR shim for `{}`", + tcx.item_path_str(def.def_id())) + } +} + macro_rules! define_maps { (<$tcx:tt> $($(#[$attr:meta])* pub $name:ident: $node:ident($K:ty) -> $V:ty),*) => { pub struct Maps<$tcx> { providers: IndexVec>, - query_stack: RefCell>, + query_stack: RefCell)>>, $($(#[$attr])* pub $name: RefCell>>),* } @@ -196,11 +213,11 @@ macro_rules! define_maps { #[allow(bad_style)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] - pub enum Query { + pub enum Query<$tcx> { $($(#[$attr])* $name($K)),* } - impl Query { + impl<$tcx> Query<$tcx> { pub fn describe(&self, tcx: TyCtxt) -> String { match *self { $(Query::$name(key) => queries::$name::describe(tcx, key)),* @@ -233,7 +250,7 @@ macro_rules! define_maps { mut span: Span, key: $K, f: F) - -> Result> + -> Result> where F: FnOnce(&$V) -> R { if let Some(result) = tcx.maps.$name.borrow().get(&key) { @@ -256,7 +273,7 @@ macro_rules! define_maps { } pub fn try_get(tcx: TyCtxt<'a, $tcx, 'lcx>, span: Span, key: $K) - -> Result<$V, CycleError<'a>> { + -> Result<$V, CycleError<'a, $tcx>> { Self::try_get_with(tcx, span, key, Clone::clone) } @@ -387,7 +404,9 @@ define_maps! { <'tcx> /// Results of evaluating monomorphic constants embedded in /// other items, such as enum variant explicit discriminants. - pub monomorphic_const_eval: MonomorphicConstEval(DefId) -> Result, ()> + pub monomorphic_const_eval: MonomorphicConstEval(DefId) -> Result, ()>, + + pub mir_shims: mir_shim(ty::InstanceDef<'tcx>) -> &'tcx RefCell> } fn coherent_trait_dep_node((_, def_id): (CrateNum, DefId)) -> DepNode { @@ -397,3 +416,7 @@ fn coherent_trait_dep_node((_, def_id): (CrateNum, DefId)) -> DepNode { fn coherent_inherent_impls_dep_node(_: CrateNum) -> DepNode { DepNode::Coherence } + +fn mir_shim(instance: ty::InstanceDef) -> DepNode { + instance.dep_node() +} diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 360fa24bf3687..c4192ffc697ce 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -73,6 +73,8 @@ pub use self::contents::TypeContents; pub use self::context::{TyCtxt, GlobalArenas, tls}; pub use self::context::{Lift, TypeckTables}; +pub use self::instance::{Instance, InstanceDef}; + pub use self::trait_def::{TraitDef, TraitFlags}; pub use self::maps::queries; @@ -98,6 +100,7 @@ pub mod util; mod contents; mod context; mod flags; +mod instance; mod structural_impls; mod sty; @@ -2309,6 +2312,16 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { queries::mir::get(self, DUMMY_SP, did).borrow() } + /// Return the possibly-auto-generated MIR of a (DefId, Subst) pair. + pub fn instance_mir(self, instance: ty::InstanceDef<'gcx>) + -> Ref<'gcx, Mir<'gcx>> + { + match instance { + ty::InstanceDef::Item(did) if true => self.item_mir(did), + _ => queries::mir_shims::get(self, DUMMY_SP, instance).borrow(), + } + } + /// Given the DefId of an item, returns its MIR, borrowed immutably. /// Returns None if there is no MIR for the DefId pub fn maybe_item_mir(self, did: DefId) -> Option>> { diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index fd95724990941..2344305fa9a7e 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -398,6 +398,18 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } def_id } + + /// Given the def-id of some item that has no type parameters, make + /// a suitable "empty substs" for it. + pub fn empty_substs_for_def_id(self, item_def_id: DefId) -> &'tcx ty::Substs<'tcx> { + ty::Substs::for_item(self, item_def_id, + |_, _| self.mk_region(ty::ReErased), + |_, _| { + bug!("empty_substs_for_def_id: {:?} has type parameters", item_def_id) + }) + } + + } pub struct TypeIdHasher<'a, 'gcx: 'a+'tcx, 'tcx: 'a, W> { diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 19028bfa531be..9e6b77dbabdef 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -60,5 +60,6 @@ use rustc::ty::maps::Providers; pub fn provide(providers: &mut Providers) { mir_map::provide(providers); + shim::provide(providers); transform::qualify_consts::provide(providers); } \ No newline at end of file diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 3705a317715f4..0fbd488ffa360 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -13,14 +13,31 @@ use rustc::infer; use rustc::mir::*; use rustc::mir::transform::MirSource; use rustc::ty; +use rustc::ty::maps::Providers; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use syntax::ast; use syntax_pos::Span; +use std::cell::RefCell; use std::iter; +pub fn provide(providers: &mut Providers) { + providers.mir_shims = make_shim; +} + +fn make_shim<'a, 'tcx>(_tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, + instance: ty::InstanceDef<'tcx>) + -> &'tcx RefCell> +{ + match instance { + ty::InstanceDef::Item(..) => + bug!("item {:?} passed to make_shim", instance), + ty::InstanceDef::FnPtrShim(..) => unimplemented!() + } +} + fn local_decls_for_sig<'tcx>(sig: &ty::FnSig<'tcx>) -> IndexVec> { diff --git a/src/librustc_trans/back/symbol_export.rs b/src/librustc_trans/back/symbol_export.rs index bea3ca8df70e0..005fb3533ab0b 100644 --- a/src/librustc_trans/back/symbol_export.rs +++ b/src/librustc_trans/back/symbol_export.rs @@ -11,6 +11,7 @@ use context::SharedCrateContext; use monomorphize::Instance; use symbol_map::SymbolMap; +use back::symbol_names::symbol_name; use util::nodemap::FxHashMap; use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE}; use rustc::session::config; @@ -106,7 +107,7 @@ impl ExportedSymbols { .exported_symbols(cnum) .iter() .map(|&def_id| { - let name = Instance::mono(scx, def_id).symbol_name(scx); + let name = symbol_name(Instance::mono(scx.tcx(), def_id), scx); let export_level = if special_runtime_crate { // We can probably do better here by just ensuring that // it has hidden visibility rather than public @@ -218,9 +219,9 @@ fn symbol_for_def_id<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, } } - let instance = Instance::mono(scx, def_id); + let instance = Instance::mono(scx.tcx(), def_id); symbol_map.get(TransItem::Fn(instance)) .map(str::to_owned) - .unwrap_or_else(|| instance.symbol_name(scx)) + .unwrap_or_else(|| symbol_name(instance, scx)) } diff --git a/src/librustc_trans/back/symbol_names.rs b/src/librustc_trans/back/symbol_names.rs index fe58bc8f5f28b..518995dfedcc2 100644 --- a/src/librustc_trans/back/symbol_names.rs +++ b/src/librustc_trans/back/symbol_names.rs @@ -168,105 +168,105 @@ fn get_symbol_hash<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, format!("h{:016x}", hasher.finish()) } -impl<'a, 'tcx> Instance<'tcx> { - pub fn symbol_name(self, scx: &SharedCrateContext<'a, 'tcx>) -> String { - let Instance { def: def_id, substs } = self; +pub fn symbol_name<'a, 'tcx>(instance: Instance<'tcx>, + scx: &SharedCrateContext<'a, 'tcx>) -> String { + let def_id = instance.def_id(); + let substs = instance.substs; - debug!("symbol_name(def_id={:?}, substs={:?})", - def_id, substs); + debug!("symbol_name(def_id={:?}, substs={:?})", + def_id, substs); - let node_id = scx.tcx().hir.as_local_node_id(def_id); + let node_id = scx.tcx().hir.as_local_node_id(def_id); - if let Some(id) = node_id { - if scx.sess().plugin_registrar_fn.get() == Some(id) { - let svh = &scx.link_meta().crate_hash; - let idx = def_id.index; - return scx.sess().generate_plugin_registrar_symbol(svh, idx); - } - if scx.sess().derive_registrar_fn.get() == Some(id) { - let svh = &scx.link_meta().crate_hash; - let idx = def_id.index; - return scx.sess().generate_derive_registrar_symbol(svh, idx); - } + if let Some(id) = node_id { + if scx.sess().plugin_registrar_fn.get() == Some(id) { + let svh = &scx.link_meta().crate_hash; + let idx = def_id.index; + return scx.sess().generate_plugin_registrar_symbol(svh, idx); } - - // FIXME(eddyb) Precompute a custom symbol name based on attributes. - let attrs = scx.tcx().get_attrs(def_id); - let is_foreign = if let Some(id) = node_id { - match scx.tcx().hir.get(id) { - hir_map::NodeForeignItem(_) => true, - _ => false - } - } else { - scx.sess().cstore.is_foreign_item(def_id) - }; - - if let Some(name) = weak_lang_items::link_name(&attrs) { - return name.to_string(); + if scx.sess().derive_registrar_fn.get() == Some(id) { + let svh = &scx.link_meta().crate_hash; + let idx = def_id.index; + return scx.sess().generate_derive_registrar_symbol(svh, idx); } + } - if is_foreign { - if let Some(name) = attr::first_attr_value_str_by_name(&attrs, "link_name") { - return name.to_string(); - } - // Don't mangle foreign items. - return scx.tcx().item_name(def_id).as_str().to_string(); + // FIXME(eddyb) Precompute a custom symbol name based on attributes. + let attrs = scx.tcx().get_attrs(def_id); + let is_foreign = if let Some(id) = node_id { + match scx.tcx().hir.get(id) { + hir_map::NodeForeignItem(_) => true, + _ => false } + } else { + scx.sess().cstore.is_foreign_item(def_id) + }; - if let Some(name) = attr::find_export_name_attr(scx.sess().diagnostic(), &attrs) { - // Use provided name + if let Some(name) = weak_lang_items::link_name(&attrs) { + return name.to_string(); + } + + if is_foreign { + if let Some(name) = attr::first_attr_value_str_by_name(&attrs, "link_name") { return name.to_string(); } + // Don't mangle foreign items. + return scx.tcx().item_name(def_id).as_str().to_string(); + } - if attr::contains_name(&attrs, "no_mangle") { - // Don't mangle - return scx.tcx().item_name(def_id).as_str().to_string(); - } + if let Some(name) = attr::find_export_name_attr(scx.sess().diagnostic(), &attrs) { + // Use provided name + return name.to_string(); + } - let def_path = scx.tcx().def_path(def_id); - - // We want to compute the "type" of this item. Unfortunately, some - // kinds of items (e.g., closures) don't have an entry in the - // item-type array. So walk back up the find the closest parent - // that DOES have an entry. - let mut ty_def_id = def_id; - let instance_ty; - loop { - let key = scx.tcx().def_key(ty_def_id); - match key.disambiguated_data.data { - DefPathData::TypeNs(_) | - DefPathData::ValueNs(_) => { - instance_ty = scx.tcx().item_type(ty_def_id); - break; - } - _ => { - // if we're making a symbol for something, there ought - // to be a value or type-def or something in there - // *somewhere* - ty_def_id.index = key.parent.unwrap_or_else(|| { - bug!("finding type for {:?}, encountered def-id {:?} with no \ - parent", def_id, ty_def_id); - }); - } + if attr::contains_name(&attrs, "no_mangle") { + // Don't mangle + return scx.tcx().item_name(def_id).as_str().to_string(); + } + + let def_path = scx.tcx().def_path(def_id); + + // We want to compute the "type" of this item. Unfortunately, some + // kinds of items (e.g., closures) don't have an entry in the + // item-type array. So walk back up the find the closest parent + // that DOES have an entry. + let mut ty_def_id = def_id; + let instance_ty; + loop { + let key = scx.tcx().def_key(ty_def_id); + match key.disambiguated_data.data { + DefPathData::TypeNs(_) | + DefPathData::ValueNs(_) => { + instance_ty = scx.tcx().item_type(ty_def_id); + break; + } + _ => { + // if we're making a symbol for something, there ought + // to be a value or type-def or something in there + // *somewhere* + ty_def_id.index = key.parent.unwrap_or_else(|| { + bug!("finding type for {:?}, encountered def-id {:?} with no \ + parent", def_id, ty_def_id); + }); } } + } - // Erase regions because they may not be deterministic when hashed - // and should not matter anyhow. - let instance_ty = scx.tcx().erase_regions(&instance_ty); + // Erase regions because they may not be deterministic when hashed + // and should not matter anyhow. + let instance_ty = scx.tcx().erase_regions(&instance_ty); - let hash = get_symbol_hash(scx, &def_path, instance_ty, Some(substs)); + let hash = get_symbol_hash(scx, &def_path, instance_ty, Some(substs)); - let mut buffer = SymbolPathBuffer { - names: Vec::with_capacity(def_path.data.len()) - }; + let mut buffer = SymbolPathBuffer { + names: Vec::with_capacity(def_path.data.len()) + }; - item_path::with_forced_absolute_paths(|| { - scx.tcx().push_item_path(&mut buffer, def_id); - }); + item_path::with_forced_absolute_paths(|| { + scx.tcx().push_item_path(&mut buffer, def_id); + }); - mangle(buffer.names.into_iter(), &hash) - } + mangle(buffer.names.into_iter(), &hash) } struct SymbolPathBuffer { diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index fe2b21895cce6..ce767468c012b 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -565,11 +565,11 @@ pub fn memcpy_ty<'a, 'tcx>( } pub fn call_memset<'a, 'tcx>(b: &Builder<'a, 'tcx>, - ptr: ValueRef, - fill_byte: ValueRef, - size: ValueRef, - align: ValueRef, - volatile: bool) -> ValueRef { + ptr: ValueRef, + fill_byte: ValueRef, + size: ValueRef, + align: ValueRef, + volatile: bool) -> ValueRef { let ptr_width = &b.ccx.sess().target.target.target_pointer_width[..]; let intrinsic_key = format!("llvm.memset.p0i8.i{}", ptr_width); let llintrinsicfn = b.ccx.get_intrinsic(&intrinsic_key); @@ -581,7 +581,7 @@ pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance let _s = if ccx.sess().trans_stats() { let mut instance_name = String::new(); DefPathBasedNames::new(ccx.tcx(), true, true) - .push_def_path(instance.def, &mut instance_name); + .push_def_path(instance.def_id(), &mut instance_name); Some(StatRecorder::new(ccx, instance_name)) } else { None @@ -592,7 +592,7 @@ pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance // release builds. info!("trans_instance({})", instance); - let fn_ty = common::def_ty(ccx.shared(), instance.def, instance.substs); + let fn_ty = common::instance_ty(ccx.shared(), &instance); let sig = common::ty_fn_sig(ccx, fn_ty); let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&sig); @@ -607,7 +607,7 @@ pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance attributes::emit_uwtable(lldecl, true); } - let mir = ccx.tcx().item_mir(instance.def); + let mir = ccx.tcx().instance_mir(instance.def); mir::trans_mir(ccx, lldecl, &mir, instance, sig); } @@ -668,7 +668,7 @@ pub fn maybe_create_entry_wrapper(ccx: &CrateContext) { ccx.tcx().sess.span_fatal(span, "compilation successful"); } - let instance = Instance::mono(ccx.shared(), main_def_id); + let instance = Instance::mono(ccx.tcx(), main_def_id); if !ccx.codegen_unit().contains_item(&TransItem::Fn(instance)) { // We want to create the wrapper in the same codegen unit as Rust's main diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index a5b42a973cf27..19fc4e013fae1 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -27,11 +27,12 @@ use common::{self, CrateContext}; use cleanup::CleanupScope; use mir::lvalue::LvalueRef; use consts; -use common::def_ty; +use common::instance_ty; use declare; use value::Value; use meth; use monomorphize::Instance; +use back::symbol_names::symbol_name; use trans_item::TransItem; use type_of; use rustc::ty::{self, Ty, TypeFoldable}; @@ -77,7 +78,8 @@ impl<'tcx> Callee<'tcx> { return Callee::trait_method(ccx, trait_id, def_id, substs); } - let fn_ty = def_ty(ccx.shared(), def_id, substs); + let instance = ty::Instance::new(def_id, substs); + let fn_ty = instance_ty(ccx.shared(), &instance); if let ty::TyFnDef(.., f) = fn_ty.sty { if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic { return Callee { @@ -87,7 +89,7 @@ impl<'tcx> Callee<'tcx> { } } - let (llfn, ty) = get_fn(ccx, def_id, substs); + let (llfn, ty) = get_fn(ccx, instance); Callee::ptr(llfn, ty) } @@ -104,13 +106,13 @@ impl<'tcx> Callee<'tcx> { match common::fulfill_obligation(ccx.shared(), DUMMY_SP, trait_ref) { traits::VtableImpl(vtable_impl) => { let name = tcx.item_name(def_id); - let (def_id, substs) = traits::find_method(tcx, name, substs, &vtable_impl); + let instance = common::find_method(tcx, name, substs, &vtable_impl); // Translate the function, bypassing Callee::def. // That is because default methods have the same ID as the // trait method used to look up the impl method that ended // up here, so calling Callee::def would infinitely recurse. - let (llfn, ty) = get_fn(ccx, def_id, substs); + let (llfn, ty) = get_fn(ccx, instance); Callee::ptr(llfn, ty) } traits::VtableClosure(vtable_closure) => { @@ -125,7 +127,7 @@ impl<'tcx> Callee<'tcx> { instance, trait_closure_kind); - let method_ty = def_ty(ccx.shared(), def_id, substs); + let method_ty = instance_ty(ccx.shared(), &instance); Callee::ptr(llfn, method_ty) } traits::VtableFnPointer(vtable_fn_pointer) => { @@ -135,13 +137,13 @@ impl<'tcx> Callee<'tcx> { trait_closure_kind, vtable_fn_pointer.fn_ty); - let method_ty = def_ty(ccx.shared(), def_id, substs); + let method_ty = instance_ty(ccx.shared(), &instance); Callee::ptr(llfn, method_ty) } traits::VtableObject(ref data) => { Callee { data: Virtual(tcx.get_vtable_index_of_object_method(data, def_id)), - ty: def_ty(ccx.shared(), def_id, substs) + ty: instance_ty(ccx.shared(), &Instance::new(def_id, substs)) } } vtable => { @@ -183,7 +185,7 @@ fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, -> ValueRef { // If this is a closure, redirect to it. - let (llfn, _) = get_fn(ccx, def_id, substs.substs); + let (llfn, _) = get_fn(ccx, Instance::new(def_id, substs.substs)); // If the closure is a Fn closure, but a FnOnce is needed (etc), // then adapt the self type @@ -292,7 +294,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( let llonce_fn_ty = tcx.mk_fn_ptr(ty::Binder(sig)); // Create the by-value helper. - let function_name = method_instance.symbol_name(ccx.shared()); + let function_name = symbol_name(method_instance, ccx.shared()); let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty); attributes::set_frame_pointer_elimination(ccx, lloncefn); @@ -438,7 +440,7 @@ fn trans_fn_pointer_shim<'a, 'tcx>( debug!("tuple_fn_ty: {:?}", tuple_fn_ty); // - let function_name = method_instance.symbol_name(ccx.shared()); + let function_name = symbol_name(method_instance, ccx.shared()); let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty); attributes::set_frame_pointer_elimination(ccx, llfn); // @@ -489,21 +491,17 @@ fn trans_fn_pointer_shim<'a, 'tcx>( /// - `def_id`: def id of the fn or method item being referenced /// - `substs`: values for each of the fn/method's parameters fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx>) + instance: Instance<'tcx>) -> (ValueRef, Ty<'tcx>) { let tcx = ccx.tcx(); - debug!("get_fn(def_id={:?}, substs={:?})", def_id, substs); + debug!("get_fn(instance={:?})", instance); - assert!(!substs.needs_infer()); - assert!(!substs.has_escaping_regions()); - assert!(!substs.has_param_types()); - - let substs = tcx.normalize_associated_type(&substs); - let instance = Instance::new(def_id, substs); - let fn_ty = common::def_ty(ccx.shared(), def_id, substs); + assert!(!instance.substs.needs_infer()); + assert!(!instance.substs.has_escaping_regions()); + assert!(!instance.substs.has_param_types()); + let fn_ty = common::instance_ty(ccx.shared(), &instance); if let Some(&llfn) = ccx.instances().borrow().get(&instance) { return (llfn, fn_ty); } @@ -553,7 +551,7 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, assert_eq!(common::val_ty(llfn), llptrty); debug!("get_fn: not casting pointer!"); - let attrs = ccx.tcx().get_attrs(def_id); + let attrs = instance.def.attrs(ccx.tcx()); attributes::from_fn_attrs(ccx, &attrs, llfn); let is_local_def = ccx.shared().translation_items().borrow() @@ -565,7 +563,9 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::ExternalLinkage); } } - if ccx.use_dll_storage_attrs() && ccx.sess().cstore.is_dllimport_foreign_item(def_id) { + if ccx.use_dll_storage_attrs() && + ccx.sess().cstore.is_dllimport_foreign_item(instance.def_id()) + { unsafe { llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport); } diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 5e6b10f826ce0..271f91f9adbdb 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -207,7 +207,7 @@ use syntax_pos::DUMMY_SP; use base::custom_coerce_unsize_info; use callee::needs_fn_once_adapter_shim; use context::SharedCrateContext; -use common::{def_ty, fulfill_obligation}; +use common::{def_ty, find_method, instance_ty, fulfill_obligation}; use glue::{self, DropGlueKind}; use monomorphize::{self, Instance}; use util::nodemap::{FxHashSet, FxHashMap, DefIdMap}; @@ -337,21 +337,22 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>, } TransItem::Static(node_id) => { let def_id = scx.tcx().hir.local_def_id(node_id); + let instance = Instance::mono(scx.tcx(), def_id); // Sanity check whether this ended up being collected accidentally - debug_assert!(should_trans_locally(scx.tcx(), def_id)); + debug_assert!(should_trans_locally(scx.tcx(), &instance)); - let ty = def_ty(scx, def_id, Substs::empty()); + let ty = instance_ty(scx, &instance); let ty = glue::get_drop_glue_type(scx, ty); neighbors.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); recursion_depth_reset = None; - collect_neighbours(scx, Instance::mono(scx, def_id), &mut neighbors); + collect_neighbours(scx, instance, &mut neighbors); } TransItem::Fn(instance) => { // Sanity check whether this ended up being collected accidentally - debug_assert!(should_trans_locally(scx.tcx(), instance.def)); + debug_assert!(should_trans_locally(scx.tcx(), &instance)); // Keep track of the monomorphization recursion depth recursion_depth_reset = Some(check_recursion_limit(scx.tcx(), @@ -395,9 +396,8 @@ fn check_recursion_limit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, recursion_depths: &mut DefIdMap) -> (DefId, usize) { - let recursion_depth = recursion_depths.get(&instance.def) - .map(|x| *x) - .unwrap_or(0); + let def_id = instance.def_id(); + let recursion_depth = recursion_depths.get(&def_id).cloned().unwrap_or(0); debug!(" => recursion depth={}", recursion_depth); // Code that needs to instantiate the same function recursively @@ -406,16 +406,16 @@ fn check_recursion_limit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if recursion_depth > tcx.sess.recursion_limit.get() { let error = format!("reached the recursion limit while instantiating `{}`", instance); - if let Some(node_id) = tcx.hir.as_local_node_id(instance.def) { + if let Some(node_id) = tcx.hir.as_local_node_id(def_id) { tcx.sess.span_fatal(tcx.hir.span(node_id), &error); } else { tcx.sess.fatal(&error); } } - recursion_depths.insert(instance.def, recursion_depth + 1); + recursion_depths.insert(def_id, recursion_depth + 1); - (instance.def, recursion_depth) + (def_id, recursion_depth) } fn check_type_length_limit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -438,7 +438,7 @@ fn check_type_length_limit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let instance_name = instance.to_string(); let msg = format!("reached the type-length limit while instantiating `{:.64}...`", instance_name); - let mut diag = if let Some(node_id) = tcx.hir.as_local_node_id(instance.def) { + let mut diag = if let Some(node_id) = tcx.hir.as_local_node_id(instance.def_id()) { tcx.sess.struct_span_fatal(tcx.hir.span(node_id), &msg) } else { tcx.sess.struct_fatal(&msg) @@ -493,33 +493,24 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { let source_ty = operand.ty(self.mir, self.scx.tcx()); match source_ty.sty { ty::TyClosure(def_id, substs) => { - let closure_trans_item = - create_fn_trans_item(self.scx, - def_id, - substs.substs, - self.param_substs); - self.output.push(closure_trans_item); + let substs = monomorphize::apply_param_substs( + self.scx, self.param_substs, &substs.substs); + self.output.push(create_fn_trans_item( + Instance::new(def_id, substs) + )); } _ => bug!(), } } mir::Rvalue::Box(..) => { - let exchange_malloc_fn_def_id = - self.scx - .tcx() - .lang_items - .require(ExchangeMallocFnLangItem) - .unwrap_or_else(|e| self.scx.sess().fatal(&e)); - - if should_trans_locally(self.scx.tcx(), exchange_malloc_fn_def_id) { - let empty_substs = self.scx.empty_substs_for_def_id(exchange_malloc_fn_def_id); - let exchange_malloc_fn_trans_item = - create_fn_trans_item(self.scx, - exchange_malloc_fn_def_id, - empty_substs, - self.param_substs); - - self.output.push(exchange_malloc_fn_trans_item); + let tcx = self.scx.tcx(); + let exchange_malloc_fn_def_id = tcx + .lang_items + .require(ExchangeMallocFnLangItem) + .unwrap_or_else(|e| self.scx.sess().fatal(&e)); + let instance = Instance::mono(tcx, exchange_malloc_fn_def_id); + if should_trans_locally(tcx, &instance) { + self.output.push(create_fn_trans_item(instance)); } } _ => { /* not interesting */ } @@ -564,8 +555,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { let substs = monomorphize::apply_param_substs(self.scx, self.param_substs, &substs); - - let instance = Instance::new(def_id, substs).resolve_const(self.scx); + let instance = monomorphize::resolve_const(self.scx, def_id, substs); collect_neighbours(self.scx, instance, self.output); } @@ -586,28 +576,24 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { // // Calling do_static_dispatch() here will map the def_id of // `std::cmp::partial_cmp` to the def_id of `i32::partial_cmp` + + let callee_substs = monomorphize::apply_param_substs(self.scx, + self.param_substs, + &callee_substs); let dispatched = do_static_dispatch(self.scx, callee_def_id, - callee_substs, - self.param_substs); + callee_substs); if let StaticDispatchResult::Dispatched { - def_id: callee_def_id, - substs: callee_substs, - fn_once_adjustment, - } = dispatched { + instance, fn_once_adjustment + } = 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.scx.tcx(), callee_def_id) { - // ... and create one if it does. - let trans_item = create_fn_trans_item(self.scx, - callee_def_id, - callee_substs, - self.param_substs); - self.output.push(trans_item); + if should_trans_locally(self.scx.tcx(), &instance) { + self.output.push(create_fn_trans_item(instance)); // This call will instantiate an FnOnce adapter, which drops // the closure environment. Therefore we need to make sure @@ -624,26 +610,6 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { } self.super_operand(operand, location); - - fn can_result_in_trans_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> bool { - match tcx.item_type(def_id).sty { - ty::TyFnDef(def_id, _, _) => { - // foreign items are linked from another library, not - // translated locally. - if let Some(hir_map::NodeForeignItem(_)) = tcx.hir.get_if_local(def_id) { - return false; - } - } - ty::TyClosure(..) => { - // TODO: trans items for closures - } - _ => return false - } - - should_trans_locally(tcx, def_id) - } } // This takes care of the "drop_in_place" intrinsic for which we otherwise @@ -695,22 +661,30 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { // Returns true if we should translate an instance in the local crate. // Returns false if we can just link to the upstream crate and therefore don't // need a translation item. -fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) +fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instance<'tcx>) -> bool { - if def_id.is_local() { - true - } else { - if tcx.sess.cstore.is_exported_symbol(def_id) || - tcx.sess.cstore.is_foreign_item(def_id) { - // We can link to the item in question, no instance needed in this - // crate - false - } else { - if !tcx.sess.cstore.is_item_mir_available(def_id) { - bug!("Cannot create local trans-item for {:?}", def_id) + let def_id = match instance.def { + ty::InstanceDef::Item(def_id) => def_id, + ty::InstanceDef::FnPtrShim(..) => return true + }; + match tcx.hir.get_if_local(def_id) { + Some(hir_map::NodeForeignItem(..)) => { + false // foreign items are linked against, not translated. + } + Some(_) => true, + None => { + if tcx.sess.cstore.is_exported_symbol(def_id) || + tcx.sess.cstore.is_foreign_item(def_id) + { + // We can link to the item in question, no instance needed + // in this crate + false + } else { + if !tcx.sess.cstore.is_item_mir_available(def_id) { + bug!("Cannot create local trans-item for {:?}", def_id) + } + true } - true } } } @@ -731,14 +705,14 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, // Make sure the BoxFreeFn lang-item gets translated if there is a boxed value. if ty.is_box() { - let def_id = scx.tcx().require_lang_item(BoxFreeFnLangItem); - if should_trans_locally(scx.tcx(), def_id) { - let box_free_fn_trans_item = - create_fn_trans_item(scx, - def_id, - scx.tcx().mk_substs(iter::once(Kind::from(ty.boxed_ty()))), - scx.tcx().intern_substs(&[])); - output.push(box_free_fn_trans_item); + let tcx = scx.tcx(); + let def_id = tcx.require_lang_item(BoxFreeFnLangItem); + let box_free_instance = Instance::new( + def_id, + tcx.mk_substs(iter::once(Kind::from(ty.boxed_ty()))) + ); + if should_trans_locally(tcx, &box_free_instance) { + output.push(create_fn_trans_item(box_free_instance)); } } @@ -768,13 +742,9 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, traits::VtableImpl(data) => data.substs, _ => bug!() }; - - if should_trans_locally(scx.tcx(), destructor.did) { - let trans_item = create_fn_trans_item(scx, - destructor.did, - substs, - scx.tcx().intern_substs(&[])); - output.push(trans_item); + let instance = Instance::new(destructor.did, substs); + if should_trans_locally(scx.tcx(), &instance) { + output.push(create_fn_trans_item(instance)); } // This type has a Drop implementation, we'll need the contents-only @@ -847,72 +817,59 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, } } +enum StaticDispatchResult<'tcx> { + // The call could be resolved statically as going to the method with + // `instance`. + Dispatched { + instance: Instance<'tcx>, + // If this is a call to a closure that needs an FnOnce adjustment, + // this contains the new self type of the call (= type of the closure + // environment) + fn_once_adjustment: Option>, + }, + // This goes to somewhere that we don't know at compile-time + Unknown +} + fn do_static_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, fn_def_id: DefId, - fn_substs: &'tcx Substs<'tcx>, - param_substs: &'tcx Substs<'tcx>) + fn_substs: &'tcx Substs<'tcx>) -> StaticDispatchResult<'tcx> { - debug!("do_static_dispatch(fn_def_id={}, fn_substs={:?}, param_substs={:?})", + debug!("do_static_dispatch(fn_def_id={}, fn_substs={:?})", def_id_to_string(scx.tcx(), fn_def_id), - fn_substs, - param_substs); - + fn_substs); if let Some(trait_def_id) = scx.tcx().trait_of_item(fn_def_id) { debug!(" => trait method, attempting to find impl"); do_static_trait_method_dispatch(scx, &scx.tcx().associated_item(fn_def_id), trait_def_id, - fn_substs, - param_substs) + fn_substs) } else { debug!(" => regular function"); // The function is not part of an impl or trait, no dispatching // to be done StaticDispatchResult::Dispatched { - def_id: fn_def_id, - substs: fn_substs, + instance: Instance::new(fn_def_id, fn_substs), fn_once_adjustment: None, } } } -enum StaticDispatchResult<'tcx> { - // The call could be resolved statically as going to the method with - // `def_id` and `substs`. - Dispatched { - def_id: DefId, - substs: &'tcx Substs<'tcx>, - - // If this is a call to a closure that needs an FnOnce adjustment, - // this contains the new self type of the call (= type of the closure - // environment) - fn_once_adjustment: Option>, - }, - // This goes to somewhere that we don't know at compile-time - Unknown -} - // Given a trait-method and substitution information, find out the actual // implementation of the trait method. fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, trait_method: &ty::AssociatedItem, trait_id: DefId, - callee_substs: &'tcx Substs<'tcx>, - param_substs: &'tcx Substs<'tcx>) + rcvr_substs: &'tcx Substs<'tcx>) -> StaticDispatchResult<'tcx> { let tcx = scx.tcx(); debug!("do_static_trait_method_dispatch(trait_method={}, \ trait_id={}, \ - callee_substs={:?}, \ - param_substs={:?}", + rcvr_substs={:?})", def_id_to_string(scx.tcx(), trait_method.def_id), def_id_to_string(scx.tcx(), trait_id), - callee_substs, - param_substs); + rcvr_substs); - let rcvr_substs = monomorphize::apply_param_substs(scx, - param_substs, - &callee_substs); let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); let vtbl = fulfill_obligation(scx, DUMMY_SP, ty::Binder(trait_ref)); @@ -920,13 +877,8 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, // the actual function: match vtbl { traits::VtableImpl(impl_data) => { - let (def_id, substs) = traits::find_method(tcx, - trait_method.name, - rcvr_substs, - &impl_data); StaticDispatchResult::Dispatched { - def_id: def_id, - substs: substs, + instance: find_method(tcx, trait_method.name, rcvr_substs, &impl_data), fn_once_adjustment: None, } } @@ -950,8 +902,7 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, }; StaticDispatchResult::Dispatched { - def_id: closure_def_id, - substs: closure_data.substs.substs, + instance: Instance::new(closure_def_id, closure_data.substs.substs), fn_once_adjustment: fn_once_adjustment, } } @@ -961,7 +912,7 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, if let ty::TyFnDef(def_id, substs, _) = data.fn_ty.sty { // The destination of the pointer might be something that needs // further dispatching, such as a trait method, so we do that. - do_static_dispatch(scx, def_id, substs, param_substs) + do_static_dispatch(scx, def_id, substs) } else { StaticDispatchResult::Unknown } @@ -1066,28 +1017,9 @@ fn find_vtable_types_for_unsizing<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, } } -fn create_fn_trans_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, - def_id: DefId, - fn_substs: &'tcx Substs<'tcx>, - param_substs: &'tcx Substs<'tcx>) - -> TransItem<'tcx> { - let tcx = scx.tcx(); - - debug!("create_fn_trans_item(def_id={}, fn_substs={:?}, param_substs={:?})", - def_id_to_string(tcx, def_id), - 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(scx, - param_substs, - &fn_substs); - assert!(concrete_substs.is_normalized_for_trans(), - "concrete_substs not normalized for trans: {:?}", - concrete_substs); - TransItem::Fn(Instance::new(def_id, concrete_substs)) +fn create_fn_trans_item<'a, 'tcx>(instance: Instance<'tcx>) -> TransItem<'tcx> { + debug!("create_fn_trans_item(instance={})", instance); + TransItem::Fn(instance) } /// Creates a `TransItem` for each method that is referenced by the vtable for @@ -1102,8 +1034,6 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(scx: &SharedCrateContext<'a, if let ty::TyDynamic(ref trait_ty, ..) = trait_ty.sty { if let Some(principal) = trait_ty.principal() { let poly_trait_ref = principal.with_self_ty(scx.tcx(), impl_ty); - let param_substs = scx.tcx().intern_substs(&[]); - assert!(!poly_trait_ref.has_escaping_regions()); // Walk all methods of the trait, including those of its supertraits @@ -1111,19 +1041,18 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(scx: &SharedCrateContext<'a, let methods = methods.filter_map(|method| method) .filter_map(|(def_id, substs)| { if let StaticDispatchResult::Dispatched { - def_id, - substs, + instance, // We already add the drop-glue for the closure env // unconditionally below. fn_once_adjustment: _ , - } = do_static_dispatch(scx, def_id, substs, param_substs) { - Some((def_id, substs)) + } = do_static_dispatch(scx, def_id, substs) { + Some(instance) } else { None } }) - .filter(|&(def_id, _)| should_trans_locally(scx.tcx(), def_id)) - .map(|(def_id, substs)| create_fn_trans_item(scx, def_id, substs, param_substs)); + .filter(|&instance| should_trans_locally(scx.tcx(), &instance)) + .map(|instance| create_fn_trans_item(instance)); output.extend(methods); } // Also add the destructor @@ -1195,7 +1124,7 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> { debug!("RootCollector: ItemFn({})", def_id_to_string(self.scx.tcx(), def_id)); - let instance = Instance::mono(self.scx, def_id); + let instance = Instance::mono(self.scx.tcx(), def_id); self.output.push(TransItem::Fn(instance)); } } @@ -1233,7 +1162,7 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> { debug!("RootCollector: MethodImplItem({})", def_id_to_string(self.scx.tcx(), def_id)); - let instance = Instance::mono(self.scx, def_id); + let instance = Instance::mono(self.scx.tcx(), def_id); self.output.push(TransItem::Fn(instance)); } } @@ -1278,31 +1207,22 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(scx: &SharedCrateContext<'a, ' // The substitutions we have are on the impl, so we grab // the method type from the impl to substitute into. - let impl_substs = Substs::for_item(tcx, impl_def_id, - |_, _| tcx.mk_region(ty::ReErased), - |_, _| tcx.types.err); + let impl_substs = tcx.empty_substs_for_def_id(impl_def_id); let impl_data = traits::VtableImplData { impl_def_id: impl_def_id, substs: impl_substs, nested: vec![] }; - let (def_id, substs) = traits::find_method(tcx, - method.name, - callee_substs, - &impl_data); + let instance = find_method(tcx, method.name, callee_substs, &impl_data); - let predicates = tcx.item_predicates(def_id).predicates - .subst(tcx, substs); + let predicates = tcx.item_predicates(instance.def_id()).predicates + .subst(tcx, impl_substs); if !traits::normalize_and_test_predicates(tcx, predicates) { continue; } - if should_trans_locally(tcx, method.def_id) { - let item = create_fn_trans_item(scx, - method.def_id, - callee_substs, - tcx.erase_regions(&substs)); - output.push(item); + if should_trans_locally(tcx, &instance) { + output.push(create_fn_trans_item(instance)); } } } @@ -1318,7 +1238,7 @@ fn collect_neighbours<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, instance: Instance<'tcx>, output: &mut Vec>) { - let mir = scx.tcx().item_mir(instance.def); + let mir = scx.tcx().instance_mir(instance.def); let mut visitor = MirNeighborCollector { scx: scx, diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index 0e536d58a56fb..4389207cdf294 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -602,7 +602,14 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } } -pub fn requests_inline(tcx: TyCtxt, def_id: DefId) -> bool { +pub fn requests_inline<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance: &ty::Instance<'tcx> +) -> bool { + let def_id = match instance.def { + ty::InstanceDef::Item(def_id) => def_id, + _ => return true + }; match tcx.def_key(def_id).disambiguated_data.data { DefPathData::StructCtor | DefPathData::EnumVariant(..) | @@ -610,7 +617,6 @@ pub fn requests_inline(tcx: TyCtxt, def_id: DefId) -> bool { _ => attr::requests_inline(&tcx.get_attrs(def_id)[..]), } } - /// Given a DefId and some Substs, produces the monomorphic item type. pub fn def_ty<'a, 'tcx>(shared: &SharedCrateContext<'a, 'tcx>, def_id: DefId, @@ -620,3 +626,23 @@ pub fn def_ty<'a, 'tcx>(shared: &SharedCrateContext<'a, 'tcx>, let ty = shared.tcx().item_type(def_id); monomorphize::apply_param_substs(shared, substs, &ty) } + +/// Return the substituted type of an instance. +pub fn instance_ty<'a, 'tcx>(shared: &SharedCrateContext<'a, 'tcx>, + instance: &ty::Instance<'tcx>) + -> Ty<'tcx> +{ + let ty = instance.def.def_ty(shared.tcx()); + monomorphize::apply_param_substs(shared, instance.substs, &ty) +} + +pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + name: ast::Name, + substs: &'tcx Substs<'tcx>, + impl_data: &traits::VtableImplData<'tcx, ()>) + -> ty::Instance<'tcx> +{ + let (def_id, substs) = traits::find_method(tcx, name, substs, impl_data); + let substs = tcx.erase_regions(&substs); + ty::Instance::new(def_id, substs) +} diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs index bf1d9886ae7f0..0c3d211912add 100644 --- a/src/librustc_trans/consts.rs +++ b/src/librustc_trans/consts.rs @@ -9,6 +9,7 @@ // except according to those terms. +use back::symbol_names; use llvm; use llvm::{SetUnnamedAddr}; use llvm::{ValueRef, True}; @@ -24,7 +25,6 @@ use monomorphize::Instance; use type_::Type; use type_of; use rustc::ty; -use rustc::ty::subst::Substs; use rustc::hir; @@ -80,12 +80,12 @@ pub fn addr_of(ccx: &CrateContext, } pub fn get_static(ccx: &CrateContext, def_id: DefId) -> ValueRef { - let instance = Instance::mono(ccx.shared(), def_id); + let instance = Instance::mono(ccx.tcx(), def_id); if let Some(&g) = ccx.instances().borrow().get(&instance) { return g; } - let ty = common::def_ty(ccx.shared(), def_id, Substs::empty()); + let ty = common::instance_ty(ccx.shared(), &instance); let g = if let Some(id) = ccx.tcx().hir.as_local_node_id(def_id) { let llty = type_of::type_of(ccx, ty); @@ -114,7 +114,7 @@ pub fn get_static(ccx: &CrateContext, def_id: DefId) -> ValueRef { hir_map::NodeForeignItem(&hir::ForeignItem { ref attrs, span, node: hir::ForeignItemStatic(..), .. }) => { - let sym = instance.symbol_name(ccx.shared()); + let sym = symbol_names::symbol_name(instance, ccx.shared()); let g = if let Some(name) = attr::first_attr_value_str_by_name(&attrs, "linkage") { // If this is a static with a linkage specified, then we need to handle @@ -174,7 +174,7 @@ pub fn get_static(ccx: &CrateContext, def_id: DefId) -> ValueRef { g } else { - let sym = instance.symbol_name(ccx.shared()); + let sym = symbol_names::symbol_name(instance, ccx.shared()); // FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow? // FIXME(nagisa): investigate whether it can be changed into define_global @@ -235,7 +235,8 @@ pub fn trans_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, v }; - let ty = common::def_ty(ccx.shared(), def_id, Substs::empty()); + let instance = Instance::mono(ccx.tcx(), def_id); + let ty = common::instance_ty(ccx.shared(), &instance); let llty = type_of::type_of(ccx, ty); let g = if val_llty == llty { g diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index 52851ea995d4b..b7381dd07dcfa 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -546,16 +546,6 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { &self.translation_items } - /// Given the def-id of some item that has no type parameters, make - /// a suitable "empty substs" for it. - pub fn empty_substs_for_def_id(&self, item_def_id: DefId) -> &'tcx Substs<'tcx> { - Substs::for_item(self.tcx(), item_def_id, - |_, _| self.tcx().mk_region(ty::ReErased), - |_, _| { - bug!("empty_substs_for_def_id: {:?} has type parameters", item_def_id) - }) - } - pub fn metadata_symbol_name(&self) -> String { format!("rust_metadata_{}_{}", self.link_meta().crate_name, @@ -886,7 +876,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { /// Given the def-id of some item that has no type parameters, make /// a suitable "empty substs" for it. pub fn empty_substs_for_def_id(&self, item_def_id: DefId) -> &'tcx Substs<'tcx> { - self.shared().empty_substs_for_def_id(item_def_id) + self.tcx().empty_substs_for_def_id(item_def_id) } /// Generate a new symbol name with the given prefix. This symbol name must diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs index 1d4aebf135b9e..8e86b50b3f7dd 100644 --- a/src/librustc_trans/debuginfo/mod.rs +++ b/src/librustc_trans/debuginfo/mod.rs @@ -205,7 +205,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, return FunctionDebugContext::DebugInfoDisabled; } - for attr in cx.tcx().get_attrs(instance.def).iter() { + for attr in instance.def.attrs(cx.tcx()).iter() { if attr.check_name("no_debug") { return FunctionDebugContext::FunctionWithoutDebugInfo; } @@ -229,11 +229,11 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, }; // Find the enclosing function, in case this is a closure. - let def_key = cx.tcx().def_key(instance.def); + let def_key = cx.tcx().def_key(instance.def_id()); let mut name = def_key.disambiguated_data.data.to_string(); let name_len = name.len(); - let fn_def_id = cx.tcx().closure_base_def_id(instance.def); + let fn_def_id = cx.tcx().closure_base_def_id(instance.def_id()); // Get_template_parameters() will append a `<...>` clause to the function // name if necessary. @@ -246,11 +246,11 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, &mut name); // Build the linkage_name out of the item path and "template" parameters. - let linkage_name = mangled_name_of_item(cx, instance.def, &name[name_len..]); + let linkage_name = mangled_name_of_item(cx, instance.def_id(), &name[name_len..]); let scope_line = span_start(cx, span).line; - let local_id = cx.tcx().hir.as_local_node_id(instance.def); + let local_id = cx.tcx().hir.as_local_node_id(instance.def_id()); let is_local_to_unit = local_id.map_or(false, |id| is_node_local_to_unit(cx, id)); let function_name = CString::new(name).unwrap(); @@ -394,7 +394,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, // First, let's see if this is a method within an inherent impl. Because // if yes, we want to make the result subroutine DIE a child of the // subroutine's self-type. - let self_type = cx.tcx().impl_of_method(instance.def).and_then(|impl_def_id| { + let self_type = cx.tcx().impl_of_method(instance.def_id()).and_then(|impl_def_id| { // If the method does *not* belong to a trait, proceed if cx.tcx().trait_id_of_impl(impl_def_id).is_none() { let impl_self_ty = @@ -417,9 +417,9 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, self_type.unwrap_or_else(|| { namespace::item_namespace(cx, DefId { - krate: instance.def.krate, + krate: instance.def_id().krate, index: cx.tcx() - .def_key(instance.def) + .def_key(instance.def_id()) .parent .expect("get_containing_scope: missing parent?") }) diff --git a/src/librustc_trans/meth.rs b/src/librustc_trans/meth.rs index a3f4168e96f2a..bfd9f69a92218 100644 --- a/src/librustc_trans/meth.rs +++ b/src/librustc_trans/meth.rs @@ -21,6 +21,7 @@ use machine; use monomorphize::Instance; use type_::Type; use type_of::*; +use back::symbol_names; use value::Value; use rustc::ty; @@ -70,7 +71,7 @@ pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, let function_name = match callee.ty.sty { ty::TyFnDef(def_id, substs, _) => { let instance = Instance::new(def_id, substs); - instance.symbol_name(ccx.shared()) + symbol_names::symbol_name(instance, ccx.shared()) } _ => bug!() }; diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index b6fcc990344ce..deb1073cf9aed 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -30,7 +30,7 @@ use common::{C_array, C_bool, C_bytes, C_floating_f64, C_integral, C_big_integra use common::{C_null, C_struct, C_str_slice, C_undef, C_uint, C_vector, is_undef}; use common::const_to_opt_u128; use consts; -use monomorphize::{self, Instance}; +use monomorphize; use type_of; use type_::Type; use value::Value; @@ -245,11 +245,12 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { } fn trans_def(ccx: &'a CrateContext<'a, 'tcx>, - instance: Instance<'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>, args: IndexVec>) -> Result, ConstEvalErr<'tcx>> { - let instance = instance.resolve_const(ccx.shared()); - let mir = ccx.tcx().item_mir(instance.def); + let instance = monomorphize::resolve_const(ccx.shared(), def_id, substs); + let mir = ccx.tcx().instance_mir(instance.def); MirConstContext::new(ccx, &mir, instance.substs, args).trans() } @@ -332,10 +333,8 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { mir::TerminatorKind::Call { ref func, ref args, ref destination, .. } => { let fn_ty = func.ty(self.mir, tcx); let fn_ty = self.monomorphize(&fn_ty); - let instance = match fn_ty.sty { - ty::TyFnDef(def_id, substs, _) => { - Instance::new(def_id, substs) - } + let (def_id, substs) = match fn_ty.sty { + ty::TyFnDef(def_id, substs, _) => (def_id, substs), _ => span_bug!(span, "calling {:?} (of type {}) in constant", func, fn_ty) }; @@ -348,7 +347,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { } } if let Some((ref dest, target)) = *destination { - match MirConstContext::trans_def(self.ccx, instance, const_args) { + match MirConstContext::trans_def(self.ccx, def_id, substs, const_args) { Ok(value) => self.store(dest, value, span), Err(err) => if failure.is_ok() { failure = Err(err); } } @@ -485,8 +484,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { } let substs = self.monomorphize(&substs); - let instance = Instance::new(def_id, substs); - MirConstContext::trans_def(self.ccx, instance, IndexVec::new()) + MirConstContext::trans_def(self.ccx, def_id, substs, IndexVec::new()) } mir::Literal::Promoted { index } => { let mir = &self.mir.promoted[index]; @@ -588,7 +586,8 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { // Now create its substs [Closure, Tuple] let input = tcx.closure_type(def_id) .subst(tcx, substs.substs).input(0); - let substs = tcx.mk_substs([operand.ty, input.skip_binder()] + let input = tcx.erase_late_bound_regions_and_normalize(&input); + let substs = tcx.mk_substs([operand.ty, input] .iter().cloned().map(Kind::from)); Callee::def(self.ccx, call_once, substs) .reify(self.ccx) @@ -935,8 +934,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } let substs = self.monomorphize(&substs); - let instance = Instance::new(def_id, substs); - MirConstContext::trans_def(bcx.ccx, instance, IndexVec::new()) + MirConstContext::trans_def(bcx.ccx, def_id, substs, IndexVec::new()) } mir::Literal::Promoted { index } => { let mir = &self.mir.promoted[index]; @@ -964,8 +962,8 @@ pub fn trans_static_initializer<'a, 'tcx>( def_id: DefId) -> Result> { - let instance = Instance::mono(ccx.shared(), def_id); - MirConstContext::trans_def(ccx, instance, IndexVec::new()).map(|c| c.llval) + MirConstContext::trans_def(ccx, def_id, Substs::empty(), IndexVec::new()) + .map(|c| c.llval) } /// Construct a constant value, suitable for initializing a diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index b6af4e52e820b..bbaf0f9d35fa6 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -203,7 +203,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // Now create its substs [Closure, Tuple] let input = bcx.tcx().closure_type(def_id) .subst(bcx.tcx(), substs.substs).input(0); - let substs = bcx.tcx().mk_substs([operand.ty, input.skip_binder()] + let input = + bcx.tcx().erase_late_bound_regions_and_normalize(&input); + let substs = bcx.tcx().mk_substs([operand.ty, input] .iter().cloned().map(Kind::from)); OperandValue::Immediate( Callee::def(bcx.ccx, call_once, substs) diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs index 4b31d5b7f88de..3b746af275a2a 100644 --- a/src/librustc_trans/monomorphize.rs +++ b/src/librustc_trans/monomorphize.rs @@ -15,54 +15,31 @@ use rustc::traits; use rustc::ty::fold::{TypeFolder, TypeFoldable}; use rustc::ty::subst::{Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::util::ppaux; use rustc::util::common::MemoizationMap; use syntax::codemap::DUMMY_SP; -use std::fmt; - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct Instance<'tcx> { - pub def: DefId, - pub substs: &'tcx Substs<'tcx>, -} - -impl<'tcx> fmt::Display for Instance<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ppaux::parameterized(f, &self.substs, self.def, &[]) - } -} - -impl<'a, 'tcx> Instance<'tcx> { - pub fn new(def_id: DefId, substs: &'tcx Substs<'tcx>) - -> Instance<'tcx> { - assert!(substs.regions().all(|&r| r == ty::ReErased)); - Instance { def: def_id, substs: substs } - } - - pub fn mono(scx: &SharedCrateContext<'a, 'tcx>, def_id: DefId) -> Instance<'tcx> { - Instance::new(def_id, scx.empty_substs_for_def_id(def_id)) - } - - /// For associated constants from traits, return the impl definition. - pub fn resolve_const(&self, scx: &SharedCrateContext<'a, 'tcx>) -> Self { - if let Some(trait_id) = scx.tcx().trait_of_item(self.def) { - let trait_ref = ty::TraitRef::new(trait_id, self.substs); - let trait_ref = ty::Binder(trait_ref); - let vtable = fulfill_obligation(scx, DUMMY_SP, trait_ref); - if let traits::VtableImpl(vtable_impl) = vtable { - let name = scx.tcx().item_name(self.def); - let ac = scx.tcx().associated_items(vtable_impl.impl_def_id) - .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name); - if let Some(ac) = ac { - return Instance::new(ac.def_id, vtable_impl.substs); - } +pub use rustc::ty::Instance; + +/// For associated constants from traits, return the impl definition. +pub fn resolve_const<'a, 'tcx>( + scx: &SharedCrateContext<'a, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx> +) -> Instance<'tcx> { + if let Some(trait_id) = scx.tcx().trait_of_item(def_id) { + let trait_ref = ty::TraitRef::new(trait_id, substs); + let trait_ref = ty::Binder(trait_ref); + let vtable = fulfill_obligation(scx, DUMMY_SP, trait_ref); + if let traits::VtableImpl(vtable_impl) = vtable { + let name = scx.tcx().item_name(def_id); + let ac = scx.tcx().associated_items(vtable_impl.impl_def_id) + .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name); + if let Some(ac) = ac { + return Instance::new(ac.def_id, vtable_impl.substs); } } - - *self } + + Instance::new(def_id, substs) } /// Monomorphizes a type from the AST by first applying the in-scope diff --git a/src/librustc_trans/partitioning.rs b/src/librustc_trans/partitioning.rs index cc9fd8f46f6f0..1232c6cd28e52 100644 --- a/src/librustc_trans/partitioning.rs +++ b/src/librustc_trans/partitioning.rs @@ -110,7 +110,7 @@ use rustc::dep_graph::{DepNode, WorkProductId}; use rustc::hir::def_id::DefId; use rustc::hir::map::DefPathData; use rustc::session::config::NUMBERED_CODEGEN_UNIT_MARKER; -use rustc::ty::TyCtxt; +use rustc::ty::{self, TyCtxt}; use rustc::ty::item_path::characteristic_def_id_of_type; use rustc_incremental::IchHasher; use std::cmp::Ordering; @@ -186,7 +186,8 @@ impl<'tcx> CodegenUnit<'tcx> { symbol_name.hash(&mut state); let exported = match item { TransItem::Fn(ref instance) => { - let node_id = scx.tcx().hir.as_local_node_id(instance.def); + let node_id = + scx.tcx().hir.as_local_node_id(instance.def_id()); node_id.map(|node_id| exported_symbols.contains(&node_id)) .unwrap_or(false) } @@ -241,7 +242,7 @@ impl<'tcx> CodegenUnit<'tcx> { fn local_node_id(tcx: TyCtxt, trans_item: TransItem) -> Option { match trans_item { TransItem::Fn(instance) => { - tcx.hir.as_local_node_id(instance.def) + tcx.hir.as_local_node_id(instance.def_id()) } TransItem::Static(node_id) => Some(node_id), TransItem::DropGlue(_) => None, @@ -455,17 +456,22 @@ fn characteristic_def_id_of_trans_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 't let tcx = scx.tcx(); match trans_item { TransItem::Fn(instance) => { + let def_id = match instance.def { + ty::InstanceDef::Item(def_id) => def_id, + ty::InstanceDef::FnPtrShim(..) => return None + }; + // If this is a method, we want to put it into the same module as // its self-type. If the self-type does not provide a characteristic // DefId, we use the location of the impl after all. - if tcx.trait_of_item(instance.def).is_some() { + if tcx.trait_of_item(def_id).is_some() { let self_ty = instance.substs.type_at(0); // This is an implementation of a trait method. - return characteristic_def_id_of_type(self_ty).or(Some(instance.def)); + return characteristic_def_id_of_type(self_ty).or(Some(def_id)); } - if let Some(impl_def_id) = tcx.impl_of_method(instance.def) { + if let Some(impl_def_id) = tcx.impl_of_method(def_id) { // This is a method within an inherent impl, find out what the // self-type is: let impl_self_ty = common::def_ty(scx, impl_def_id, instance.substs); @@ -474,7 +480,7 @@ fn characteristic_def_id_of_trans_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 't } } - Some(instance.def) + Some(def_id) } TransItem::DropGlue(dg) => characteristic_def_id_of_type(dg.ty()), TransItem::Static(node_id) => Some(tcx.hir.local_def_id(node_id)), diff --git a/src/librustc_trans/symbol_map.rs b/src/librustc_trans/symbol_map.rs index 880c65937e308..cd285bfaa6010 100644 --- a/src/librustc_trans/symbol_map.rs +++ b/src/librustc_trans/symbol_map.rs @@ -97,7 +97,7 @@ impl<'tcx> SymbolMap<'tcx> { trans_item: TransItem<'tcx>) -> Option { match trans_item { TransItem::Fn(Instance { def, .. }) => { - tcx.hir.as_local_node_id(def) + tcx.hir.as_local_node_id(def.def_id()) } TransItem::Static(node_id) => Some(node_id), TransItem::DropGlue(_) => None, diff --git a/src/librustc_trans/symbol_names_test.rs b/src/librustc_trans/symbol_names_test.rs index 02e1290b57783..fe551b06b3d95 100644 --- a/src/librustc_trans/symbol_names_test.rs +++ b/src/librustc_trans/symbol_names_test.rs @@ -14,6 +14,7 @@ //! item-path. This is used for unit testing the code that generates //! paths etc in all kinds of annoying scenarios. +use back::symbol_names; use rustc::hir; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use syntax::ast; @@ -51,8 +52,8 @@ impl<'a, 'tcx> SymbolNamesTest<'a, 'tcx> { for attr in tcx.get_attrs(def_id).iter() { if attr.check_name(SYMBOL_NAME) { // for now, can only use on monomorphic names - let instance = Instance::mono(self.scx, def_id); - let name = instance.symbol_name(self.scx); + let instance = Instance::mono(tcx, def_id); + let name = symbol_names::symbol_name(instance, self.scx); tcx.sess.span_err(attr.span, &format!("symbol-name({})", name)); } else if attr.check_name(ITEM_PATH) { let path = tcx.item_path_str(def_id); @@ -86,4 +87,3 @@ impl<'a, 'tcx> Visitor<'tcx> for SymbolNamesTest<'a, 'tcx> { intravisit::walk_impl_item(self, ii) } } - diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs index 5ec9c2a59957d..13af081e0b698 100644 --- a/src/librustc_trans/trans_item.rs +++ b/src/librustc_trans/trans_item.rs @@ -96,7 +96,7 @@ impl<'a, 'tcx> TransItem<'tcx> { } TransItem::Fn(instance) => { let _task = ccx.tcx().dep_graph.in_task( - DepNode::TransCrateItem(instance.def)); // (*) + DepNode::TransCrateItem(instance.def_id())); // (*) base::trans_instance(&ccx, instance); } @@ -147,7 +147,8 @@ impl<'a, 'tcx> TransItem<'tcx> { linkage: llvm::Linkage, symbol_name: &str) { let def_id = ccx.tcx().hir.local_def_id(node_id); - let ty = common::def_ty(ccx.shared(), def_id, Substs::empty()); + let instance = Instance::mono(ccx.tcx(), def_id); + let ty = common::instance_ty(ccx.shared(), &instance); let llty = type_of::type_of(ccx, ty); let g = declare::define_global(ccx, symbol_name, llty).unwrap_or_else(|| { @@ -157,7 +158,6 @@ impl<'a, 'tcx> TransItem<'tcx> { unsafe { llvm::LLVMRustSetLinkage(g, linkage) }; - let instance = Instance::mono(ccx.shared(), def_id); ccx.instances().borrow_mut().insert(instance, g); ccx.statics().borrow_mut().insert(g, def_id); } @@ -169,8 +169,8 @@ impl<'a, 'tcx> TransItem<'tcx> { assert!(!instance.substs.needs_infer() && !instance.substs.has_param_types()); - let mono_ty = common::def_ty(ccx.shared(), instance.def, instance.substs); - let attrs = ccx.tcx().get_attrs(instance.def); + let mono_ty = common::instance_ty(ccx.shared(), &instance); + let attrs = instance.def.attrs(ccx.tcx()); let lldecl = declare::declare_fn(ccx, symbol_name, mono_ty); unsafe { llvm::LLVMRustSetLinkage(lldecl, linkage) }; base::set_link_section(ccx, lldecl, &attrs); @@ -180,7 +180,7 @@ impl<'a, 'tcx> TransItem<'tcx> { } debug!("predefine_fn: mono_ty = {:?} instance = {:?}", mono_ty, instance); - match ccx.tcx().def_key(instance.def).disambiguated_data.data { + match ccx.tcx().def_key(instance.def_id()).disambiguated_data.data { DefPathData::StructCtor | DefPathData::EnumVariant(..) | DefPathData::ClosureExpr => { @@ -229,10 +229,10 @@ impl<'a, 'tcx> TransItem<'tcx> { pub fn compute_symbol_name(&self, scx: &SharedCrateContext<'a, 'tcx>) -> String { match *self { - TransItem::Fn(instance) => instance.symbol_name(scx), + TransItem::Fn(instance) => symbol_names::symbol_name(instance, scx), TransItem::Static(node_id) => { let def_id = scx.tcx().hir.local_def_id(node_id); - Instance::mono(scx, def_id).symbol_name(scx) + symbol_names::symbol_name(Instance::mono(scx.tcx(), def_id), scx) } TransItem::DropGlue(dg) => { let prefix = match dg { @@ -244,21 +244,13 @@ impl<'a, 'tcx> TransItem<'tcx> { } } - pub fn is_from_extern_crate(&self) -> bool { - match *self { - TransItem::Fn(ref instance) => !instance.def.is_local(), - TransItem::DropGlue(..) | - TransItem::Static(..) => false, - } - } - pub fn instantiation_mode(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> InstantiationMode { match *self { TransItem::Fn(ref instance) => { if self.explicit_linkage(tcx).is_none() && - common::requests_inline(tcx, instance.def) + common::requests_inline(tcx, instance) { InstantiationMode::LocalCopy } else { @@ -282,7 +274,7 @@ impl<'a, 'tcx> TransItem<'tcx> { pub fn explicit_linkage(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Option { let def_id = match *self { - TransItem::Fn(ref instance) => instance.def, + TransItem::Fn(ref instance) => instance.def_id(), TransItem::Static(node_id) => tcx.hir.local_def_id(node_id), TransItem::DropGlue(..) => return None, }; @@ -587,7 +579,7 @@ impl<'a, 'tcx> DefPathBasedNames<'a, 'tcx> { pub fn push_instance_as_string(&self, instance: Instance<'tcx>, output: &mut String) { - self.push_def_path(instance.def, output); + self.push_def_path(instance.def_id(), output); self.push_type_params(instance.substs, iter::empty(), output); } } From bf80fec326d0fa7b58882d6f2102808a3f220651 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Mon, 6 Mar 2017 12:58:51 +0200 Subject: [PATCH 47/54] translate function shims using MIR --- src/librustc_mir/shim.rs | 127 ++++++++++++++++- src/librustc_trans/callee.rs | 134 +----------------- src/librustc_trans/collector.rs | 14 +- .../item-collection/function-as-argument.rs | 2 + .../trait-method-as-argument.rs | 6 + src/test/run-pass/mir_calls_to_shims.rs | 56 ++++++++ 6 files changed, 199 insertions(+), 140 deletions(-) create mode 100644 src/test/run-pass/mir_calls_to_shims.rs diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 0fbd488ffa360..bf0b1796dffaf 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -12,30 +12,42 @@ use rustc::hir; use rustc::infer; use rustc::mir::*; use rustc::mir::transform::MirSource; -use rustc::ty; +use rustc::ty::{self, Ty}; use rustc::ty::maps::Providers; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; +use syntax::abi::Abi; use syntax::ast; +use syntax::codemap::DUMMY_SP; use syntax_pos::Span; use std::cell::RefCell; use std::iter; +use std::mem; pub fn provide(providers: &mut Providers) { providers.mir_shims = make_shim; } -fn make_shim<'a, 'tcx>(_tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, +fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx RefCell> { - match instance { + debug!("make_shim({:?})", instance); + let result = match instance { ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance), - ty::InstanceDef::FnPtrShim(..) => unimplemented!() - } + ty::InstanceDef::FnPtrShim(_, ty) => { + build_fn_ptr_shim(tcx, ty, instance.def_ty(tcx)) + } + }; + debug!("make_shim({:?}) = {:?}", instance, result); + + let result = tcx.alloc_mir(result); + // Perma-borrow MIR from shims to prevent mutation. + mem::forget(result.borrow()); + result } fn local_decls_for_sig<'tcx>(sig: &ty::FnSig<'tcx>) @@ -54,6 +66,111 @@ fn local_decls_for_sig<'tcx>(sig: &ty::FnSig<'tcx>) })).collect() } + +fn build_fn_ptr_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, + fn_ty: Ty<'tcx>, + sig_ty: Ty<'tcx>) + -> Mir<'tcx> +{ + debug!("build_fn_ptr_shim(fn_ty={:?}, sig_ty={:?})", fn_ty, sig_ty); + let trait_sig = match sig_ty.sty { + ty::TyFnDef(_, _, fty) => tcx.erase_late_bound_regions(&fty), + _ => bug!("unexpected type for shim {:?}", sig_ty) + }; + + let self_ty = match trait_sig.inputs()[0].sty { + ty::TyParam(..) => fn_ty, + ty::TyRef(r, mt) => tcx.mk_ref(r, ty::TypeAndMut { + ty: fn_ty, + mutbl: mt.mutbl + }), + _ => bug!("unexpected self_ty {:?}", trait_sig), + }; + + let fn_ptr_sig = match fn_ty.sty { + ty::TyFnPtr(fty) | + ty::TyFnDef(_, _, fty) => + tcx.erase_late_bound_regions_and_normalize(&fty), + _ => bug!("non-fn-ptr {:?} in build_fn_ptr_shim", fn_ty) + }; + + let sig = tcx.mk_fn_sig( + [ + self_ty, + tcx.intern_tup(fn_ptr_sig.inputs(), false) + ].iter().cloned(), + fn_ptr_sig.output(), + false, + hir::Unsafety::Normal, + Abi::RustCall, + ); + + let local_decls = local_decls_for_sig(&sig); + let source_info = SourceInfo { + span: DUMMY_SP, + scope: ARGUMENT_VISIBILITY_SCOPE + }; + + let fn_ptr = Lvalue::Local(Local::new(1+0)); + let fn_ptr = match trait_sig.inputs()[0].sty { + ty::TyParam(..) => fn_ptr, + ty::TyRef(..) => Lvalue::Projection(box Projection { + base: fn_ptr, elem: ProjectionElem::Deref + }), + _ => bug!("unexpected self_ty {:?}", trait_sig), + }; + let fn_args = Local::new(1+1); + + let return_block_id = BasicBlock::new(1); + + // return = ADT(arg0, arg1, ...); return + let start_block = BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info: source_info, + kind: TerminatorKind::Call { + func: Operand::Consume(fn_ptr), + args: fn_ptr_sig.inputs().iter().enumerate().map(|(i, ity)| { + Operand::Consume(Lvalue::Projection(box Projection { + base: Lvalue::Local(fn_args), + elem: ProjectionElem::Field( + Field::new(i), *ity + ) + })) + }).collect(), + // FIXME: can we pass a Some destination for an uninhabited ty? + destination: Some((Lvalue::Local(RETURN_POINTER), + return_block_id)), + cleanup: None + } + }), + is_cleanup: false + }; + let return_block = BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info: source_info, + kind: TerminatorKind::Return + }), + is_cleanup: false + }; + + let mut mir = Mir::new( + vec![start_block, return_block].into_iter().collect(), + IndexVec::from_elem_n( + VisibilityScopeData { span: DUMMY_SP, parent_scope: None }, 1 + ), + IndexVec::new(), + sig.output(), + local_decls, + sig.inputs().len(), + vec![], + DUMMY_SP + ); + mir.spread_arg = Some(fn_args); + mir +} + pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, ctor_id: ast::NodeId, fields: &[hir::StructField], diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 19fc4e013fae1..2294e8a0002e0 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -36,7 +36,6 @@ use back::symbol_names::symbol_name; use trans_item::TransItem; use type_of; use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::hir; use std::iter; use syntax_pos::DUMMY_SP; @@ -130,15 +129,14 @@ impl<'tcx> Callee<'tcx> { let method_ty = instance_ty(ccx.shared(), &instance); Callee::ptr(llfn, method_ty) } - traits::VtableFnPointer(vtable_fn_pointer) => { - let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap(); - let instance = Instance::new(def_id, substs); - let llfn = trans_fn_pointer_shim(ccx, instance, - trait_closure_kind, - vtable_fn_pointer.fn_ty); + traits::VtableFnPointer(data) => { + let instance = ty::Instance { + def: ty::InstanceDef::FnPtrShim(def_id, data.fn_ty), + substs: substs, + }; - let method_ty = instance_ty(ccx.shared(), &instance); - Callee::ptr(llfn, method_ty) + let (llfn, ty) = get_fn(ccx, instance); + Callee::ptr(llfn, ty) } traits::VtableObject(ref data) => { Callee { @@ -363,124 +361,6 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( lloncefn } -/// Translates an adapter that implements the `Fn` trait for a fn -/// pointer. This is basically the equivalent of something like: -/// -/// ``` -/// impl<'a> Fn(&'a int) -> &'a int for fn(&int) -> &int { -/// extern "rust-abi" fn call(&self, args: (&'a int,)) -> &'a int { -/// (*self)(args.0) -/// } -/// } -/// ``` -/// -/// but for the bare function type given. -fn trans_fn_pointer_shim<'a, 'tcx>( - ccx: &'a CrateContext<'a, 'tcx>, - method_instance: Instance<'tcx>, - closure_kind: ty::ClosureKind, - bare_fn_ty: Ty<'tcx>) - -> ValueRef -{ - let tcx = ccx.tcx(); - - // Normalize the type for better caching. - let bare_fn_ty = tcx.normalize_associated_type(&bare_fn_ty); - - // If this is an impl of `Fn` or `FnMut` trait, the receiver is `&self`. - let is_by_ref = match closure_kind { - ty::ClosureKind::Fn | ty::ClosureKind::FnMut => true, - ty::ClosureKind::FnOnce => false, - }; - - let llfnpointer = match bare_fn_ty.sty { - ty::TyFnDef(def_id, substs, _) => { - // Function definitions have to be turned into a pointer. - let llfn = Callee::def(ccx, def_id, substs).reify(ccx); - if !is_by_ref { - // A by-value fn item is ignored, so the shim has - // the same signature as the original function. - return llfn; - } - Some(llfn) - } - _ => None - }; - - let bare_fn_ty_maybe_ref = if is_by_ref { - tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), bare_fn_ty) - } else { - bare_fn_ty - }; - - // Check if we already trans'd this shim. - if let Some(&llval) = ccx.fn_pointer_shims().borrow().get(&bare_fn_ty_maybe_ref) { - return llval; - } - - debug!("trans_fn_pointer_shim(bare_fn_ty={:?})", - bare_fn_ty); - - // Construct the "tuply" version of `bare_fn_ty`. It takes two arguments: `self`, - // which is the fn pointer, and `args`, which is the arguments tuple. - let sig = bare_fn_ty.fn_sig(); - let sig = tcx.erase_late_bound_regions_and_normalize(&sig); - assert_eq!(sig.unsafety, hir::Unsafety::Normal); - assert_eq!(sig.abi, Abi::Rust); - let tuple_input_ty = tcx.intern_tup(sig.inputs(), false); - let sig = tcx.mk_fn_sig( - [bare_fn_ty_maybe_ref, tuple_input_ty].iter().cloned(), - sig.output(), - false, - hir::Unsafety::Normal, - Abi::RustCall - ); - let fn_ty = FnType::new(ccx, sig, &[]); - let tuple_fn_ty = tcx.mk_fn_ptr(ty::Binder(sig)); - debug!("tuple_fn_ty: {:?}", tuple_fn_ty); - - // - let function_name = symbol_name(method_instance, ccx.shared()); - let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty); - attributes::set_frame_pointer_elimination(ccx, llfn); - // - let bcx = Builder::new_block(ccx, llfn, "entry-block"); - - let mut llargs = get_params(llfn); - - let self_arg = llargs.remove(fn_ty.ret.is_indirect() as usize); - let llfnpointer = llfnpointer.unwrap_or_else(|| { - // the first argument (`self`) will be ptr to the fn pointer - if is_by_ref { - bcx.load(self_arg, None) - } else { - self_arg - } - }); - - let callee = Callee { - data: Fn(llfnpointer), - ty: bare_fn_ty - }; - let fn_ret = callee.ty.fn_ret(); - let fn_ty = callee.direct_fn_type(ccx, &[]); - let llret = bcx.call(llfnpointer, &llargs, None); - fn_ty.apply_attrs_callsite(llret); - - if fn_ret.0.is_never() { - bcx.unreachable(); - } else { - if fn_ty.ret.is_indirect() || fn_ty.ret.is_ignore() { - bcx.ret_void(); - } else { - bcx.ret(llret); - } - } - - ccx.fn_pointer_shims().borrow_mut().insert(bare_fn_ty_maybe_ref, llfn); - - llfn -} /// Translates a reference to a fn/method item, monomorphizing and /// inlining as it goes. diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 271f91f9adbdb..c4239fa2ae65f 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -907,14 +907,12 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, } } traits::VtableFnPointer(ref data) => { - // If we know the destination of this fn-pointer, we'll have to make - // sure that this destination actually gets instantiated. - if let ty::TyFnDef(def_id, substs, _) = data.fn_ty.sty { - // The destination of the pointer might be something that needs - // further dispatching, such as a trait method, so we do that. - do_static_dispatch(scx, def_id, substs) - } else { - StaticDispatchResult::Unknown + StaticDispatchResult::Dispatched { + instance: Instance { + def: ty::InstanceDef::FnPtrShim(trait_method.def_id, data.fn_ty), + substs: trait_ref.substs + }, + fn_once_adjustment: None, } } // Trait object shims are always instantiated in-place, and as they are diff --git a/src/test/codegen-units/item-collection/function-as-argument.rs b/src/test/codegen-units/item-collection/function-as-argument.rs index 3a9d56c2a8bf7..51df38cabef30 100644 --- a/src/test/codegen-units/item-collection/function-as-argument.rs +++ b/src/test/codegen-units/item-collection/function-as-argument.rs @@ -28,10 +28,12 @@ fn main() { //~ TRANS_ITEM fn function_as_argument::take_fn_once[0] //~ TRANS_ITEM fn function_as_argument::function[0] + //~ TRANS_ITEM fn core::ops[0]::FnOnce[0]::call_once[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] + //~ TRANS_ITEM fn core::ops[0]::FnOnce[0]::call_once[0] take_fn_once(function, 'c', 0f64); //~ TRANS_ITEM fn function_as_argument::take_fn_pointer[0] diff --git a/src/test/codegen-units/item-collection/trait-method-as-argument.rs b/src/test/codegen-units/item-collection/trait-method-as-argument.rs index e7006d73ef166..f7afd3f0891e3 100644 --- a/src/test/codegen-units/item-collection/trait-method-as-argument.rs +++ b/src/test/codegen-units/item-collection/trait-method-as-argument.rs @@ -40,22 +40,28 @@ fn take_foo_mut T>(mut f: F, arg: T) -> T { fn main() { //~ TRANS_ITEM fn trait_method_as_argument::take_foo_once[0] u32> //~ TRANS_ITEM fn trait_method_as_argument::{{impl}}[0]::foo[0] + //~ TRANS_ITEM fn core::ops[0]::FnOnce[0]::call_once[0] u32, (u32)> 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] + //~ TRANS_ITEM fn core::ops[0]::FnOnce[0]::call_once[0] char, (char)> take_foo_once(Trait::foo, 'c'); //~ TRANS_ITEM fn trait_method_as_argument::take_foo[0] u32> + //~ TRANS_ITEM fn core::ops[0]::Fn[0]::call[0] u32, (u32)> take_foo(Trait::foo, 0u32); //~ TRANS_ITEM fn trait_method_as_argument::take_foo[0] char> + //~ TRANS_ITEM fn core::ops[0]::Fn[0]::call[0] char, (char)> take_foo(Trait::foo, 'c'); //~ TRANS_ITEM fn trait_method_as_argument::take_foo_mut[0] u32> + //~ TRANS_ITEM fn core::ops[0]::FnMut[0]::call_mut[0] char, (char)> take_foo_mut(Trait::foo, 0u32); //~ TRANS_ITEM fn trait_method_as_argument::take_foo_mut[0] char> + //~ TRANS_ITEM fn core::ops[0]::FnMut[0]::call_mut[0] u32, (u32)> take_foo_mut(Trait::foo, 'c'); } diff --git a/src/test/run-pass/mir_calls_to_shims.rs b/src/test/run-pass/mir_calls_to_shims.rs new file mode 100644 index 0000000000000..7300a322ec4b7 --- /dev/null +++ b/src/test/run-pass/mir_calls_to_shims.rs @@ -0,0 +1,56 @@ +// Copyright 2016 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. + +#![feature(fn_traits)] +#![feature(never_type)] + +use std::panic; + +fn foo(x: u32, y: u32) -> u32 { x/y } +fn foo_diverges() -> ! { panic!() } + +fn test_fn_ptr(mut t: T) + where T: Fn(u32, u32) -> u32, +{ + let as_fn = >::call; + assert_eq!(as_fn(&t, (9, 3)), 3); + let as_fn_mut = >::call_mut; + assert_eq!(as_fn_mut(&mut t, (18, 3)), 6); + let as_fn_once = >::call_once; + assert_eq!(as_fn_once(t, (24, 3)), 8); +} + +fn assert_panics(f: F) where F: FnOnce() { + let f = panic::AssertUnwindSafe(f); + let result = panic::catch_unwind(move || { + f.0() + }); + if let Ok(..) = result { + panic!("diverging function returned"); + } +} + +fn test_fn_ptr_panic(mut t: T) + where T: Fn() -> ! +{ + let as_fn = >::call; + assert_panics(|| as_fn(&t, ())); + let as_fn_mut = >::call_mut; + assert_panics(|| as_fn_mut(&mut t, ())); + let as_fn_once = >::call_once; + assert_panics(|| as_fn_once(t, ())); +} + +fn main() { + test_fn_ptr(foo); + test_fn_ptr(foo as fn(u32, u32) -> u32); + test_fn_ptr_panic(foo_diverges); + test_fn_ptr_panic(foo_diverges as fn() -> !); +} From aac5ba5dab758803b06add74457eba03bbbd9930 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 8 Mar 2017 01:41:26 +0200 Subject: [PATCH 48/54] resolve instances to ty::Instance directly This removes the duplication between collector, callee, and (eventually) MIRI. --- src/librustc/traits/mod.rs | 2 +- src/librustc/traits/specialize/mod.rs | 25 ++- src/librustc/ty/context.rs | 9 + src/librustc/ty/instance.rs | 34 +++- src/librustc/ty/util.rs | 2 - src/librustc_mir/shim.rs | 1 + src/librustc_trans/base.rs | 24 +-- src/librustc_trans/callee.rs | 186 ++++--------------- src/librustc_trans/collector.rs | 235 ++++++----------------- src/librustc_trans/common.rs | 82 +-------- src/librustc_trans/glue.rs | 20 +- src/librustc_trans/lib.rs | 1 + src/librustc_trans/mir/constant.rs | 2 +- src/librustc_trans/monomorphize.rs | 256 ++++++++++++++++++++++++-- src/librustc_trans/partitioning.rs | 5 +- 15 files changed, 389 insertions(+), 495 deletions(-) diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 7e7d06e4b814e..c71fc28b4d6b3 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -40,7 +40,7 @@ pub use self::select::{EvaluationCache, SelectionContext, SelectionCache}; pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch}; pub use self::select::{MethodMatchedData}; // intentionally don't export variants pub use self::specialize::{OverlapError, specialization_graph, specializes, translate_substs}; -pub use self::specialize::{SpecializesCache, find_method}; +pub use self::specialize::{SpecializesCache, find_associated_item}; pub use self::util::elaborate_predicates; pub use self::util::supertraits; pub use self::util::Supertraits; diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index 2c99ee21b0f73..50a4d982832ac 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -29,8 +29,6 @@ use traits::{self, Reveal, ObligationCause}; use ty::{self, TyCtxt, TypeFoldable}; use syntax_pos::DUMMY_SP; -use syntax::ast; - pub mod specialization_graph; /// Information pertinent to an overlapping impl error. @@ -106,22 +104,23 @@ pub fn translate_substs<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, } /// Given a selected impl described by `impl_data`, returns the -/// definition and substitions for the method with the name `name`, -/// and trait method substitutions `substs`, in that impl, a less -/// specialized impl, or the trait default, whichever applies. -pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - name: ast::Name, - substs: &'tcx Substs<'tcx>, - impl_data: &super::VtableImplData<'tcx, ()>) - -> (DefId, &'tcx Substs<'tcx>) -{ +/// definition and substitions for the method with the name `name` +/// the kind `kind`, and trait method substitutions `substs`, in +/// that impl, a less specialized impl, or the trait default, +/// whichever applies. +pub fn find_associated_item<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + item: &ty::AssociatedItem, + substs: &'tcx Substs<'tcx>, + impl_data: &super::VtableImplData<'tcx, ()>, +) -> (DefId, &'tcx Substs<'tcx>) { assert!(!substs.needs_infer()); let trait_def_id = tcx.trait_id_of_impl(impl_data.impl_def_id).unwrap(); let trait_def = tcx.lookup_trait_def(trait_def_id); let ancestors = trait_def.ancestors(impl_data.impl_def_id); - match ancestors.defs(tcx, name, ty::AssociatedKind::Method).next() { + match ancestors.defs(tcx, item.name, item.kind).next() { Some(node_item) => { let substs = tcx.infer_ctxt((), Reveal::All).enter(|infcx| { let substs = substs.rebase_onto(tcx, trait_def_id, impl_data.substs); @@ -137,7 +136,7 @@ pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (node_item.item.def_id, substs) } None => { - bug!("method {:?} not found in {:?}", name, impl_data.impl_def_id) + bug!("{:?} not found in {:?}", item, impl_data.impl_def_id) } } } diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index a0aeb4107c156..5543223105b44 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1469,6 +1469,15 @@ impl InternIteratorElement for T { } } +impl<'a, T, R> InternIteratorElement for &'a T + where T: Clone + 'a +{ + type Output = R; + fn intern_with, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output { + f(&iter.cloned().collect::>()) + } +} + impl InternIteratorElement for Result { type Output = Result; fn intern_with, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output { diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index fdcfb3ebd3ce5..d93482acbcb57 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -27,8 +27,17 @@ pub struct Instance<'tcx> { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum InstanceDef<'tcx> { Item(DefId), + Intrinsic(DefId), // ::call_* + // def-id is FnTrait::call_* FnPtrShim(DefId, Ty<'tcx>), + // ::fn + Virtual(DefId, usize), + // <[mut closure] as FnOnce>::call_once + ClosureOnceShim { + call_once: DefId, + closure_did: DefId + }, } impl<'tcx> InstanceDef<'tcx> { @@ -36,8 +45,12 @@ impl<'tcx> InstanceDef<'tcx> { pub fn def_id(&self) -> DefId { match *self { InstanceDef::Item(def_id) | - InstanceDef::FnPtrShim(def_id, _) - => def_id + InstanceDef::FnPtrShim(def_id, _) | + InstanceDef::Virtual(def_id, _) | + InstanceDef::Intrinsic(def_id, ) | + InstanceDef::ClosureOnceShim { + call_once: def_id, closure_did: _ + } => def_id } } @@ -73,14 +86,23 @@ impl<'tcx> InstanceDef<'tcx> { impl<'tcx> fmt::Display for Instance<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ppaux::parameterized(f, self.substs, self.def_id(), &[])?; match self.def { - InstanceDef::Item(def) => { - ppaux::parameterized(f, self.substs, def, &[]) + InstanceDef::Item(_) => Ok(()), + InstanceDef::Intrinsic(_) => { + write!(f, " - intrinsic") } - InstanceDef::FnPtrShim(def, ty) => { - ppaux::parameterized(f, self.substs, def, &[])?; + InstanceDef::Virtual(_, num) => { + write!(f, " - shim(#{})", num) + } + InstanceDef::FnPtrShim(_, ty) => { write!(f, " - shim({:?})", ty) } + InstanceDef::ClosureOnceShim { + call_once: _, closure_did + } => { + write!(f, " - shim({:?})", closure_did) + } } } } diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 2344305fa9a7e..1d816d342c4fd 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -408,8 +408,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { bug!("empty_substs_for_def_id: {:?} has type parameters", item_def_id) }) } - - } pub struct TypeIdHasher<'a, 'gcx: 'a+'tcx, 'tcx: 'a, W> { diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index bf0b1796dffaf..2a053535e7e10 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -41,6 +41,7 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, ty::InstanceDef::FnPtrShim(_, ty) => { build_fn_ptr_shim(tcx, ty, instance.def_ty(tcx)) } + _ => bug!("unknown shim kind") }; debug!("make_shim({:?}) = {:?}", instance, result); diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index ce767468c012b..10ab199671f64 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -36,9 +36,7 @@ use llvm::{Linkage, ValueRef, Vector, get_param}; use llvm; use rustc::hir::def_id::LOCAL_CRATE; use middle::lang_items::StartFnLangItem; -use rustc::traits; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::adjustment::CustomCoerceUnsized; use rustc::dep_graph::{AssertDepGraphSafe, DepNode, WorkProduct}; use rustc::hir::map as hir_map; use rustc::util::common::time; @@ -54,7 +52,6 @@ use common::{C_bool, C_bytes_in_context, C_i32, C_uint}; use collector::{self, TransItemCollectionMode}; use common::{C_struct_in_context, C_u64, C_undef}; use common::CrateContext; -use common::{fulfill_obligation}; use common::{type_is_zero_size, val_ty}; use common; use consts; @@ -80,7 +77,7 @@ use std::ffi::{CStr, CString}; use std::rc::Rc; use std::str; use std::i32; -use syntax_pos::{Span, DUMMY_SP}; +use syntax_pos::Span; use syntax::attr; use rustc::hir; use rustc::ty::layout::{self, Layout}; @@ -313,25 +310,6 @@ pub fn coerce_unsized_into<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, } } -pub fn custom_coerce_unsize_info<'scx, 'tcx>(scx: &SharedCrateContext<'scx, 'tcx>, - source_ty: Ty<'tcx>, - target_ty: Ty<'tcx>) - -> CustomCoerceUnsized { - let trait_ref = ty::Binder(ty::TraitRef { - def_id: scx.tcx().lang_items.coerce_unsized_trait().unwrap(), - substs: scx.tcx().mk_substs_trait(source_ty, &[target_ty]) - }); - - match fulfill_obligation(scx, DUMMY_SP, trait_ref) { - traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => { - scx.tcx().custom_coerce_unsized_kind(impl_def_id) - } - vtable => { - bug!("invalid CoerceUnsized vtable: {:?}", vtable); - } - } -} - pub fn cast_shift_expr_rhs( cx: &Builder, op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef ) -> ValueRef { diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 2294e8a0002e0..70da2a69ef425 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -19,13 +19,13 @@ pub use self::CalleeData::*; use llvm::{self, ValueRef, get_params}; use rustc::hir::def_id::DefId; use rustc::ty::subst::{Substs, Subst}; -use rustc::traits; use abi::{Abi, FnType}; use attributes; use builder::Builder; use common::{self, CrateContext}; use cleanup::CleanupScope; use mir::lvalue::LvalueRef; +use monomorphize; use consts; use common::instance_ty; use declare; @@ -38,8 +38,6 @@ use type_of; use rustc::ty::{self, Ty, TypeFoldable}; use std::iter; -use syntax_pos::DUMMY_SP; - use mir::lvalue::Alignment; #[derive(Debug)] @@ -60,94 +58,39 @@ pub struct Callee<'tcx> { } impl<'tcx> Callee<'tcx> { - /// Function pointer. - pub fn ptr(llfn: ValueRef, ty: Ty<'tcx>) -> Callee<'tcx> { - Callee { - data: Fn(llfn), - ty: ty - } - } - /// Function or method definition. pub fn def<'a>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Callee<'tcx> { - let tcx = ccx.tcx(); - - if let Some(trait_id) = tcx.trait_of_item(def_id) { - return Callee::trait_method(ccx, trait_id, def_id, substs); - } - - let instance = ty::Instance::new(def_id, substs); - let fn_ty = instance_ty(ccx.shared(), &instance); - if let ty::TyFnDef(.., f) = fn_ty.sty { - if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic { - return Callee { - data: Intrinsic, - ty: fn_ty + let instance = monomorphize::resolve(ccx.shared(), def_id, substs); + let ty = instance_ty(ccx.shared(), &instance); + let data = match instance.def { + ty::InstanceDef::Intrinsic(_) => Intrinsic, + ty::InstanceDef::ClosureOnceShim { .. } => { + let closure_ty = instance.substs.type_at(0); + let (closure_def_id, closure_substs) = match closure_ty.sty { + ty::TyClosure(def_id, substs) => (def_id, substs), + _ => bug!("bad closure instance {:?}", instance) }; - } - } - - let (llfn, ty) = get_fn(ccx, instance); - Callee::ptr(llfn, ty) - } - /// Trait method, which has to be resolved to an impl method. - pub fn trait_method<'a>(ccx: &CrateContext<'a, 'tcx>, - trait_id: DefId, - def_id: DefId, - substs: &'tcx Substs<'tcx>) - -> Callee<'tcx> { - let tcx = ccx.tcx(); - - let trait_ref = ty::TraitRef::from_method(tcx, trait_id, substs); - let trait_ref = tcx.normalize_associated_type(&ty::Binder(trait_ref)); - match common::fulfill_obligation(ccx.shared(), DUMMY_SP, trait_ref) { - traits::VtableImpl(vtable_impl) => { - let name = tcx.item_name(def_id); - let instance = common::find_method(tcx, name, substs, &vtable_impl); - - // Translate the function, bypassing Callee::def. - // That is because default methods have the same ID as the - // trait method used to look up the impl method that ended - // up here, so calling Callee::def would infinitely recurse. - let (llfn, ty) = get_fn(ccx, instance); - Callee::ptr(llfn, ty) - } - traits::VtableClosure(vtable_closure) => { - // The substitutions should have no type parameters remaining - // after passing through fulfill_obligation - let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap(); - let instance = Instance::new(def_id, substs); - let llfn = trans_closure_method( + Fn(trans_fn_once_adapter_shim( ccx, - vtable_closure.closure_def_id, - vtable_closure.substs, + closure_def_id, + closure_substs, instance, - trait_closure_kind); - - let method_ty = instance_ty(ccx.shared(), &instance); - Callee::ptr(llfn, method_ty) - } - traits::VtableFnPointer(data) => { - let instance = ty::Instance { - def: ty::InstanceDef::FnPtrShim(def_id, data.fn_ty), - substs: substs, - }; - - let (llfn, ty) = get_fn(ccx, instance); - Callee::ptr(llfn, ty) + get_fn( + ccx, + Instance::new(closure_def_id, closure_substs.substs) + ) + )) } - traits::VtableObject(ref data) => { - Callee { - data: Virtual(tcx.get_vtable_index_of_object_method(data, def_id)), - ty: instance_ty(ccx.shared(), &Instance::new(def_id, substs)) - } + ty::InstanceDef::Virtual(_, n) => Virtual(n), + ty::InstanceDef::FnPtrShim(..) | + ty::InstanceDef::Item(..) => { + Fn(get_fn(ccx, instance)) } - vtable => { - bug!("resolved vtable bad vtable {:?} in trans", vtable); - } - } + }; + + Callee { data, ty } } /// Get the abi::FnType for a direct call. Mainly deals with the fact @@ -155,7 +98,8 @@ impl<'tcx> Callee<'tcx> { /// The extra argument types are for variadic (extern "C") functions. pub fn direct_fn_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>, extra_args: &[Ty<'tcx>]) -> FnType { - let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&self.ty.fn_sig()); + let sig = common::ty_fn_sig(ccx, self.ty); + let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&sig); let mut fn_ty = FnType::unadjusted(ccx, sig, extra_args); if let Virtual(_) = self.data { // Don't pass the vtable, it's not an argument of the virtual fn. @@ -175,72 +119,6 @@ impl<'tcx> Callee<'tcx> { } } -fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, - def_id: DefId, - substs: ty::ClosureSubsts<'tcx>, - method_instance: Instance<'tcx>, - trait_closure_kind: ty::ClosureKind) - -> ValueRef -{ - // If this is a closure, redirect to it. - let (llfn, _) = get_fn(ccx, Instance::new(def_id, substs.substs)); - - // If the closure is a Fn closure, but a FnOnce is needed (etc), - // then adapt the self type - let llfn_closure_kind = ccx.tcx().closure_kind(def_id); - - debug!("trans_closure_adapter_shim(llfn_closure_kind={:?}, \ - trait_closure_kind={:?}, llfn={:?})", - llfn_closure_kind, trait_closure_kind, Value(llfn)); - - match needs_fn_once_adapter_shim(llfn_closure_kind, trait_closure_kind) { - Ok(true) => trans_fn_once_adapter_shim(ccx, - def_id, - substs, - method_instance, - llfn), - Ok(false) => llfn, - Err(()) => { - bug!("trans_closure_adapter_shim: cannot convert {:?} to {:?}", - llfn_closure_kind, - trait_closure_kind); - } - } -} - -pub fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, - trait_closure_kind: ty::ClosureKind) - -> Result -{ - match (actual_closure_kind, trait_closure_kind) { - (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | - (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => { - // No adapter needed. - Ok(false) - } - (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { - // The closure fn `llfn` is a `fn(&self, ...)`. We want a - // `fn(&mut self, ...)`. In fact, at trans time, these are - // basically the same thing, so we can just return llfn. - Ok(false) - } - (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { - // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut - // self, ...)`. We want a `fn(self, ...)`. We can produce - // this by doing something like: - // - // fn call_once(self, ...) { call_mut(&self, ...) } - // fn call_once(mut self, ...) { call_mut(&mut self, ...) } - // - // These are both the same at trans time. - Ok(true) - } - _ => Err(()), - } -} - fn trans_fn_once_adapter_shim<'a, 'tcx>( ccx: &'a CrateContext<'a, 'tcx>, def_id: DefId, @@ -307,7 +185,6 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( // the first argument (`self`) will be the (by value) closure env. let mut llargs = get_params(lloncefn); - let fn_ret = callee.ty.fn_ret(); let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); let self_idx = fn_ty.ret.is_indirect() as usize; let env_arg = &orig_fn_ty.args[0]; @@ -344,7 +221,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( } fn_ty.apply_attrs_callsite(llret); - if fn_ret.0.is_never() { + if sig.output().is_never() { bcx.unreachable(); } else { self_scope.trans(&bcx); @@ -372,7 +249,8 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( /// - `substs`: values for each of the fn/method's parameters fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance<'tcx>) - -> (ValueRef, Ty<'tcx>) { + -> ValueRef +{ let tcx = ccx.tcx(); debug!("get_fn(instance={:?})", instance); @@ -383,7 +261,7 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let fn_ty = common::instance_ty(ccx.shared(), &instance); if let Some(&llfn) = ccx.instances().borrow().get(&instance) { - return (llfn, fn_ty); + return llfn; } let sym = ccx.symbol_map().get_or_compute(ccx.shared(), @@ -455,5 +333,5 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ccx.instances().borrow_mut().insert(instance, llfn); - (llfn, fn_ty) + llfn } diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index c4239fa2ae65f..9476ffc944485 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -203,11 +203,8 @@ use rustc::mir::visit as mir_visit; use rustc::mir::visit::Visitor as MirVisitor; use syntax::abi::Abi; -use syntax_pos::DUMMY_SP; -use base::custom_coerce_unsize_info; -use callee::needs_fn_once_adapter_shim; use context::SharedCrateContext; -use common::{def_ty, find_method, instance_ty, fulfill_obligation}; +use common::{def_ty, instance_ty}; use glue::{self, DropGlueKind}; use monomorphize::{self, Instance}; use util::nodemap::{FxHashSet, FxHashMap, DefIdMap}; @@ -555,7 +552,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { let substs = monomorphize::apply_param_substs(self.scx, self.param_substs, &substs); - let instance = monomorphize::resolve_const(self.scx, def_id, substs); + let instance = monomorphize::resolve(self.scx, def_id, substs); collect_neighbours(self.scx, instance, self.output); } @@ -580,32 +577,23 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { let callee_substs = monomorphize::apply_param_substs(self.scx, self.param_substs, &callee_substs); - let dispatched = do_static_dispatch(self.scx, - callee_def_id, - callee_substs); - - if let StaticDispatchResult::Dispatched { - instance, fn_once_adjustment - } = 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 should_trans_locally(self.scx.tcx(), &instance) { - self.output.push(create_fn_trans_item(instance)); - - // This call will instantiate an FnOnce adapter, which drops - // the closure environment. Therefore we need to make sure - // that we collect the drop-glue for the environment type. - if let Some(env_ty) = fn_once_adjustment { - let env_ty = glue::get_drop_glue_type(self.scx, env_ty); - if self.scx.type_needs_drop(env_ty) { - let dg = DropGlueKind::Ty(env_ty); - self.output.push(TransItem::DropGlue(dg)); - } + let instance = + monomorphize::resolve(self.scx, callee_def_id, callee_substs); + if should_trans_locally(self.scx.tcx(), &instance) { + if let ty::InstanceDef::ClosureOnceShim { .. } = instance.def { + // This call will instantiate an FnOnce adapter, which + // drops the closure environment. Therefore we need to + // make sure that we collect the drop-glue for the + // environment type. + + let env_ty = instance.substs.type_at(0); + let env_ty = glue::get_drop_glue_type(self.scx, env_ty); + if self.scx.type_needs_drop(env_ty) { + let dg = DropGlueKind::Ty(env_ty); + self.output.push(TransItem::DropGlue(dg)); } } + self.output.push(create_fn_trans_item(instance)); } } @@ -664,8 +652,13 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instance<'tcx>) -> bool { let def_id = match instance.def { - ty::InstanceDef::Item(def_id) => def_id, - ty::InstanceDef::FnPtrShim(..) => return true + ty::InstanceDef::Item(def_id) | + ty::InstanceDef::ClosureOnceShim { + call_once: _, closure_did: def_id + } => def_id, + ty::InstanceDef::FnPtrShim(..) => return true, + ty::InstanceDef::Virtual(..) | + ty::InstanceDef::Intrinsic(_) => return false }; match tcx.hir.get_if_local(def_id) { Some(hir_map::NodeForeignItem(..)) => { @@ -718,31 +711,21 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, // If the type implements Drop, also add a translation item for the // monomorphized Drop::drop() implementation. - let destructor = match ty.sty { - ty::TyAdt(def, _) => def.destructor(scx.tcx()), - _ => None + let has_dtor = match ty.sty { + ty::TyAdt(def, _) => def.has_dtor(scx.tcx()), + _ => false }; - if let (Some(destructor), false) = (destructor, ty.is_box()) { - use rustc::ty::ToPolyTraitRef; - + if has_dtor && !ty.is_box() { let drop_trait_def_id = scx.tcx() .lang_items .drop_trait() .unwrap(); - - let self_type_substs = scx.tcx().mk_substs_trait(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(scx, DUMMY_SP, trait_ref) { - traits::VtableImpl(data) => data.substs, - _ => bug!() - }; - let instance = Instance::new(destructor.did, substs); + let drop_method = scx.tcx().associated_items(drop_trait_def_id) + .find(|it| it.kind == ty::AssociatedKind::Method) + .unwrap().def_id; + let substs = scx.tcx().mk_substs_trait(ty, &[]); + let instance = monomorphize::resolve(scx, drop_method, substs); if should_trans_locally(scx.tcx(), &instance) { output.push(create_fn_trans_item(instance)); } @@ -817,115 +800,6 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, } } -enum StaticDispatchResult<'tcx> { - // The call could be resolved statically as going to the method with - // `instance`. - Dispatched { - instance: Instance<'tcx>, - // If this is a call to a closure that needs an FnOnce adjustment, - // this contains the new self type of the call (= type of the closure - // environment) - fn_once_adjustment: Option>, - }, - // This goes to somewhere that we don't know at compile-time - Unknown -} - -fn do_static_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, - fn_def_id: DefId, - fn_substs: &'tcx Substs<'tcx>) - -> StaticDispatchResult<'tcx> { - debug!("do_static_dispatch(fn_def_id={}, fn_substs={:?})", - def_id_to_string(scx.tcx(), fn_def_id), - fn_substs); - if let Some(trait_def_id) = scx.tcx().trait_of_item(fn_def_id) { - debug!(" => trait method, attempting to find impl"); - do_static_trait_method_dispatch(scx, - &scx.tcx().associated_item(fn_def_id), - trait_def_id, - fn_substs) - } else { - debug!(" => regular function"); - // The function is not part of an impl or trait, no dispatching - // to be done - StaticDispatchResult::Dispatched { - instance: Instance::new(fn_def_id, fn_substs), - fn_once_adjustment: None, - } - } -} - -// Given a trait-method and substitution information, find out the actual -// implementation of the trait method. -fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, - trait_method: &ty::AssociatedItem, - trait_id: DefId, - rcvr_substs: &'tcx Substs<'tcx>) - -> StaticDispatchResult<'tcx> { - let tcx = scx.tcx(); - debug!("do_static_trait_method_dispatch(trait_method={}, \ - trait_id={}, \ - rcvr_substs={:?})", - def_id_to_string(scx.tcx(), trait_method.def_id), - def_id_to_string(scx.tcx(), trait_id), - rcvr_substs); - - let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); - let vtbl = fulfill_obligation(scx, DUMMY_SP, ty::Binder(trait_ref)); - - // Now that we know which impl is being used, we can dispatch to - // the actual function: - match vtbl { - traits::VtableImpl(impl_data) => { - StaticDispatchResult::Dispatched { - instance: find_method(tcx, trait_method.name, rcvr_substs, &impl_data), - fn_once_adjustment: None, - } - } - traits::VtableClosure(closure_data) => { - let closure_def_id = closure_data.closure_def_id; - let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap(); - let actual_closure_kind = tcx.closure_kind(closure_def_id); - - let needs_fn_once_adapter_shim = - match needs_fn_once_adapter_shim(actual_closure_kind, - trait_closure_kind) { - Ok(true) => true, - _ => false, - }; - - let fn_once_adjustment = if needs_fn_once_adapter_shim { - Some(tcx.mk_closure_from_closure_substs(closure_def_id, - closure_data.substs)) - } else { - None - }; - - StaticDispatchResult::Dispatched { - instance: Instance::new(closure_def_id, closure_data.substs.substs), - fn_once_adjustment: fn_once_adjustment, - } - } - traits::VtableFnPointer(ref data) => { - StaticDispatchResult::Dispatched { - instance: Instance { - def: ty::InstanceDef::FnPtrShim(trait_method.def_id, data.fn_ty), - substs: trait_ref.substs - }, - fn_once_adjustment: None, - } - } - // Trait object shims are always instantiated in-place, and as they are - // just an ABI-adjusting indirect call they do not have any dependencies. - traits::VtableObject(..) => { - StaticDispatchResult::Unknown - } - _ => { - bug!("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. @@ -991,7 +865,8 @@ fn find_vtable_types_for_unsizing<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, &ty::TyAdt(target_adt_def, target_substs)) => { assert_eq!(source_adt_def, target_adt_def); - let kind = custom_coerce_unsize_info(scx, source_ty, target_ty); + let kind = + monomorphize::custom_coerce_unsize_info(scx, source_ty, target_ty); let coerce_index = match kind { CustomCoerceUnsized::Struct(i) => i @@ -1017,6 +892,20 @@ fn find_vtable_types_for_unsizing<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, fn create_fn_trans_item<'a, 'tcx>(instance: Instance<'tcx>) -> TransItem<'tcx> { debug!("create_fn_trans_item(instance={})", instance); + let instance = match instance.def { + ty::InstanceDef::ClosureOnceShim { .. } => { + // HACK: don't create ClosureOnce trans items for now + // have someone else generate the drop glue + let closure_ty = instance.substs.type_at(0); + match closure_ty.sty { + ty::TyClosure(def_id, substs) => { + Instance::new(def_id, substs.substs) + } + _ => bug!("bad closure instance {:?}", instance) + } + } + _ => instance + }; TransItem::Fn(instance) } @@ -1037,18 +926,7 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(scx: &SharedCrateContext<'a, // Walk all methods of the trait, including those of its supertraits let methods = traits::get_vtable_methods(scx.tcx(), poly_trait_ref); let methods = methods.filter_map(|method| method) - .filter_map(|(def_id, substs)| { - if let StaticDispatchResult::Dispatched { - instance, - // We already add the drop-glue for the closure env - // unconditionally below. - fn_once_adjustment: _ , - } = do_static_dispatch(scx, def_id, substs) { - Some(instance) - } else { - None - } - }) + .map(|(def_id, substs)| monomorphize::resolve(scx, def_id, substs)) .filter(|&instance| should_trans_locally(scx.tcx(), &instance)) .map(|instance| create_fn_trans_item(instance)); output.extend(methods); @@ -1203,18 +1081,11 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(scx: &SharedCrateContext<'a, ' continue; } - // The substitutions we have are on the impl, so we grab - // the method type from the impl to substitute into. - let impl_substs = tcx.empty_substs_for_def_id(impl_def_id); - let impl_data = traits::VtableImplData { - impl_def_id: impl_def_id, - substs: impl_substs, - nested: vec![] - }; - let instance = find_method(tcx, method.name, callee_substs, &impl_data); + let instance = + monomorphize::resolve(scx, method.def_id, callee_substs); let predicates = tcx.item_predicates(instance.def_id()).predicates - .subst(tcx, impl_substs); + .subst(tcx, instance.substs); if !traits::normalize_and_test_predicates(tcx, predicates) { continue; } diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index 4389207cdf294..431325c5ec74e 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -17,7 +17,6 @@ use llvm::{ValueRef, ContextRef, TypeKind}; use llvm::{True, False, Bool, OperandBundleDef}; use rustc::hir::def_id::DefId; use rustc::hir::map::DefPathData; -use rustc::util::common::MemoizationMap; use middle::lang_items::LangItem; use base; use builder::Builder; @@ -30,13 +29,11 @@ use value::Value; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::layout::Layout; use rustc::ty::subst::{Subst, Substs}; -use rustc::traits::{self, SelectionContext, Reveal}; use rustc::hir; use libc::{c_uint, c_char}; use std::iter; -use syntax::ast; use syntax::attr; use syntax::symbol::InternedString; use syntax_pos::Span; @@ -427,73 +424,6 @@ pub fn is_null(val: ValueRef) -> bool { } } -/// Attempts to resolve an obligation. The result is a shallow vtable resolution -- meaning that we -/// do not (necessarily) resolve all nested obligations on the impl. Note that type check should -/// guarantee to us that all nested obligations *could be* resolved if we wanted to. -pub fn fulfill_obligation<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, - span: Span, - trait_ref: ty::PolyTraitRef<'tcx>) - -> traits::Vtable<'tcx, ()> -{ - let tcx = scx.tcx(); - - // Remove any references to regions; this helps improve caching. - let trait_ref = tcx.erase_regions(&trait_ref); - - scx.trait_cache().memoize(trait_ref, || { - debug!("trans::fulfill_obligation(trait_ref={:?}, def_id={:?})", - trait_ref, trait_ref.def_id()); - - // Do the initial selection for the obligation. This yields the - // shallow result we are looking for -- that is, what specific impl. - tcx.infer_ctxt((), Reveal::All).enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - - let obligation_cause = traits::ObligationCause::misc(span, - ast::DUMMY_NODE_ID); - let obligation = traits::Obligation::new(obligation_cause, - trait_ref.to_poly_trait_predicate()); - - let selection = match selcx.select(&obligation) { - Ok(Some(selection)) => selection, - Ok(None) => { - // Ambiguity can happen when monomorphizing during trans - // expands to some humongo type that never occurred - // statically -- this humongo type can then overflow, - // leading to an ambiguous result. So report this as an - // overflow bug, since I believe this is the only case - // where ambiguity can result. - debug!("Encountered ambiguity selecting `{:?}` during trans, \ - presuming due to overflow", - trait_ref); - tcx.sess.span_fatal(span, - "reached the recursion limit during monomorphization \ - (selection ambiguity)"); - } - Err(e) => { - span_bug!(span, "Encountered error `{:?}` selecting `{:?}` during trans", - e, trait_ref) - } - }; - - debug!("fulfill_obligation: selection={:?}", selection); - - // Currently, we use a fulfillment context to completely resolve - // all nested obligations. This is because they can inform the - // inference of the impl's type parameters. - let mut fulfill_cx = traits::FulfillmentContext::new(); - let vtable = selection.map(|predicate| { - debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); - fulfill_cx.register_predicate_obligation(&infcx, predicate); - }); - let vtable = infcx.drain_fulfillment_cx_or_panic(span, &mut fulfill_cx, &vtable); - - info!("Cache miss: {:?} => {:?}", trait_ref, vtable); - vtable - }) - }) -} - pub fn langcall(tcx: TyCtxt, span: Option, msg: &str, @@ -617,6 +547,7 @@ pub fn requests_inline<'a, 'tcx>( _ => attr::requests_inline(&tcx.get_attrs(def_id)[..]), } } + /// Given a DefId and some Substs, produces the monomorphic item type. pub fn def_ty<'a, 'tcx>(shared: &SharedCrateContext<'a, 'tcx>, def_id: DefId, @@ -635,14 +566,3 @@ pub fn instance_ty<'a, 'tcx>(shared: &SharedCrateContext<'a, 'tcx>, let ty = instance.def.def_ty(shared.tcx()); monomorphize::apply_param_substs(shared, instance.substs, &ty) } - -pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - name: ast::Name, - substs: &'tcx Substs<'tcx>, - impl_data: &traits::VtableImplData<'tcx, ()>) - -> ty::Instance<'tcx> -{ - let (def_id, substs) = traits::find_method(tcx, name, substs, impl_data); - let substs = tcx.erase_regions(&substs); - ty::Instance::new(def_id, substs) -} diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs index 35ebd67b5f8c1..2eb94aa56ab50 100644 --- a/src/librustc_trans/glue.rs +++ b/src/librustc_trans/glue.rs @@ -39,7 +39,6 @@ use value::Value; use Disr; use builder::Builder; -use syntax_pos::DUMMY_SP; use mir::lvalue::Alignment; pub fn trans_exchange_free_ty<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, ptr: LvalueRef<'tcx>) { @@ -241,8 +240,6 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi let shallow_drop = def.is_union(); let tcx = bcx.tcx(); - let def = t.ty_adt_def().unwrap(); - // Be sure to put the contents into a scope so we can use an invoke // instruction to call the user destructor but still call the field // destructors if the user destructor panics. @@ -256,17 +253,12 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi } else { CleanupScope::noop() }; - - let trait_ref = ty::Binder(ty::TraitRef { - def_id: tcx.lang_items.drop_trait().unwrap(), - substs: tcx.mk_substs_trait(t, &[]) - }); - let vtbl = match fulfill_obligation(bcx.ccx.shared(), DUMMY_SP, trait_ref) { - traits::VtableImpl(data) => data, - _ => bug!("dtor for {:?} is not an impl???", t) - }; - let dtor_did = def.destructor(tcx).unwrap().did; - let callee = Callee::def(bcx.ccx, dtor_did, vtbl.substs); + let drop_trait_def_id = tcx.lang_items.drop_trait().unwrap(); + let drop_method = tcx.associated_items(drop_trait_def_id) + .find(|it| it.kind == ty::AssociatedKind::Method) + .unwrap().def_id; + let self_type_substs = tcx.mk_substs_trait(t, &[]); + let callee = Callee::def(bcx.ccx, drop_method, self_type_substs); let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); let llret; let args = &[ptr.llval, ptr.llextra][..1 + ptr.has_extra() as usize]; diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 1530fcda3d3ea..49cc0b5fad401 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -28,6 +28,7 @@ #![feature(box_syntax)] #![feature(const_fn)] #![feature(custom_attribute)] +#![cfg_attr(stage0, feature(field_init_shorthand))] #![allow(unused_attributes)] #![feature(i128_type)] #![feature(libc)] diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index deb1073cf9aed..69009ab356044 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -249,7 +249,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { substs: &'tcx Substs<'tcx>, args: IndexVec>) -> Result, ConstEvalErr<'tcx>> { - let instance = monomorphize::resolve_const(ccx.shared(), def_id, substs); + let instance = monomorphize::resolve(ccx.shared(), def_id, substs); let mir = ccx.tcx().instance_mir(instance.def); MirConstContext::new(ccx, &mir, instance.substs, args).trans() } diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs index 3b746af275a2a..bf073d8b97844 100644 --- a/src/librustc_trans/monomorphize.rs +++ b/src/librustc_trans/monomorphize.rs @@ -8,38 +8,260 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use abi::Abi; use common::*; + use rustc::hir::def_id::DefId; use rustc::infer::TransNormalize; -use rustc::traits; +use rustc::traits::{self, SelectionContext, Reveal}; +use rustc::ty::adjustment::CustomCoerceUnsized; use rustc::ty::fold::{TypeFolder, TypeFoldable}; -use rustc::ty::subst::{Subst, Substs}; +use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::util::common::MemoizationMap; -use syntax::codemap::DUMMY_SP; +use syntax::ast; +use syntax::codemap::{Span, DUMMY_SP}; pub use rustc::ty::Instance; -/// For associated constants from traits, return the impl definition. -pub fn resolve_const<'a, 'tcx>( - scx: &SharedCrateContext<'a, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx> +fn fn_once_adapter_instance<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + closure_did: DefId, + substs: ty::ClosureSubsts<'tcx>, + ) -> Instance<'tcx> { + debug!("fn_once_adapter_shim({:?}, {:?})", + closure_did, + substs); + let fn_once = tcx.lang_items.fn_once_trait().unwrap(); + let call_once = tcx.associated_items(fn_once) + .find(|it| it.kind == ty::AssociatedKind::Method) + .unwrap().def_id; + let def = ty::InstanceDef::ClosureOnceShim { call_once, closure_did }; + + let self_ty = tcx.mk_closure_from_closure_substs( + closure_did, substs); + + let sig = tcx.closure_type(closure_did).subst(tcx, substs.substs); + let sig = tcx.erase_late_bound_regions_and_normalize(&sig); + assert_eq!(sig.inputs().len(), 1); + let substs = tcx.mk_substs([ + Kind::from(self_ty), + Kind::from(sig.inputs()[0]), + ].iter().cloned()); + + debug!("fn_once_adapter_shim: self_ty={:?} sig={:?}", self_ty, sig); + Instance { def, substs } +} + +/// Attempts to resolve an obligation. The result is a shallow vtable resolution -- meaning that we +/// do not (necessarily) resolve all nested obligations on the impl. Note that type check should +/// guarantee to us that all nested obligations *could be* resolved if we wanted to. +fn fulfill_obligation<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, + span: Span, + trait_ref: ty::PolyTraitRef<'tcx>) + -> traits::Vtable<'tcx, ()> +{ + let tcx = scx.tcx(); + + // Remove any references to regions; this helps improve caching. + let trait_ref = tcx.erase_regions(&trait_ref); + + scx.trait_cache().memoize(trait_ref, || { + debug!("trans::fulfill_obligation(trait_ref={:?}, def_id={:?})", + trait_ref, trait_ref.def_id()); + + // Do the initial selection for the obligation. This yields the + // shallow result we are looking for -- that is, what specific impl. + tcx.infer_ctxt((), Reveal::All).enter(|infcx| { + let mut selcx = SelectionContext::new(&infcx); + + let obligation_cause = traits::ObligationCause::misc(span, + ast::DUMMY_NODE_ID); + let obligation = traits::Obligation::new(obligation_cause, + trait_ref.to_poly_trait_predicate()); + + let selection = match selcx.select(&obligation) { + Ok(Some(selection)) => selection, + Ok(None) => { + // Ambiguity can happen when monomorphizing during trans + // expands to some humongo type that never occurred + // statically -- this humongo type can then overflow, + // leading to an ambiguous result. So report this as an + // overflow bug, since I believe this is the only case + // where ambiguity can result. + debug!("Encountered ambiguity selecting `{:?}` during trans, \ + presuming due to overflow", + trait_ref); + tcx.sess.span_fatal(span, + "reached the recursion limit during monomorphization \ + (selection ambiguity)"); + } + Err(e) => { + span_bug!(span, "Encountered error `{:?}` selecting `{:?}` during trans", + e, trait_ref) + } + }; + + debug!("fulfill_obligation: selection={:?}", selection); + + // Currently, we use a fulfillment context to completely resolve + // all nested obligations. This is because they can inform the + // inference of the impl's type parameters. + let mut fulfill_cx = traits::FulfillmentContext::new(); + let vtable = selection.map(|predicate| { + debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); + let vtable = infcx.drain_fulfillment_cx_or_panic(span, &mut fulfill_cx, &vtable); + + info!("Cache miss: {:?} => {:?}", trait_ref, vtable); + vtable + }) + }) +} + +fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, + trait_closure_kind: ty::ClosureKind) + -> Result +{ + match (actual_closure_kind, trait_closure_kind) { + (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | + (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => { + // No adapter needed. + Ok(false) + } + (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { + // The closure fn `llfn` is a `fn(&self, ...)`. We want a + // `fn(&mut self, ...)`. In fact, at trans time, these are + // basically the same thing, so we can just return llfn. + Ok(false) + } + (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { + // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut + // self, ...)`. We want a `fn(self, ...)`. We can produce + // this by doing something like: + // + // fn call_once(self, ...) { call_mut(&self, ...) } + // fn call_once(mut self, ...) { call_mut(&mut self, ...) } + // + // These are both the same at trans time. + Ok(true) + } + _ => Err(()), + } +} + +fn resolve_associated_item<'a, 'tcx>( + scx: &SharedCrateContext<'a, 'tcx>, + trait_item: &ty::AssociatedItem, + trait_id: DefId, + rcvr_substs: &'tcx Substs<'tcx> ) -> Instance<'tcx> { - if let Some(trait_id) = scx.tcx().trait_of_item(def_id) { - let trait_ref = ty::TraitRef::new(trait_id, substs); - let trait_ref = ty::Binder(trait_ref); - let vtable = fulfill_obligation(scx, DUMMY_SP, trait_ref); - if let traits::VtableImpl(vtable_impl) = vtable { - let name = scx.tcx().item_name(def_id); - let ac = scx.tcx().associated_items(vtable_impl.impl_def_id) - .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name); - if let Some(ac) = ac { - return Instance::new(ac.def_id, vtable_impl.substs); + let tcx = scx.tcx(); + let def_id = trait_item.def_id; + debug!("resolve_associated_item(trait_item={:?}, \ + trait_id={:?}, \ + rcvr_substs={:?})", + def_id, trait_id, rcvr_substs); + + let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); + let vtbl = fulfill_obligation(scx, DUMMY_SP, ty::Binder(trait_ref)); + + // Now that we know which impl is being used, we can dispatch to + // the actual function: + match vtbl { + traits::VtableImpl(impl_data) => { + let (def_id, substs) = traits::find_associated_item( + tcx, trait_item, rcvr_substs, &impl_data); + let substs = tcx.erase_regions(&substs); + ty::Instance::new(def_id, substs) + } + traits::VtableClosure(closure_data) => { + let closure_def_id = closure_data.closure_def_id; + let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap(); + let actual_closure_kind = tcx.closure_kind(closure_def_id); + + match needs_fn_once_adapter_shim(actual_closure_kind, + trait_closure_kind) { + Ok(true) => fn_once_adapter_instance( + tcx, closure_def_id, closure_data.substs), + _ => Instance::new(closure_def_id, closure_data.substs.substs), + } + } + traits::VtableFnPointer(ref data) => { + Instance { + def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), + substs: rcvr_substs + } + } + traits::VtableObject(ref data) => { + let index = tcx.get_vtable_index_of_object_method(data, def_id); + Instance { + def: ty::InstanceDef::Virtual(def_id, index), + substs: rcvr_substs } } + _ => { + bug!("static call to invalid vtable: {:?}", vtbl) + } } +} - Instance::new(def_id, substs) +/// The point where linking happens. Resolve a (def_id, substs) +/// pair to an instance. +pub fn resolve<'a, 'tcx>( + scx: &SharedCrateContext<'a, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx> +) -> Instance<'tcx> { + debug!("resolve(def_id={:?}, substs={:?})", + def_id, substs); + let result = if let Some(trait_def_id) = scx.tcx().trait_of_item(def_id) { + debug!(" => associated item, attempting to find impl"); + let item = scx.tcx().associated_item(def_id); + resolve_associated_item(scx, &item, trait_def_id, substs) + } else { + let item_type = def_ty(scx, def_id, substs); + let def = match item_type.sty { + ty::TyFnDef(_, _, f) if + f.abi() == Abi::RustIntrinsic || + f.abi() == Abi::PlatformIntrinsic => + { + debug!(" => intrinsic"); + ty::InstanceDef::Intrinsic(def_id) + } + _ => { + debug!(" => free item"); + ty::InstanceDef::Item(def_id) + } + }; + Instance { def, substs } + }; + debug!("resolve(def_id={:?}, substs={:?}) = {}", + def_id, substs, result); + result +} + +pub fn custom_coerce_unsize_info<'scx, 'tcx>(scx: &SharedCrateContext<'scx, 'tcx>, + source_ty: Ty<'tcx>, + target_ty: Ty<'tcx>) + -> CustomCoerceUnsized { + let trait_ref = ty::Binder(ty::TraitRef { + def_id: scx.tcx().lang_items.coerce_unsized_trait().unwrap(), + substs: scx.tcx().mk_substs_trait(source_ty, &[target_ty]) + }); + + match fulfill_obligation(scx, DUMMY_SP, trait_ref) { + traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => { + scx.tcx().custom_coerce_unsized_kind(impl_def_id) + } + vtable => { + bug!("invalid CoerceUnsized vtable: {:?}", vtable); + } + } } /// Monomorphizes a type from the AST by first applying the in-scope diff --git a/src/librustc_trans/partitioning.rs b/src/librustc_trans/partitioning.rs index 1232c6cd28e52..d0cf32508d44c 100644 --- a/src/librustc_trans/partitioning.rs +++ b/src/librustc_trans/partitioning.rs @@ -458,7 +458,10 @@ fn characteristic_def_id_of_trans_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 't TransItem::Fn(instance) => { let def_id = match instance.def { ty::InstanceDef::Item(def_id) => def_id, - ty::InstanceDef::FnPtrShim(..) => return None + ty::InstanceDef::FnPtrShim(..) | + ty::InstanceDef::ClosureOnceShim { .. } | + ty::InstanceDef::Intrinsic(..) | + ty::InstanceDef::Virtual(..) => return None }; // If this is a method, we want to put it into the same module as From 65a4266f1f4ce9dac5a6ef588443b7cac911d265 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 8 Mar 2017 18:33:21 +0200 Subject: [PATCH 49/54] refactor away callee::Callee and translate virtual calls through a MIR shim These changes are in the same commit to avoid needing to adapt meth::trans_object_shim to the new scheme. One codegen-units test is broken because we instantiate the shims even when they are not needed. This will be fixed in the next PR. --- src/librustc_mir/lib.rs | 1 + src/librustc_mir/mir_map.rs | 5 +- src/librustc_mir/shim.rs | 213 ++++++++++++++++++----------- src/librustc_trans/abi.rs | 31 ++++- src/librustc_trans/base.rs | 8 +- src/librustc_trans/callee.rs | 150 +++++++------------- src/librustc_trans/collector.rs | 2 +- src/librustc_trans/context.rs | 6 +- src/librustc_trans/glue.rs | 20 +-- src/librustc_trans/meth.rs | 78 +---------- src/librustc_trans/mir/block.rs | 156 +++++++++++---------- src/librustc_trans/mir/constant.rs | 8 +- src/librustc_trans/mir/rvalue.rs | 13 +- 13 files changed, 324 insertions(+), 367 deletions(-) diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 9e6b77dbabdef..2718a0204a1ed 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -23,6 +23,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(associated_consts)] #![feature(box_patterns)] #![feature(box_syntax)] +#![cfg_attr(stage0, feature(field_init_shorthand))] #![feature(i128_type)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 3fa7131a2b6b0..2d2b90235ca3b 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -252,12 +252,9 @@ fn closure_self_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, -> Ty<'tcx> { let closure_ty = tcx.body_tables(body_id).node_id_to_type(closure_expr_id); - // We're just hard-coding the idea that the signature will be - // &self or &mut self and hence will have a bound region with - // number 0, hokey. let region = ty::Region::ReFree(ty::FreeRegion { scope: tcx.region_maps.item_extent(body_id.node_id), - bound_region: ty::BoundRegion::BrAnon(0), + bound_region: ty::BoundRegion::BrEnv, }); let region = tcx.mk_region(region); diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 2a053535e7e10..97c83c7a42e28 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -9,17 +9,19 @@ // except according to those terms. use rustc::hir; +use rustc::hir::def_id::DefId; use rustc::infer; +use rustc::middle::region::ROOT_CODE_EXTENT; use rustc::mir::*; use rustc::mir::transform::MirSource; use rustc::ty::{self, Ty}; +use rustc::ty::subst::Subst; use rustc::ty::maps::Providers; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use syntax::abi::Abi; use syntax::ast; -use syntax::codemap::DUMMY_SP; use syntax_pos::Span; use std::cell::RefCell; @@ -35,11 +37,51 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, -> &'tcx RefCell> { debug!("make_shim({:?})", instance); + let did = instance.def_id(); + let span = tcx.def_span(did); + let param_env = + tcx.construct_parameter_environment(span, did, ROOT_CODE_EXTENT); + let result = match instance { ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance), - ty::InstanceDef::FnPtrShim(_, ty) => { - build_fn_ptr_shim(tcx, ty, instance.def_ty(tcx)) + ty::InstanceDef::FnPtrShim(def_id, ty) => { + let trait_ = tcx.trait_of_item(def_id).unwrap(); + let adjustment = match tcx.lang_items.fn_trait_kind(trait_) { + Some(ty::ClosureKind::FnOnce) => Adjustment::Identity, + Some(ty::ClosureKind::FnMut) | + Some(ty::ClosureKind::Fn) => Adjustment::Deref, + None => bug!("fn pointer {:?} is not an fn", ty) + }; + // HACK: we need the "real" argument types for the MIR, + // but because our substs are (Self, Args), where Args + // is a tuple, we must include the *concrete* argument + // types in the MIR. They will be substituted again with + // the param-substs, but because they are concrete, this + // will not do any harm. + let sig = tcx.erase_late_bound_regions(&ty.fn_sig()); + let arg_tys = sig.inputs(); + + build_call_shim( + tcx, + ¶m_env, + def_id, + adjustment, + CallKind::Indirect, + Some(arg_tys) + ) + } + ty::InstanceDef::Virtual(def_id, _) => { + // We are translating a call back to our def-id, which + // trans::mir knows to turn to an actual virtual call. + build_call_shim( + tcx, + ¶m_env, + def_id, + Adjustment::Identity, + CallKind::Direct(def_id), + None + ) } _ => bug!("unknown shim kind") }; @@ -51,124 +93,135 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, result } +#[derive(Copy, Clone, Debug, PartialEq)] +enum Adjustment { + Identity, + Deref, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +enum CallKind { + Indirect, + Direct(DefId), +} + +fn temp_decl(mutability: Mutability, ty: Ty) -> LocalDecl { + LocalDecl { mutability, ty, name: None, source_info: None } +} + fn local_decls_for_sig<'tcx>(sig: &ty::FnSig<'tcx>) -> IndexVec> { - iter::once(LocalDecl { - mutability: Mutability::Mut, - ty: sig.output(), - name: None, - source_info: None - }).chain(sig.inputs().iter().map(|ity| LocalDecl { - mutability: Mutability::Not, - ty: *ity, - name: None, - source_info: None, - })).collect() + iter::once(temp_decl(Mutability::Mut, sig.output())) + .chain(sig.inputs().iter().map( + |ity| temp_decl(Mutability::Not, ity))) + .collect() } - -fn build_fn_ptr_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, - fn_ty: Ty<'tcx>, - sig_ty: Ty<'tcx>) - -> Mir<'tcx> +/// Build a "call" shim for `def_id`. The shim calls the +/// function specified by `call_kind`, first adjusting its first +/// argument according to `rcvr_adjustment`. +/// +/// If `untuple_args` is a vec of types, the second argument of the +/// function will be untupled as these types. +fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, + param_env: &ty::ParameterEnvironment<'tcx>, + def_id: DefId, + rcvr_adjustment: Adjustment, + call_kind: CallKind, + untuple_args: Option<&[Ty<'tcx>]>) + -> Mir<'tcx> { - debug!("build_fn_ptr_shim(fn_ty={:?}, sig_ty={:?})", fn_ty, sig_ty); - let trait_sig = match sig_ty.sty { - ty::TyFnDef(_, _, fty) => tcx.erase_late_bound_regions(&fty), - _ => bug!("unexpected type for shim {:?}", sig_ty) - }; + debug!("build_call_shim(def_id={:?}, rcvr_adjustment={:?}, \ + call_kind={:?}, untuple_args={:?})", + def_id, rcvr_adjustment, call_kind, untuple_args); - let self_ty = match trait_sig.inputs()[0].sty { - ty::TyParam(..) => fn_ty, - ty::TyRef(r, mt) => tcx.mk_ref(r, ty::TypeAndMut { - ty: fn_ty, - mutbl: mt.mutbl - }), - _ => bug!("unexpected self_ty {:?}", trait_sig), - }; + let fn_ty = tcx.item_type(def_id).subst(tcx, param_env.free_substs); + // Not normalizing here without a param env. + let sig = tcx.erase_late_bound_regions(&fn_ty.fn_sig()); + let span = tcx.def_span(def_id); - let fn_ptr_sig = match fn_ty.sty { - ty::TyFnPtr(fty) | - ty::TyFnDef(_, _, fty) => - tcx.erase_late_bound_regions_and_normalize(&fty), - _ => bug!("non-fn-ptr {:?} in build_fn_ptr_shim", fn_ty) - }; - - let sig = tcx.mk_fn_sig( - [ - self_ty, - tcx.intern_tup(fn_ptr_sig.inputs(), false) - ].iter().cloned(), - fn_ptr_sig.output(), - false, - hir::Unsafety::Normal, - Abi::RustCall, - ); + debug!("build_call_shim: sig={:?}", sig); let local_decls = local_decls_for_sig(&sig); - let source_info = SourceInfo { - span: DUMMY_SP, - scope: ARGUMENT_VISIBILITY_SCOPE + let source_info = SourceInfo { span, scope: ARGUMENT_VISIBILITY_SCOPE }; + + let rcvr_l = Lvalue::Local(Local::new(1+0)); + + let return_block_id = BasicBlock::new(1); + + let rcvr = match rcvr_adjustment { + Adjustment::Identity => Operand::Consume(rcvr_l), + Adjustment::Deref => Operand::Consume(Lvalue::Projection( + box Projection { base: rcvr_l, elem: ProjectionElem::Deref } + )) }; - let fn_ptr = Lvalue::Local(Local::new(1+0)); - let fn_ptr = match trait_sig.inputs()[0].sty { - ty::TyParam(..) => fn_ptr, - ty::TyRef(..) => Lvalue::Projection(box Projection { - base: fn_ptr, elem: ProjectionElem::Deref - }), - _ => bug!("unexpected self_ty {:?}", trait_sig), + let (callee, mut args) = match call_kind { + CallKind::Indirect => (rcvr, vec![]), + CallKind::Direct(def_id) => ( + Operand::Constant(Constant { + span: span, + ty: tcx.item_type(def_id).subst(tcx, param_env.free_substs), + literal: Literal::Item { def_id, substs: param_env.free_substs }, + }), + vec![rcvr] + ) }; - let fn_args = Local::new(1+1); - let return_block_id = BasicBlock::new(1); + if let Some(untuple_args) = untuple_args { + args.extend(untuple_args.iter().enumerate().map(|(i, ity)| { + let arg_lv = Lvalue::Local(Local::new(1+1)); + Operand::Consume(Lvalue::Projection(box Projection { + base: arg_lv, + elem: ProjectionElem::Field(Field::new(i), *ity) + })) + })); + } else { + args.extend((1..sig.inputs().len()).map(|i| { + Operand::Consume(Lvalue::Local(Local::new(1+i))) + })); + } - // return = ADT(arg0, arg1, ...); return - let start_block = BasicBlockData { + let mut blocks = IndexVec::new(); + blocks.push(BasicBlockData { statements: vec![], terminator: Some(Terminator { source_info: source_info, kind: TerminatorKind::Call { - func: Operand::Consume(fn_ptr), - args: fn_ptr_sig.inputs().iter().enumerate().map(|(i, ity)| { - Operand::Consume(Lvalue::Projection(box Projection { - base: Lvalue::Local(fn_args), - elem: ProjectionElem::Field( - Field::new(i), *ity - ) - })) - }).collect(), - // FIXME: can we pass a Some destination for an uninhabited ty? + func: callee, + args: args, destination: Some((Lvalue::Local(RETURN_POINTER), return_block_id)), cleanup: None } }), is_cleanup: false - }; - let return_block = BasicBlockData { + }); + blocks.push(BasicBlockData { statements: vec![], terminator: Some(Terminator { source_info: source_info, kind: TerminatorKind::Return }), is_cleanup: false - }; + }); let mut mir = Mir::new( - vec![start_block, return_block].into_iter().collect(), + blocks, IndexVec::from_elem_n( - VisibilityScopeData { span: DUMMY_SP, parent_scope: None }, 1 + VisibilityScopeData { span: span, parent_scope: None }, 1 ), IndexVec::new(), sig.output(), local_decls, sig.inputs().len(), vec![], - DUMMY_SP + span ); - mir.spread_arg = Some(fn_args); + if let Abi::RustCall = sig.abi { + mir.spread_arg = Some(Local::new(sig.inputs().len())); + } mir } diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index 0bbe981f2f72c..710aa1fbbc6d9 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -11,7 +11,7 @@ use llvm::{self, ValueRef, Integer, Pointer, Float, Double, Struct, Array, Vector, AttributePlace}; use base; use builder::Builder; -use common::{type_is_fat_ptr, C_uint}; +use common::{self, type_is_fat_ptr, C_uint}; use context::CrateContext; use cabi_x86; use cabi_x86_64; @@ -334,9 +334,30 @@ impl FnType { fn_ty } - pub fn unadjusted<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + pub fn new_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> FnType { + let mut fn_ty = FnType::unadjusted(ccx, sig, extra_args); + // Don't pass the vtable, it's not an argument of the virtual fn. + fn_ty.args[1].ignore(); + fn_ty.adjust_for_abi(ccx, sig); + fn_ty + } + + pub fn from_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + instance: &ty::Instance<'tcx>, + extra_args: &[Ty<'tcx>]) -> FnType + { + let ity = common::instance_ty(ccx.shared(), instance); + let sig = common::ty_fn_sig(ccx, ity); + let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&sig); + + Self::new(ccx, sig, extra_args) + } + + fn unadjusted<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + sig: ty::FnSig<'tcx>, + extra_args: &[Ty<'tcx>]) -> FnType { use self::Abi::*; let cconv = match ccx.sess().target.target.adjust_abi(sig.abi) { RustIntrinsic | PlatformIntrinsic | @@ -532,9 +553,9 @@ impl FnType { } } - pub fn adjust_for_abi<'a, 'tcx>(&mut self, - ccx: &CrateContext<'a, 'tcx>, - sig: ty::FnSig<'tcx>) { + fn adjust_for_abi<'a, 'tcx>(&mut self, + ccx: &CrateContext<'a, 'tcx>, + sig: ty::FnSig<'tcx>) { let abi = sig.abi; if abi == Abi::Unadjusted { return } diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 10ab199671f64..a58764878a318 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -47,7 +47,7 @@ use abi; use mir::lvalue::LvalueRef; use attributes; use builder::Builder; -use callee::{Callee}; +use callee; use common::{C_bool, C_bytes_in_context, C_i32, C_uint}; use collector::{self, TransItemCollectionMode}; use common::{C_struct_in_context, C_u64, C_undef}; @@ -654,7 +654,7 @@ pub fn maybe_create_entry_wrapper(ccx: &CrateContext) { return; } - let main_llfn = Callee::def(ccx, main_def_id, instance.substs).reify(ccx); + let main_llfn = callee::get_fn(ccx, instance); let et = ccx.sess().entry_type.get().unwrap(); match et { @@ -688,8 +688,8 @@ pub fn maybe_create_entry_wrapper(ccx: &CrateContext) { let (start_fn, args) = if use_start_lang_item { let start_def_id = ccx.tcx().require_lang_item(StartFnLangItem); - let empty_substs = ccx.tcx().intern_substs(&[]); - let start_fn = Callee::def(ccx, start_def_id, empty_substs).reify(ccx); + let start_instance = Instance::mono(ccx.tcx(), start_def_id); + let start_fn = callee::get_fn(ccx, start_instance); (start_fn, vec![bld.pointercast(rust_main, Type::i8p(ccx).ptr_to()), get_param(llfn, 0), get_param(llfn, 1)]) } else { diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 70da2a69ef425..5c7be56b56dad 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -14,8 +14,6 @@ //! and methods are represented as just a fn ptr and not a full //! closure. -pub use self::CalleeData::*; - use llvm::{self, ValueRef, get_params}; use rustc::hir::def_id::DefId; use rustc::ty::subst::{Substs, Subst}; @@ -27,98 +25,17 @@ use cleanup::CleanupScope; use mir::lvalue::LvalueRef; use monomorphize; use consts; -use common::instance_ty; use declare; use value::Value; -use meth; use monomorphize::Instance; use back::symbol_names::symbol_name; use trans_item::TransItem; use type_of; -use rustc::ty::{self, Ty, TypeFoldable}; +use rustc::ty::{self, TypeFoldable}; use std::iter; use mir::lvalue::Alignment; -#[derive(Debug)] -pub enum CalleeData { - /// Function pointer. - Fn(ValueRef), - - Intrinsic, - - /// Trait object found in the vtable at that index. - Virtual(usize) -} - -#[derive(Debug)] -pub struct Callee<'tcx> { - pub data: CalleeData, - pub ty: Ty<'tcx> -} - -impl<'tcx> Callee<'tcx> { - /// Function or method definition. - pub fn def<'a>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>) - -> Callee<'tcx> { - let instance = monomorphize::resolve(ccx.shared(), def_id, substs); - let ty = instance_ty(ccx.shared(), &instance); - let data = match instance.def { - ty::InstanceDef::Intrinsic(_) => Intrinsic, - ty::InstanceDef::ClosureOnceShim { .. } => { - let closure_ty = instance.substs.type_at(0); - let (closure_def_id, closure_substs) = match closure_ty.sty { - ty::TyClosure(def_id, substs) => (def_id, substs), - _ => bug!("bad closure instance {:?}", instance) - }; - - Fn(trans_fn_once_adapter_shim( - ccx, - closure_def_id, - closure_substs, - instance, - get_fn( - ccx, - Instance::new(closure_def_id, closure_substs.substs) - ) - )) - } - ty::InstanceDef::Virtual(_, n) => Virtual(n), - ty::InstanceDef::FnPtrShim(..) | - ty::InstanceDef::Item(..) => { - Fn(get_fn(ccx, instance)) - } - }; - - Callee { data, ty } - } - - /// Get the abi::FnType for a direct call. Mainly deals with the fact - /// that a Virtual call doesn't take the vtable, like its shim does. - /// The extra argument types are for variadic (extern "C") functions. - pub fn direct_fn_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>, - extra_args: &[Ty<'tcx>]) -> FnType { - let sig = common::ty_fn_sig(ccx, self.ty); - let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&sig); - let mut fn_ty = FnType::unadjusted(ccx, sig, extra_args); - if let Virtual(_) = self.data { - // Don't pass the vtable, it's not an argument of the virtual fn. - fn_ty.args[1].ignore(); - } - fn_ty.adjust_for_abi(ccx, sig); - fn_ty - } - - /// Turn the callee into a function pointer. - pub fn reify<'a>(self, ccx: &CrateContext<'a, 'tcx>) -> ValueRef { - match self.data { - Fn(llfn) => llfn, - Virtual(_) => meth::trans_object_shim(ccx, self), - Intrinsic => bug!("intrinsic {} getting reified", self.ty) - } - } -} - fn trans_fn_once_adapter_shim<'a, 'tcx>( ccx: &'a CrateContext<'a, 'tcx>, def_id: DefId, @@ -145,13 +62,14 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( let sig = tcx.closure_type(def_id).subst(tcx, substs.substs); let sig = tcx.erase_late_bound_regions_and_normalize(&sig); assert_eq!(sig.abi, Abi::RustCall); - let llref_fn_ty = tcx.mk_fn_ptr(ty::Binder(tcx.mk_fn_sig( + let llref_fn_sig = tcx.mk_fn_sig( iter::once(ref_closure_ty).chain(sig.inputs().iter().cloned()), sig.output(), sig.variadic, sig.unsafety, Abi::RustCall - ))); + ); + let llref_fn_ty = tcx.mk_fn_ptr(ty::Binder(llref_fn_sig)); debug!("trans_fn_once_adapter_shim: llref_fn_ty={:?}", llref_fn_ty); @@ -177,15 +95,10 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( let orig_fn_ty = fn_ty; let mut bcx = Builder::new_block(ccx, lloncefn, "entry-block"); - let callee = Callee { - data: Fn(llreffn), - ty: llref_fn_ty - }; - // the first argument (`self`) will be the (by value) closure env. let mut llargs = get_params(lloncefn); - let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); + let fn_ty = FnType::new(ccx, llref_fn_sig, &[]); let self_idx = fn_ty.ret.is_indirect() as usize; let env_arg = &orig_fn_ty.args[0]; let env = if env_arg.is_indirect() { @@ -210,14 +123,13 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( // to drop `self` when the body returns, or in case it unwinds. let self_scope = CleanupScope::schedule_drop_mem(&bcx, env); - let llfn = callee.reify(bcx.ccx); let llret; if let Some(landing_pad) = self_scope.landing_pad { let normal_bcx = bcx.build_sibling_block("normal-return"); - llret = bcx.invoke(llfn, &llargs[..], normal_bcx.llbb(), landing_pad, None); + llret = bcx.invoke(llreffn, &llargs[..], normal_bcx.llbb(), landing_pad, None); bcx = normal_bcx; } else { - llret = bcx.call(llfn, &llargs[..], None); + llret = bcx.call(llreffn, &llargs[..], None); } fn_ty.apply_attrs_callsite(llret); @@ -247,9 +159,9 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( /// - `ccx`: the crate context /// - `def_id`: def id of the fn or method item being referenced /// - `substs`: values for each of the fn/method's parameters -fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - instance: Instance<'tcx>) - -> ValueRef +fn do_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + instance: Instance<'tcx>) + -> ValueRef { let tcx = ccx.tcx(); @@ -335,3 +247,45 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, llfn } + +pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + instance: Instance<'tcx>) + -> ValueRef +{ + match instance.def { + ty::InstanceDef::Intrinsic(_) => { + bug!("intrinsic {} getting reified", instance) + } + ty::InstanceDef::ClosureOnceShim { .. } => { + let closure_ty = instance.substs.type_at(0); + let (closure_def_id, closure_substs) = match closure_ty.sty { + ty::TyClosure(def_id, substs) => (def_id, substs), + _ => bug!("bad closure instance {:?}", instance) + }; + + trans_fn_once_adapter_shim( + ccx, + closure_def_id, + closure_substs, + instance, + do_get_fn( + ccx, + Instance::new(closure_def_id, closure_substs.substs) + ) + ) + } + ty::InstanceDef::FnPtrShim(..) | + ty::InstanceDef::Item(..) | + ty::InstanceDef::Virtual(..) => { + do_get_fn(ccx, instance) + } + } +} + +pub fn resolve_and_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>) + -> ValueRef +{ + get_fn(ccx, monomorphize::resolve(ccx.shared(), def_id, substs)) +} diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 9476ffc944485..2113b9b203ced 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -656,8 +656,8 @@ fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instan ty::InstanceDef::ClosureOnceShim { call_once: _, closure_did: def_id } => def_id, - ty::InstanceDef::FnPtrShim(..) => return true, ty::InstanceDef::Virtual(..) | + ty::InstanceDef::FnPtrShim(..) => return true, ty::InstanceDef::Intrinsic(_) => return false }; match tcx.hir.get_if_local(def_id) { diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index b7381dd07dcfa..9297c0108468a 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -18,7 +18,7 @@ use rustc::hir::def::ExportMap; use rustc::hir::def_id::DefId; use rustc::traits; use debuginfo; -use callee::Callee; +use callee; use base; use declare; use glue::DropGlueKind; @@ -920,7 +920,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { let tcx = self.tcx(); let llfn = match tcx.lang_items.eh_personality() { Some(def_id) if !base::wants_msvc_seh(self.sess()) => { - Callee::def(self, def_id, tcx.intern_substs(&[])).reify(self) + callee::resolve_and_get_fn(self, def_id, tcx.intern_substs(&[])) } _ => { let name = if base::wants_msvc_seh(self.sess()) { @@ -948,7 +948,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { let tcx = self.tcx(); assert!(self.sess().target.target.options.custom_unwind_resume); if let Some(def_id) = tcx.lang_items.eh_unwind_resume() { - let llfn = Callee::def(self, def_id, tcx.intern_substs(&[])).reify(self); + let llfn = callee::resolve_and_get_fn(self, def_id, tcx.intern_substs(&[])); unwresume.set(Some(llfn)); return llfn; } diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs index 2eb94aa56ab50..9a639ed21f1ac 100644 --- a/src/librustc_trans/glue.rs +++ b/src/librustc_trans/glue.rs @@ -24,9 +24,10 @@ use rustc::ty::{self, layout, AdtDef, AdtKind, Ty, TypeFoldable}; use rustc::ty::subst::Kind; use rustc::mir::tcx::LvalueTy; use mir::lvalue::LvalueRef; +use abi::FnType; use adt; use base::*; -use callee::Callee; +use callee::get_fn; use cleanup::CleanupScope; use common::*; use machine::*; @@ -45,11 +46,10 @@ pub fn trans_exchange_free_ty<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, ptr: LvalueRef< let content_ty = ptr.ty.to_ty(bcx.tcx()); let def_id = langcall(bcx.tcx(), None, "", BoxFreeFnLangItem); let substs = bcx.tcx().mk_substs(iter::once(Kind::from(content_ty))); - let callee = Callee::def(bcx.ccx, def_id, substs); + let instance = monomorphize::resolve(bcx.ccx.shared(), def_id, substs); - let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); - - let llret = bcx.call(callee.reify(bcx.ccx), + let fn_ty = FnType::from_instance(bcx.ccx, &instance, &[]); + let llret = bcx.call(get_fn(bcx.ccx, instance), &[ptr.llval, ptr.llextra][..1 + ptr.has_extra() as usize], None); fn_ty.apply_attrs_callsite(llret); } @@ -258,16 +258,18 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi .find(|it| it.kind == ty::AssociatedKind::Method) .unwrap().def_id; let self_type_substs = tcx.mk_substs_trait(t, &[]); - let callee = Callee::def(bcx.ccx, drop_method, self_type_substs); - let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); + let drop_instance = monomorphize::resolve( + bcx.ccx.shared(), drop_method, self_type_substs); + let fn_ty = FnType::from_instance(bcx.ccx, &drop_instance, &[]); + let llfn = get_fn(bcx.ccx, drop_instance); let llret; let args = &[ptr.llval, ptr.llextra][..1 + ptr.has_extra() as usize]; if let Some(landing_pad) = contents_scope.landing_pad { let normal_bcx = bcx.build_sibling_block("normal-return"); - llret = bcx.invoke(callee.reify(ccx), args, normal_bcx.llbb(), landing_pad, None); + llret = bcx.invoke(llfn, args, normal_bcx.llbb(), landing_pad, None); bcx = normal_bcx; } else { - llret = bcx.call(callee.reify(bcx.ccx), args, None); + llret = bcx.call(llfn, args, None); } fn_ty.apply_attrs_callsite(llret); contents_scope.trans(&bcx); diff --git a/src/librustc_trans/meth.rs b/src/librustc_trans/meth.rs index bfd9f69a92218..6b823756c8eab 100644 --- a/src/librustc_trans/meth.rs +++ b/src/librustc_trans/meth.rs @@ -8,20 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use attributes; -use llvm::{ValueRef, get_params}; +use llvm::ValueRef; use rustc::traits; -use callee::{Callee, CalleeData}; +use callee; use common::*; use builder::Builder; use consts; -use declare; use glue; use machine; -use monomorphize::Instance; use type_::Type; use type_of::*; -use back::symbol_names; use value::Value; use rustc::ty; @@ -42,74 +38,6 @@ pub fn get_virtual_method<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, ptr } -/// Generate a shim function that allows an object type like `SomeTrait` to -/// implement the type `SomeTrait`. Imagine a trait definition: -/// -/// trait SomeTrait { fn get(&self) -> i32; ... } -/// -/// And a generic bit of code: -/// -/// fn foo(t: &T) { -/// let x = SomeTrait::get; -/// x(t) -/// } -/// -/// What is the value of `x` when `foo` is invoked with `T=SomeTrait`? -/// The answer is that it is a shim function generated by this routine: -/// -/// fn shim(t: &SomeTrait) -> i32 { -/// // ... call t.get() virtually ... -/// } -/// -/// In fact, all virtual calls can be thought of as normal trait calls -/// that go through this shim function. -pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, - callee: Callee<'tcx>) - -> ValueRef { - debug!("trans_object_shim({:?})", callee); - - let function_name = match callee.ty.sty { - ty::TyFnDef(def_id, substs, _) => { - let instance = Instance::new(def_id, substs); - symbol_names::symbol_name(instance, ccx.shared()) - } - _ => bug!() - }; - - let llfn = declare::define_internal_fn(ccx, &function_name, callee.ty); - attributes::set_frame_pointer_elimination(ccx, llfn); - - let bcx = Builder::new_block(ccx, llfn, "entry-block"); - - let mut llargs = get_params(llfn); - let fn_ret = callee.ty.fn_ret(); - let fn_ty = callee.direct_fn_type(ccx, &[]); - - let fn_ptr = match callee.data { - CalleeData::Virtual(idx) => { - let fn_ptr = get_virtual_method(&bcx, - llargs.remove(fn_ty.ret.is_indirect() as usize + 1), idx); - let llty = fn_ty.llvm_type(bcx.ccx).ptr_to(); - bcx.pointercast(fn_ptr, llty) - }, - _ => bug!("trans_object_shim called with non-virtual callee"), - }; - let llret = bcx.call(fn_ptr, &llargs, None); - fn_ty.apply_attrs_callsite(llret); - - if fn_ret.0.is_never() { - bcx.unreachable(); - } else { - if fn_ty.ret.is_indirect() || fn_ty.ret.is_ignore() { - bcx.ret_void(); - } else { - bcx.ret(llret); - } - } - - llfn -} - /// Creates a dynamic vtable for the given type and vtable origin. /// This is used only for objects. /// @@ -150,7 +78,7 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let trait_ref = trait_ref.with_self_ty(tcx, ty); let methods = traits::get_vtable_methods(tcx, trait_ref).map(|opt_mth| { opt_mth.map_or(nullptr, |(def_id, substs)| { - Callee::def(ccx, def_id, substs).reify(ccx) + callee::resolve_and_get_fn(ccx, def_id, substs) }) }); components.extend(methods); diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 2f1a2c9134c39..761f6b208e747 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -16,13 +16,14 @@ use rustc::ty::{self, layout, TypeFoldable}; use rustc::mir; use abi::{Abi, FnType, ArgType}; use base::{self, Lifetime}; -use callee::{Callee, CalleeData, Fn, Intrinsic, Virtual}; +use callee; use builder::Builder; use common::{self, Funclet}; use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef}; use consts; use machine::llalign_of_min; use meth; +use monomorphize; use type_of::{self, align_of}; use glue; use type_::Type; @@ -340,9 +341,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // Obtain the panic entry point. let def_id = common::langcall(bcx.tcx(), Some(span), "", lang_item); - let callee = Callee::def(bcx.ccx, def_id, - bcx.ccx.empty_substs_for_def_id(def_id)); - let llfn = callee.reify(bcx.ccx); + let instance = ty::Instance::mono(bcx.tcx(), def_id); + let llfn = callee::get_fn(bcx.ccx, instance); // Translate the actual panic invoke/call. if let Some(unwind) = cleanup { @@ -365,30 +365,30 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. let callee = self.trans_operand(&bcx, func); - let (mut callee, sig) = match callee.ty.sty { + let (instance, mut llfn, sig) = match callee.ty.sty { ty::TyFnDef(def_id, substs, sig) => { - (Callee::def(bcx.ccx, def_id, substs), sig) + (Some(monomorphize::resolve(bcx.ccx.shared(), def_id, substs)), + None, + sig) } ty::TyFnPtr(sig) => { - (Callee { - data: Fn(callee.immediate()), - ty: callee.ty - }, sig) + (None, + Some(callee.immediate()), + sig) } _ => bug!("{} is not callable", callee.ty) }; - + let def = instance.map(|i| i.def); let sig = bcx.tcx().erase_late_bound_regions_and_normalize(&sig); let abi = sig.abi; // Handle intrinsics old trans wants Expr's for, ourselves. - let intrinsic = match (&callee.ty.sty, &callee.data) { - (&ty::TyFnDef(def_id, ..), &Intrinsic) => { - Some(bcx.tcx().item_name(def_id).as_str()) - } + let intrinsic = match def { + Some(ty::InstanceDef::Intrinsic(def_id)) + => Some(bcx.tcx().item_name(def_id).as_str()), _ => None }; - let mut intrinsic = intrinsic.as_ref().map(|s| &s[..]); + let intrinsic = intrinsic.as_ref().map(|s| &s[..]); if intrinsic == Some("move_val_init") { let &(_, target) = destination.as_ref().unwrap(); @@ -412,15 +412,17 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let op_ty = op_arg.ty(&self.mir, bcx.tcx()); self.monomorphize(&op_ty) }).collect::>(); - let fn_ty = callee.direct_fn_type(bcx.ccx, &extra_args); + + let fn_ty = match def { + Some(ty::InstanceDef::Virtual(..)) => { + FnType::new_vtable(bcx.ccx, sig, &extra_args) + } + _ => FnType::new(bcx.ccx, sig, &extra_args) + }; if intrinsic == Some("drop_in_place") { let &(_, target) = destination.as_ref().unwrap(); - let ty = if let ty::TyFnDef(_, substs, _) = callee.ty.sty { - substs.type_at(0) - } else { - bug!("Unexpected ty: {}", callee.ty); - }; + let ty = instance.unwrap().substs.type_at(0); // Double check for necessity to drop if !bcx.ccx.shared().type_needs_drop(ty) { @@ -430,8 +432,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let drop_fn = glue::get_drop_glue(bcx.ccx, ty); let llty = fn_ty.llvm_type(bcx.ccx).ptr_to(); - callee.data = Fn(bcx.pointercast(drop_fn, llty)); - intrinsic = None; + llfn = Some(bcx.pointercast(drop_fn, llty)); } // The arguments we'll be passing. Plus one to account for outptr, if used. @@ -440,12 +441,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // Prepare the return value destination let ret_dest = if let Some((ref dest, _)) = *destination { - let is_intrinsic = if let Intrinsic = callee.data { - true - } else { - false - }; - self.make_return_dest(&bcx, dest, &fn_ty.ret, &mut llargs, is_intrinsic) + let is_intrinsic = intrinsic.is_some(); + self.make_return_dest(&bcx, dest, &fn_ty.ret, &mut llargs, + is_intrinsic) } else { ReturnDest::Nothing }; @@ -483,52 +481,56 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let op = self.trans_operand(&bcx, arg); self.trans_argument(&bcx, op, &mut llargs, &fn_ty, - &mut idx, &mut callee.data); + &mut idx, &mut llfn, &def); } if let Some(tup) = untuple { self.trans_arguments_untupled(&bcx, tup, &mut llargs, &fn_ty, - &mut idx, &mut callee.data) + &mut idx, &mut llfn, &def) } - let fn_ptr = match callee.data { - Intrinsic => { - use intrinsic::trans_intrinsic_call; - - let (dest, llargs) = match ret_dest { - _ if fn_ty.ret.is_indirect() => { - (llargs[0], &llargs[1..]) - } - ReturnDest::Nothing => { - (C_undef(fn_ty.ret.original_ty.ptr_to()), &llargs[..]) - } - ReturnDest::IndirectOperand(dst, _) | - ReturnDest::Store(dst) => (dst, &llargs[..]), - ReturnDest::DirectOperand(_) => - bug!("Cannot use direct operand with an intrinsic call") - }; - - trans_intrinsic_call(&bcx, callee.ty, &fn_ty, &llargs, dest, - terminator.source_info.span); + if intrinsic.is_some() && intrinsic != Some("drop_in_place") { + use intrinsic::trans_intrinsic_call; - if let ReturnDest::IndirectOperand(dst, _) = ret_dest { - // Make a fake operand for store_return - let op = OperandRef { - val: Ref(dst, Alignment::AbiAligned), - ty: sig.output(), - }; - self.store_return(&bcx, ret_dest, fn_ty.ret, op); + let (dest, llargs) = match ret_dest { + _ if fn_ty.ret.is_indirect() => { + (llargs[0], &llargs[1..]) } - - if let Some((_, target)) = *destination { - funclet_br(self, bcx, target); - } else { - bcx.unreachable(); + ReturnDest::Nothing => { + (C_undef(fn_ty.ret.original_ty.ptr_to()), &llargs[..]) } + ReturnDest::IndirectOperand(dst, _) | + ReturnDest::Store(dst) => (dst, &llargs[..]), + ReturnDest::DirectOperand(_) => + bug!("Cannot use direct operand with an intrinsic call") + }; - return; + let callee_ty = common::instance_ty( + bcx.ccx.shared(), instance.as_ref().unwrap()); + trans_intrinsic_call(&bcx, callee_ty, &fn_ty, &llargs, dest, + terminator.source_info.span); + + if let ReturnDest::IndirectOperand(dst, _) = ret_dest { + // Make a fake operand for store_return + let op = OperandRef { + val: Ref(dst, Alignment::AbiAligned), + ty: sig.output(), + }; + self.store_return(&bcx, ret_dest, fn_ty.ret, op); + } + + if let Some((_, target)) = *destination { + funclet_br(self, bcx, target); + } else { + bcx.unreachable(); } - Fn(f) => f, - Virtual(_) => bug!("Virtual fn ptr not extracted") + + return; + } + + let fn_ptr = match (llfn, instance) { + (Some(llfn), _) => llfn, + (None, Some(instance)) => callee::get_fn(bcx.ccx, instance), + _ => span_bug!(span, "no llfn for call"), }; // Many different ways to call a function handled here @@ -578,16 +580,17 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { llargs: &mut Vec, fn_ty: &FnType, next_idx: &mut usize, - callee: &mut CalleeData) { + llfn: &mut Option, + def: &Option>) { if let Pair(a, b) = op.val { // Treat the values in a fat pointer separately. if common::type_is_fat_ptr(bcx.ccx, op.ty) { let (ptr, meta) = (a, b); if *next_idx == 0 { - if let Virtual(idx) = *callee { - let llfn = meth::get_virtual_method(bcx, meta, idx); + if let Some(ty::InstanceDef::Virtual(_, idx)) = *def { + let llmeth = meth::get_virtual_method(bcx, meta, idx); let llty = fn_ty.llvm_type(bcx.ccx).ptr_to(); - *callee = Fn(bcx.pointercast(llfn, llty)); + *llfn = Some(bcx.pointercast(llmeth, llty)); } } @@ -596,8 +599,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // We won't be checking the type again. ty: bcx.tcx().types.err }; - self.trans_argument(bcx, imm_op(ptr), llargs, fn_ty, next_idx, callee); - self.trans_argument(bcx, imm_op(meta), llargs, fn_ty, next_idx, callee); + self.trans_argument(bcx, imm_op(ptr), llargs, fn_ty, next_idx, llfn, def); + self.trans_argument(bcx, imm_op(meta), llargs, fn_ty, next_idx, llfn, def); return; } } @@ -660,7 +663,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { llargs: &mut Vec, fn_ty: &FnType, next_idx: &mut usize, - callee: &mut CalleeData) { + llfn: &mut Option, + def: &Option>) { let tuple = self.trans_operand(bcx, operand); let arg_types = match tuple.ty.sty { @@ -686,7 +690,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { val: val, ty: ty }; - self.trans_argument(bcx, op, llargs, fn_ty, next_idx, callee); + self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def); } } @@ -708,7 +712,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { val: Immediate(elem), ty: ty }; - self.trans_argument(bcx, op, llargs, fn_ty, next_idx, callee); + self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def); } } Pair(a, b) => { @@ -724,7 +728,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { val: Immediate(elem), ty: ty }; - self.trans_argument(bcx, op, llargs, fn_ty, next_idx, callee); + self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def); } } } diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 69009ab356044..107b0982af982 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -23,7 +23,7 @@ use rustc::ty::cast::{CastTy, IntTy}; use rustc::ty::subst::{Kind, Substs, Subst}; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use {abi, adt, base, Disr, machine}; -use callee::Callee; +use callee; use builder::Builder; use common::{self, CrateContext, const_get_elt, val_ty}; use common::{C_array, C_bool, C_bytes, C_floating_f64, C_integral, C_big_integral}; @@ -565,8 +565,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { mir::CastKind::ReifyFnPointer => { match operand.ty.sty { ty::TyFnDef(def_id, substs, _) => { - Callee::def(self.ccx, def_id, substs) - .reify(self.ccx) + callee::resolve_and_get_fn(self.ccx, def_id, substs) } _ => { span_bug!(span, "{} cannot be reified to a fn ptr", @@ -589,8 +588,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { let input = tcx.erase_late_bound_regions_and_normalize(&input); let substs = tcx.mk_substs([operand.ty, input] .iter().cloned().map(Kind::from)); - Callee::def(self.ccx, call_once, substs) - .reify(self.ccx) + callee::resolve_and_get_fn(self.ccx, call_once, substs) } _ => { bug!("{} cannot be cast to a fn ptr", operand.ty) diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index bbaf0f9d35fa6..3832c21d10a49 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -19,7 +19,7 @@ use middle::lang_items::ExchangeMallocFnLangItem; use base; use builder::Builder; -use callee::Callee; +use callee; use common::{self, val_ty, C_bool, C_null, C_uint}; use common::{C_integral}; use adt; @@ -183,8 +183,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { match operand.ty.sty { ty::TyFnDef(def_id, substs, _) => { OperandValue::Immediate( - Callee::def(bcx.ccx, def_id, substs) - .reify(bcx.ccx)) + callee::resolve_and_get_fn(bcx.ccx, def_id, substs)) } _ => { bug!("{} cannot be reified to a fn ptr", operand.ty) @@ -208,8 +207,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let substs = bcx.tcx().mk_substs([operand.ty, input] .iter().cloned().map(Kind::from)); OperandValue::Immediate( - Callee::def(bcx.ccx, call_once, substs) - .reify(bcx.ccx)) + callee::resolve_and_get_fn(bcx.ccx, call_once, substs) + ) } _ => { bug!("{} cannot be cast to a fn ptr", operand.ty) @@ -463,8 +462,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { bcx.sess().fatal(&format!("allocation of `{}` {}", box_ty, s)); } }; - let r = Callee::def(bcx.ccx, def_id, bcx.tcx().intern_substs(&[])) - .reify(bcx.ccx); + let instance = ty::Instance::mono(bcx.tcx(), def_id); + let r = callee::get_fn(bcx.ccx, instance); let val = bcx.pointercast(bcx.call(r, &[llsize, llalign], None), llty_ptr); let operand = OperandRef { From a5e3c3d5b85e415ad2094f476d9f1ac29a48e413 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 8 Mar 2017 21:21:27 +0200 Subject: [PATCH 50/54] collector: collect functions when they are called/reified This avoids the creation of unneeded vtable shims. --- src/librustc_trans/collector.rs | 179 +++++++++++++++----------------- 1 file changed, 86 insertions(+), 93 deletions(-) diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 2113b9b203ced..40a89783d91cf 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -202,7 +202,6 @@ use rustc::mir::{self, Location}; use rustc::mir::visit as mir_visit; use rustc::mir::visit::Visitor as MirVisitor; -use syntax::abi::Abi; use context::SharedCrateContext; use common::{def_ty, instance_ty}; use glue::{self, DropGlueKind}; @@ -486,6 +485,14 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { self.output); } } + mir::Rvalue::Cast(mir::CastKind::ReifyFnPointer, ref operand, _) => { + let fn_ty = operand.ty(self.mir, self.scx.tcx()); + let fn_ty = monomorphize::apply_param_substs( + self.scx, + self.param_substs, + &fn_ty); + visit_fn_use(self.scx, fn_ty, false, &mut self.output); + } mir::Rvalue::Cast(mir::CastKind::ClosureFnPointer, ref operand, _) => { let source_ty = operand.ty(self.mir, self.scx.tcx()); match source_ty.sty { @@ -537,111 +544,97 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { self.super_lvalue(lvalue, context, location); } - fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) { - debug!("visiting operand {:?}", *operand); - - let callee = match *operand { - mir::Operand::Constant(ref constant) => { - if let ty::TyFnDef(def_id, substs, _) = constant.ty.sty { - // This is something that can act as a callee, proceed - Some((def_id, substs)) - } else { - // This is not a callee, but we still have to look for - // references to `const` items - if let mir::Literal::Item { def_id, substs } = constant.literal { - let substs = monomorphize::apply_param_substs(self.scx, - self.param_substs, - &substs); - let instance = monomorphize::resolve(self.scx, def_id, substs); - collect_neighbours(self.scx, instance, self.output); - } + fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: Location) { + debug!("visiting constant {:?} @ {:?}", *constant, location); - None - } - } - _ => 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` + if let ty::TyFnDef(..) = constant.ty.sty { + // function definitions are zero-sized, and only generate + // IR when they are called/reified. + self.super_constant(constant, location); + return + } - let callee_substs = monomorphize::apply_param_substs(self.scx, - self.param_substs, - &callee_substs); - let instance = - monomorphize::resolve(self.scx, callee_def_id, callee_substs); - if should_trans_locally(self.scx.tcx(), &instance) { - if let ty::InstanceDef::ClosureOnceShim { .. } = instance.def { - // This call will instantiate an FnOnce adapter, which - // drops the closure environment. Therefore we need to - // make sure that we collect the drop-glue for the - // environment type. - - let env_ty = instance.substs.type_at(0); - let env_ty = glue::get_drop_glue_type(self.scx, env_ty); - if self.scx.type_needs_drop(env_ty) { - let dg = DropGlueKind::Ty(env_ty); - self.output.push(TransItem::DropGlue(dg)); - } - } - self.output.push(create_fn_trans_item(instance)); - } + if let mir::Literal::Item { def_id, substs } = constant.literal { + let substs = monomorphize::apply_param_substs(self.scx, + self.param_substs, + &substs); + let instance = monomorphize::resolve(self.scx, def_id, substs); + collect_neighbours(self.scx, instance, self.output); } - self.super_operand(operand, location); + self.super_constant(constant, location); } - // This takes care of the "drop_in_place" intrinsic for which we otherwise - // we would not register drop-glues. fn visit_terminator_kind(&mut self, block: mir::BasicBlock, kind: &mir::TerminatorKind<'tcx>, location: Location) { let tcx = self.scx.tcx(); - match *kind { - mir::TerminatorKind::Call { - func: mir::Operand::Constant(ref constant), - ref args, - .. - } => { - match constant.ty.sty { - ty::TyFnDef(def_id, _, bare_fn_ty) - if is_drop_in_place_intrinsic(tcx, def_id, bare_fn_ty) => { - let operand_ty = args[0].ty(self.mir, tcx); - if let ty::TyRawPtr(mt) = operand_ty.sty { - let operand_ty = monomorphize::apply_param_substs(self.scx, - self.param_substs, - &mt.ty); - let ty = glue::get_drop_glue_type(self.scx, operand_ty); - self.output.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); - } else { - bug!("Has the drop_in_place() intrinsic's signature changed?") - } - } - _ => { /* Nothing to do. */ } - } - } - _ => { /* Nothing to do. */ } + if let mir::TerminatorKind::Call { + ref func, + .. + } = *kind { + let callee_ty = func.ty(self.mir, tcx); + let callee_ty = monomorphize::apply_param_substs( + self.scx, self.param_substs, &callee_ty); + visit_fn_use(self.scx, callee_ty, true, &mut self.output); } self.super_terminator_kind(block, kind, location); + } +} + +fn visit_fn_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, + ty: ty::Ty<'tcx>, + is_direct_call: bool, + output: &mut Vec>) +{ + debug!("visit_fn_use({:?}, is_direct_call={:?})", ty, is_direct_call); + let (def_id, substs) = match ty.sty { + ty::TyFnDef(def_id, substs, _) => (def_id, substs), + _ => return + }; - fn is_drop_in_place_intrinsic<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId, - bare_fn_ty: ty::PolyFnSig<'tcx>) - -> bool { - (bare_fn_ty.abi() == Abi::RustIntrinsic || - bare_fn_ty.abi() == Abi::PlatformIntrinsic) && - tcx.item_name(def_id) == "drop_in_place" + let instance = monomorphize::resolve(scx, def_id, substs); + if !should_trans_locally(scx.tcx(), &instance) { + return + } + + match instance.def { + ty::InstanceDef::ClosureOnceShim { .. } => { + // This call will instantiate an FnOnce adapter, which + // drops the closure environment. Therefore we need to + // make sure that we collect the drop-glue for the + // environment type along with the instance. + + let env_ty = instance.substs.type_at(0); + let env_ty = glue::get_drop_glue_type(scx, env_ty); + if scx.type_needs_drop(env_ty) { + let dg = DropGlueKind::Ty(env_ty); + output.push(TransItem::DropGlue(dg)); + } + output.push(create_fn_trans_item(instance)); + } + ty::InstanceDef::Intrinsic(..) => { + if !is_direct_call { + bug!("intrinsic {:?} being reified", ty); + } + if scx.tcx().item_name(def_id) == "drop_in_place" { + // drop_in_place is a call to drop glue, need to instantiate + // that. + let ty = glue::get_drop_glue_type(scx, substs.type_at(0)); + output.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); + } + } + ty::InstanceDef::Virtual(..) => { + // don't need to emit shim if we are calling directly. + if !is_direct_call { + output.push(create_fn_trans_item(instance)); + } + } + ty::InstanceDef::Item(..) | + ty::InstanceDef::FnPtrShim(..) => { + output.push(create_fn_trans_item(instance)); } } } @@ -657,8 +650,8 @@ fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instan call_once: _, closure_did: def_id } => def_id, ty::InstanceDef::Virtual(..) | - ty::InstanceDef::FnPtrShim(..) => return true, - ty::InstanceDef::Intrinsic(_) => return false + ty::InstanceDef::FnPtrShim(..) | + ty::InstanceDef::Intrinsic(_) => return true }; match tcx.hir.get_if_local(def_id) { Some(hir_map::NodeForeignItem(..)) => { From e1f3c67cb4e2eb1595040888e897e48c64afb4be Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 8 Mar 2017 23:19:09 +0200 Subject: [PATCH 51/54] translate closure shims using MIR --- src/librustc/ty/instance.rs | 16 +-- src/librustc_mir/shim.rs | 121 +++++++++++++++----- src/librustc_trans/callee.rs | 173 ++--------------------------- src/librustc_trans/cleanup.rs | 18 --- src/librustc_trans/collector.rs | 43 +------ src/librustc_trans/mir/rvalue.rs | 21 +--- src/librustc_trans/monomorphize.rs | 95 ++++++++-------- src/test/run-pass/issue-29948.rs | 17 ++- 8 files changed, 183 insertions(+), 321 deletions(-) diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index d93482acbcb57..aeb1fe079ff25 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -34,10 +34,7 @@ pub enum InstanceDef<'tcx> { // ::fn Virtual(DefId, usize), // <[mut closure] as FnOnce>::call_once - ClosureOnceShim { - call_once: DefId, - closure_did: DefId - }, + ClosureOnceShim { call_once: DefId }, } impl<'tcx> InstanceDef<'tcx> { @@ -48,9 +45,8 @@ impl<'tcx> InstanceDef<'tcx> { InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) | InstanceDef::Intrinsic(def_id, ) | - InstanceDef::ClosureOnceShim { - call_once: def_id, closure_did: _ - } => def_id + InstanceDef::ClosureOnceShim { call_once: def_id } + => def_id } } @@ -98,10 +94,8 @@ impl<'tcx> fmt::Display for Instance<'tcx> { InstanceDef::FnPtrShim(_, ty) => { write!(f, " - shim({:?})", ty) } - InstanceDef::ClosureOnceShim { - call_once: _, closure_did - } => { - write!(f, " - shim({:?})", closure_did) + InstanceDef::ClosureOnceShim { .. } => { + write!(f, " - shim") } } } diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 97c83c7a42e28..35ad296006dad 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -83,7 +83,25 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, None ) } - _ => bug!("unknown shim kind") + ty::InstanceDef::ClosureOnceShim { call_once } => { + let fn_mut = tcx.lang_items.fn_mut_trait().unwrap(); + let call_mut = tcx.global_tcx() + .associated_items(fn_mut) + .find(|it| it.kind == ty::AssociatedKind::Method) + .unwrap().def_id; + + build_call_shim( + tcx, + ¶m_env, + call_once, + Adjustment::RefMut, + CallKind::Direct(call_mut), + None + ) + } + ty::InstanceDef::Intrinsic(_) => { + bug!("creating shims from intrinsics ({:?}) is unsupported", instance) + } }; debug!("make_shim({:?}) = {:?}", instance, result); @@ -97,6 +115,7 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, enum Adjustment { Identity, Deref, + RefMut, } #[derive(Copy, Clone, Debug, PartialEq)] @@ -143,18 +162,37 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, debug!("build_call_shim: sig={:?}", sig); - let local_decls = local_decls_for_sig(&sig); + let mut local_decls = local_decls_for_sig(&sig); let source_info = SourceInfo { span, scope: ARGUMENT_VISIBILITY_SCOPE }; - let rcvr_l = Lvalue::Local(Local::new(1+0)); - - let return_block_id = BasicBlock::new(1); + let rcvr_arg = Local::new(1+0); + let rcvr_l = Lvalue::Local(rcvr_arg); + let mut statements = vec![]; let rcvr = match rcvr_adjustment { Adjustment::Identity => Operand::Consume(rcvr_l), Adjustment::Deref => Operand::Consume(Lvalue::Projection( box Projection { base: rcvr_l, elem: ProjectionElem::Deref } - )) + )), + Adjustment::RefMut => { + // let rcvr = &mut rcvr; + let re_erased = tcx.mk_region(ty::ReErased); + let ref_rcvr = local_decls.push(temp_decl( + Mutability::Not, + tcx.mk_ref(re_erased, ty::TypeAndMut { + ty: sig.inputs()[0], + mutbl: hir::Mutability::MutMutable + }) + )); + statements.push(Statement { + source_info: source_info, + kind: StatementKind::Assign( + Lvalue::Local(ref_rcvr), + Rvalue::Ref(re_erased, BorrowKind::Mut, rcvr_l) + ) + }); + Operand::Consume(Lvalue::Local(ref_rcvr)) + } }; let (callee, mut args) = match call_kind { @@ -184,28 +222,57 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, } let mut blocks = IndexVec::new(); - blocks.push(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { - source_info: source_info, - kind: TerminatorKind::Call { - func: callee, - args: args, - destination: Some((Lvalue::Local(RETURN_POINTER), - return_block_id)), - cleanup: None + let block = |blocks: &mut IndexVec<_, _>, statements, kind, is_cleanup| { + blocks.push(BasicBlockData { + statements, + terminator: Some(Terminator { source_info, kind }), + is_cleanup + }) + }; + + let have_unwind = match (rcvr_adjustment, tcx.sess.no_landing_pads()) { + (Adjustment::RefMut, false) => true, + _ => false + }; + + // BB #0 + block(&mut blocks, statements, TerminatorKind::Call { + func: callee, + args: args, + destination: Some((Lvalue::Local(RETURN_POINTER), + BasicBlock::new(1))), + cleanup: if have_unwind { + Some(BasicBlock::new(3)) + } else { + None + } + }, false); + + if let Adjustment::RefMut = rcvr_adjustment { + // BB #1 - drop for Self + block(&mut blocks, vec![], TerminatorKind::Drop { + location: Lvalue::Local(rcvr_arg), + target: BasicBlock::new(2), + unwind: if have_unwind { + Some(BasicBlock::new(4)) + } else { + None } - }), - is_cleanup: false - }); - blocks.push(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { - source_info: source_info, - kind: TerminatorKind::Return - }), - is_cleanup: false - }); + }, false); + } + // BB #1/#2 - return + block(&mut blocks, vec![], TerminatorKind::Return, false); + if have_unwind { + // BB #3 - drop if closure panics + block(&mut blocks, vec![], TerminatorKind::Drop { + location: Lvalue::Local(rcvr_arg), + target: BasicBlock::new(4), + unwind: None + }, true); + + // BB #4 - resume + block(&mut blocks, vec![], TerminatorKind::Resume, true); + } let mut mir = Mir::new( blocks, diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 5c7be56b56dad..4d0bd9fa201d8 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -14,142 +14,18 @@ //! and methods are represented as just a fn ptr and not a full //! closure. -use llvm::{self, ValueRef, get_params}; +use llvm::{self, ValueRef}; use rustc::hir::def_id::DefId; -use rustc::ty::subst::{Substs, Subst}; -use abi::{Abi, FnType}; +use rustc::ty::subst::Substs; use attributes; -use builder::Builder; use common::{self, CrateContext}; -use cleanup::CleanupScope; -use mir::lvalue::LvalueRef; use monomorphize; use consts; use declare; -use value::Value; use monomorphize::Instance; -use back::symbol_names::symbol_name; use trans_item::TransItem; use type_of; -use rustc::ty::{self, TypeFoldable}; -use std::iter; - -use mir::lvalue::Alignment; - -fn trans_fn_once_adapter_shim<'a, 'tcx>( - ccx: &'a CrateContext<'a, 'tcx>, - def_id: DefId, - substs: ty::ClosureSubsts<'tcx>, - method_instance: Instance<'tcx>, - llreffn: ValueRef) - -> ValueRef -{ - if let Some(&llfn) = ccx.instances().borrow().get(&method_instance) { - return llfn; - } - - debug!("trans_fn_once_adapter_shim(def_id={:?}, substs={:?}, llreffn={:?})", - def_id, substs, Value(llreffn)); - - let tcx = ccx.tcx(); - - // Find a version of the closure type. Substitute static for the - // region since it doesn't really matter. - let closure_ty = tcx.mk_closure_from_closure_substs(def_id, substs); - let ref_closure_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), closure_ty); - - // Make a version with the type of by-ref closure. - let sig = tcx.closure_type(def_id).subst(tcx, substs.substs); - let sig = tcx.erase_late_bound_regions_and_normalize(&sig); - assert_eq!(sig.abi, Abi::RustCall); - let llref_fn_sig = tcx.mk_fn_sig( - iter::once(ref_closure_ty).chain(sig.inputs().iter().cloned()), - sig.output(), - sig.variadic, - sig.unsafety, - Abi::RustCall - ); - let llref_fn_ty = tcx.mk_fn_ptr(ty::Binder(llref_fn_sig)); - debug!("trans_fn_once_adapter_shim: llref_fn_ty={:?}", - llref_fn_ty); - - - // Make a version of the closure type with the same arguments, but - // with argument #0 being by value. - let sig = tcx.mk_fn_sig( - iter::once(closure_ty).chain(sig.inputs().iter().cloned()), - sig.output(), - sig.variadic, - sig.unsafety, - Abi::RustCall - ); - - let fn_ty = FnType::new(ccx, sig, &[]); - let llonce_fn_ty = tcx.mk_fn_ptr(ty::Binder(sig)); - - // Create the by-value helper. - let function_name = symbol_name(method_instance, ccx.shared()); - let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty); - attributes::set_frame_pointer_elimination(ccx, lloncefn); - - let orig_fn_ty = fn_ty; - let mut bcx = Builder::new_block(ccx, lloncefn, "entry-block"); - - // the first argument (`self`) will be the (by value) closure env. - - let mut llargs = get_params(lloncefn); - let fn_ty = FnType::new(ccx, llref_fn_sig, &[]); - let self_idx = fn_ty.ret.is_indirect() as usize; - let env_arg = &orig_fn_ty.args[0]; - let env = if env_arg.is_indirect() { - LvalueRef::new_sized_ty(llargs[self_idx], closure_ty, Alignment::AbiAligned) - } else { - let scratch = LvalueRef::alloca(&bcx, closure_ty, "self"); - let mut llarg_idx = self_idx; - env_arg.store_fn_arg(&bcx, &mut llarg_idx, scratch.llval); - scratch - }; - - debug!("trans_fn_once_adapter_shim: env={:?}", env); - // Adjust llargs such that llargs[self_idx..] has the call arguments. - // For zero-sized closures that means sneaking in a new argument. - if env_arg.is_ignore() { - llargs.insert(self_idx, env.llval); - } else { - llargs[self_idx] = env.llval; - } - - // Call the by-ref closure body with `self` in a cleanup scope, - // to drop `self` when the body returns, or in case it unwinds. - let self_scope = CleanupScope::schedule_drop_mem(&bcx, env); - - let llret; - if let Some(landing_pad) = self_scope.landing_pad { - let normal_bcx = bcx.build_sibling_block("normal-return"); - llret = bcx.invoke(llreffn, &llargs[..], normal_bcx.llbb(), landing_pad, None); - bcx = normal_bcx; - } else { - llret = bcx.call(llreffn, &llargs[..], None); - } - fn_ty.apply_attrs_callsite(llret); - - if sig.output().is_never() { - bcx.unreachable(); - } else { - self_scope.trans(&bcx); - - if fn_ty.ret.is_indirect() || fn_ty.ret.is_ignore() { - bcx.ret_void(); - } else { - bcx.ret(llret); - } - } - - ccx.instances().borrow_mut().insert(method_instance, lloncefn); - - lloncefn -} - +use rustc::ty::TypeFoldable; /// Translates a reference to a fn/method item, monomorphizing and /// inlining as it goes. @@ -157,11 +33,10 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( /// # Parameters /// /// - `ccx`: the crate context -/// - `def_id`: def id of the fn or method item being referenced -/// - `substs`: values for each of the fn/method's parameters -fn do_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - instance: Instance<'tcx>) - -> ValueRef +/// - `instance`: the instance to be instantiated +pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + instance: Instance<'tcx>) + -> ValueRef { let tcx = ccx.tcx(); @@ -248,40 +123,6 @@ fn do_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, llfn } -pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - instance: Instance<'tcx>) - -> ValueRef -{ - match instance.def { - ty::InstanceDef::Intrinsic(_) => { - bug!("intrinsic {} getting reified", instance) - } - ty::InstanceDef::ClosureOnceShim { .. } => { - let closure_ty = instance.substs.type_at(0); - let (closure_def_id, closure_substs) = match closure_ty.sty { - ty::TyClosure(def_id, substs) => (def_id, substs), - _ => bug!("bad closure instance {:?}", instance) - }; - - trans_fn_once_adapter_shim( - ccx, - closure_def_id, - closure_substs, - instance, - do_get_fn( - ccx, - Instance::new(closure_def_id, closure_substs.substs) - ) - ) - } - ty::InstanceDef::FnPtrShim(..) | - ty::InstanceDef::Item(..) | - ty::InstanceDef::Virtual(..) => { - do_get_fn(ccx, instance) - } - } -} - pub fn resolve_and_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>) diff --git a/src/librustc_trans/cleanup.rs b/src/librustc_trans/cleanup.rs index 5d89a67d3fd80..2b2e5e85ea50d 100644 --- a/src/librustc_trans/cleanup.rs +++ b/src/librustc_trans/cleanup.rs @@ -93,24 +93,6 @@ impl<'tcx> DropValue<'tcx> { } impl<'a, 'tcx> CleanupScope<'tcx> { - /// Schedules a (deep) drop of `val`, which is a pointer to an instance of `ty` - pub fn schedule_drop_mem( - bcx: &Builder<'a, 'tcx>, val: LvalueRef<'tcx> - ) -> CleanupScope<'tcx> { - if let LvalueTy::Downcast { .. } = val.ty { - bug!("Cannot drop downcast ty yet"); - } - if !bcx.ccx.shared().type_needs_drop(val.ty.to_ty(bcx.tcx())) { - return CleanupScope::noop(); - } - let drop = DropValue { - val: val, - skip_dtor: false, - }; - - CleanupScope::new(bcx, drop) - } - /// Issue #23611: Schedules a (deep) drop of the contents of /// `val`, which is a pointer to an instance of struct/enum type /// `ty`. The scheduled code handles extracting the discriminant diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 40a89783d91cf..f076fc4710222 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -497,11 +497,9 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { let source_ty = operand.ty(self.mir, self.scx.tcx()); match source_ty.sty { ty::TyClosure(def_id, substs) => { - let substs = monomorphize::apply_param_substs( - self.scx, self.param_substs, &substs.substs); - self.output.push(create_fn_trans_item( - Instance::new(def_id, substs) - )); + let instance = monomorphize::resolve_closure( + self.scx, def_id, substs, ty::ClosureKind::FnOnce); + self.output.push(create_fn_trans_item(instance)); } _ => bug!(), } @@ -601,20 +599,6 @@ fn visit_fn_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, } match instance.def { - ty::InstanceDef::ClosureOnceShim { .. } => { - // This call will instantiate an FnOnce adapter, which - // drops the closure environment. Therefore we need to - // make sure that we collect the drop-glue for the - // environment type along with the instance. - - let env_ty = instance.substs.type_at(0); - let env_ty = glue::get_drop_glue_type(scx, env_ty); - if scx.type_needs_drop(env_ty) { - let dg = DropGlueKind::Ty(env_ty); - output.push(TransItem::DropGlue(dg)); - } - output.push(create_fn_trans_item(instance)); - } ty::InstanceDef::Intrinsic(..) => { if !is_direct_call { bug!("intrinsic {:?} being reified", ty); @@ -632,6 +616,7 @@ fn visit_fn_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, output.push(create_fn_trans_item(instance)); } } + ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Item(..) | ty::InstanceDef::FnPtrShim(..) => { output.push(create_fn_trans_item(instance)); @@ -645,10 +630,8 @@ fn visit_fn_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instance<'tcx>) -> bool { let def_id = match instance.def { - ty::InstanceDef::Item(def_id) | - ty::InstanceDef::ClosureOnceShim { - call_once: _, closure_did: def_id - } => def_id, + ty::InstanceDef::Item(def_id) => def_id, + ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Virtual(..) | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::Intrinsic(_) => return true @@ -885,20 +868,6 @@ fn find_vtable_types_for_unsizing<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, fn create_fn_trans_item<'a, 'tcx>(instance: Instance<'tcx>) -> TransItem<'tcx> { debug!("create_fn_trans_item(instance={})", instance); - let instance = match instance.def { - ty::InstanceDef::ClosureOnceShim { .. } => { - // HACK: don't create ClosureOnce trans items for now - // have someone else generate the drop glue - let closure_ty = instance.substs.type_at(0); - match closure_ty.sty { - ty::TyClosure(def_id, substs) => { - Instance::new(def_id, substs.substs) - } - _ => bug!("bad closure instance {:?}", instance) - } - } - _ => instance - }; TransItem::Fn(instance) } diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 3832c21d10a49..178347369c915 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -12,7 +12,6 @@ use llvm::{self, ValueRef}; use rustc::ty::{self, Ty}; use rustc::ty::cast::{CastTy, IntTy}; use rustc::ty::layout::Layout; -use rustc::ty::subst::{Kind, Subst}; use rustc::mir::tcx::LvalueTy; use rustc::mir; use middle::lang_items::ExchangeMallocFnLangItem; @@ -24,6 +23,7 @@ use common::{self, val_ty, C_bool, C_null, C_uint}; use common::{C_integral}; use adt; use machine; +use monomorphize; use type_::Type; use type_of; use tvec; @@ -193,22 +193,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { mir::CastKind::ClosureFnPointer => { match operand.ty.sty { ty::TyClosure(def_id, substs) => { - // Get the def_id for FnOnce::call_once - let fn_once = bcx.tcx().lang_items.fn_once_trait().unwrap(); - let call_once = bcx.tcx() - .global_tcx().associated_items(fn_once) - .find(|it| it.kind == ty::AssociatedKind::Method) - .unwrap().def_id; - // Now create its substs [Closure, Tuple] - let input = bcx.tcx().closure_type(def_id) - .subst(bcx.tcx(), substs.substs).input(0); - let input = - bcx.tcx().erase_late_bound_regions_and_normalize(&input); - let substs = bcx.tcx().mk_substs([operand.ty, input] - .iter().cloned().map(Kind::from)); - OperandValue::Immediate( - callee::resolve_and_get_fn(bcx.ccx, call_once, substs) - ) + let instance = monomorphize::resolve_closure( + bcx.ccx.shared(), def_id, substs, ty::ClosureKind::FnOnce); + OperandValue::Immediate(callee::get_fn(bcx.ccx, instance)) } _ => { bug!("{} cannot be cast to a fn ptr", operand.ty) diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs index bf073d8b97844..0d8aa0f4bda0e 100644 --- a/src/librustc_trans/monomorphize.rs +++ b/src/librustc_trans/monomorphize.rs @@ -37,7 +37,7 @@ fn fn_once_adapter_instance<'a, 'tcx>( let call_once = tcx.associated_items(fn_once) .find(|it| it.kind == ty::AssociatedKind::Method) .unwrap().def_id; - let def = ty::InstanceDef::ClosureOnceShim { call_once, closure_did }; + let def = ty::InstanceDef::ClosureOnceShim { call_once }; let self_ty = tcx.mk_closure_from_closure_substs( closure_did, substs); @@ -54,6 +54,54 @@ fn fn_once_adapter_instance<'a, 'tcx>( Instance { def, substs } } +fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, + trait_closure_kind: ty::ClosureKind) + -> Result +{ + match (actual_closure_kind, trait_closure_kind) { + (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | + (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => { + // No adapter needed. + Ok(false) + } + (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { + // The closure fn `llfn` is a `fn(&self, ...)`. We want a + // `fn(&mut self, ...)`. In fact, at trans time, these are + // basically the same thing, so we can just return llfn. + Ok(false) + } + (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { + // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut + // self, ...)`. We want a `fn(self, ...)`. We can produce + // this by doing something like: + // + // fn call_once(self, ...) { call_mut(&self, ...) } + // fn call_once(mut self, ...) { call_mut(&mut self, ...) } + // + // These are both the same at trans time. + Ok(true) + } + _ => Err(()), + } +} + +pub fn resolve_closure<'a, 'tcx> ( + scx: &SharedCrateContext<'a, 'tcx>, + def_id: DefId, + substs: ty::ClosureSubsts<'tcx>, + requested_kind: ty::ClosureKind) + -> Instance<'tcx> +{ + let actual_kind = scx.tcx().closure_kind(def_id); + + match needs_fn_once_adapter_shim(actual_kind, requested_kind) { + Ok(true) => fn_once_adapter_instance(scx.tcx(), def_id, substs), + _ => Instance::new(def_id, substs.substs) + } +} + /// Attempts to resolve an obligation. The result is a shallow vtable resolution -- meaning that we /// do not (necessarily) resolve all nested obligations on the impl. Note that type check should /// guarantee to us that all nested obligations *could be* resolved if we wanted to. @@ -121,39 +169,6 @@ fn fulfill_obligation<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, }) } -fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, - trait_closure_kind: ty::ClosureKind) - -> Result -{ - match (actual_closure_kind, trait_closure_kind) { - (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | - (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => { - // No adapter needed. - Ok(false) - } - (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { - // The closure fn `llfn` is a `fn(&self, ...)`. We want a - // `fn(&mut self, ...)`. In fact, at trans time, these are - // basically the same thing, so we can just return llfn. - Ok(false) - } - (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { - // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut - // self, ...)`. We want a `fn(self, ...)`. We can produce - // this by doing something like: - // - // fn call_once(self, ...) { call_mut(&self, ...) } - // fn call_once(mut self, ...) { call_mut(&mut self, ...) } - // - // These are both the same at trans time. - Ok(true) - } - _ => Err(()), - } -} - fn resolve_associated_item<'a, 'tcx>( scx: &SharedCrateContext<'a, 'tcx>, trait_item: &ty::AssociatedItem, @@ -180,16 +195,9 @@ fn resolve_associated_item<'a, 'tcx>( ty::Instance::new(def_id, substs) } traits::VtableClosure(closure_data) => { - let closure_def_id = closure_data.closure_def_id; let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap(); - let actual_closure_kind = tcx.closure_kind(closure_def_id); - - match needs_fn_once_adapter_shim(actual_closure_kind, - trait_closure_kind) { - Ok(true) => fn_once_adapter_instance( - tcx, closure_def_id, closure_data.substs), - _ => Instance::new(closure_def_id, closure_data.substs.substs), - } + resolve_closure(scx, closure_data.closure_def_id, closure_data.substs, + trait_closure_kind) } traits::VtableFnPointer(ref data) => { Instance { @@ -279,7 +287,6 @@ pub fn apply_param_substs<'a, 'tcx, T>(scx: &SharedCrateContext<'a, 'tcx>, AssociatedTypeNormalizer::new(scx).fold(&substituted) } - /// Returns the normalized type of a struct field pub fn field_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, param_substs: &Substs<'tcx>, diff --git a/src/test/run-pass/issue-29948.rs b/src/test/run-pass/issue-29948.rs index ec2b53313faa7..281dde15bd336 100644 --- a/src/test/run-pass/issue-29948.rs +++ b/src/test/run-pass/issue-29948.rs @@ -8,6 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::panic; + +impl<'a> panic::UnwindSafe for Foo<'a> {} +impl<'a> panic::RefUnwindSafe for Foo<'a> {} + struct Foo<'a>(&'a mut bool); impl<'a> Drop for Foo<'a> { @@ -28,5 +33,15 @@ fn main() { f(x); } assert!(ran_drop); -} + let mut ran_drop = false; + { + let x = Foo(&mut ran_drop); + let result = panic::catch_unwind(move || { + let x = move || { let _ = x; panic!() }; + f(x); + }); + assert!(result.is_err()); + } + assert!(ran_drop); +} From 2b9fea1300b515e0f8929bb3a09d4fb6fef3f0ea Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 9 Mar 2017 20:10:05 +0200 Subject: [PATCH 52/54] move the drop expansion code to rustc_mir --- .../borrowck/mir/dataflow/graphviz.rs | 3 +- .../borrowck/mir/dataflow/impls.rs | 2 +- .../borrowck/mir/elaborate_drops.rs | 727 ++++-------------- src/librustc_borrowck/borrowck/mir/mod.rs | 17 +- src/librustc_driver/pretty.rs | 3 +- src/librustc_mir/lib.rs | 4 +- src/librustc_mir/mir_map.rs | 6 +- src/librustc_mir/transform/copy_prop.rs | 2 +- src/librustc_mir/transform/dump_mir.rs | 4 +- src/librustc_mir/{ => util}/def_use.rs | 0 src/librustc_mir/util/elaborate_drops.rs | 561 ++++++++++++++ src/librustc_mir/{ => util}/graphviz.rs | 0 src/librustc_mir/util/mod.rs | 20 + .../mir => librustc_mir/util}/patch.rs | 0 src/librustc_mir/{ => util}/pretty.rs | 0 15 files changed, 728 insertions(+), 621 deletions(-) rename src/librustc_mir/{ => util}/def_use.rs (100%) create mode 100644 src/librustc_mir/util/elaborate_drops.rs rename src/librustc_mir/{ => util}/graphviz.rs (100%) create mode 100644 src/librustc_mir/util/mod.rs rename src/{librustc_borrowck/borrowck/mir => librustc_mir/util}/patch.rs (100%) rename src/librustc_mir/{ => util}/pretty.rs (100%) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs index b15c1873f9bd8..7f95f07f48d4a 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs @@ -15,6 +15,7 @@ use rustc::mir::{BasicBlock, Mir}; use rustc_data_structures::bitslice::bits_to_string; use rustc_data_structures::indexed_set::{IdxSet}; use rustc_data_structures::indexed_vec::Idx; +use rustc_mir::util as mir_util; use dot; use dot::IntoCow; @@ -219,7 +220,7 @@ impl<'a, 'tcx, MWF, P> dot::Labeller<'a> for Graph<'a, 'tcx, MWF, P> } Ok(()) } - ::rustc_mir::graphviz::write_node_label( + mir_util::write_graphviz_node_label( *n, self.mbcx.mir(), &mut v, 4, |w| { let flow = self.mbcx.flow_state(); diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs index 7888a56d39dfb..da8aa231ccf15 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs @@ -14,10 +14,10 @@ use rustc_data_structures::bitslice::BitSlice; // adds set_bit/get_bit to &[usiz use rustc_data_structures::bitslice::{BitwiseOperator}; use rustc_data_structures::indexed_set::{IdxSet}; use rustc_data_structures::indexed_vec::Idx; +use rustc_mir::util::elaborate_drops::DropFlagState; use super::super::gather_moves::{HasMoveData, MoveData, MoveOutIndex, MovePathIndex}; use super::super::MoveDataParamEnv; -use super::super::DropFlagState; use super::super::drop_flag_effects_for_function_entry; use super::super::drop_flag_effects_for_location; use super::super::on_lookup_result_bits; diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index a71d23e7e1e7e..88ec86cc95d61 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -13,22 +13,20 @@ use super::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; use super::dataflow::{DataflowResults}; use super::{drop_flag_effects_for_location, on_all_children_bits}; use super::on_lookup_result_bits; -use super::{DropFlagState, MoveDataParamEnv}; -use super::patch::MirPatch; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::subst::{Kind, Subst, Substs}; -use rustc::ty::util::IntTypeExt; +use super::MoveDataParamEnv; +use rustc::ty::{self, TyCtxt}; use rustc::mir::*; use rustc::mir::transform::{Pass, MirPass, MirSource}; use rustc::middle::const_val::ConstVal; -use rustc::middle::lang_items; use rustc::util::nodemap::FxHashMap; use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_data_structures::indexed_vec::Idx; +use rustc_mir::util::patch::MirPatch; +use rustc_mir::util::elaborate_drops::{DropFlagState, elaborate_drop}; +use rustc_mir::util::elaborate_drops::{DropElaborator, DropStyle, DropFlagMode}; use syntax_pos::Span; use std::fmt; -use std::iter; use std::u32; pub struct ElaborateDrops; @@ -109,12 +107,116 @@ impl InitializationData { } } -impl fmt::Debug for InitializationData { +struct Elaborator<'a, 'b: 'a, 'tcx: 'b> { + init_data: &'a InitializationData, + ctxt: &'a mut ElaborateDropsCtxt<'b, 'tcx>, +} + +impl<'a, 'b, 'tcx> fmt::Debug for Elaborator<'a, 'b, 'tcx> { fn fmt(&self, _f: &mut fmt::Formatter) -> Result<(), fmt::Error> { Ok(()) } } +impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { + type Path = MovePathIndex; + + fn patch(&mut self) -> &mut MirPatch<'tcx> { + &mut self.ctxt.patch + } + + fn mir(&self) -> &'a Mir<'tcx> { + self.ctxt.mir + } + + fn tcx(&self) -> ty::TyCtxt<'a, 'tcx, 'tcx> { + self.ctxt.tcx + } + + fn param_env(&self) -> &'a ty::ParameterEnvironment<'tcx> { + self.ctxt.param_env() + } + + fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle { + let ((maybe_live, maybe_dead), multipart) = match mode { + DropFlagMode::Shallow => (self.init_data.state(path), false), + DropFlagMode::Deep => { + let mut some_live = false; + let mut some_dead = false; + let mut children_count = 0; + on_all_children_bits( + self.tcx(), self.mir(), self.ctxt.move_data(), + path, |child| { + if self.ctxt.path_needs_drop(child) { + let (live, dead) = self.init_data.state(child); + debug!("elaborate_drop: state({:?}) = {:?}", + child, (live, dead)); + some_live |= live; + some_dead |= dead; + children_count += 1; + } + }); + ((some_live, some_dead), children_count != 1) + } + }; + match (maybe_live, maybe_dead, multipart) { + (false, _, _) => DropStyle::Dead, + (true, false, _) => DropStyle::Static, + (true, true, false) => DropStyle::Conditional, + (true, true, true) => DropStyle::Open, + } + } + + fn clear_drop_flag(&mut self, loc: Location, path: Self::Path, mode: DropFlagMode) { + match mode { + DropFlagMode::Shallow => { + self.ctxt.set_drop_flag(loc, path, DropFlagState::Absent); + } + DropFlagMode::Deep => { + on_all_children_bits( + self.tcx(), self.mir(), self.ctxt.move_data(), path, + |child| self.ctxt.set_drop_flag(loc, child, DropFlagState::Absent) + ); + } + } + } + + fn field_subpath(&self, path: Self::Path, field: Field) -> Option { + super::move_path_children_matching(self.ctxt.move_data(), path, |p| { + match p { + &Projection { + elem: ProjectionElem::Field(idx, _), .. + } => idx == field, + _ => false + } + }) + } + + fn deref_subpath(&self, path: Self::Path) -> Option { + super::move_path_children_matching(self.ctxt.move_data(), path, |p| { + match p { + &Projection { elem: ProjectionElem::Deref, .. } => true, + _ => false + } + }) + } + + fn downcast_subpath(&self, path: Self::Path, variant: usize) -> Option { + super::move_path_children_matching(self.ctxt.move_data(), path, |p| { + match p { + &Projection { + elem: ProjectionElem::Downcast(_, idx), .. + } => idx == variant, + _ => false + } + }) + } + + fn get_drop_flag(&mut self, path: Self::Path) -> Option> { + self.ctxt.drop_flag(path).map(Operand::Consume) + } +} + struct ElaborateDropsCtxt<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>, @@ -125,19 +227,6 @@ struct ElaborateDropsCtxt<'a, 'tcx: 'a> { patch: MirPatch<'tcx>, } -#[derive(Copy, Clone, Debug)] -struct DropCtxt<'a, 'tcx: 'a> { - source_info: SourceInfo, - is_cleanup: bool, - - init_data: &'a InitializationData, - - lvalue: &'a Lvalue<'tcx>, - path: MovePathIndex, - succ: BasicBlock, - unwind: Option -} - impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn move_data(&self) -> &'b MoveData<'tcx> { &self.env.move_data } fn param_env(&self) -> &'b ty::ParameterEnvironment<'tcx> { @@ -254,19 +343,22 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let init_data = self.initialization_data_at(loc); match self.move_data().rev_lookup.find(location) { LookupResult::Exact(path) => { - self.elaborate_drop(&DropCtxt { - source_info: terminator.source_info, - is_cleanup: data.is_cleanup, - init_data: &init_data, - lvalue: location, - path: path, - succ: target, - unwind: if data.is_cleanup { + elaborate_drop( + &mut Elaborator { + init_data: &init_data, + ctxt: self + }, + terminator.source_info, + data.is_cleanup, + location, + path, + target, + if data.is_cleanup { None } else { Some(Option::unwrap_or(unwind, resume_block)) - } - }, bb); + }, + bb) } LookupResult::Parent(..) => { span_bug!(terminator.source_info.span, @@ -343,15 +435,18 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { debug!("elaborate_drop_and_replace({:?}) - tracked {:?}", terminator, path); let init_data = self.initialization_data_at(loc); - self.elaborate_drop(&DropCtxt { - source_info: terminator.source_info, - is_cleanup: data.is_cleanup, - init_data: &init_data, - lvalue: location, - path: path, - succ: target, - unwind: Some(unwind) - }, bb); + elaborate_drop( + &mut Elaborator { + init_data: &init_data, + ctxt: self + }, + terminator.source_info, + data.is_cleanup, + location, + path, + target, + Some(unwind), + bb); on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| { self.set_drop_flag(Location { block: target, statement_index: 0 }, child, DropFlagState::Present); @@ -372,547 +467,6 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { } } - /// This elaborates a single drop instruction, located at `bb`, and - /// patches over it. - /// - /// The elaborated drop checks the drop flags to only drop what - /// is initialized. - /// - /// In addition, the relevant drop flags also need to be cleared - /// to avoid double-drops. However, in the middle of a complex - /// drop, one must avoid clearing some of the flags before they - /// are read, as that would cause a memory leak. - /// - /// In particular, when dropping an ADT, multiple fields may be - /// joined together under the `rest` subpath. They are all controlled - /// by the primary drop flag, but only the last rest-field dropped - /// should clear it (and it must also not clear anything else). - /// - /// FIXME: I think we should just control the flags externally - /// and then we do not need this machinery. - fn elaborate_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, bb: BasicBlock) { - debug!("elaborate_drop({:?})", c); - - let mut some_live = false; - let mut some_dead = false; - let mut children_count = 0; - on_all_children_bits( - self.tcx, self.mir, self.move_data(), - c.path, |child| { - if self.path_needs_drop(child) { - let (live, dead) = c.init_data.state(child); - debug!("elaborate_drop: state({:?}) = {:?}", - child, (live, dead)); - some_live |= live; - some_dead |= dead; - children_count += 1; - } - }); - - debug!("elaborate_drop({:?}): live - {:?}", c, - (some_live, some_dead)); - match (some_live, some_dead) { - (false, false) | (false, true) => { - // dead drop - patch it out - self.patch.patch_terminator(bb, TerminatorKind::Goto { - target: c.succ - }); - } - (true, false) => { - // static drop - just set the flag - self.patch.patch_terminator(bb, TerminatorKind::Drop { - location: c.lvalue.clone(), - target: c.succ, - unwind: c.unwind - }); - self.drop_flags_for_drop(c, bb); - } - (true, true) => { - // dynamic drop - let drop_bb = if children_count == 1 || self.must_complete_drop(c) { - self.conditional_drop(c) - } else { - self.open_drop(c) - }; - self.patch.patch_terminator(bb, TerminatorKind::Goto { - target: drop_bb - }); - } - } - } - - /// Return the lvalue and move path for each field of `variant`, - /// (the move path is `None` if the field is a rest field). - fn move_paths_for_fields(&self, - base_lv: &Lvalue<'tcx>, - variant_path: MovePathIndex, - variant: &'tcx ty::VariantDef, - substs: &'tcx Substs<'tcx>) - -> Vec<(Lvalue<'tcx>, Option)> - { - variant.fields.iter().enumerate().map(|(i, f)| { - let subpath = - super::move_path_children_matching(self.move_data(), variant_path, |p| { - match p { - &Projection { - elem: ProjectionElem::Field(idx, _), .. - } => idx.index() == i, - _ => false - } - }); - - let field_ty = - self.tcx.normalize_associated_type_in_env( - &f.ty(self.tcx, substs), - self.param_env() - ); - (base_lv.clone().field(Field::new(i), field_ty), subpath) - }).collect() - } - - /// Create one-half of the drop ladder for a list of fields, and return - /// the list of steps in it in reverse order. - /// - /// `unwind_ladder` is such a list of steps in reverse order, - /// which is called instead of the next step if the drop unwinds - /// (the first field is never reached). If it is `None`, all - /// unwind targets are left blank. - fn drop_halfladder<'a>(&mut self, - c: &DropCtxt<'a, 'tcx>, - unwind_ladder: Option>, - succ: BasicBlock, - fields: &[(Lvalue<'tcx>, Option)], - is_cleanup: bool) - -> Vec - { - let mut unwind_succ = if is_cleanup { - None - } else { - c.unwind - }; - - let mut succ = self.new_block( - c, c.is_cleanup, TerminatorKind::Goto { target: succ } - ); - - // Always clear the "master" drop flag at the bottom of the - // ladder. This is needed because the "master" drop flag - // protects the ADT's discriminant, which is invalidated - // after the ADT is dropped. - self.set_drop_flag( - Location { block: succ, statement_index: 0 }, - c.path, - DropFlagState::Absent - ); - - fields.iter().rev().enumerate().map(|(i, &(ref lv, path))| { - succ = if let Some(path) = path { - debug!("drop_ladder: for std field {} ({:?})", i, lv); - - self.elaborated_drop_block(&DropCtxt { - source_info: c.source_info, - is_cleanup: is_cleanup, - init_data: c.init_data, - lvalue: lv, - path: path, - succ: succ, - unwind: unwind_succ, - }) - } else { - debug!("drop_ladder: for rest field {} ({:?})", i, lv); - - self.complete_drop(&DropCtxt { - source_info: c.source_info, - is_cleanup: is_cleanup, - init_data: c.init_data, - lvalue: lv, - path: c.path, - succ: succ, - unwind: unwind_succ, - }, false) - }; - - unwind_succ = unwind_ladder.as_ref().map(|p| p[i]); - succ - }).collect() - } - - /// Create a full drop ladder, consisting of 2 connected half-drop-ladders - /// - /// For example, with 3 fields, the drop ladder is - /// - /// .d0: - /// ELAB(drop location.0 [target=.d1, unwind=.c1]) - /// .d1: - /// ELAB(drop location.1 [target=.d2, unwind=.c2]) - /// .d2: - /// ELAB(drop location.2 [target=`c.succ`, unwind=`c.unwind`]) - /// .c1: - /// ELAB(drop location.1 [target=.c2]) - /// .c2: - /// ELAB(drop location.2 [target=`c.unwind]) - fn drop_ladder<'a>(&mut self, - c: &DropCtxt<'a, 'tcx>, - fields: Vec<(Lvalue<'tcx>, Option)>) - -> BasicBlock - { - debug!("drop_ladder({:?}, {:?})", c, fields); - - let mut fields = fields; - fields.retain(|&(ref lvalue, _)| { - let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); - self.tcx.type_needs_drop_given_env(ty, self.param_env()) - }); - - debug!("drop_ladder - fields needing drop: {:?}", fields); - - let unwind_ladder = if c.is_cleanup { - None - } else { - Some(self.drop_halfladder(c, None, c.unwind.unwrap(), &fields, true)) - }; - - self.drop_halfladder(c, unwind_ladder, c.succ, &fields, c.is_cleanup) - .last().cloned().unwrap_or(c.succ) - } - - fn open_drop_for_tuple<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, tys: &[Ty<'tcx>]) - -> BasicBlock - { - debug!("open_drop_for_tuple({:?}, {:?})", c, tys); - - let fields = tys.iter().enumerate().map(|(i, &ty)| { - (c.lvalue.clone().field(Field::new(i), ty), - super::move_path_children_matching( - self.move_data(), c.path, |proj| match proj { - &Projection { - elem: ProjectionElem::Field(f, _), .. - } => f.index() == i, - _ => false - } - )) - }).collect(); - - self.drop_ladder(c, fields) - } - - fn open_drop_for_box<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, ty: Ty<'tcx>) - -> BasicBlock - { - debug!("open_drop_for_box({:?}, {:?})", c, ty); - - let interior_path = super::move_path_children_matching( - self.move_data(), c.path, |proj| match proj { - &Projection { elem: ProjectionElem::Deref, .. } => true, - _ => false - }).unwrap(); - - let interior = c.lvalue.clone().deref(); - let inner_c = DropCtxt { - lvalue: &interior, - unwind: c.unwind.map(|u| { - self.box_free_block(c, ty, u, true) - }), - succ: self.box_free_block(c, ty, c.succ, c.is_cleanup), - path: interior_path, - ..*c - }; - - self.elaborated_drop_block(&inner_c) - } - - fn open_drop_for_adt<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, - adt: &'tcx ty::AdtDef, substs: &'tcx Substs<'tcx>) - -> BasicBlock { - debug!("open_drop_for_adt({:?}, {:?}, {:?})", c, adt, substs); - - match adt.variants.len() { - 1 => { - let fields = self.move_paths_for_fields( - c.lvalue, - c.path, - &adt.variants[0], - substs - ); - self.drop_ladder(c, fields) - } - _ => { - let mut values = Vec::with_capacity(adt.variants.len()); - let mut blocks = Vec::with_capacity(adt.variants.len()); - let mut otherwise = None; - for (variant_index, discr) in adt.discriminants(self.tcx).enumerate() { - let subpath = super::move_path_children_matching( - self.move_data(), c.path, |proj| match proj { - &Projection { - elem: ProjectionElem::Downcast(_, idx), .. - } => idx == variant_index, - _ => false - }); - if let Some(variant_path) = subpath { - let base_lv = c.lvalue.clone().elem( - ProjectionElem::Downcast(adt, variant_index) - ); - let fields = self.move_paths_for_fields( - &base_lv, - variant_path, - &adt.variants[variant_index], - substs); - values.push(discr); - blocks.push(self.drop_ladder(c, fields)); - } else { - // variant not found - drop the entire enum - if let None = otherwise { - otherwise = Some(self.complete_drop(c, true)); - } - } - } - if let Some(block) = otherwise { - blocks.push(block); - } else { - values.pop(); - } - // If there are multiple variants, then if something - // is present within the enum the discriminant, tracked - // by the rest path, must be initialized. - // - // Additionally, we do not want to switch on the - // discriminant after it is free-ed, because that - // way lies only trouble. - let discr_ty = adt.repr.discr_type().to_ty(self.tcx); - let discr = Lvalue::Local(self.patch.new_temp(discr_ty)); - let switch_block = self.patch.new_block(BasicBlockData { - statements: vec![ - Statement { - source_info: c.source_info, - kind: StatementKind::Assign(discr.clone(), - Rvalue::Discriminant(c.lvalue.clone())) - } - ], - terminator: Some(Terminator { - source_info: c.source_info, - kind: TerminatorKind::SwitchInt { - discr: Operand::Consume(discr), - switch_ty: discr_ty, - values: From::from(values), - targets: blocks, - } - }), - is_cleanup: c.is_cleanup, - }); - self.drop_flag_test_block(c, switch_block) - } - } - } - - /// The slow-path - create an "open", elaborated drop for a type - /// which is moved-out-of only partially, and patch `bb` to a jump - /// to it. This must not be called on ADTs with a destructor, - /// as these can't be moved-out-of, except for `Box`, which is - /// special-cased. - /// - /// This creates a "drop ladder" that drops the needed fields of the - /// ADT, both in the success case or if one of the destructors fail. - fn open_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock { - let ty = c.lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); - match ty.sty { - ty::TyClosure(def_id, substs) => { - let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx).collect(); - self.open_drop_for_tuple(c, &tys) - } - ty::TyTuple(tys, _) => { - self.open_drop_for_tuple(c, tys) - } - ty::TyAdt(def, _) if def.is_box() => { - self.open_drop_for_box(c, ty.boxed_ty()) - } - ty::TyAdt(def, substs) => { - self.open_drop_for_adt(c, def, substs) - } - _ => bug!("open drop from non-ADT `{:?}`", ty) - } - } - - /// Return a basic block that drop an lvalue using the context - /// and path in `c`. If `update_drop_flag` is true, also - /// clear `c`. - /// - /// if FLAG(c.path) - /// if(update_drop_flag) FLAG(c.path) = false - /// drop(c.lv) - fn complete_drop<'a>( - &mut self, - c: &DropCtxt<'a, 'tcx>, - update_drop_flag: bool) - -> BasicBlock - { - debug!("complete_drop({:?},{:?})", c, update_drop_flag); - - let drop_block = self.drop_block(c); - if update_drop_flag { - self.set_drop_flag( - Location { block: drop_block, statement_index: 0 }, - c.path, - DropFlagState::Absent - ); - } - - self.drop_flag_test_block(c, drop_block) - } - - /// Create a simple conditional drop. - /// - /// if FLAG(c.lv) - /// FLAGS(c.lv) = false - /// drop(c.lv) - fn conditional_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) - -> BasicBlock - { - debug!("conditional_drop({:?})", c); - let drop_bb = self.drop_block(c); - self.drop_flags_for_drop(c, drop_bb); - - self.drop_flag_test_block(c, drop_bb) - } - - fn new_block<'a>(&mut self, - c: &DropCtxt<'a, 'tcx>, - is_cleanup: bool, - k: TerminatorKind<'tcx>) - -> BasicBlock - { - self.patch.new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { - source_info: c.source_info, kind: k - }), - is_cleanup: is_cleanup - }) - } - - fn elaborated_drop_block<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock { - debug!("elaborated_drop_block({:?})", c); - let blk = self.drop_block(c); - self.elaborate_drop(c, blk); - blk - } - - fn drop_flag_test_block<'a>(&mut self, - c: &DropCtxt<'a, 'tcx>, - on_set: BasicBlock) - -> BasicBlock { - self.drop_flag_test_block_with_succ(c, c.is_cleanup, on_set, c.succ) - } - - fn drop_flag_test_block_with_succ<'a>(&mut self, - c: &DropCtxt<'a, 'tcx>, - is_cleanup: bool, - on_set: BasicBlock, - on_unset: BasicBlock) - -> BasicBlock - { - let (maybe_live, maybe_dead) = c.init_data.state(c.path); - debug!("drop_flag_test_block({:?},{:?},{:?}) - {:?}", - c, is_cleanup, on_set, (maybe_live, maybe_dead)); - - match (maybe_live, maybe_dead) { - (false, _) => on_unset, - (true, false) => on_set, - (true, true) => { - let flag = self.drop_flag(c.path).unwrap(); - let term = TerminatorKind::if_(self.tcx, Operand::Consume(flag), on_set, on_unset); - self.new_block(c, is_cleanup, term) - } - } - } - - fn drop_block<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock { - self.new_block(c, c.is_cleanup, TerminatorKind::Drop { - location: c.lvalue.clone(), - target: c.succ, - unwind: c.unwind - }) - } - - fn box_free_block<'a>( - &mut self, - c: &DropCtxt<'a, 'tcx>, - ty: Ty<'tcx>, - target: BasicBlock, - is_cleanup: bool - ) -> BasicBlock { - let block = self.unelaborated_free_block(c, ty, target, is_cleanup); - self.drop_flag_test_block_with_succ(c, is_cleanup, block, target) - } - - fn unelaborated_free_block<'a>( - &mut self, - c: &DropCtxt<'a, 'tcx>, - ty: Ty<'tcx>, - target: BasicBlock, - is_cleanup: bool - ) -> BasicBlock { - let mut statements = vec![]; - if let Some(&flag) = self.drop_flags.get(&c.path) { - statements.push(Statement { - source_info: c.source_info, - kind: StatementKind::Assign( - Lvalue::Local(flag), - self.constant_bool(c.source_info.span, false) - ) - }); - } - - let tcx = self.tcx; - let unit_temp = Lvalue::Local(self.patch.new_temp(tcx.mk_nil())); - let free_func = tcx.require_lang_item(lang_items::BoxFreeFnLangItem); - let substs = tcx.mk_substs(iter::once(Kind::from(ty))); - let fty = tcx.item_type(free_func).subst(tcx, substs); - - self.patch.new_block(BasicBlockData { - statements: statements, - terminator: Some(Terminator { - source_info: c.source_info, kind: TerminatorKind::Call { - func: Operand::Constant(Constant { - span: c.source_info.span, - ty: fty, - literal: Literal::Item { - def_id: free_func, - substs: substs - } - }), - args: vec![Operand::Consume(c.lvalue.clone())], - destination: Some((unit_temp, target)), - cleanup: None - } - }), - is_cleanup: is_cleanup - }) - } - - fn must_complete_drop<'a>(&self, c: &DropCtxt<'a, 'tcx>) -> bool { - // if we have a destuctor, we must *not* split the drop. - - // dataflow can create unneeded children in some cases - // - be sure to ignore them. - - let ty = c.lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); - - match ty.sty { - ty::TyAdt(def, _) => { - if def.has_dtor(self.tcx) && !def.is_box() { - self.tcx.sess.span_warn( - c.source_info.span, - &format!("dataflow bug??? moving out of type with dtor {:?}", - c)); - true - } else { - false - } - } - _ => false - } - } - fn constant_bool(&self, span: Span, val: bool) -> Rvalue<'tcx> { Rvalue::Use(Operand::Constant(Constant { span: span, @@ -1023,15 +577,4 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { } } } - - fn drop_flags_for_drop<'a>(&mut self, - c: &DropCtxt<'a, 'tcx>, - bb: BasicBlock) - { - let loc = self.patch.terminator_loc(self.mir, bb); - on_all_children_bits( - self.tcx, self.mir, self.move_data(), c.path, - |child| self.set_drop_flag(loc, child, DropFlagState::Absent) - ); - } } diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index 1c9ee335699ae..9237bb31f6bd7 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -16,12 +16,12 @@ use syntax_pos::DUMMY_SP; use rustc::mir::{self, BasicBlock, BasicBlockData, Mir, Statement, Terminator, Location}; use rustc::session::Session; use rustc::ty::{self, TyCtxt}; +use rustc_mir::util::elaborate_drops::DropFlagState; mod abs_domain; pub mod elaborate_drops; mod dataflow; mod gather_moves; -mod patch; // mod graphviz; use self::dataflow::{BitDenotation}; @@ -183,21 +183,6 @@ impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> { } } -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -enum DropFlagState { - Present, // i.e. initialized - Absent, // i.e. deinitialized or "moved" -} - -impl DropFlagState { - fn value(self) -> bool { - match self { - DropFlagState::Present => true, - DropFlagState::Absent => false - } - } -} - fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>, path: MovePathIndex, mut cond: F) diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index b6978478085d5..6cd97e9559885 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -26,8 +26,7 @@ use rustc::session::config::Input; use rustc_borrowck as borrowck; use rustc_borrowck::graphviz as borrowck_dot; -use rustc_mir::pretty::write_mir_pretty; -use rustc_mir::graphviz::write_mir_graphviz; +use rustc_mir::util::{write_mir_pretty, write_mir_graphviz}; use syntax::ast::{self, BlockCheckMode}; use syntax::fold::{self, Folder}; diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 2718a0204a1ed..590c6a430b98a 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -49,13 +49,11 @@ pub mod diagnostics; pub mod build; pub mod callgraph; -pub mod def_use; -pub mod graphviz; mod hair; mod shim; pub mod mir_map; -pub mod pretty; pub mod transform; +pub mod util; use rustc::ty::maps::Providers; diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 2d2b90235ca3b..8c138d779c106 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -23,8 +23,8 @@ use rustc::mir::Mir; use rustc::mir::transform::MirSource; use rustc::mir::visit::MutVisitor; use shim; -use pretty; use hair::cx::Cx; +use util as mir_util; use rustc::traits::Reveal; use rustc::ty::{self, Ty, TyCtxt}; @@ -175,7 +175,7 @@ fn build_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) mem::transmute::>(mir) }; - pretty::dump_mir(tcx, "mir_map", &0, src, &mir); + mir_util::dump_mir(tcx, "mir_map", &0, src, &mir); tcx.alloc_mir(mir) }) @@ -234,7 +234,7 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mem::transmute::>(mir) }; - pretty::dump_mir(tcx, "mir_map", &0, src, &mir); + mir_util::dump_mir(tcx, "mir_map", &0, src, &mir); tcx.alloc_mir(mir) }) diff --git a/src/librustc_mir/transform/copy_prop.rs b/src/librustc_mir/transform/copy_prop.rs index 2194c20c2f442..5d127a5aed461 100644 --- a/src/librustc_mir/transform/copy_prop.rs +++ b/src/librustc_mir/transform/copy_prop.rs @@ -29,11 +29,11 @@ //! (non-mutating) use of `SRC`. These restrictions are conservative and may be relaxed in the //! future. -use def_use::DefUseAnalysis; use rustc::mir::{Constant, Local, LocalKind, Location, Lvalue, Mir, Operand, Rvalue, StatementKind}; use rustc::mir::transform::{MirPass, MirSource, Pass}; use rustc::mir::visit::MutVisitor; use rustc::ty::TyCtxt; +use util::def_use::DefUseAnalysis; use transform::qualify_consts; pub struct CopyPropagation; diff --git a/src/librustc_mir/transform/dump_mir.rs b/src/librustc_mir/transform/dump_mir.rs index 035f33de91aa5..f22a71636a98e 100644 --- a/src/librustc_mir/transform/dump_mir.rs +++ b/src/librustc_mir/transform/dump_mir.rs @@ -15,7 +15,7 @@ use std::fmt; use rustc::ty::TyCtxt; use rustc::mir::*; use rustc::mir::transform::{Pass, MirPass, MirPassHook, MirSource}; -use pretty; +use util as mir_util; pub struct Marker<'a>(pub &'a str); @@ -56,7 +56,7 @@ impl<'tcx> MirPassHook<'tcx> for DumpMir { pass: &Pass, is_after: bool) { - pretty::dump_mir( + mir_util::dump_mir( tcx, &*pass.name(), &Disambiguator { diff --git a/src/librustc_mir/def_use.rs b/src/librustc_mir/util/def_use.rs similarity index 100% rename from src/librustc_mir/def_use.rs rename to src/librustc_mir/util/def_use.rs diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs new file mode 100644 index 0000000000000..ce6debab9a4b8 --- /dev/null +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -0,0 +1,561 @@ +// Copyright 2017 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. + +use std::fmt; +use rustc::mir::*; +use rustc::middle::lang_items; +use rustc::ty::{self, Ty}; +use rustc::ty::subst::{Kind, Subst, Substs}; +use rustc::ty::util::IntTypeExt; +use rustc_data_structures::indexed_vec::Idx; +use util::patch::MirPatch; + +use std::iter; + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum DropFlagState { + Present, // i.e. initialized + Absent, // i.e. deinitialized or "moved" +} + +impl DropFlagState { + pub fn value(self) -> bool { + match self { + DropFlagState::Present => true, + DropFlagState::Absent => false + } + } +} + +#[derive(Debug)] +pub enum DropStyle { + Dead, + Static, + Conditional, + Open, +} + +#[derive(Debug)] +pub enum DropFlagMode { + Shallow, + Deep +} + +pub trait DropElaborator<'a, 'tcx: 'a> : fmt::Debug { + type Path : Copy + fmt::Debug; + + fn patch(&mut self) -> &mut MirPatch<'tcx>; + fn mir(&self) -> &'a Mir<'tcx>; + fn tcx(&self) -> ty::TyCtxt<'a, 'tcx, 'tcx>; + fn param_env(&self) -> &'a ty::ParameterEnvironment<'tcx>; + + fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle; + fn get_drop_flag(&mut self, path: Self::Path) -> Option>; + fn clear_drop_flag(&mut self, location: Location, path: Self::Path, mode: DropFlagMode); + + + fn field_subpath(&self, path: Self::Path, field: Field) -> Option; + fn deref_subpath(&self, path: Self::Path) -> Option; + fn downcast_subpath(&self, path: Self::Path, variant: usize) -> Option; +} + +#[derive(Debug)] +struct DropCtxt<'l, 'b: 'l, 'tcx: 'b, D> + where D : DropElaborator<'b, 'tcx> + 'l +{ + elaborator: &'l mut D, + + source_info: SourceInfo, + is_cleanup: bool, + + lvalue: &'l Lvalue<'tcx>, + path: D::Path, + succ: BasicBlock, + unwind: Option, +} + +pub fn elaborate_drop<'b, 'tcx, D>( + elaborator: &mut D, + source_info: SourceInfo, + is_cleanup: bool, + lvalue: &Lvalue<'tcx>, + path: D::Path, + succ: BasicBlock, + unwind: Option, + bb: BasicBlock) + where D: DropElaborator<'b, 'tcx> +{ + DropCtxt { + elaborator, source_info, is_cleanup, lvalue, path, succ, unwind + }.elaborate_drop(bb) +} + +impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> + where D: DropElaborator<'b, 'tcx> +{ + fn lvalue_ty(&self, lvalue: &Lvalue<'tcx>) -> Ty<'tcx> { + lvalue.ty(self.elaborator.mir(), self.tcx()).to_ty(self.tcx()) + } + + fn tcx(&self) -> ty::TyCtxt<'b, 'tcx, 'tcx> { + self.elaborator.tcx() + } + + /// This elaborates a single drop instruction, located at `bb`, and + /// patches over it. + /// + /// The elaborated drop checks the drop flags to only drop what + /// is initialized. + /// + /// In addition, the relevant drop flags also need to be cleared + /// to avoid double-drops. However, in the middle of a complex + /// drop, one must avoid clearing some of the flags before they + /// are read, as that would cause a memory leak. + /// + /// In particular, when dropping an ADT, multiple fields may be + /// joined together under the `rest` subpath. They are all controlled + /// by the primary drop flag, but only the last rest-field dropped + /// should clear it (and it must also not clear anything else). + /// + /// FIXME: I think we should just control the flags externally + /// and then we do not need this machinery. + pub fn elaborate_drop<'a>(&mut self, bb: BasicBlock) { + debug!("elaborate_drop({:?})", self); + let style = self.elaborator.drop_style(self.path, DropFlagMode::Deep); + debug!("elaborate_drop({:?}): live - {:?}", self, style); + match style { + DropStyle::Dead => { + self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto { + target: self.succ + }); + } + DropStyle::Static => { + let loc = self.terminator_loc(bb); + self.elaborator.clear_drop_flag(loc, self.path, DropFlagMode::Deep); + self.elaborator.patch().patch_terminator(bb, TerminatorKind::Drop { + location: self.lvalue.clone(), + target: self.succ, + unwind: self.unwind + }); + } + DropStyle::Conditional => { + let drop_bb = self.complete_drop(Some(DropFlagMode::Deep)); + self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto { + target: drop_bb + }); + } + DropStyle::Open => { + let drop_bb = self.open_drop(); + self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto { + target: drop_bb + }); + } + } + } + + /// Return the lvalue and move path for each field of `variant`, + /// (the move path is `None` if the field is a rest field). + fn move_paths_for_fields(&self, + base_lv: &Lvalue<'tcx>, + variant_path: D::Path, + variant: &'tcx ty::VariantDef, + substs: &'tcx Substs<'tcx>) + -> Vec<(Lvalue<'tcx>, Option)> + { + variant.fields.iter().enumerate().map(|(i, f)| { + let field = Field::new(i); + let subpath = self.elaborator.field_subpath(variant_path, field); + + let field_ty = + self.tcx().normalize_associated_type_in_env( + &f.ty(self.tcx(), substs), + self.elaborator.param_env() + ); + (base_lv.clone().field(field, field_ty), subpath) + }).collect() + } + + fn drop_subpath(&mut self, + is_cleanup: bool, + lvalue: &Lvalue<'tcx>, + path: Option, + succ: BasicBlock, + unwind: Option) + -> BasicBlock + { + if let Some(path) = path { + debug!("drop_subpath: for std field {:?}", lvalue); + + DropCtxt { + elaborator: self.elaborator, + source_info: self.source_info, + path, lvalue, succ, unwind, is_cleanup + }.elaborated_drop_block() + } else { + debug!("drop_subpath: for rest field {:?}", lvalue); + + DropCtxt { + elaborator: self.elaborator, + source_info: self.source_info, + lvalue, succ, unwind, is_cleanup, + // Using `self.path` here to condition the drop on + // our own drop flag. + path: self.path + }.complete_drop(None) + } + } + + /// Create one-half of the drop ladder for a list of fields, and return + /// the list of steps in it in reverse order. + /// + /// `unwind_ladder` is such a list of steps in reverse order, + /// which is called instead of the next step if the drop unwinds + /// (the first field is never reached). If it is `None`, all + /// unwind targets are left blank. + fn drop_halfladder<'a>(&mut self, + unwind_ladder: Option>, + succ: BasicBlock, + fields: &[(Lvalue<'tcx>, Option)], + is_cleanup: bool) + -> Vec + { + let mut unwind_succ = if is_cleanup { + None + } else { + self.unwind + }; + + let goto = TerminatorKind::Goto { target: succ }; + let mut succ = self.new_block(is_cleanup, goto); + + // Always clear the "master" drop flag at the bottom of the + // ladder. This is needed because the "master" drop flag + // protects the ADT's discriminant, which is invalidated + // after the ADT is dropped. + let succ_loc = Location { block: succ, statement_index: 0 }; + self.elaborator.clear_drop_flag(succ_loc, self.path, DropFlagMode::Shallow); + + fields.iter().rev().enumerate().map(|(i, &(ref lv, path))| { + succ = self.drop_subpath(is_cleanup, lv, path, succ, unwind_succ); + unwind_succ = unwind_ladder.as_ref().map(|p| p[i]); + succ + }).collect() + } + + /// Create a full drop ladder, consisting of 2 connected half-drop-ladders + /// + /// For example, with 3 fields, the drop ladder is + /// + /// .d0: + /// ELAB(drop location.0 [target=.d1, unwind=.c1]) + /// .d1: + /// ELAB(drop location.1 [target=.d2, unwind=.c2]) + /// .d2: + /// ELAB(drop location.2 [target=`self.succ`, unwind=`self.unwind`]) + /// .c1: + /// ELAB(drop location.1 [target=.c2]) + /// .c2: + /// ELAB(drop location.2 [target=`self.unwind]) + fn drop_ladder<'a>(&mut self, + fields: Vec<(Lvalue<'tcx>, Option)>) + -> BasicBlock + { + debug!("drop_ladder({:?}, {:?})", self, fields); + + let mut fields = fields; + fields.retain(|&(ref lvalue, _)| { + self.tcx().type_needs_drop_given_env( + self.lvalue_ty(lvalue), self.elaborator.param_env()) + }); + + debug!("drop_ladder - fields needing drop: {:?}", fields); + + let unwind_ladder = if self.is_cleanup { + None + } else { + let unwind = self.unwind.unwrap(); // FIXME(#6393) + Some(self.drop_halfladder(None, unwind, &fields, true)) + }; + + let succ = self.succ; // FIXME(#6393) + let is_cleanup = self.is_cleanup; + self.drop_halfladder(unwind_ladder, succ, &fields, is_cleanup) + .last().cloned().unwrap_or(succ) + } + + fn open_drop_for_tuple<'a>(&mut self, tys: &[Ty<'tcx>]) + -> BasicBlock + { + debug!("open_drop_for_tuple({:?}, {:?})", self, tys); + + let fields = tys.iter().enumerate().map(|(i, &ty)| { + (self.lvalue.clone().field(Field::new(i), ty), + self.elaborator.field_subpath(self.path, Field::new(i))) + }).collect(); + + self.drop_ladder(fields) + } + + fn open_drop_for_box<'a>(&mut self, ty: Ty<'tcx>) -> BasicBlock + { + debug!("open_drop_for_box({:?}, {:?})", self, ty); + + let interior = self.lvalue.clone().deref(); + let interior_path = self.elaborator.deref_subpath(self.path); + + let succ = self.succ; // FIXME(#6393) + let is_cleanup = self.is_cleanup; + let succ = self.box_free_block(ty, succ, is_cleanup); + let unwind_succ = self.unwind.map(|u| { + self.box_free_block(ty, u, true) + }); + + self.drop_subpath(is_cleanup, &interior, interior_path, succ, unwind_succ) + } + + fn open_drop_for_adt<'a>(&mut self, adt: &'tcx ty::AdtDef, substs: &'tcx Substs<'tcx>) + -> BasicBlock { + debug!("open_drop_for_adt({:?}, {:?}, {:?})", self, adt, substs); + + match adt.variants.len() { + 1 => { + let fields = self.move_paths_for_fields( + self.lvalue, + self.path, + &adt.variants[0], + substs + ); + self.drop_ladder(fields) + } + _ => { + let mut values = Vec::with_capacity(adt.variants.len()); + let mut blocks = Vec::with_capacity(adt.variants.len()); + let mut otherwise = None; + for (variant_index, discr) in adt.discriminants(self.tcx()).enumerate() { + let subpath = self.elaborator.downcast_subpath( + self.path, variant_index); + if let Some(variant_path) = subpath { + let base_lv = self.lvalue.clone().elem( + ProjectionElem::Downcast(adt, variant_index) + ); + let fields = self.move_paths_for_fields( + &base_lv, + variant_path, + &adt.variants[variant_index], + substs); + values.push(discr); + blocks.push(self.drop_ladder(fields)); + } else { + // variant not found - drop the entire enum + if let None = otherwise { + otherwise = + Some(self.complete_drop(Some(DropFlagMode::Shallow))); + } + } + } + if let Some(block) = otherwise { + blocks.push(block); + } else { + values.pop(); + } + // If there are multiple variants, then if something + // is present within the enum the discriminant, tracked + // by the rest path, must be initialized. + // + // Additionally, we do not want to switch on the + // discriminant after it is free-ed, because that + // way lies only trouble. + let discr_ty = adt.repr.discr_type().to_ty(self.tcx()); + let discr = Lvalue::Local(self.new_temp(discr_ty)); + let discr_rv = Rvalue::Discriminant(self.lvalue.clone()); + let switch_block = self.elaborator.patch().new_block(BasicBlockData { + statements: vec![ + Statement { + source_info: self.source_info, + kind: StatementKind::Assign(discr.clone(), discr_rv), + } + ], + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::SwitchInt { + discr: Operand::Consume(discr), + switch_ty: discr_ty, + values: From::from(values), + targets: blocks, + } + }), + is_cleanup: self.is_cleanup, + }); + self.drop_flag_test_block(switch_block) + } + } + } + + /// The slow-path - create an "open", elaborated drop for a type + /// which is moved-out-of only partially, and patch `bb` to a jump + /// to it. This must not be called on ADTs with a destructor, + /// as these can't be moved-out-of, except for `Box`, which is + /// special-cased. + /// + /// This creates a "drop ladder" that drops the needed fields of the + /// ADT, both in the success case or if one of the destructors fail. + fn open_drop<'a>(&mut self) -> BasicBlock { + let ty = self.lvalue_ty(self.lvalue); + match ty.sty { + ty::TyClosure(def_id, substs) => { + let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect(); + self.open_drop_for_tuple(&tys) + } + ty::TyTuple(tys, _) => { + self.open_drop_for_tuple(tys) + } + ty::TyAdt(def, _) if def.is_box() => { + self.open_drop_for_box(ty.boxed_ty()) + } + ty::TyAdt(def, substs) => { + self.open_drop_for_adt(def, substs) + } + _ => bug!("open drop from non-ADT `{:?}`", ty) + } + } + + /// Return a basic block that drop an lvalue using the context + /// and path in `c`. If `mode` is something, also clear `c` + /// according to it. + /// + /// if FLAG(self.path) + /// if let Some(mode) = mode: FLAG(self.path)[mode] = false + /// drop(self.lv) + fn complete_drop<'a>(&mut self, drop_mode: Option) -> BasicBlock + { + debug!("complete_drop({:?},{:?})", self, drop_mode); + + let drop_block = self.drop_block(); + if let Some(mode) = drop_mode { + let block_start = Location { block: drop_block, statement_index: 0 }; + self.elaborator.clear_drop_flag(block_start, self.path, mode); + } + + self.drop_flag_test_block(drop_block) + } + + fn elaborated_drop_block<'a>(&mut self) -> BasicBlock { + debug!("elaborated_drop_block({:?})", self); + let blk = self.drop_block(); + self.elaborate_drop(blk); + blk + } + + fn box_free_block<'a>( + &mut self, + ty: Ty<'tcx>, + target: BasicBlock, + is_cleanup: bool + ) -> BasicBlock { + let block = self.unelaborated_free_block(ty, target, is_cleanup); + self.drop_flag_test_block_with_succ(is_cleanup, block, target) + } + + fn unelaborated_free_block<'a>( + &mut self, + ty: Ty<'tcx>, + target: BasicBlock, + is_cleanup: bool + ) -> BasicBlock { + let tcx = self.tcx(); + let unit_temp = Lvalue::Local(self.new_temp(tcx.mk_nil())); + let free_func = tcx.require_lang_item(lang_items::BoxFreeFnLangItem); + let substs = tcx.mk_substs(iter::once(Kind::from(ty))); + let fty = tcx.item_type(free_func).subst(tcx, substs); + + let free_block = self.elaborator.patch().new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info: self.source_info, kind: TerminatorKind::Call { + func: Operand::Constant(Constant { + span: self.source_info.span, + ty: fty, + literal: Literal::Item { + def_id: free_func, + substs: substs + } + }), + args: vec![Operand::Consume(self.lvalue.clone())], + destination: Some((unit_temp, target)), + cleanup: None + } + }), + is_cleanup: is_cleanup + }); + let block_start = Location { block: free_block, statement_index: 0 }; + self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow); + free_block + } + + fn drop_block<'a>(&mut self) -> BasicBlock { + let block = TerminatorKind::Drop { + location: self.lvalue.clone(), + target: self.succ, + unwind: self.unwind + }; + let is_cleanup = self.is_cleanup; // FIXME(#6393) + self.new_block(is_cleanup, block) + } + + fn drop_flag_test_block<'a>(&mut self, on_set: BasicBlock) -> BasicBlock { + let is_cleanup = self.is_cleanup; + let succ = self.succ; // FIXME(#6393) + self.drop_flag_test_block_with_succ(is_cleanup, on_set, succ) + } + + fn drop_flag_test_block_with_succ<'a>(&mut self, + is_cleanup: bool, + on_set: BasicBlock, + on_unset: BasicBlock) + -> BasicBlock + { + let style = self.elaborator.drop_style(self.path, DropFlagMode::Shallow); + debug!("drop_flag_test_block({:?},{:?},{:?}) - {:?}", + self, is_cleanup, on_set, style); + + match style { + DropStyle::Dead => on_unset, + DropStyle::Static => on_set, + DropStyle::Conditional | DropStyle::Open => { + let flag = self.elaborator.get_drop_flag(self.path).unwrap(); + let term = TerminatorKind::if_(self.tcx(), flag, on_set, on_unset); + self.new_block(is_cleanup, term) + } + } + } + + fn new_block<'a>(&mut self, + is_cleanup: bool, + k: TerminatorKind<'tcx>) + -> BasicBlock + { + self.elaborator.patch().new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info: self.source_info, kind: k + }), + is_cleanup: is_cleanup + }) + } + + fn new_temp(&mut self, ty: Ty<'tcx>) -> Local { + self.elaborator.patch().new_temp(ty) + } + + fn terminator_loc(&mut self, bb: BasicBlock) -> Location { + let mir = self.elaborator.mir(); + self.elaborator.patch().terminator_loc(mir, bb) + } +} diff --git a/src/librustc_mir/graphviz.rs b/src/librustc_mir/util/graphviz.rs similarity index 100% rename from src/librustc_mir/graphviz.rs rename to src/librustc_mir/util/graphviz.rs diff --git a/src/librustc_mir/util/mod.rs b/src/librustc_mir/util/mod.rs new file mode 100644 index 0000000000000..cafc5bca76acd --- /dev/null +++ b/src/librustc_mir/util/mod.rs @@ -0,0 +1,20 @@ +// Copyright 2017 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. + +pub mod elaborate_drops; +pub mod def_use; +pub mod patch; + +mod graphviz; +mod pretty; + +pub use self::pretty::{dump_mir, write_mir_pretty}; +pub use self::graphviz::{write_mir_graphviz}; +pub use self::graphviz::write_node_label as write_graphviz_node_label; diff --git a/src/librustc_borrowck/borrowck/mir/patch.rs b/src/librustc_mir/util/patch.rs similarity index 100% rename from src/librustc_borrowck/borrowck/mir/patch.rs rename to src/librustc_mir/util/patch.rs diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/util/pretty.rs similarity index 100% rename from src/librustc_mir/pretty.rs rename to src/librustc_mir/util/pretty.rs From 26df816f520d00fe5ddf332a0a08ad6e26a5698d Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 9 Mar 2017 20:36:01 +0200 Subject: [PATCH 53/54] apply pre-trans passes to Shim MIR --- src/librustc_mir/shim.rs | 23 +++---- src/librustc_mir/transform/add_call_guards.rs | 68 ++++++++++--------- src/librustc_mir/transform/no_landing_pads.rs | 10 ++- src/librustc_mir/transform/simplify.rs | 14 ++-- 4 files changed, 62 insertions(+), 53 deletions(-) diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 35ad296006dad..2d9ef124ad244 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -28,6 +28,8 @@ use std::cell::RefCell; use std::iter; use std::mem; +use transform::{add_call_guards, no_landing_pads, simplify}; + pub fn provide(providers: &mut Providers) { providers.mir_shims = make_shim; } @@ -42,7 +44,7 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, let param_env = tcx.construct_parameter_environment(span, did, ROOT_CODE_EXTENT); - let result = match instance { + let mut result = match instance { ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance), ty::InstanceDef::FnPtrShim(def_id, ty) => { @@ -103,6 +105,10 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, bug!("creating shims from intrinsics ({:?}) is unsupported", instance) } }; + debug!("make_shim({:?}) = untransformed {:?}", instance, result); + no_landing_pads::no_landing_pads(tcx, &mut result); + simplify::simplify_cfg(&mut result); + add_call_guards::add_call_guards(&mut result); debug!("make_shim({:?}) = {:?}", instance, result); let result = tcx.alloc_mir(result); @@ -230,18 +236,13 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, }) }; - let have_unwind = match (rcvr_adjustment, tcx.sess.no_landing_pads()) { - (Adjustment::RefMut, false) => true, - _ => false - }; - // BB #0 block(&mut blocks, statements, TerminatorKind::Call { func: callee, args: args, destination: Some((Lvalue::Local(RETURN_POINTER), BasicBlock::new(1))), - cleanup: if have_unwind { + cleanup: if let Adjustment::RefMut = rcvr_adjustment { Some(BasicBlock::new(3)) } else { None @@ -253,16 +254,12 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, block(&mut blocks, vec![], TerminatorKind::Drop { location: Lvalue::Local(rcvr_arg), target: BasicBlock::new(2), - unwind: if have_unwind { - Some(BasicBlock::new(4)) - } else { - None - } + unwind: None }, false); } // BB #1/#2 - return block(&mut blocks, vec![], TerminatorKind::Return, false); - if have_unwind { + if let Adjustment::RefMut = rcvr_adjustment { // BB #3 - drop if closure panics block(&mut blocks, vec![], TerminatorKind::Drop { location: Lvalue::Local(rcvr_arg), diff --git a/src/librustc_mir/transform/add_call_guards.rs b/src/librustc_mir/transform/add_call_guards.rs index 89e644e4fb077..80b17c6a008f5 100644 --- a/src/librustc_mir/transform/add_call_guards.rs +++ b/src/librustc_mir/transform/add_call_guards.rs @@ -37,46 +37,50 @@ pub struct AddCallGuards; impl<'tcx> MirPass<'tcx> for AddCallGuards { fn run_pass<'a>(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, _src: MirSource, mir: &mut Mir<'tcx>) { - let pred_count: IndexVec<_, _> = - mir.predecessors().iter().map(|ps| ps.len()).collect(); + add_call_guards(mir); + } +} + +pub fn add_call_guards(mir: &mut Mir) { + let pred_count: IndexVec<_, _> = + mir.predecessors().iter().map(|ps| ps.len()).collect(); - // We need a place to store the new blocks generated - let mut new_blocks = Vec::new(); + // We need a place to store the new blocks generated + let mut new_blocks = Vec::new(); - let cur_len = mir.basic_blocks().len(); + let cur_len = mir.basic_blocks().len(); - for block in mir.basic_blocks_mut() { - match block.terminator { - Some(Terminator { - kind: TerminatorKind::Call { - destination: Some((_, ref mut destination)), - cleanup: Some(_), - .. - }, source_info - }) if pred_count[*destination] > 1 => { - // It's a critical edge, break it - let call_guard = BasicBlockData { - statements: vec![], - is_cleanup: block.is_cleanup, - terminator: Some(Terminator { - source_info: source_info, - kind: TerminatorKind::Goto { target: *destination } - }) - }; + for block in mir.basic_blocks_mut() { + match block.terminator { + Some(Terminator { + kind: TerminatorKind::Call { + destination: Some((_, ref mut destination)), + cleanup: Some(_), + .. + }, source_info + }) if pred_count[*destination] > 1 => { + // It's a critical edge, break it + let call_guard = BasicBlockData { + statements: vec![], + is_cleanup: block.is_cleanup, + terminator: Some(Terminator { + source_info: source_info, + kind: TerminatorKind::Goto { target: *destination } + }) + }; - // Get the index it will be when inserted into the MIR - let idx = cur_len + new_blocks.len(); - new_blocks.push(call_guard); - *destination = BasicBlock::new(idx); - } - _ => {} + // Get the index it will be when inserted into the MIR + let idx = cur_len + new_blocks.len(); + new_blocks.push(call_guard); + *destination = BasicBlock::new(idx); } + _ => {} } + } - debug!("Broke {} N edges", new_blocks.len()); + debug!("Broke {} N edges", new_blocks.len()); - mir.basic_blocks_mut().extend(new_blocks); - } + mir.basic_blocks_mut().extend(new_blocks); } impl Pass for AddCallGuards {} diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs index 55a26f4b37fe2..3654ae6940c52 100644 --- a/src/librustc_mir/transform/no_landing_pads.rs +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -42,12 +42,16 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads { } } +pub fn no_landing_pads<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &mut Mir<'tcx>) { + if tcx.sess.no_landing_pads() { + NoLandingPads.visit_mir(mir); + } +} + impl<'tcx> MirPass<'tcx> for NoLandingPads { fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, _: MirSource, mir: &mut Mir<'tcx>) { - if tcx.sess.no_landing_pads() { - self.visit_mir(mir); - } + no_landing_pads(tcx, mir) } } diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs index a762507f35e7e..0a8f147b21410 100644 --- a/src/librustc_mir/transform/simplify.rs +++ b/src/librustc_mir/transform/simplify.rs @@ -53,14 +53,18 @@ impl<'a> SimplifyCfg<'a> { } } +pub fn simplify_cfg(mir: &mut Mir) { + CfgSimplifier::new(mir).simplify(); + remove_dead_blocks(mir); + + // FIXME: Should probably be moved into some kind of pass manager + mir.basic_blocks_mut().raw.shrink_to_fit(); +} + impl<'l, 'tcx> MirPass<'tcx> for SimplifyCfg<'l> { fn run_pass<'a>(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, _src: MirSource, mir: &mut Mir<'tcx>) { debug!("SimplifyCfg({:?}) - simplifying {:?}", self.label, mir); - CfgSimplifier::new(mir).simplify(); - remove_dead_blocks(mir); - - // FIXME: Should probably be moved into some kind of pass manager - mir.basic_blocks_mut().raw.shrink_to_fit(); + simplify_cfg(mir); } } From f2c7917402ebd858f5b517a3406bec8ef187bfb1 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 14 Mar 2017 01:08:21 +0200 Subject: [PATCH 54/54] translate drop glue using MIR Drop of arrays is now translated in trans::block in an ugly way that I should clean up in a later PR, and does not handle panics in the middle of an array drop, but this commit & PR are growing too big. --- src/libcore/intrinsics.rs | 8 +- src/libcore/ptr.rs | 29 ++ src/librustc/middle/lang_items.rs | 4 +- src/librustc/mir/mod.rs | 18 +- src/librustc/ty/instance.rs | 9 +- .../borrowck/mir/gather_moves.rs | 2 +- src/librustc_mir/shim.rs | 132 +++++- src/librustc_mir/util/elaborate_drops.rs | 301 +++++++++---- src/librustc_trans/abi.rs | 24 +- src/librustc_trans/adt.rs | 22 - src/librustc_trans/attributes.rs | 1 + src/librustc_trans/base.rs | 4 +- src/librustc_trans/callee.rs | 3 + src/librustc_trans/cleanup.rs | 144 ------- src/librustc_trans/collector.rs | 260 ++++------- src/librustc_trans/common.rs | 13 +- src/librustc_trans/context.rs | 16 +- src/librustc_trans/glue.rs | 403 +----------------- src/librustc_trans/lib.rs | 1 - src/librustc_trans/meth.rs | 52 ++- src/librustc_trans/mir/block.rs | 89 ++-- src/librustc_trans/mir/lvalue.rs | 58 +-- src/librustc_trans/mir/operand.rs | 24 +- src/librustc_trans/mir/rvalue.rs | 6 +- src/librustc_trans/monomorphize.rs | 27 +- src/librustc_trans/partitioning.rs | 5 +- src/librustc_trans/symbol_map.rs | 1 - src/librustc_trans/trans_item.rs | 80 +--- src/librustc_trans/tvec.rs | 7 +- .../cross-crate-generic-functions.rs | 2 - .../drop_in_place_intrinsic.rs | 7 +- .../item-collection/function-as-argument.rs | 2 - .../item-collection/generic-drop-glue.rs | 19 +- .../instantiation-through-vtable.rs | 3 +- .../items-within-generic-items.rs | 2 - .../item-collection/non-generic-drop-glue.rs | 8 +- .../item-collection/non-generic-functions.rs | 2 - .../item-collection/overloaded-operators.rs | 2 - .../item-collection/static-init.rs | 1 - .../item-collection/statics-and-consts.rs | 2 - .../item-collection/trait-implementations.rs | 2 - .../trait-method-as-argument.rs | 2 - .../trait-method-default-impl.rs | 2 - .../item-collection/transitive-drop-glue.rs | 21 +- .../item-collection/tuple-drop-glue.rs | 9 +- .../codegen-units/item-collection/unsizing.rs | 8 +- .../unused-traits-and-generics.rs | 1 - .../partitioning/extern-drop-glue.rs | 7 +- .../partitioning/extern-generic.rs | 2 - .../partitioning/local-drop-glue.rs | 9 +- .../partitioning/regular-modules.rs | 2 - .../codegen-units/partitioning/statics.rs | 2 - .../partitioning/vtable-through-const.rs | 2 +- 53 files changed, 721 insertions(+), 1141 deletions(-) delete mode 100644 src/librustc_trans/cleanup.rs diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 12410c08f399b..e74e13d1a6b05 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -46,8 +46,13 @@ issue = "0")] #![allow(missing_docs)] -extern "rust-intrinsic" { +#[cfg(not(stage0))] +#[stable(feature = "drop_in_place", since = "1.8.0")] +#[rustc_deprecated(reason = "no longer an intrinsic - use `ptr::drop_in_place` directly", + since = "1.18.0")] +pub use ptr::drop_in_place; +extern "rust-intrinsic" { // NB: These intrinsics take raw pointers because they mutate aliased // memory, which is not valid for either `&` or `&mut`. @@ -622,6 +627,7 @@ extern "rust-intrinsic" { pub fn size_of_val(_: &T) -> usize; pub fn min_align_of_val(_: &T) -> usize; + #[cfg(stage0)] /// Executes the destructor (if any) of the pointed-to value. /// /// This has two use cases: diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 0b7aa4fa9117c..c4409d3cb5590 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -37,9 +37,38 @@ pub use intrinsics::copy; #[stable(feature = "rust1", since = "1.0.0")] pub use intrinsics::write_bytes; +#[cfg(stage0)] #[stable(feature = "drop_in_place", since = "1.8.0")] pub use intrinsics::drop_in_place; +#[cfg(not(stage0))] +/// Executes the destructor (if any) of the pointed-to value. +/// +/// This has two use cases: +/// +/// * It is *required* to use `drop_in_place` to drop unsized types like +/// trait objects, because they can't be read out onto the stack and +/// dropped normally. +/// +/// * It is friendlier to the optimizer to do this over `ptr::read` when +/// dropping manually allocated memory (e.g. when writing Box/Rc/Vec), +/// as the compiler doesn't need to prove that it's sound to elide the +/// copy. +/// +/// # Undefined Behavior +/// +/// This has all the same safety problems as `ptr::read` with respect to +/// invalid pointers, types, and double drops. +#[stable(feature = "drop_in_place", since = "1.8.0")] +#[lang="drop_in_place"] +#[inline] +#[allow(unconditional_recursion)] +pub unsafe fn drop_in_place(to_drop: *mut T) { + // Code here does not matter - this is replaced by the + // real drop glue by the compiler. + drop_in_place(to_drop); +} + /// Creates a null raw pointer. /// /// # Examples diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index b9f1611f62baf..81a415a2f5309 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -335,7 +335,7 @@ language_item_table! { ExchangeMallocFnLangItem, "exchange_malloc", exchange_malloc_fn; BoxFreeFnLangItem, "box_free", box_free_fn; - StrDupUniqFnLangItem, "strdup_uniq", strdup_uniq_fn; + DropInPlaceFnLangItem, "drop_in_place", drop_in_place_fn; StartFnLangItem, "start", start_fn; @@ -355,8 +355,6 @@ language_item_table! { ContravariantLifetimeItem, "contravariant_lifetime", contravariant_lifetime; InvariantLifetimeItem, "invariant_lifetime", invariant_lifetime; - NoCopyItem, "no_copy_bound", no_copy_bound; - NonZeroItem, "non_zero", non_zero; DebugTraitLangItem, "debug_trait", debug_trait; diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index fea576f706780..33df648f172d6 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -17,7 +17,7 @@ use rustc_data_structures::control_flow_graph::{GraphPredecessors, GraphSuccesso use rustc_data_structures::control_flow_graph::ControlFlowGraph; use hir::def::CtorKind; use hir::def_id::DefId; -use ty::subst::Substs; +use ty::subst::{Subst, Substs}; use ty::{self, AdtDef, ClosureSubsts, Region, Ty}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use util::ppaux; @@ -982,6 +982,22 @@ impl<'tcx> Debug for Operand<'tcx> { } } +impl<'tcx> Operand<'tcx> { + pub fn item<'a>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>, + span: Span) + -> Self + { + Operand::Constant(Constant { + span: span, + ty: tcx.item_type(def_id).subst(tcx, substs), + literal: Literal::Item { def_id, substs } + }) + } + +} + /////////////////////////////////////////////////////////////////////////// /// Rvalues diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index aeb1fe079ff25..67287f1b4ff72 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -35,6 +35,8 @@ pub enum InstanceDef<'tcx> { Virtual(DefId, usize), // <[mut closure] as FnOnce>::call_once ClosureOnceShim { call_once: DefId }, + // drop_in_place::; None for empty drop glue. + DropGlue(DefId, Option>), } impl<'tcx> InstanceDef<'tcx> { @@ -46,7 +48,8 @@ impl<'tcx> InstanceDef<'tcx> { InstanceDef::Virtual(def_id, _) | InstanceDef::Intrinsic(def_id, ) | InstanceDef::ClosureOnceShim { call_once: def_id } - => def_id + => def_id, + InstanceDef::DropGlue(def_id, _) => def_id } } @@ -65,6 +68,7 @@ impl<'tcx> InstanceDef<'tcx> { // real on-demand. let ty = match self { &InstanceDef::FnPtrShim(_, ty) => Some(ty), + &InstanceDef::DropGlue(_, ty) => ty, _ => None }.into_iter(); @@ -97,6 +101,9 @@ impl<'tcx> fmt::Display for Instance<'tcx> { InstanceDef::ClosureOnceShim { .. } => { write!(f, " - shim") } + InstanceDef::DropGlue(_, ty) => { + write!(f, " - shim({:?})", ty) + } } } } diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index 8d866676dbd18..81037fe40d9da 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -437,7 +437,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } Rvalue::Ref(..) | Rvalue::Discriminant(..) | - Rvalue::Len(..) => {} + Rvalue::Len(..) | Rvalue::Box(..) => { // This returns an rvalue with uninitialized contents. We can't // move out of it here because it is an rvalue - assignments always diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 2d9ef124ad244..26d5b7fd38ab0 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -15,7 +15,7 @@ use rustc::middle::region::ROOT_CODE_EXTENT; use rustc::mir::*; use rustc::mir::transform::MirSource; use rustc::ty::{self, Ty}; -use rustc::ty::subst::Subst; +use rustc::ty::subst::{Kind, Subst}; use rustc::ty::maps::Providers; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; @@ -25,10 +25,13 @@ use syntax::ast; use syntax_pos::Span; use std::cell::RefCell; +use std::fmt; use std::iter; use std::mem; use transform::{add_call_guards, no_landing_pads, simplify}; +use util::elaborate_drops::{self, DropElaborator, DropStyle, DropFlagMode}; +use util::patch::MirPatch; pub fn provide(providers: &mut Providers) { providers.mir_shims = make_shim; @@ -101,6 +104,9 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, None ) } + ty::InstanceDef::DropGlue(def_id, ty) => { + build_drop_shim(tcx, ¶m_env, def_id, ty) + } ty::InstanceDef::Intrinsic(_) => { bug!("creating shims from intrinsics ({:?}) is unsupported", instance) } @@ -143,6 +149,129 @@ fn local_decls_for_sig<'tcx>(sig: &ty::FnSig<'tcx>) .collect() } +fn build_drop_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, + param_env: &ty::ParameterEnvironment<'tcx>, + def_id: DefId, + ty: Option>) + -> Mir<'tcx> +{ + debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty); + + let substs = if let Some(ty) = ty { + tcx.mk_substs(iter::once(Kind::from(ty))) + } else { + param_env.free_substs + }; + let fn_ty = tcx.item_type(def_id).subst(tcx, substs); + let sig = tcx.erase_late_bound_regions(&fn_ty.fn_sig()); + let span = tcx.def_span(def_id); + + let source_info = SourceInfo { span, scope: ARGUMENT_VISIBILITY_SCOPE }; + + let return_block = BasicBlock::new(1); + let mut blocks = IndexVec::new(); + let block = |blocks: &mut IndexVec<_, _>, kind| { + blocks.push(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { source_info, kind }), + is_cleanup: false + }) + }; + block(&mut blocks, TerminatorKind::Goto { target: return_block }); + block(&mut blocks, TerminatorKind::Return); + + let mut mir = Mir::new( + blocks, + IndexVec::from_elem_n( + VisibilityScopeData { span: span, parent_scope: None }, 1 + ), + IndexVec::new(), + sig.output(), + local_decls_for_sig(&sig), + sig.inputs().len(), + vec![], + span + ); + + if let Some(..) = ty { + let patch = { + let mut elaborator = DropShimElaborator { + mir: &mir, + patch: MirPatch::new(&mir), + tcx, param_env + }; + let dropee = Lvalue::Projection( + box Projection { + base: Lvalue::Local(Local::new(1+0)), + elem: ProjectionElem::Deref + } + ); + let resume_block = elaborator.patch.resume_block(); + elaborate_drops::elaborate_drop( + &mut elaborator, + source_info, + false, + &dropee, + (), + return_block, + Some(resume_block), + START_BLOCK + ); + elaborator.patch + }; + patch.apply(&mut mir); + } + + mir +} + +pub struct DropShimElaborator<'a, 'tcx: 'a> { + mir: &'a Mir<'tcx>, + patch: MirPatch<'tcx>, + tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, + param_env: &'a ty::ParameterEnvironment<'tcx>, +} + +impl<'a, 'tcx> fmt::Debug for DropShimElaborator<'a, 'tcx> { + fn fmt(&self, _f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + Ok(()) + } +} + +impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { + type Path = (); + + fn patch(&mut self) -> &mut MirPatch<'tcx> { &mut self.patch } + fn mir(&self) -> &'a Mir<'tcx> { self.mir } + fn tcx(&self) -> ty::TyCtxt<'a, 'tcx, 'tcx> { self.tcx } + fn param_env(&self) -> &'a ty::ParameterEnvironment<'tcx> { self.param_env } + + fn drop_style(&self, _path: Self::Path, mode: DropFlagMode) -> DropStyle { + if let DropFlagMode::Shallow = mode { + DropStyle::Static + } else { + DropStyle::Open + } + } + + fn get_drop_flag(&mut self, _path: Self::Path) -> Option> { + None + } + + fn clear_drop_flag(&mut self, _location: Location, _path: Self::Path, _mode: DropFlagMode) { + } + + fn field_subpath(&self, _path: Self::Path, _field: Field) -> Option { + None + } + fn deref_subpath(&self, _path: Self::Path) -> Option { + None + } + fn downcast_subpath(&self, _path: Self::Path, _variant: usize) -> Option { + Some(()) + } +} + /// Build a "call" shim for `def_id`. The shim calls the /// function specified by `call_kind`, first adjusting its first /// argument according to `rcvr_adjustment`. @@ -162,7 +291,6 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, def_id, rcvr_adjustment, call_kind, untuple_args); let fn_ty = tcx.item_type(def_id).subst(tcx, param_env.free_substs); - // Not normalizing here without a param env. let sig = tcx.erase_late_bound_regions(&fn_ty.fn_sig()); let span = tcx.def_span(def_id); diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index ce6debab9a4b8..d0f142ad7d7a8 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -9,10 +9,12 @@ // except according to those terms. use std::fmt; +use rustc::hir; use rustc::mir::*; +use rustc::middle::const_val::ConstInt; use rustc::middle::lang_items; use rustc::ty::{self, Ty}; -use rustc::ty::subst::{Kind, Subst, Substs}; +use rustc::ty::subst::{Kind, Substs}; use rustc::ty::util::IntTypeExt; use rustc_data_structures::indexed_vec::Idx; use util::patch::MirPatch; @@ -92,6 +94,7 @@ pub fn elaborate_drop<'b, 'tcx, D>( bb: BasicBlock) where D: DropElaborator<'b, 'tcx> { + assert_eq!(unwind.is_none(), is_cleanup); DropCtxt { elaborator, source_info, is_cleanup, lvalue, path, succ, unwind }.elaborate_drop(bb) @@ -146,7 +149,10 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> }); } DropStyle::Conditional => { - let drop_bb = self.complete_drop(Some(DropFlagMode::Deep)); + let is_cleanup = self.is_cleanup; // FIXME(#6393) + let succ = self.succ; + let drop_bb = self.complete_drop( + is_cleanup, Some(DropFlagMode::Deep), succ); self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto { target: drop_bb }); @@ -208,7 +214,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> // Using `self.path` here to condition the drop on // our own drop flag. path: self.path - }.complete_drop(None) + }.complete_drop(is_cleanup, None, succ) } } @@ -220,7 +226,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> /// (the first field is never reached). If it is `None`, all /// unwind targets are left blank. fn drop_halfladder<'a>(&mut self, - unwind_ladder: Option>, + unwind_ladder: Option<&[BasicBlock]>, succ: BasicBlock, fields: &[(Lvalue<'tcx>, Option)], is_cleanup: bool) @@ -262,10 +268,10 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> /// .c1: /// ELAB(drop location.1 [target=.c2]) /// .c2: - /// ELAB(drop location.2 [target=`self.unwind]) + /// ELAB(drop location.2 [target=`self.unwind`]) fn drop_ladder<'a>(&mut self, fields: Vec<(Lvalue<'tcx>, Option)>) - -> BasicBlock + -> (BasicBlock, Option) { debug!("drop_ladder({:?}, {:?})", self, fields); @@ -286,8 +292,12 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let succ = self.succ; // FIXME(#6393) let is_cleanup = self.is_cleanup; - self.drop_halfladder(unwind_ladder, succ, &fields, is_cleanup) - .last().cloned().unwrap_or(succ) + let normal_ladder = + self.drop_halfladder(unwind_ladder.as_ref().map(|x| &**x), + succ, &fields, is_cleanup); + + (normal_ladder.last().cloned().unwrap_or(succ), + unwind_ladder.and_then(|l| l.last().cloned()).or(self.unwind)) } fn open_drop_for_tuple<'a>(&mut self, tys: &[Ty<'tcx>]) @@ -300,7 +310,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> self.elaborator.field_subpath(self.path, Field::new(i))) }).collect(); - self.drop_ladder(fields) + self.drop_ladder(fields).0 } fn open_drop_for_box<'a>(&mut self, ty: Ty<'tcx>) -> BasicBlock @@ -323,7 +333,33 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> fn open_drop_for_adt<'a>(&mut self, adt: &'tcx ty::AdtDef, substs: &'tcx Substs<'tcx>) -> BasicBlock { debug!("open_drop_for_adt({:?}, {:?}, {:?})", self, adt, substs); + if adt.variants.len() == 0 { + return self.elaborator.patch().new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::Unreachable + }), + is_cleanup: self.is_cleanup + }); + } + + let contents_drop = if adt.is_union() { + (self.succ, self.unwind) + } else { + self.open_drop_for_adt_contents(adt, substs) + }; + if adt.has_dtor(self.tcx()) { + self.destructor_call_block(contents_drop) + } else { + contents_drop.0 + } + } + + fn open_drop_for_adt_contents<'a>(&mut self, adt: &'tcx ty::AdtDef, + substs: &'tcx Substs<'tcx>) + -> (BasicBlock, Option) { match adt.variants.len() { 1 => { let fields = self.move_paths_for_fields( @@ -335,9 +371,19 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> self.drop_ladder(fields) } _ => { + let is_cleanup = self.is_cleanup; + let succ = self.succ; + let unwind = self.unwind; // FIXME(#6393) + let mut values = Vec::with_capacity(adt.variants.len()); - let mut blocks = Vec::with_capacity(adt.variants.len()); + let mut normal_blocks = Vec::with_capacity(adt.variants.len()); + let mut unwind_blocks = if is_cleanup { + None + } else { + Some(Vec::with_capacity(adt.variants.len())) + }; let mut otherwise = None; + let mut unwind_otherwise = None; for (variant_index, discr) in adt.discriminants(self.tcx()).enumerate() { let subpath = self.elaborator.downcast_subpath( self.path, variant_index); @@ -351,53 +397,146 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> &adt.variants[variant_index], substs); values.push(discr); - blocks.push(self.drop_ladder(fields)); + if let Some(ref mut unwind_blocks) = unwind_blocks { + // We can't use the half-ladder from the original + // drop ladder, because this breaks the + // "funclet can't have 2 successor funclets" + // requirement from MSVC: + // + // switch unwind-switch + // / \ / \ + // v1.0 v2.0 v2.0-unwind v1.0-unwind + // | | / | + // v1.1-unwind v2.1-unwind | + // ^ | + // \-------------------------------/ + // + // Create a duplicate half-ladder to avoid that. We + // could technically only do this on MSVC, but I + // I want to minimize the divergence between MSVC + // and non-MSVC. + + let unwind = unwind.unwrap(); + let halfladder = self.drop_halfladder( + None, unwind, &fields, true); + unwind_blocks.push( + halfladder.last().cloned().unwrap_or(unwind) + ); + } + let (normal, _) = self.drop_ladder(fields); + normal_blocks.push(normal); } else { // variant not found - drop the entire enum if let None = otherwise { - otherwise = - Some(self.complete_drop(Some(DropFlagMode::Shallow))); + otherwise = Some(self.complete_drop( + is_cleanup, + Some(DropFlagMode::Shallow), + succ)); + unwind_otherwise = unwind.map(|unwind| self.complete_drop( + true, + Some(DropFlagMode::Shallow), + unwind + )); } } } if let Some(block) = otherwise { - blocks.push(block); + normal_blocks.push(block); + if let Some(ref mut unwind_blocks) = unwind_blocks { + unwind_blocks.push(unwind_otherwise.unwrap()); + } } else { values.pop(); } - // If there are multiple variants, then if something - // is present within the enum the discriminant, tracked - // by the rest path, must be initialized. - // - // Additionally, we do not want to switch on the - // discriminant after it is free-ed, because that - // way lies only trouble. - let discr_ty = adt.repr.discr_type().to_ty(self.tcx()); - let discr = Lvalue::Local(self.new_temp(discr_ty)); - let discr_rv = Rvalue::Discriminant(self.lvalue.clone()); - let switch_block = self.elaborator.patch().new_block(BasicBlockData { - statements: vec![ - Statement { - source_info: self.source_info, - kind: StatementKind::Assign(discr.clone(), discr_rv), - } - ], - terminator: Some(Terminator { - source_info: self.source_info, - kind: TerminatorKind::SwitchInt { - discr: Operand::Consume(discr), - switch_ty: discr_ty, - values: From::from(values), - targets: blocks, - } - }), - is_cleanup: self.is_cleanup, - }); - self.drop_flag_test_block(switch_block) + + (self.adt_switch_block(is_cleanup, adt, normal_blocks, &values, succ), + unwind_blocks.map(|unwind_blocks| { + self.adt_switch_block( + is_cleanup, adt, unwind_blocks, &values, unwind.unwrap() + ) + })) } } } + fn adt_switch_block(&mut self, + is_cleanup: bool, + adt: &'tcx ty::AdtDef, + blocks: Vec, + values: &[ConstInt], + succ: BasicBlock) + -> BasicBlock { + // If there are multiple variants, then if something + // is present within the enum the discriminant, tracked + // by the rest path, must be initialized. + // + // Additionally, we do not want to switch on the + // discriminant after it is free-ed, because that + // way lies only trouble. + let discr_ty = adt.repr.discr_type().to_ty(self.tcx()); + let discr = Lvalue::Local(self.new_temp(discr_ty)); + let discr_rv = Rvalue::Discriminant(self.lvalue.clone()); + let switch_block = self.elaborator.patch().new_block(BasicBlockData { + statements: vec![ + Statement { + source_info: self.source_info, + kind: StatementKind::Assign(discr.clone(), discr_rv), + } + ], + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::SwitchInt { + discr: Operand::Consume(discr), + switch_ty: discr_ty, + values: From::from(values.to_owned()), + targets: blocks, + } + }), + is_cleanup: is_cleanup, + }); + self.drop_flag_test_block(is_cleanup, switch_block, succ) + } + + fn destructor_call_block<'a>(&mut self, (succ, unwind): (BasicBlock, Option)) + -> BasicBlock + { + debug!("destructor_call_block({:?}, {:?})", self, succ); + let tcx = self.tcx(); + let drop_trait = tcx.lang_items.drop_trait().unwrap(); + let drop_fn = tcx.associated_items(drop_trait).next().unwrap(); + let ty = self.lvalue_ty(self.lvalue); + let substs = tcx.mk_substs(iter::once(Kind::from(ty))); + + let re_erased = tcx.mk_region(ty::ReErased); + let ref_ty = tcx.mk_ref(re_erased, ty::TypeAndMut { + ty: ty, + mutbl: hir::Mutability::MutMutable + }); + let ref_lvalue = self.new_temp(ref_ty); + let unit_temp = Lvalue::Local(self.new_temp(tcx.mk_nil())); + + self.elaborator.patch().new_block(BasicBlockData { + statements: vec![Statement { + source_info: self.source_info, + kind: StatementKind::Assign( + Lvalue::Local(ref_lvalue), + Rvalue::Ref(re_erased, BorrowKind::Mut, self.lvalue.clone()) + ) + }], + terminator: Some(Terminator { + kind: TerminatorKind::Call { + func: Operand::item(tcx, drop_fn.def_id, substs, + self.source_info.span), + args: vec![Operand::Consume(Lvalue::Local(ref_lvalue))], + destination: Some((unit_temp, succ)), + cleanup: unwind, + }, + source_info: self.source_info + }), + is_cleanup: self.is_cleanup, + }) + } + /// The slow-path - create an "open", elaborated drop for a type /// which is moved-out-of only partially, and patch `bb` to a jump /// to it. This must not be called on ADTs with a destructor, @@ -408,6 +547,8 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> /// ADT, both in the success case or if one of the destructors fail. fn open_drop<'a>(&mut self) -> BasicBlock { let ty = self.lvalue_ty(self.lvalue); + let is_cleanup = self.is_cleanup; // FIXME(#6393) + let succ = self.succ; match ty.sty { ty::TyClosure(def_id, substs) => { let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect(); @@ -422,6 +563,14 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> ty::TyAdt(def, substs) => { self.open_drop_for_adt(def, substs) } + ty::TyDynamic(..) => { + self.complete_drop(is_cleanup, Some(DropFlagMode::Deep), succ) + } + ty::TyArray(..) | ty::TySlice(..) => { + // FIXME(#34708): handle partially-dropped + // array/slice elements. + self.complete_drop(is_cleanup, Some(DropFlagMode::Deep), succ) + } _ => bug!("open drop from non-ADT `{:?}`", ty) } } @@ -433,22 +582,27 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> /// if FLAG(self.path) /// if let Some(mode) = mode: FLAG(self.path)[mode] = false /// drop(self.lv) - fn complete_drop<'a>(&mut self, drop_mode: Option) -> BasicBlock + fn complete_drop<'a>(&mut self, + is_cleanup: bool, + drop_mode: Option, + succ: BasicBlock) -> BasicBlock { debug!("complete_drop({:?},{:?})", self, drop_mode); - let drop_block = self.drop_block(); + let drop_block = self.drop_block(is_cleanup, succ); if let Some(mode) = drop_mode { let block_start = Location { block: drop_block, statement_index: 0 }; self.elaborator.clear_drop_flag(block_start, self.path, mode); } - self.drop_flag_test_block(drop_block) + self.drop_flag_test_block(is_cleanup, drop_block, succ) } fn elaborated_drop_block<'a>(&mut self) -> BasicBlock { debug!("elaborated_drop_block({:?})", self); - let blk = self.drop_block(); + let is_cleanup = self.is_cleanup; // FIXME(#6393) + let succ = self.succ; + let blk = self.drop_block(is_cleanup, succ); self.elaborate_drop(blk); blk } @@ -460,7 +614,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> is_cleanup: bool ) -> BasicBlock { let block = self.unelaborated_free_block(ty, target, is_cleanup); - self.drop_flag_test_block_with_succ(is_cleanup, block, target) + self.drop_flag_test_block(is_cleanup, block, target) } fn unelaborated_free_block<'a>( @@ -473,53 +627,34 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let unit_temp = Lvalue::Local(self.new_temp(tcx.mk_nil())); let free_func = tcx.require_lang_item(lang_items::BoxFreeFnLangItem); let substs = tcx.mk_substs(iter::once(Kind::from(ty))); - let fty = tcx.item_type(free_func).subst(tcx, substs); - let free_block = self.elaborator.patch().new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { - source_info: self.source_info, kind: TerminatorKind::Call { - func: Operand::Constant(Constant { - span: self.source_info.span, - ty: fty, - literal: Literal::Item { - def_id: free_func, - substs: substs - } - }), - args: vec![Operand::Consume(self.lvalue.clone())], - destination: Some((unit_temp, target)), - cleanup: None - } - }), - is_cleanup: is_cleanup - }); + let call = TerminatorKind::Call { + func: Operand::item(tcx, free_func, substs, self.source_info.span), + args: vec![Operand::Consume(self.lvalue.clone())], + destination: Some((unit_temp, target)), + cleanup: None + }; // FIXME(#6393) + let free_block = self.new_block(is_cleanup, call); + let block_start = Location { block: free_block, statement_index: 0 }; self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow); free_block } - fn drop_block<'a>(&mut self) -> BasicBlock { + fn drop_block<'a>(&mut self, is_cleanup: bool, succ: BasicBlock) -> BasicBlock { let block = TerminatorKind::Drop { location: self.lvalue.clone(), - target: self.succ, - unwind: self.unwind + target: succ, + unwind: if is_cleanup { None } else { self.unwind } }; - let is_cleanup = self.is_cleanup; // FIXME(#6393) self.new_block(is_cleanup, block) } - fn drop_flag_test_block<'a>(&mut self, on_set: BasicBlock) -> BasicBlock { - let is_cleanup = self.is_cleanup; - let succ = self.succ; // FIXME(#6393) - self.drop_flag_test_block_with_succ(is_cleanup, on_set, succ) - } - - fn drop_flag_test_block_with_succ<'a>(&mut self, - is_cleanup: bool, - on_set: BasicBlock, - on_unset: BasicBlock) - -> BasicBlock + fn drop_flag_test_block(&mut self, + is_cleanup: bool, + on_set: BasicBlock, + on_unset: BasicBlock) + -> BasicBlock { let style = self.elaborator.drop_style(self.path, DropFlagMode::Shallow); debug!("drop_flag_test_block({:?},{:?},{:?}) - {:?}", diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index 710aa1fbbc6d9..27a19d211c290 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -11,7 +11,7 @@ use llvm::{self, ValueRef, Integer, Pointer, Float, Double, Struct, Array, Vector, AttributePlace}; use base; use builder::Builder; -use common::{self, type_is_fat_ptr, C_uint}; +use common::{type_is_fat_ptr, C_uint}; use context::CrateContext; use cabi_x86; use cabi_x86_64; @@ -59,6 +59,7 @@ enum ArgKind { pub use self::attr_impl::ArgAttribute; #[allow(non_upper_case_globals)] +#[allow(unused)] mod attr_impl { // The subset of llvm::Attribute needed for arguments, packed into a bitfield. bitflags! { @@ -223,16 +224,6 @@ impl ArgType { self.kind == ArgKind::Ignore } - /// Get the LLVM type for an lvalue of the original Rust type of - /// this argument/return, i.e. the result of `type_of::type_of`. - pub fn memory_ty(&self, ccx: &CrateContext) -> Type { - if self.original_ty == Type::i1(ccx) { - Type::i8(ccx) - } else { - self.original_ty - } - } - /// Store a direct/indirect value described by this ArgType into a /// lvalue for the original Rust type of this argument/return. /// Can be used for both storing formal arguments into Rust variables @@ -344,17 +335,6 @@ impl FnType { fn_ty } - pub fn from_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - instance: &ty::Instance<'tcx>, - extra_args: &[Ty<'tcx>]) -> FnType - { - let ity = common::instance_ty(ccx.shared(), instance); - let sig = common::ty_fn_sig(ccx, ity); - let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&sig); - - Self::new(ccx, sig, extra_args) - } - fn unadjusted<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> FnType { diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs index 11d3fae823830..058f37f62dd82 100644 --- a/src/librustc_trans/adt.rs +++ b/src/librustc_trans/adt.rs @@ -363,28 +363,6 @@ fn load_discr(bcx: &Builder, ity: layout::Integer, ptr: ValueRef, } } -/// Yield information about how to dispatch a case of the -/// discriminant-like value returned by `trans_switch`. -/// -/// This should ideally be less tightly tied to `_match`. -pub fn trans_case<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, value: Disr) -> ValueRef { - let l = bcx.ccx.layout_of(t); - match *l { - layout::CEnum { discr, .. } - | layout::General { discr, .. }=> { - C_integral(Type::from_integer(bcx.ccx, discr), value.0, true) - } - layout::RawNullablePointer { .. } | - layout::StructWrappedNullablePointer { .. } => { - assert!(value == Disr(0) || value == Disr(1)); - C_bool(bcx.ccx, value != Disr(0)) - } - _ => { - bug!("{} does not have a discriminant. Represented as {:#?}", t, l); - } - } -} - /// Set the discriminant for a new value of the given case of the given /// representation. pub fn trans_set_discr<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, val: ValueRef, to: Disr) { diff --git a/src/librustc_trans/attributes.rs b/src/librustc_trans/attributes.rs index efdd1b736f0e7..6bef31ccf64a4 100644 --- a/src/librustc_trans/attributes.rs +++ b/src/librustc_trans/attributes.rs @@ -17,6 +17,7 @@ pub use syntax::attr::InlineAttr; use syntax::ast; use context::CrateContext; + /// Mark LLVM function to use provided inline heuristic. #[inline] pub fn inline(val: ValueRef, inline: InlineAttr) { diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index a58764878a318..6593b8e68d425 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -403,7 +403,9 @@ pub fn load_ty<'a, 'tcx>(b: &Builder<'a, 'tcx>, ptr: ValueRef, // a char is a Unicode codepoint, and so takes values from 0 // to 0x10FFFF inclusive only. b.load_range_assert(ptr, 0, 0x10FFFF + 1, llvm::False, alignment.to_align()) - } else if (t.is_region_ptr() || t.is_box()) && !common::type_is_fat_ptr(ccx, t) { + } else if (t.is_region_ptr() || t.is_box() || t.is_fn()) + && !common::type_is_fat_ptr(ccx, t) + { b.load_nonnull(ptr, alignment.to_align()) } else { b.load(ptr, alignment.to_align()) diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 4d0bd9fa201d8..aefee51191ac4 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -96,6 +96,9 @@ pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, assert_eq!(common::val_ty(llfn), llptrty); debug!("get_fn: not casting pointer!"); + if common::is_inline_instance(tcx, &instance) { + attributes::inline(llfn, attributes::InlineAttr::Hint); + } let attrs = instance.def.attrs(ccx.tcx()); attributes::from_fn_attrs(ccx, &attrs, llfn); diff --git a/src/librustc_trans/cleanup.rs b/src/librustc_trans/cleanup.rs deleted file mode 100644 index 2b2e5e85ea50d..0000000000000 --- a/src/librustc_trans/cleanup.rs +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2013-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. - -//! ## The Cleanup module -//! -//! The cleanup module tracks what values need to be cleaned up as scopes -//! are exited, either via panic or just normal control flow. -//! -//! Cleanup items can be scheduled into any of the scopes on the stack. -//! Typically, when a scope is finished, we generate the cleanup code. This -//! corresponds to a normal exit from a block (for example, an expression -//! completing evaluation successfully without panic). - -use llvm::BasicBlockRef; -use base; -use mir::lvalue::LvalueRef; -use rustc::mir::tcx::LvalueTy; -use builder::Builder; -use common::Funclet; -use glue; -use type_::Type; - -pub struct CleanupScope<'tcx> { - // Cleanup to run upon scope exit. - cleanup: Option>, - - // Computed on creation if compiling with landing pads (!sess.no_landing_pads) - pub landing_pad: Option, -} - -#[derive(Copy, Clone)] -pub struct DropValue<'tcx> { - val: LvalueRef<'tcx>, - skip_dtor: bool, -} - -impl<'tcx> DropValue<'tcx> { - fn trans<'a>(&self, funclet: Option<&'a Funclet>, bcx: &Builder<'a, 'tcx>) { - glue::call_drop_glue(bcx, self.val, self.skip_dtor, funclet) - } - - /// Creates a landing pad for the top scope. The landing pad will perform all cleanups necessary - /// for an unwind and then `resume` to continue error propagation: - /// - /// landing_pad -> ... cleanups ... -> [resume] - /// - /// This should only be called once per function, as it creates an alloca for the landingpad. - fn get_landing_pad<'a>(&self, bcx: &Builder<'a, 'tcx>) -> BasicBlockRef { - debug!("get_landing_pad"); - let bcx = bcx.build_sibling_block("cleanup_unwind"); - let llpersonality = bcx.ccx.eh_personality(); - bcx.set_personality_fn(llpersonality); - - if base::wants_msvc_seh(bcx.sess()) { - let pad = bcx.cleanup_pad(None, &[]); - let funclet = Some(Funclet::new(pad)); - self.trans(funclet.as_ref(), &bcx); - - bcx.cleanup_ret(pad, None); - } else { - // The landing pad return type (the type being propagated). Not sure - // what this represents but it's determined by the personality - // function and this is what the EH proposal example uses. - let llretty = Type::struct_(bcx.ccx, &[Type::i8p(bcx.ccx), Type::i32(bcx.ccx)], false); - - // The only landing pad clause will be 'cleanup' - let llretval = bcx.landing_pad(llretty, llpersonality, 1, bcx.llfn()); - - // The landing pad block is a cleanup - bcx.set_cleanup(llretval); - - // Insert cleanup instructions into the cleanup block - self.trans(None, &bcx); - - if !bcx.sess().target.target.options.custom_unwind_resume { - bcx.resume(llretval); - } else { - let exc_ptr = bcx.extract_value(llretval, 0); - bcx.call(bcx.ccx.eh_unwind_resume(), &[exc_ptr], None); - bcx.unreachable(); - } - } - - bcx.llbb() - } -} - -impl<'a, 'tcx> CleanupScope<'tcx> { - /// Issue #23611: Schedules a (deep) drop of the contents of - /// `val`, which is a pointer to an instance of struct/enum type - /// `ty`. The scheduled code handles extracting the discriminant - /// and dropping the contents associated with that variant - /// *without* executing any associated drop implementation. - pub fn schedule_drop_adt_contents( - bcx: &Builder<'a, 'tcx>, val: LvalueRef<'tcx> - ) -> CleanupScope<'tcx> { - if let LvalueTy::Downcast { .. } = val.ty { - bug!("Cannot drop downcast ty yet"); - } - // `if` below could be "!contents_needs_drop"; skipping drop - // is just an optimization, so sound to be conservative. - if !bcx.ccx.shared().type_needs_drop(val.ty.to_ty(bcx.tcx())) { - return CleanupScope::noop(); - } - - let drop = DropValue { - val: val, - skip_dtor: true, - }; - - CleanupScope::new(bcx, drop) - } - - fn new(bcx: &Builder<'a, 'tcx>, drop_val: DropValue<'tcx>) -> CleanupScope<'tcx> { - CleanupScope { - cleanup: Some(drop_val), - landing_pad: if !bcx.sess().no_landing_pads() { - Some(drop_val.get_landing_pad(bcx)) - } else { - None - }, - } - } - - pub fn noop() -> CleanupScope<'tcx> { - CleanupScope { - cleanup: None, - landing_pad: None, - } - } - - pub fn trans(self, bcx: &'a Builder<'a, 'tcx>) { - if let Some(cleanup) = self.cleanup { - cleanup.trans(None, &bcx); - } - } -} diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index f076fc4710222..500802a4135d0 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -193,25 +193,21 @@ use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::map as hir_map; use rustc::hir::def_id::DefId; -use rustc::middle::lang_items::{BoxFreeFnLangItem, ExchangeMallocFnLangItem}; +use rustc::middle::lang_items::{ExchangeMallocFnLangItem}; use rustc::traits; -use rustc::ty::subst::{Kind, Substs, Subst}; +use rustc::ty::subst::{Substs, Subst}; use rustc::ty::{self, TypeFoldable, TyCtxt}; use rustc::ty::adjustment::CustomCoerceUnsized; use rustc::mir::{self, Location}; -use rustc::mir::visit as mir_visit; use rustc::mir::visit::Visitor as MirVisitor; use context::SharedCrateContext; use common::{def_ty, instance_ty}; -use glue::{self, DropGlueKind}; use monomorphize::{self, Instance}; use util::nodemap::{FxHashSet, FxHashMap, DefIdMap}; use trans_item::{TransItem, DefPathBasedNames, InstantiationMode}; -use std::iter; - #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] pub enum TransItemCollectionMode { Eager, @@ -327,10 +323,6 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>, let recursion_depth_reset; match starting_point { - TransItem::DropGlue(t) => { - find_drop_glue_neighbors(scx, t, &mut neighbors); - recursion_depth_reset = None; - } TransItem::Static(node_id) => { let def_id = scx.tcx().hir.local_def_id(node_id); let instance = Instance::mono(scx.tcx(), def_id); @@ -339,8 +331,7 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>, debug_assert!(should_trans_locally(scx.tcx(), &instance)); let ty = instance_ty(scx, &instance); - let ty = glue::get_drop_glue_type(scx, ty); - neighbors.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); + visit_drop_use(scx, ty, true, &mut neighbors); recursion_depth_reset = None; @@ -396,6 +387,14 @@ fn check_recursion_limit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let recursion_depth = recursion_depths.get(&def_id).cloned().unwrap_or(0); debug!(" => recursion depth={}", recursion_depth); + let recursion_depth = if Some(def_id) == tcx.lang_items.drop_in_place_fn() { + // HACK: drop_in_place creates tight monomorphization loops. Give + // it more margin. + recursion_depth / 4 + } else { + recursion_depth + }; + // Code that needs to instantiate the same function recursively // more than the recursion limit is assumed to be causing an // infinite expansion. @@ -521,27 +520,6 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { self.super_rvalue(rvalue, location); } - fn visit_lvalue(&mut self, - lvalue: &mir::Lvalue<'tcx>, - context: mir_visit::LvalueContext<'tcx>, - location: Location) { - debug!("visiting lvalue {:?}", *lvalue); - - if let mir_visit::LvalueContext::Drop = context { - let ty = lvalue.ty(self.mir, self.scx.tcx()) - .to_ty(self.scx.tcx()); - - let ty = monomorphize::apply_param_substs(self.scx, - self.param_substs, - &ty); - assert!(ty.is_normalized_for_trans()); - let ty = glue::get_drop_glue_type(self.scx, ty); - self.output.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); - } - - self.super_lvalue(lvalue, context, location); - } - fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: Location) { debug!("visiting constant {:?} @ {:?}", *constant, location); @@ -568,54 +546,90 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { kind: &mir::TerminatorKind<'tcx>, location: Location) { let tcx = self.scx.tcx(); - if let mir::TerminatorKind::Call { - ref func, - .. - } = *kind { - let callee_ty = func.ty(self.mir, tcx); - let callee_ty = monomorphize::apply_param_substs( - self.scx, self.param_substs, &callee_ty); - visit_fn_use(self.scx, callee_ty, true, &mut self.output); + match *kind { + mir::TerminatorKind::Call { ref func, .. } => { + let callee_ty = func.ty(self.mir, tcx); + let callee_ty = monomorphize::apply_param_substs( + self.scx, self.param_substs, &callee_ty); + visit_fn_use(self.scx, callee_ty, true, &mut self.output); + } + mir::TerminatorKind::Drop { ref location, .. } | + mir::TerminatorKind::DropAndReplace { ref location, .. } => { + let ty = location.ty(self.mir, self.scx.tcx()) + .to_ty(self.scx.tcx()); + let ty = monomorphize::apply_param_substs(self.scx, + self.param_substs, + &ty); + visit_drop_use(self.scx, ty, true, self.output); + } + mir::TerminatorKind::Goto { .. } | + mir::TerminatorKind::SwitchInt { .. } | + mir::TerminatorKind::Resume | + mir::TerminatorKind::Return | + mir::TerminatorKind::Unreachable | + mir::TerminatorKind::Assert { .. } => {} } self.super_terminator_kind(block, kind, location); } } +fn visit_drop_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, + ty: ty::Ty<'tcx>, + is_direct_call: bool, + output: &mut Vec>) +{ + let instance = monomorphize::resolve_drop_in_place(scx, ty); + visit_instance_use(scx, instance, is_direct_call, output); +} + fn visit_fn_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, ty: ty::Ty<'tcx>, is_direct_call: bool, output: &mut Vec>) { - debug!("visit_fn_use({:?}, is_direct_call={:?})", ty, is_direct_call); - let (def_id, substs) = match ty.sty { - ty::TyFnDef(def_id, substs, _) => (def_id, substs), - _ => return - }; + if let ty::TyFnDef(def_id, substs, _) = ty.sty { + let instance = monomorphize::resolve(scx, def_id, substs); + visit_instance_use(scx, instance, is_direct_call, output); + } +} - let instance = monomorphize::resolve(scx, def_id, substs); +fn visit_instance_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, + instance: ty::Instance<'tcx>, + is_direct_call: bool, + output: &mut Vec>) +{ + debug!("visit_item_use({:?}, is_direct_call={:?})", instance, is_direct_call); if !should_trans_locally(scx.tcx(), &instance) { return } match instance.def { - ty::InstanceDef::Intrinsic(..) => { + ty::InstanceDef::Intrinsic(def_id) => { if !is_direct_call { - bug!("intrinsic {:?} being reified", ty); - } - if scx.tcx().item_name(def_id) == "drop_in_place" { - // drop_in_place is a call to drop glue, need to instantiate - // that. - let ty = glue::get_drop_glue_type(scx, substs.type_at(0)); - output.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); + bug!("intrinsic {:?} being reified", def_id); } } - ty::InstanceDef::Virtual(..) => { + ty::InstanceDef::Virtual(..) | + ty::InstanceDef::DropGlue(_, None) => { // don't need to emit shim if we are calling directly. if !is_direct_call { output.push(create_fn_trans_item(instance)); } } + ty::InstanceDef::DropGlue(_, Some(ty)) => { + match ty.sty { + ty::TyArray(ety, _) | + ty::TySlice(ety) + if is_direct_call => + { + // drop of arrays/slices is translated in-line. + visit_drop_use(scx, ety, false, output); + } + _ => {} + }; + output.push(create_fn_trans_item(instance)); + } ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Item(..) | ty::InstanceDef::FnPtrShim(..) => { @@ -634,6 +648,7 @@ fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instan ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Virtual(..) | ty::InstanceDef::FnPtrShim(..) | + ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Intrinsic(_) => return true }; match tcx.hir.get_if_local(def_id) { @@ -658,124 +673,6 @@ fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instan } } -fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, - dg: DropGlueKind<'tcx>, - output: &mut Vec>) { - let ty = match dg { - DropGlueKind::Ty(ty) => ty, - DropGlueKind::TyContents(_) => { - // We already collected the neighbors of this item via the - // DropGlueKind::Ty variant. - return - } - }; - - debug!("find_drop_glue_neighbors: {}", type_to_string(scx.tcx(), ty)); - - // Make sure the BoxFreeFn lang-item gets translated if there is a boxed value. - if ty.is_box() { - let tcx = scx.tcx(); - let def_id = tcx.require_lang_item(BoxFreeFnLangItem); - let box_free_instance = Instance::new( - def_id, - tcx.mk_substs(iter::once(Kind::from(ty.boxed_ty()))) - ); - if should_trans_locally(tcx, &box_free_instance) { - output.push(create_fn_trans_item(box_free_instance)); - } - } - - // If the type implements Drop, also add a translation item for the - // monomorphized Drop::drop() implementation. - let has_dtor = match ty.sty { - ty::TyAdt(def, _) => def.has_dtor(scx.tcx()), - _ => false - }; - - if has_dtor && !ty.is_box() { - let drop_trait_def_id = scx.tcx() - .lang_items - .drop_trait() - .unwrap(); - let drop_method = scx.tcx().associated_items(drop_trait_def_id) - .find(|it| it.kind == ty::AssociatedKind::Method) - .unwrap().def_id; - let substs = scx.tcx().mk_substs_trait(ty, &[]); - let instance = monomorphize::resolve(scx, drop_method, substs); - if should_trans_locally(scx.tcx(), &instance) { - output.push(create_fn_trans_item(instance)); - } - - // This type has a Drop implementation, we'll need the contents-only - // version of the glue too. - output.push(TransItem::DropGlue(DropGlueKind::TyContents(ty))); - } - - // Finally add the types of nested values - match ty.sty { - ty::TyBool | - ty::TyChar | - ty::TyInt(_) | - ty::TyUint(_) | - ty::TyStr | - ty::TyFloat(_) | - ty::TyRawPtr(_) | - ty::TyRef(..) | - ty::TyFnDef(..) | - ty::TyFnPtr(_) | - ty::TyNever | - ty::TyDynamic(..) => { - /* nothing to do */ - } - ty::TyAdt(def, _) if def.is_box() => { - let inner_type = glue::get_drop_glue_type(scx, ty.boxed_ty()); - if scx.type_needs_drop(inner_type) { - output.push(TransItem::DropGlue(DropGlueKind::Ty(inner_type))); - } - } - ty::TyAdt(def, substs) => { - for field in def.all_fields() { - let field_type = def_ty(scx, field.did, substs); - let field_type = glue::get_drop_glue_type(scx, field_type); - - if scx.type_needs_drop(field_type) { - output.push(TransItem::DropGlue(DropGlueKind::Ty(field_type))); - } - } - } - ty::TyClosure(def_id, substs) => { - for upvar_ty in substs.upvar_tys(def_id, scx.tcx()) { - let upvar_ty = glue::get_drop_glue_type(scx, upvar_ty); - if scx.type_needs_drop(upvar_ty) { - output.push(TransItem::DropGlue(DropGlueKind::Ty(upvar_ty))); - } - } - } - ty::TySlice(inner_type) | - ty::TyArray(inner_type, _) => { - let inner_type = glue::get_drop_glue_type(scx, inner_type); - if scx.type_needs_drop(inner_type) { - output.push(TransItem::DropGlue(DropGlueKind::Ty(inner_type))); - } - } - ty::TyTuple(args, _) => { - for arg in args { - let arg = glue::get_drop_glue_type(scx, arg); - if scx.type_needs_drop(arg) { - output.push(TransItem::DropGlue(DropGlueKind::Ty(arg))); - } - } - } - ty::TyProjection(_) | - ty::TyParam(_) | - ty::TyInfer(_) | - ty::TyAnon(..) | - ty::TyError => { - bug!("encountered unexpected type"); - } - } -} - /// 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. @@ -894,8 +791,7 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(scx: &SharedCrateContext<'a, output.extend(methods); } // Also add the destructor - let dg_type = glue::get_drop_glue_type(scx, impl_ty); - output.push(TransItem::DropGlue(DropGlueKind::Ty(dg_type))); + visit_drop_use(scx, impl_ty, false, output); } } @@ -940,8 +836,7 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> { def_id_to_string(self.scx.tcx(), def_id)); let ty = def_ty(self.scx, def_id, Substs::empty()); - let ty = glue::get_drop_glue_type(self.scx, ty); - self.output.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); + visit_drop_use(self.scx, ty, true, self.output); } } } @@ -1093,12 +988,3 @@ fn def_id_to_string<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, printer.push_def_path(def_id, &mut output); output } - -fn type_to_string<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - ty: ty::Ty<'tcx>) - -> String { - let mut output = String::new(); - let printer = DefPathBasedNames::new(tcx, false, false); - printer.push_type_name(ty, &mut output); - output -} diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index 431325c5ec74e..a0906bb02f5a3 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -535,16 +535,27 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, pub fn requests_inline<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &ty::Instance<'tcx> +) -> bool { + if is_inline_instance(tcx, instance) { + return true + } + attr::requests_inline(&instance.def.attrs(tcx)[..]) +} + +pub fn is_inline_instance<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance: &ty::Instance<'tcx> ) -> bool { let def_id = match instance.def { ty::InstanceDef::Item(def_id) => def_id, + ty::InstanceDef::DropGlue(_, Some(_)) => return false, _ => return true }; match tcx.def_key(def_id).disambiguated_data.data { DefPathData::StructCtor | DefPathData::EnumVariant(..) | DefPathData::ClosureExpr => true, - _ => attr::requests_inline(&tcx.get_attrs(def_id)[..]), + _ => false } } diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index 9297c0108468a..1c1395f1b7762 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -21,7 +21,6 @@ use debuginfo; use callee; use base; use declare; -use glue::DropGlueKind; use monomorphize::Instance; use partitioning::CodegenUnit; @@ -46,7 +45,7 @@ use std::str; use syntax::ast; use syntax::symbol::InternedString; use syntax_pos::DUMMY_SP; -use abi::{Abi, FnType}; +use abi::Abi; pub struct Stats { pub n_glues_created: Cell, @@ -94,8 +93,6 @@ pub struct LocalCrateContext<'tcx> { previous_work_product: Option, codegen_unit: CodegenUnit<'tcx>, needs_unwind_cleanup_cache: RefCell, bool>>, - fn_pointer_shims: RefCell, ValueRef>>, - drop_glues: RefCell, (ValueRef, FnType)>>, /// Cache instances of monomorphic and polymorphic items instances: RefCell, ValueRef>>, /// Cache generated vtables @@ -587,8 +584,6 @@ impl<'tcx> LocalCrateContext<'tcx> { previous_work_product: previous_work_product, codegen_unit: codegen_unit, needs_unwind_cleanup_cache: RefCell::new(FxHashMap()), - fn_pointer_shims: RefCell::new(FxHashMap()), - drop_glues: RefCell::new(FxHashMap()), instances: RefCell::new(FxHashMap()), vtables: RefCell::new(FxHashMap()), const_cstr_cache: RefCell::new(FxHashMap()), @@ -723,15 +718,6 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &self.local().needs_unwind_cleanup_cache } - pub fn fn_pointer_shims(&self) -> &RefCell, ValueRef>> { - &self.local().fn_pointer_shims - } - - pub fn drop_glues<'a>(&'a self) - -> &'a RefCell, (ValueRef, FnType)>> { - &self.local().drop_glues - } - pub fn instances<'a>(&'a self) -> &'a RefCell, ValueRef>> { &self.local().instances } diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs index 9a639ed21f1ac..41a9ab2842dcd 100644 --- a/src/librustc_trans/glue.rs +++ b/src/librustc_trans/glue.rs @@ -13,69 +13,35 @@ // Code relating to drop glue. use std; -use std::iter; use llvm; -use llvm::{ValueRef, get_param}; -use middle::lang_items::BoxFreeFnLangItem; -use rustc::ty::subst::{Substs}; +use llvm::{ValueRef}; use rustc::traits; -use rustc::ty::{self, layout, AdtDef, AdtKind, Ty, TypeFoldable}; -use rustc::ty::subst::Kind; -use rustc::mir::tcx::LvalueTy; -use mir::lvalue::LvalueRef; -use abi::FnType; -use adt; -use base::*; -use callee::get_fn; -use cleanup::CleanupScope; +use rustc::ty::{self, Ty, TypeFoldable}; use common::*; use machine::*; +use meth; use monomorphize; -use trans_item::TransItem; -use tvec; -use type_of::{type_of, sizing_type_of, align_of}; -use type_::Type; +use type_of::{sizing_type_of, align_of}; use value::Value; -use Disr; use builder::Builder; -use mir::lvalue::Alignment; - -pub fn trans_exchange_free_ty<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, ptr: LvalueRef<'tcx>) { - let content_ty = ptr.ty.to_ty(bcx.tcx()); - let def_id = langcall(bcx.tcx(), None, "", BoxFreeFnLangItem); - let substs = bcx.tcx().mk_substs(iter::once(Kind::from(content_ty))); - let instance = monomorphize::resolve(bcx.ccx.shared(), def_id, substs); - - let fn_ty = FnType::from_instance(bcx.ccx, &instance, &[]); - let llret = bcx.call(get_fn(bcx.ccx, instance), - &[ptr.llval, ptr.llextra][..1 + ptr.has_extra() as usize], None); - fn_ty.apply_attrs_callsite(llret); -} - -pub fn get_drop_glue_type<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Ty<'tcx> { +pub fn needs_drop_glue<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, t: Ty<'tcx>) -> bool { assert!(t.is_normalized_for_trans()); let t = scx.tcx().erase_regions(&t); - // Even if there is no dtor for t, there might be one deeper down and we - // might need to pass in the vtable ptr. - if !scx.type_is_sized(t) { - return t; - } - // FIXME (#22815): note that type_needs_drop conservatively // approximates in some cases and may say a type expression // requires drop glue when it actually does not. // // (In this case it is not clear whether any harm is done, i.e. - // erroneously returning `t` in some cases where we could have - // returned `tcx.types.i8` does not appear unsound. The impact on + // erroneously returning `true` in some cases where we could have + // returned `false` does not appear unsound. The impact on // code quality is unknown at this time.) if !scx.type_needs_drop(t) { - return scx.tcx().types.i8; + return false; } match t.sty { ty::TyAdt(def, _) if def.is_box() => { @@ -85,210 +51,19 @@ pub fn get_drop_glue_type<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, t: Ty<'t let layout = t.layout(&infcx).unwrap(); if layout.size(&scx.tcx().data_layout).bytes() == 0 { // `Box` does not allocate. - scx.tcx().types.i8 + false } else { - t + true } }) } else { - t + true } } - _ => t - } -} - -fn drop_ty<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, args: LvalueRef<'tcx>) { - call_drop_glue(bcx, args, false, None) -} - -pub fn call_drop_glue<'a, 'tcx>( - bcx: &Builder<'a, 'tcx>, - mut args: LvalueRef<'tcx>, - skip_dtor: bool, - funclet: Option<&'a Funclet>, -) { - let t = args.ty.to_ty(bcx.tcx()); - // NB: v is an *alias* of type t here, not a direct value. - debug!("call_drop_glue(t={:?}, skip_dtor={})", t, skip_dtor); - if bcx.ccx.shared().type_needs_drop(t) { - let ccx = bcx.ccx; - let g = if skip_dtor { - DropGlueKind::TyContents(t) - } else { - DropGlueKind::Ty(t) - }; - let glue = get_drop_glue_core(ccx, g); - let glue_type = get_drop_glue_type(ccx.shared(), t); - if glue_type != t { - args.llval = bcx.pointercast(args.llval, type_of(ccx, glue_type).ptr_to()); - } - - // No drop-hint ==> call standard drop glue - bcx.call(glue, &[args.llval, args.llextra][..1 + args.has_extra() as usize], - funclet.map(|b| b.bundle())); + _ => true } } -pub fn get_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> ValueRef { - get_drop_glue_core(ccx, DropGlueKind::Ty(t)) -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub enum DropGlueKind<'tcx> { - /// The normal path; runs the dtor, and then recurs on the contents - Ty(Ty<'tcx>), - /// Skips the dtor, if any, for ty; drops the contents directly. - /// Note that the dtor is only skipped at the most *shallow* - /// level, namely, an `impl Drop for Ty` itself. So, for example, - /// if Ty is Newtype(S) then only the Drop impl for Newtype itself - /// will be skipped, while the Drop impl for S, if any, will be - /// invoked. - TyContents(Ty<'tcx>), -} - -impl<'tcx> DropGlueKind<'tcx> { - pub fn ty(&self) -> Ty<'tcx> { - match *self { DropGlueKind::Ty(t) | DropGlueKind::TyContents(t) => t } - } - - pub fn map_ty(&self, mut f: F) -> DropGlueKind<'tcx> where F: FnMut(Ty<'tcx>) -> Ty<'tcx> - { - match *self { - DropGlueKind::Ty(t) => DropGlueKind::Ty(f(t)), - DropGlueKind::TyContents(t) => DropGlueKind::TyContents(f(t)), - } - } -} - -fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKind<'tcx>) -> ValueRef { - let g = g.map_ty(|t| get_drop_glue_type(ccx.shared(), t)); - match ccx.drop_glues().borrow().get(&g) { - Some(&(glue, _)) => glue, - None => { - bug!("Could not find drop glue for {:?} -- {} -- {}.", - g, - TransItem::DropGlue(g).to_raw_string(), - ccx.codegen_unit().name()); - } - } -} - -pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKind<'tcx>) { - assert_eq!(g.ty(), get_drop_glue_type(ccx.shared(), g.ty())); - let (llfn, _) = ccx.drop_glues().borrow().get(&g).unwrap().clone(); - - let mut bcx = Builder::new_block(ccx, llfn, "entry-block"); - - ccx.stats().n_glues_created.set(ccx.stats().n_glues_created.get() + 1); - // All glue functions take values passed *by alias*; this is a - // requirement since in many contexts glue is invoked indirectly and - // the caller has no idea if it's dealing with something that can be - // passed by value. - // - // llfn is expected be declared to take a parameter of the appropriate - // type, so we don't need to explicitly cast the function parameter. - - // NB: v0 is an *alias* of type t here, not a direct value. - // Only drop the value when it ... well, we used to check for - // non-null, (and maybe we need to continue doing so), but we now - // must definitely check for special bit-patterns corresponding to - // the special dtor markings. - let t = g.ty(); - - let value = get_param(llfn, 0); - let ptr = if ccx.shared().type_is_sized(t) { - LvalueRef::new_sized_ty(value, t, Alignment::AbiAligned) - } else { - LvalueRef::new_unsized_ty(value, get_param(llfn, 1), t, Alignment::AbiAligned) - }; - - let skip_dtor = match g { - DropGlueKind::Ty(_) => false, - DropGlueKind::TyContents(_) => true - }; - - let bcx = match t.sty { - ty::TyAdt(def, _) if def.is_box() => { - // Support for Box is built-in as yet and its drop glue is special - // despite having a dummy Drop impl in the library. - assert!(!skip_dtor); - let content_ty = t.boxed_ty(); - let ptr = if !bcx.ccx.shared().type_is_sized(content_ty) { - let llbox = bcx.load(get_dataptr(&bcx, ptr.llval), None); - let info = bcx.load(get_meta(&bcx, ptr.llval), None); - LvalueRef::new_unsized_ty(llbox, info, content_ty, Alignment::AbiAligned) - } else { - LvalueRef::new_sized_ty( - bcx.load(ptr.llval, None), - content_ty, Alignment::AbiAligned) - }; - drop_ty(&bcx, ptr); - trans_exchange_free_ty(&bcx, ptr); - bcx - } - ty::TyDynamic(..) => { - // No support in vtable for distinguishing destroying with - // versus without calling Drop::drop. Assert caller is - // okay with always calling the Drop impl, if any. - assert!(!skip_dtor); - let dtor = bcx.load(ptr.llextra, None); - bcx.call(dtor, &[ptr.llval], None); - bcx - } - ty::TyAdt(def, ..) if def.has_dtor(bcx.tcx()) && !skip_dtor => { - let shallow_drop = def.is_union(); - let tcx = bcx.tcx(); - - // Be sure to put the contents into a scope so we can use an invoke - // instruction to call the user destructor but still call the field - // destructors if the user destructor panics. - // - // FIXME (#14875) panic-in-drop semantics might be unsupported; we - // might well consider changing below to more direct code. - // Issue #23611: schedule cleanup of contents, re-inspecting the - // discriminant (if any) in case of variant swap in drop code. - let contents_scope = if !shallow_drop { - CleanupScope::schedule_drop_adt_contents(&bcx, ptr) - } else { - CleanupScope::noop() - }; - let drop_trait_def_id = tcx.lang_items.drop_trait().unwrap(); - let drop_method = tcx.associated_items(drop_trait_def_id) - .find(|it| it.kind == ty::AssociatedKind::Method) - .unwrap().def_id; - let self_type_substs = tcx.mk_substs_trait(t, &[]); - let drop_instance = monomorphize::resolve( - bcx.ccx.shared(), drop_method, self_type_substs); - let fn_ty = FnType::from_instance(bcx.ccx, &drop_instance, &[]); - let llfn = get_fn(bcx.ccx, drop_instance); - let llret; - let args = &[ptr.llval, ptr.llextra][..1 + ptr.has_extra() as usize]; - if let Some(landing_pad) = contents_scope.landing_pad { - let normal_bcx = bcx.build_sibling_block("normal-return"); - llret = bcx.invoke(llfn, args, normal_bcx.llbb(), landing_pad, None); - bcx = normal_bcx; - } else { - llret = bcx.call(llfn, args, None); - } - fn_ty.apply_attrs_callsite(llret); - contents_scope.trans(&bcx); - bcx - } - ty::TyAdt(def, ..) if def.is_union() => { - bcx - } - _ => { - if bcx.ccx.shared().type_needs_drop(t) { - drop_structural_ty(bcx, ptr) - } else { - bcx - } - } - }; - bcx.ret_void(); -} - pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, info: ValueRef) -> (ValueRef, ValueRef) { debug!("calculate size of DST: {}; with lost info: {:?}", @@ -375,20 +150,8 @@ pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, inf (size, align) } ty::TyDynamic(..) => { - // info points to the vtable and the second entry in the vtable is the - // dynamic size of the object. - let info = bcx.pointercast(info, Type::int(bcx.ccx).ptr_to()); - let size_ptr = bcx.gepi(info, &[1]); - let align_ptr = bcx.gepi(info, &[2]); - - let size = bcx.load(size_ptr, None); - let align = bcx.load(align_ptr, None); - - // Vtable loads are invariant - bcx.set_invariant_load(size); - bcx.set_invariant_load(align); - - (size, align) + // load size/align from vtable + (meth::SIZE.get_usize(bcx, info), meth::ALIGN.get_usize(bcx, info)) } ty::TySlice(_) | ty::TyStr => { let unit_ty = t.sequence_element_type(bcx.tcx()); @@ -403,141 +166,3 @@ pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, inf _ => bug!("Unexpected unsized type, found {}", t) } } - -// Iterates through the elements of a structural type, dropping them. -fn drop_structural_ty<'a, 'tcx>( - cx: Builder<'a, 'tcx>, - mut ptr: LvalueRef<'tcx> -) -> Builder<'a, 'tcx> { - fn iter_variant_fields<'a, 'tcx>( - cx: &'a Builder<'a, 'tcx>, - av: LvalueRef<'tcx>, - adt_def: &'tcx AdtDef, - variant_index: usize, - substs: &'tcx Substs<'tcx> - ) { - let variant = &adt_def.variants[variant_index]; - let tcx = cx.tcx(); - for (i, field) in variant.fields.iter().enumerate() { - let arg = monomorphize::field_ty(tcx, substs, field); - let (field_ptr, align) = av.trans_field_ptr(&cx, i); - drop_ty(&cx, LvalueRef::new_sized_ty(field_ptr, arg, align)); - } - } - - let mut cx = cx; - let t = ptr.ty.to_ty(cx.tcx()); - match t.sty { - ty::TyClosure(def_id, substs) => { - for (i, upvar_ty) in substs.upvar_tys(def_id, cx.tcx()).enumerate() { - let (llupvar, align) = ptr.trans_field_ptr(&cx, i); - drop_ty(&cx, LvalueRef::new_sized_ty(llupvar, upvar_ty, align)); - } - } - ty::TyArray(_, n) => { - let base = get_dataptr(&cx, ptr.llval); - let len = C_uint(cx.ccx, n); - let unit_ty = t.sequence_element_type(cx.tcx()); - cx = tvec::slice_for_each(&cx, base, unit_ty, len, - |bb, vv| drop_ty(bb, LvalueRef::new_sized_ty(vv, unit_ty, ptr.alignment))); - } - ty::TySlice(_) | ty::TyStr => { - let unit_ty = t.sequence_element_type(cx.tcx()); - cx = tvec::slice_for_each(&cx, ptr.llval, unit_ty, ptr.llextra, - |bb, vv| drop_ty(bb, LvalueRef::new_sized_ty(vv, unit_ty, ptr.alignment))); - } - ty::TyTuple(ref args, _) => { - for (i, arg) in args.iter().enumerate() { - let (llfld_a, align) = ptr.trans_field_ptr(&cx, i); - drop_ty(&cx, LvalueRef::new_sized_ty(llfld_a, *arg, align)); - } - } - ty::TyAdt(adt, substs) => match adt.adt_kind() { - AdtKind::Struct => { - for (i, field) in adt.variants[0].fields.iter().enumerate() { - let field_ty = monomorphize::field_ty(cx.tcx(), substs, field); - let (llval, align) = ptr.trans_field_ptr(&cx, i); - let field_ptr = if cx.ccx.shared().type_is_sized(field_ty) { - LvalueRef::new_sized_ty(llval, field_ty, align) - } else { - LvalueRef::new_unsized_ty(llval, ptr.llextra, field_ty, align) - }; - drop_ty(&cx, field_ptr); - } - } - AdtKind::Union => { - bug!("Union in `glue::drop_structural_ty`"); - } - AdtKind::Enum => { - let n_variants = adt.variants.len(); - - // NB: we must hit the discriminant first so that structural - // comparison know not to proceed when the discriminants differ. - - // Obtain a representation of the discriminant sufficient to translate - // destructuring; this may or may not involve the actual discriminant. - let l = cx.ccx.layout_of(t); - match *l { - layout::Univariant { .. } | - layout::UntaggedUnion { .. } => { - if n_variants != 0 { - assert!(n_variants == 1); - ptr.ty = LvalueTy::Downcast { - adt_def: adt, - substs: substs, - variant_index: 0, - }; - iter_variant_fields(&cx, ptr, &adt, 0, substs); - } - } - layout::CEnum { .. } | - layout::General { .. } | - layout::RawNullablePointer { .. } | - layout::StructWrappedNullablePointer { .. } => { - let lldiscrim_a = adt::trans_get_discr( - &cx, t, ptr.llval, ptr.alignment, None, false); - - // Create a fall-through basic block for the "else" case of - // the switch instruction we're about to generate. Note that - // we do **not** use an Unreachable instruction here, even - // though most of the time this basic block will never be hit. - // - // When an enum is dropped it's contents are currently - // overwritten to DTOR_DONE, which means the discriminant - // could have changed value to something not within the actual - // range of the discriminant. Currently this function is only - // used for drop glue so in this case we just return quickly - // from the outer function, and any other use case will only - // call this for an already-valid enum in which case the `ret - // void` will never be hit. - let ret_void_cx = cx.build_sibling_block("enum-iter-ret-void"); - ret_void_cx.ret_void(); - let llswitch = cx.switch(lldiscrim_a, ret_void_cx.llbb(), n_variants); - let next_cx = cx.build_sibling_block("enum-iter-next"); - - for (i, discr) in adt.discriminants(cx.tcx()).enumerate() { - let variant_cx_name = format!("enum-iter-variant-{}", i); - let variant_cx = cx.build_sibling_block(&variant_cx_name); - let case_val = adt::trans_case(&cx, t, Disr::from(discr)); - variant_cx.add_case(llswitch, case_val, variant_cx.llbb()); - ptr.ty = LvalueTy::Downcast { - adt_def: adt, - substs: substs, - variant_index: i, - }; - iter_variant_fields(&variant_cx, ptr, &adt, i, substs); - variant_cx.br(next_cx.llbb()); - } - cx = next_cx; - } - _ => bug!("{} is not an enum.", t), - } - } - }, - - _ => { - cx.sess().unimpl(&format!("type in drop_structural_ty: {}", t)) - } - } - return cx; -} diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 49cc0b5fad401..f3e30ed4839ae 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -113,7 +113,6 @@ mod cabi_x86; mod cabi_x86_64; mod cabi_x86_win64; mod callee; -mod cleanup; mod collector; mod common; mod consts; diff --git a/src/librustc_trans/meth.rs b/src/librustc_trans/meth.rs index 6b823756c8eab..75ab407614050 100644 --- a/src/librustc_trans/meth.rs +++ b/src/librustc_trans/meth.rs @@ -14,28 +14,45 @@ use callee; use common::*; use builder::Builder; use consts; -use glue; use machine; +use monomorphize; use type_::Type; use type_of::*; use value::Value; use rustc::ty; -// drop_glue pointer, size, align. -const VTABLE_OFFSET: usize = 3; - -/// Extracts a method from a trait object's vtable, at the specified index. -pub fn get_virtual_method<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, - llvtable: ValueRef, - vtable_index: usize) -> ValueRef { - // Load the data pointer from the object. - debug!("get_virtual_method(vtable_index={}, llvtable={:?})", - vtable_index, Value(llvtable)); - - let ptr = bcx.load_nonnull(bcx.gepi(llvtable, &[vtable_index + VTABLE_OFFSET]), None); - // Vtable loads are invariant - bcx.set_invariant_load(ptr); - ptr +#[derive(Copy, Clone, Debug)] +pub struct VirtualIndex(usize); + +pub const DESTRUCTOR: VirtualIndex = VirtualIndex(0); +pub const SIZE: VirtualIndex = VirtualIndex(1); +pub const ALIGN: VirtualIndex = VirtualIndex(2); + +impl<'a, 'tcx> VirtualIndex { + pub fn from_index(index: usize) -> Self { + VirtualIndex(index + 3) + } + + pub fn get_fn(self, bcx: &Builder<'a, 'tcx>, llvtable: ValueRef) -> ValueRef { + // Load the data pointer from the object. + debug!("get_fn({:?}, {:?})", Value(llvtable), self); + + let ptr = bcx.load_nonnull(bcx.gepi(llvtable, &[self.0]), None); + // Vtable loads are invariant + bcx.set_invariant_load(ptr); + ptr + } + + pub fn get_usize(self, bcx: &Builder<'a, 'tcx>, llvtable: ValueRef) -> ValueRef { + // Load the data pointer from the object. + debug!("get_int({:?}, {:?})", Value(llvtable), self); + + let llvtable = bcx.pointercast(llvtable, Type::int(bcx.ccx).ptr_to()); + let ptr = bcx.load(bcx.gepi(llvtable, &[self.0]), None); + // Vtable loads are invariant + bcx.set_invariant_load(ptr); + ptr + } } /// Creates a dynamic vtable for the given type and vtable origin. @@ -68,8 +85,7 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let align = align_of(ccx, ty); let mut components: Vec<_> = [ - // Generate a destructor for the vtable. - glue::get_drop_glue(ccx, ty), + callee::get_fn(ccx, monomorphize::resolve_drop_in_place(ccx.shared(), ty)), C_uint(ccx, size), C_uint(ccx, align) ].iter().cloned().collect(); diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 761f6b208e747..226d40948c4dc 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -19,13 +19,13 @@ use base::{self, Lifetime}; use callee; use builder::Builder; use common::{self, Funclet}; -use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef}; +use common::{C_bool, C_str_slice, C_struct, C_u32, C_uint, C_undef}; use consts; use machine::llalign_of_min; use meth; use monomorphize; +use tvec; use type_of::{self, align_of}; -use glue; use type_::Type; use rustc_data_structures::indexed_vec::IndexVec; @@ -209,21 +209,49 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { mir::TerminatorKind::Drop { ref location, target, unwind } => { let ty = location.ty(&self.mir, bcx.tcx()).to_ty(bcx.tcx()); let ty = self.monomorphize(&ty); + let drop_fn = monomorphize::resolve_drop_in_place(bcx.ccx.shared(), ty); - // Double check for necessity to drop - if !bcx.ccx.shared().type_needs_drop(ty) { + if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { + // we don't actually need to drop anything. funclet_br(self, bcx, target); - return; + return } - let mut lvalue = self.trans_lvalue(&bcx, location); - let drop_fn = glue::get_drop_glue(bcx.ccx, ty); - let drop_ty = glue::get_drop_glue_type(bcx.ccx.shared(), ty); - if bcx.ccx.shared().type_is_sized(ty) && drop_ty != ty { - lvalue.llval = bcx.pointercast( - lvalue.llval, type_of::type_of(bcx.ccx, drop_ty).ptr_to()); - } - let args = &[lvalue.llval, lvalue.llextra][..1 + lvalue.has_extra() as usize]; + let lvalue = self.trans_lvalue(&bcx, location); + let (drop_fn, need_extra) = match ty.sty { + ty::TyDynamic(..) => (meth::DESTRUCTOR.get_fn(&bcx, lvalue.llextra), + false), + ty::TyArray(ety, _) | ty::TySlice(ety) => { + // FIXME: handle panics + let drop_fn = monomorphize::resolve_drop_in_place( + bcx.ccx.shared(), ety); + let drop_fn = callee::get_fn(bcx.ccx, drop_fn); + let bcx = tvec::slice_for_each( + &bcx, + lvalue.project_index(&bcx, C_uint(bcx.ccx, 0u64)), + ety, + lvalue.len(bcx.ccx), + |bcx, llval, loop_bb| { + self.set_debug_loc(&bcx, terminator.source_info); + if let Some(unwind) = unwind { + bcx.invoke( + drop_fn, + &[llval], + loop_bb, + llblock(self, unwind), + cleanup_bundle + ); + } else { + bcx.call(drop_fn, &[llval], cleanup_bundle); + bcx.br(loop_bb); + } + }); + funclet_br(self, bcx, target); + return + } + _ => (callee::get_fn(bcx.ccx, drop_fn), lvalue.has_extra()) + }; + let args = &[lvalue.llval, lvalue.llextra][..1 + need_extra as usize]; if let Some(unwind) = unwind { bcx.invoke( drop_fn, @@ -417,23 +445,14 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { Some(ty::InstanceDef::Virtual(..)) => { FnType::new_vtable(bcx.ccx, sig, &extra_args) } - _ => FnType::new(bcx.ccx, sig, &extra_args) - }; - - if intrinsic == Some("drop_in_place") { - let &(_, target) = destination.as_ref().unwrap(); - let ty = instance.unwrap().substs.type_at(0); - - // Double check for necessity to drop - if !bcx.ccx.shared().type_needs_drop(ty) { + Some(ty::InstanceDef::DropGlue(_, None)) => { + // empty drop glue - a nop. + let &(_, target) = destination.as_ref().unwrap(); funclet_br(self, bcx, target); return; } - - let drop_fn = glue::get_drop_glue(bcx.ccx, ty); - let llty = fn_ty.llvm_type(bcx.ccx).ptr_to(); - llfn = Some(bcx.pointercast(drop_fn, llty)); - } + _ => FnType::new(bcx.ccx, sig, &extra_args) + }; // The arguments we'll be passing. Plus one to account for outptr, if used. let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize; @@ -588,7 +607,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let (ptr, meta) = (a, b); if *next_idx == 0 { if let Some(ty::InstanceDef::Virtual(_, idx)) = *def { - let llmeth = meth::get_virtual_method(bcx, meta, idx); + let llmeth = meth::VirtualIndex::from_index(idx).get_fn(bcx, meta); let llty = fn_ty.llvm_type(bcx.ccx).ptr_to(); *llfn = Some(bcx.pointercast(llmeth, llty)); } @@ -756,14 +775,18 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { return block; } + let block = self.blocks[target_bb]; + let landing_pad = self.landing_pad_uncached(block); + self.landing_pads[target_bb] = Some(landing_pad); + landing_pad + } + + fn landing_pad_uncached(&mut self, target_bb: BasicBlockRef) -> BasicBlockRef { if base::wants_msvc_seh(self.ccx.sess()) { - return self.blocks[target_bb]; + return target_bb; } - let target = self.get_builder(target_bb); - let bcx = self.new_block("cleanup"); - self.landing_pads[target_bb] = Some(bcx.llbb()); let ccx = bcx.ccx; let llpersonality = self.ccx.eh_personality(); @@ -772,7 +795,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { bcx.set_cleanup(llretval); let slot = self.get_personality_slot(&bcx); bcx.store(llretval, slot, None); - bcx.br(target.llbb()); + bcx.br(target_bb); bcx.llbb() } diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs index 49e1e3855571b..dd8c1d0e1f031 100644 --- a/src/librustc_trans/mir/lvalue.rs +++ b/src/librustc_trans/mir/lvalue.rs @@ -27,7 +27,6 @@ use std::ptr; use std::ops; use super::{MirContext, LocalRef}; -use super::operand::OperandValue; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Alignment { @@ -95,16 +94,6 @@ impl<'a, 'tcx> LvalueRef<'tcx> { LvalueRef::new_sized(llval, LvalueTy::from_ty(ty), alignment) } - pub fn new_unsized_ty(llval: ValueRef, llextra: ValueRef, ty: Ty<'tcx>, alignment: Alignment) - -> LvalueRef<'tcx> { - LvalueRef { - llval: llval, - llextra: llextra, - ty: LvalueTy::from_ty(ty), - alignment: alignment, - } - } - pub fn alloca(bcx: &Builder<'a, 'tcx>, ty: Ty<'tcx>, name: &str) -> LvalueRef<'tcx> { debug!("alloca({:?}: {:?})", name, ty); let tmp = bcx.alloca(type_of::type_of(bcx.ccx, ty), name); @@ -279,6 +268,16 @@ impl<'a, 'tcx> LvalueRef<'tcx> { _ => bug!("element access in type without elements: {} represented as {:#?}", t, l) } } + + pub fn project_index(&self, bcx: &Builder<'a, 'tcx>, llindex: ValueRef) -> ValueRef { + if let ty::TySlice(_) = self.ty.to_ty(bcx.tcx()).sty { + // Slices already point to the array element type. + bcx.inbounds_gep(self.llval, &[llindex]) + } else { + let zero = common::C_uint(bcx.ccx, 0u64); + bcx.inbounds_gep(self.llval, &[zero, llindex]) + } + } } impl<'a, 'tcx> MirContext<'a, 'tcx> { @@ -314,21 +313,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { elem: mir::ProjectionElem::Deref }) => { // Load the pointer from its location. - let ptr = self.trans_consume(bcx, base); - let projected_ty = LvalueTy::from_ty(ptr.ty) - .projection_ty(tcx, &mir::ProjectionElem::Deref); - let projected_ty = self.monomorphize(&projected_ty); - let (llptr, llextra) = match ptr.val { - OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()), - OperandValue::Pair(llptr, llextra) => (llptr, llextra), - OperandValue::Ref(..) => bug!("Deref of by-Ref type {:?}", ptr.ty) - }; - LvalueRef { - llval: llptr, - llextra: llextra, - ty: projected_ty, - alignment: Alignment::AbiAligned, - } + self.trans_consume(bcx, base).deref() } mir::Lvalue::Projection(ref projection) => { let tr_base = self.trans_lvalue(bcx, &projection.base); @@ -336,17 +321,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let projected_ty = self.monomorphize(&projected_ty); let align = tr_base.alignment; - let project_index = |llindex| { - let element = if let ty::TySlice(_) = tr_base.ty.to_ty(tcx).sty { - // Slices already point to the array element type. - bcx.inbounds_gep(tr_base.llval, &[llindex]) - } else { - let zero = common::C_uint(bcx.ccx, 0u64); - bcx.inbounds_gep(tr_base.llval, &[zero, llindex]) - }; - (element, align) - }; - let ((llprojected, align), llextra) = match projection.elem { mir::ProjectionElem::Deref => bug!(), mir::ProjectionElem::Field(ref field, _) => { @@ -359,13 +333,14 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } mir::ProjectionElem::Index(ref index) => { let index = self.trans_operand(bcx, index); - (project_index(self.prepare_index(bcx, index.immediate())), ptr::null_mut()) + let llindex = self.prepare_index(bcx, index.immediate()); + ((tr_base.project_index(bcx, llindex), align), ptr::null_mut()) } mir::ProjectionElem::ConstantIndex { offset, from_end: false, min_length: _ } => { let lloffset = C_uint(bcx.ccx, offset); - (project_index(lloffset), ptr::null_mut()) + ((tr_base.project_index(bcx, lloffset), align), ptr::null_mut()) } mir::ProjectionElem::ConstantIndex { offset, from_end: true, @@ -373,11 +348,10 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let lloffset = C_uint(bcx.ccx, offset); let lllen = tr_base.len(bcx.ccx); let llindex = bcx.sub(lllen, lloffset); - (project_index(llindex), ptr::null_mut()) + ((tr_base.project_index(bcx, llindex), align), ptr::null_mut()) } mir::ProjectionElem::Subslice { from, to } => { - let llindex = C_uint(bcx.ccx, from); - let (llbase, align) = project_index(llindex); + let llbase = tr_base.project_index(bcx, C_uint(bcx.ccx, from)); let base_ty = tr_base.ty.to_ty(bcx.tcx()); match base_ty.sty { diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index 3f29545ecf45a..da24c03fdc2a0 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -9,9 +9,10 @@ // except according to those terms. use llvm::ValueRef; -use rustc::ty::Ty; +use rustc::ty::{self, Ty}; use rustc::ty::layout::Layout; use rustc::mir; +use rustc::mir::tcx::LvalueTy; use rustc_data_structures::indexed_vec::Idx; use base; @@ -22,9 +23,10 @@ use type_of; use type_::Type; use std::fmt; +use std::ptr; use super::{MirContext, LocalRef}; -use super::lvalue::Alignment; +use super::lvalue::{Alignment, LvalueRef}; /// The representation of a Rust value. The enum variant is in fact /// uniquely determined by the value's type, but is kept as a @@ -86,6 +88,22 @@ impl<'a, 'tcx> OperandRef<'tcx> { } } + pub fn deref(self) -> LvalueRef<'tcx> { + let projected_ty = self.ty.builtin_deref(true, ty::NoPreference) + .unwrap().ty; + let (llptr, llextra) = match self.val { + OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()), + OperandValue::Pair(llptr, llextra) => (llptr, llextra), + OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self) + }; + LvalueRef { + llval: llptr, + llextra: llextra, + ty: LvalueTy::from_ty(projected_ty), + alignment: Alignment::AbiAligned, + } + } + /// If this operand is a Pair, we return an /// Immediate aggregate with the two values. pub fn pack_if_pair(mut self, bcx: &Builder<'a, 'tcx>) -> OperandRef<'tcx> { @@ -236,7 +254,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } mir::Operand::Constant(ref constant) => { - let val = self.trans_constant(bcx, constant); + let val = self.trans_constant(&bcx, constant); let operand = val.to_operand(bcx.ccx); if let OperandValue::Ref(ptr, align) = operand.val { // If this is a OperandValue::Ref to an immediate constant, load it. diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 178347369c915..d487aa6cd5be6 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -98,8 +98,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let size = count.as_u64(bcx.tcx().sess.target.uint_type); let size = C_uint(bcx.ccx, size); let base = base::get_dataptr(&bcx, dest.llval); - tvec::slice_for_each(&bcx, base, tr_elem.ty, size, |bcx, llslot| { + tvec::slice_for_each(&bcx, base, tr_elem.ty, size, |bcx, llslot, loop_bb| { self.store_operand(bcx, llslot, dest.alignment.to_align(), tr_elem); + bcx.br(loop_bb); }) } @@ -459,7 +460,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { }; (bcx, operand) } - mir::Rvalue::Use(ref operand) => { let operand = self.trans_operand(&bcx, operand); (bcx, operand) @@ -662,7 +662,7 @@ pub fn rvalue_creates_operand(rvalue: &mir::Rvalue) -> bool { mir::Rvalue::UnaryOp(..) | mir::Rvalue::Discriminant(..) | mir::Rvalue::Box(..) | - mir::Rvalue::Use(..) => + mir::Rvalue::Use(..) => // (*) true, mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) => diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs index 0d8aa0f4bda0e..fcf6937d4b6d5 100644 --- a/src/librustc_trans/monomorphize.rs +++ b/src/librustc_trans/monomorphize.rs @@ -10,9 +10,11 @@ use abi::Abi; use common::*; +use glue; use rustc::hir::def_id::DefId; use rustc::infer::TransNormalize; +use rustc::middle::lang_items::DropInPlaceFnLangItem; use rustc::traits::{self, SelectionContext, Reveal}; use rustc::ty::adjustment::CustomCoerceUnsized; use rustc::ty::fold::{TypeFolder, TypeFoldable}; @@ -242,8 +244,19 @@ pub fn resolve<'a, 'tcx>( ty::InstanceDef::Intrinsic(def_id) } _ => { - debug!(" => free item"); - ty::InstanceDef::Item(def_id) + if Some(def_id) == scx.tcx().lang_items.drop_in_place_fn() { + let ty = substs.type_at(0); + if glue::needs_drop_glue(scx, ty) { + debug!(" => nontrivial drop glue"); + ty::InstanceDef::DropGlue(def_id, Some(ty)) + } else { + debug!(" => trivial drop glue"); + ty::InstanceDef::DropGlue(def_id, None) + } + } else { + debug!(" => free item"); + ty::InstanceDef::Item(def_id) + } } }; Instance { def, substs } @@ -253,6 +266,16 @@ pub fn resolve<'a, 'tcx>( result } +pub fn resolve_drop_in_place<'a, 'tcx>( + scx: &SharedCrateContext<'a, 'tcx>, + ty: Ty<'tcx>) + -> ty::Instance<'tcx> +{ + let def_id = scx.tcx().require_lang_item(DropInPlaceFnLangItem); + let substs = scx.tcx().intern_substs(&[Kind::from(ty)]); + resolve(scx, def_id, substs) +} + pub fn custom_coerce_unsize_info<'scx, 'tcx>(scx: &SharedCrateContext<'scx, 'tcx>, source_ty: Ty<'tcx>, target_ty: Ty<'tcx>) diff --git a/src/librustc_trans/partitioning.rs b/src/librustc_trans/partitioning.rs index d0cf32508d44c..90ce40cfbcf8f 100644 --- a/src/librustc_trans/partitioning.rs +++ b/src/librustc_trans/partitioning.rs @@ -194,7 +194,6 @@ impl<'tcx> CodegenUnit<'tcx> { TransItem::Static(node_id) => { exported_symbols.contains(&node_id) } - TransItem::DropGlue(..) => false, }; exported.hash(&mut state); } @@ -245,7 +244,6 @@ impl<'tcx> CodegenUnit<'tcx> { tcx.hir.as_local_node_id(instance.def_id()) } TransItem::Static(node_id) => Some(node_id), - TransItem::DropGlue(_) => None, } } } @@ -341,7 +339,6 @@ fn place_root_translation_items<'a, 'tcx, I>(scx: &SharedCrateContext<'a, 'tcx>, match trans_item { TransItem::Fn(..) | TransItem::Static(..) => llvm::ExternalLinkage, - TransItem::DropGlue(..) => unreachable!(), } } }; @@ -461,6 +458,7 @@ fn characteristic_def_id_of_trans_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 't ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Intrinsic(..) | + ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Virtual(..) => return None }; @@ -485,7 +483,6 @@ fn characteristic_def_id_of_trans_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 't Some(def_id) } - TransItem::DropGlue(dg) => characteristic_def_id_of_type(dg.ty()), TransItem::Static(node_id) => Some(tcx.hir.local_def_id(node_id)), } } diff --git a/src/librustc_trans/symbol_map.rs b/src/librustc_trans/symbol_map.rs index cd285bfaa6010..1b48e131b720a 100644 --- a/src/librustc_trans/symbol_map.rs +++ b/src/librustc_trans/symbol_map.rs @@ -100,7 +100,6 @@ impl<'tcx> SymbolMap<'tcx> { tcx.hir.as_local_node_id(def.def_id()) } TransItem::Static(node_id) => Some(node_id), - TransItem::DropGlue(_) => None, }.map(|node_id| { tcx.hir.span(node_id) }) diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs index 13af081e0b698..410e3f30be731 100644 --- a/src/librustc_trans/trans_item.rs +++ b/src/librustc_trans/trans_item.rs @@ -20,28 +20,23 @@ use consts; use context::{CrateContext, SharedCrateContext}; use common; use declare; -use glue::DropGlueKind; use llvm; use monomorphize::Instance; use rustc::dep_graph::DepNode; use rustc::hir; use rustc::hir::def_id::DefId; -use rustc::hir::map::definitions::DefPathData; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::subst::Substs; use rustc_const_eval::fatal_const_eval_err; use syntax::ast::{self, NodeId}; use syntax::attr; use type_of; -use glue; -use abi::{Abi, FnType}; use back::symbol_names; use std::fmt::Write; use std::iter; #[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] pub enum TransItem<'tcx> { - DropGlue(DropGlueKind<'tcx>), Fn(Instance<'tcx>), Static(NodeId) } @@ -100,9 +95,6 @@ impl<'a, 'tcx> TransItem<'tcx> { base::trans_instance(&ccx, instance); } - TransItem::DropGlue(dg) => { - glue::implement_drop_glue(&ccx, dg); - } } debug!("END IMPLEMENTING '{} ({})' in cgu {}", @@ -131,9 +123,6 @@ impl<'a, 'tcx> TransItem<'tcx> { TransItem::Fn(instance) => { TransItem::predefine_fn(ccx, instance, linkage, &symbol_name); } - TransItem::DropGlue(dg) => { - TransItem::predefine_drop_glue(ccx, dg, linkage, &symbol_name); - } } debug!("END PREDEFINING '{} ({})' in cgu {}", @@ -180,52 +169,14 @@ impl<'a, 'tcx> TransItem<'tcx> { } debug!("predefine_fn: mono_ty = {:?} instance = {:?}", mono_ty, instance); - match ccx.tcx().def_key(instance.def_id()).disambiguated_data.data { - DefPathData::StructCtor | - DefPathData::EnumVariant(..) | - DefPathData::ClosureExpr => { - attributes::inline(lldecl, attributes::InlineAttr::Hint); - } - _ => {} + if common::is_inline_instance(ccx.tcx(), &instance) { + attributes::inline(lldecl, attributes::InlineAttr::Hint); } - attributes::from_fn_attrs(ccx, &attrs, lldecl); ccx.instances().borrow_mut().insert(instance, lldecl); } - fn predefine_drop_glue(ccx: &CrateContext<'a, 'tcx>, - dg: glue::DropGlueKind<'tcx>, - linkage: llvm::Linkage, - symbol_name: &str) { - let tcx = ccx.tcx(); - assert_eq!(dg.ty(), glue::get_drop_glue_type(ccx.shared(), dg.ty())); - let t = dg.ty(); - - let sig = tcx.mk_fn_sig( - iter::once(tcx.mk_mut_ptr(t)), - tcx.mk_nil(), - false, - hir::Unsafety::Normal, - Abi::Rust - ); - - debug!("predefine_drop_glue: sig={}", sig); - - let fn_ty = FnType::new(ccx, sig, &[]); - let llfnty = fn_ty.llvm_type(ccx); - - assert!(declare::get_defined_value(ccx, symbol_name).is_none()); - let llfn = declare::declare_cfn(ccx, symbol_name, llfnty); - unsafe { llvm::LLVMRustSetLinkage(llfn, linkage) }; - if linkage == llvm::Linkage::LinkOnceODRLinkage || - linkage == llvm::Linkage::WeakODRLinkage { - llvm::SetUniqueComdat(ccx.llmod(), llfn); - } - attributes::set_frame_pointer_elimination(ccx, llfn); - ccx.drop_glues().borrow_mut().insert(dg, (llfn, fn_ty)); - } - pub fn compute_symbol_name(&self, scx: &SharedCrateContext<'a, 'tcx>) -> String { match *self { @@ -234,13 +185,6 @@ impl<'a, 'tcx> TransItem<'tcx> { let def_id = scx.tcx().hir.local_def_id(node_id); symbol_names::symbol_name(Instance::mono(scx.tcx(), def_id), scx) } - TransItem::DropGlue(dg) => { - let prefix = match dg { - DropGlueKind::Ty(_) => "drop", - DropGlueKind::TyContents(_) => "drop_contents", - }; - symbol_names::exported_name_from_type_and_prefix(scx, dg.ty(), prefix) - } } } @@ -257,7 +201,6 @@ impl<'a, 'tcx> TransItem<'tcx> { InstantiationMode::GloballyShared } } - TransItem::DropGlue(..) => InstantiationMode::LocalCopy, TransItem::Static(..) => InstantiationMode::GloballyShared, } } @@ -267,7 +210,6 @@ impl<'a, 'tcx> TransItem<'tcx> { TransItem::Fn(ref instance) => { instance.substs.types().next().is_some() } - TransItem::DropGlue(..) | TransItem::Static(..) => false, } } @@ -276,7 +218,6 @@ impl<'a, 'tcx> TransItem<'tcx> { let def_id = match *self { TransItem::Fn(ref instance) => instance.def_id(), TransItem::Static(node_id) => tcx.hir.local_def_id(node_id), - TransItem::DropGlue(..) => return None, }; let attributes = tcx.get_attrs(def_id); @@ -300,16 +241,6 @@ impl<'a, 'tcx> TransItem<'tcx> { let hir_map = &tcx.hir; return match *self { - TransItem::DropGlue(dg) => { - let mut s = String::with_capacity(32); - match dg { - DropGlueKind::Ty(_) => s.push_str("drop-glue "), - DropGlueKind::TyContents(_) => s.push_str("drop-glue-contents "), - }; - let printer = DefPathBasedNames::new(tcx, false, false); - printer.push_type_name(dg.ty(), &mut s); - s - } TransItem::Fn(instance) => { to_string_internal(tcx, "fn ", instance) }, @@ -334,13 +265,6 @@ impl<'a, 'tcx> TransItem<'tcx> { pub fn to_raw_string(&self) -> String { match *self { - TransItem::DropGlue(dg) => { - let prefix = match dg { - DropGlueKind::Ty(_) => "Ty", - DropGlueKind::TyContents(_) => "TyContents", - }; - format!("DropGlue({}: {})", prefix, dg.ty() as *const _ as usize) - } TransItem::Fn(instance) => { format!("Fn({:?}, {})", instance.def, diff --git a/src/librustc_trans/tvec.rs b/src/librustc_trans/tvec.rs index cbcbb02bdc890..4216a73a8dd85 100644 --- a/src/librustc_trans/tvec.rs +++ b/src/librustc_trans/tvec.rs @@ -10,7 +10,7 @@ use llvm; use builder::Builder; -use llvm::ValueRef; +use llvm::{BasicBlockRef, ValueRef}; use common::*; use rustc::ty::Ty; @@ -20,7 +20,7 @@ pub fn slice_for_each<'a, 'tcx, F>( unit_ty: Ty<'tcx>, len: ValueRef, f: F -) -> Builder<'a, 'tcx> where F: FnOnce(&Builder<'a, 'tcx>, ValueRef) { +) -> Builder<'a, 'tcx> where F: FnOnce(&Builder<'a, 'tcx>, ValueRef, BasicBlockRef) { // Special-case vectors with elements of size 0 so they don't go out of bounds (#9890) let zst = type_is_zero_size(bcx.ccx, unit_ty); let add = |bcx: &Builder, a, b| if zst { @@ -46,9 +46,8 @@ pub fn slice_for_each<'a, 'tcx, F>( let keep_going = header_bcx.icmp(llvm::IntNE, current, end); header_bcx.cond_br(keep_going, body_bcx.llbb(), next_bcx.llbb()); - f(&body_bcx, if zst { data_ptr } else { current }); let next = add(&body_bcx, current, C_uint(bcx.ccx, 1usize)); + f(&body_bcx, if zst { data_ptr } else { current }, header_bcx.llbb()); header_bcx.add_incoming_to_phi(current, next, body_bcx.llbb()); - body_bcx.br(header_bcx.llbb()); next_bcx } diff --git a/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs b/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs index ada1234b852a1..eb4f9e8e28e2d 100644 --- a/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs +++ b/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs @@ -30,5 +30,3 @@ fn main() // 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/item-collection/drop_in_place_intrinsic.rs b/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs index db940b680473a..d8e6028b799fb 100644 --- a/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs +++ b/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs @@ -11,8 +11,7 @@ // ignore-tidy-linelength // compile-flags:-Zprint-trans-items=eager -//~ TRANS_ITEM drop-glue drop_in_place_intrinsic::StructWithDtor[0] -//~ TRANS_ITEM drop-glue-contents drop_in_place_intrinsic::StructWithDtor[0] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ drop_in_place_intrinsic.cgu-0[Internal] struct StructWithDtor(u32); impl Drop for StructWithDtor { @@ -23,7 +22,7 @@ impl Drop for StructWithDtor { //~ TRANS_ITEM fn drop_in_place_intrinsic::main[0] fn main() { - //~ TRANS_ITEM drop-glue [drop_in_place_intrinsic::StructWithDtor[0]; 2] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]; 2]> @@ drop_in_place_intrinsic.cgu-0[Internal] let x = [StructWithDtor(0), StructWithDtor(1)]; drop_slice_in_place(&x); @@ -35,7 +34,7 @@ fn drop_slice_in_place(x: &[StructWithDtor]) { // This is the interesting thing in this test case: Normally we would // not have drop-glue for the unsized [StructWithDtor]. This has to be // generated though when the drop_in_place() intrinsic is used. - //~ TRANS_ITEM drop-glue [drop_in_place_intrinsic::StructWithDtor[0]] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]]> @@ drop_in_place_intrinsic.cgu-0[Internal] ::std::ptr::drop_in_place(x as *const _ as *mut [StructWithDtor]); } } diff --git a/src/test/codegen-units/item-collection/function-as-argument.rs b/src/test/codegen-units/item-collection/function-as-argument.rs index 51df38cabef30..c4aed7465bcb0 100644 --- a/src/test/codegen-units/item-collection/function-as-argument.rs +++ b/src/test/codegen-units/item-collection/function-as-argument.rs @@ -44,5 +44,3 @@ fn main() { //~ 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/item-collection/generic-drop-glue.rs b/src/test/codegen-units/item-collection/generic-drop-glue.rs index 6da8154540574..06e02b100152e 100644 --- a/src/test/codegen-units/item-collection/generic-drop-glue.rs +++ b/src/test/codegen-units/item-collection/generic-drop-glue.rs @@ -45,8 +45,7 @@ enum EnumNoDrop { struct NonGenericNoDrop(i32); struct NonGenericWithDrop(i32); -//~ TRANS_ITEM drop-glue generic_drop_glue::NonGenericWithDrop[0] -//~ TRANS_ITEM drop-glue-contents generic_drop_glue::NonGenericWithDrop[0] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ generic_drop_glue.cgu-0[Internal] impl Drop for NonGenericWithDrop { //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[2]::drop[0] @@ -55,13 +54,11 @@ impl Drop for NonGenericWithDrop { //~ TRANS_ITEM fn generic_drop_glue::main[0] fn main() { - //~ TRANS_ITEM drop-glue generic_drop_glue::StructWithDrop[0] - //~ TRANS_ITEM drop-glue-contents generic_drop_glue::StructWithDrop[0] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue.cgu-0[Internal] //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[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 drop-glue-contents generic_drop_glue::StructWithDrop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]> + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue.cgu-0[Internal] //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]> let _ = StructWithDrop { x: "&str", y: NonGenericNoDrop(0) }.y; @@ -70,19 +67,17 @@ fn main() { // 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] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue.cgu-0[Internal] let _ = StructNoDrop { x: NonGenericWithDrop(0), y: 0f64 }.y; - //~ TRANS_ITEM drop-glue generic_drop_glue::EnumWithDrop[0] - //~ TRANS_ITEM drop-glue-contents generic_drop_glue::EnumWithDrop[0] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue.cgu-0[Internal] //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[1]::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 drop-glue-contents generic_drop_glue::EnumWithDrop[0] + //~TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue.cgu-0[Internal] //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0] let _ = match EnumWithDrop::B::(1.0) { EnumWithDrop::A(x) => x, @@ -99,5 +94,3 @@ fn main() { EnumNoDrop::B(x) => x as f64 }; } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/instantiation-through-vtable.rs b/src/test/codegen-units/item-collection/instantiation-through-vtable.rs index ad466671cf79b..9c6bdb6624eae 100644 --- a/src/test/codegen-units/item-collection/instantiation-through-vtable.rs +++ b/src/test/codegen-units/item-collection/instantiation-through-vtable.rs @@ -31,12 +31,13 @@ impl Trait for Struct { fn main() { let s1 = Struct { _a: 0u32 }; - //~ TRANS_ITEM drop-glue i8 + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable.cgu-0[Internal] //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0] //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0] let _ = &s1 as &Trait; let s1 = Struct { _a: 0u64 }; + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable.cgu-0[Internal] //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0] //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0] let _ = &s1 as &Trait; diff --git a/src/test/codegen-units/item-collection/items-within-generic-items.rs b/src/test/codegen-units/item-collection/items-within-generic-items.rs index a2dcd81b6750c..75d842d3c0bfc 100644 --- a/src/test/codegen-units/item-collection/items-within-generic-items.rs +++ b/src/test/codegen-units/item-collection/items-within-generic-items.rs @@ -40,5 +40,3 @@ fn main() { //~ 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/item-collection/non-generic-drop-glue.rs b/src/test/codegen-units/item-collection/non-generic-drop-glue.rs index 91be81a0b8996..5f70ff396ddd5 100644 --- a/src/test/codegen-units/item-collection/non-generic-drop-glue.rs +++ b/src/test/codegen-units/item-collection/non-generic-drop-glue.rs @@ -13,8 +13,7 @@ #![deny(dead_code)] -//~ TRANS_ITEM drop-glue non_generic_drop_glue::StructWithDrop[0] -//~ TRANS_ITEM drop-glue-contents non_generic_drop_glue::StructWithDrop[0] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue.cgu-0[Internal] struct StructWithDrop { x: i32 } @@ -28,8 +27,7 @@ struct StructNoDrop { x: i32 } -//~ TRANS_ITEM drop-glue non_generic_drop_glue::EnumWithDrop[0] -//~ TRANS_ITEM drop-glue-contents non_generic_drop_glue::EnumWithDrop[0] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue.cgu-0[Internal] enum EnumWithDrop { A(i32) } @@ -54,5 +52,3 @@ fn main() { EnumNoDrop::A(x) => x }; } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/non-generic-functions.rs b/src/test/codegen-units/item-collection/non-generic-functions.rs index 4e2a7c8508468..26f9eb11876cb 100644 --- a/src/test/codegen-units/item-collection/non-generic-functions.rs +++ b/src/test/codegen-units/item-collection/non-generic-functions.rs @@ -77,5 +77,3 @@ fn main() { let x = Struct { _x: 0 }; x.bar(); } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/overloaded-operators.rs b/src/test/codegen-units/item-collection/overloaded-operators.rs index 0295311334b6b..05848a727e951 100644 --- a/src/test/codegen-units/item-collection/overloaded-operators.rs +++ b/src/test/codegen-units/item-collection/overloaded-operators.rs @@ -68,5 +68,3 @@ impl Deref for Equatable { &self.0 } } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/static-init.rs b/src/test/codegen-units/item-collection/static-init.rs index 41c0f46f80bfb..3c9dcf32e0c78 100644 --- a/src/test/codegen-units/item-collection/static-init.rs +++ b/src/test/codegen-units/item-collection/static-init.rs @@ -20,4 +20,3 @@ pub fn foo() { } fn main() { } //~ TRANS_ITEM fn static_init::main[0] -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/statics-and-consts.rs b/src/test/codegen-units/item-collection/statics-and-consts.rs index 7c8b2b117ef7c..89bc620b7c552 100644 --- a/src/test/codegen-units/item-collection/statics-and-consts.rs +++ b/src/test/codegen-units/item-collection/statics-and-consts.rs @@ -60,5 +60,3 @@ fn main() { //~ 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/item-collection/trait-implementations.rs b/src/test/codegen-units/item-collection/trait-implementations.rs index 2eb2212f0cacd..e8a7d8f25b22c 100644 --- a/src/test/codegen-units/item-collection/trait-implementations.rs +++ b/src/test/codegen-units/item-collection/trait-implementations.rs @@ -78,5 +78,3 @@ fn main() { //~ TRANS_ITEM fn trait_implementations::{{impl}}[3]::bar[0]<&str, &str> 0f32.bar("&str", "&str"); } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/trait-method-as-argument.rs b/src/test/codegen-units/item-collection/trait-method-as-argument.rs index f7afd3f0891e3..f095b637a84e4 100644 --- a/src/test/codegen-units/item-collection/trait-method-as-argument.rs +++ b/src/test/codegen-units/item-collection/trait-method-as-argument.rs @@ -64,5 +64,3 @@ fn main() { //~ TRANS_ITEM fn core::ops[0]::FnMut[0]::call_mut[0] u32, (u32)> take_foo_mut(Trait::foo, 'c'); } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/trait-method-default-impl.rs b/src/test/codegen-units/item-collection/trait-method-default-impl.rs index 47892781902ea..5b24a219f354b 100644 --- a/src/test/codegen-units/item-collection/trait-method-default-impl.rs +++ b/src/test/codegen-units/item-collection/trait-method-default-impl.rs @@ -66,5 +66,3 @@ fn main() { //~ 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/item-collection/transitive-drop-glue.rs b/src/test/codegen-units/item-collection/transitive-drop-glue.rs index 81a7059fe209f..e41cb34eec6ab 100644 --- a/src/test/codegen-units/item-collection/transitive-drop-glue.rs +++ b/src/test/codegen-units/item-collection/transitive-drop-glue.rs @@ -13,12 +13,11 @@ #![deny(dead_code)] -//~ TRANS_ITEM drop-glue transitive_drop_glue::Root[0] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue.cgu-0[Internal] struct Root(Intermediate); -//~ TRANS_ITEM drop-glue transitive_drop_glue::Intermediate[0] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue.cgu-0[Internal] struct Intermediate(Leaf); -//~ TRANS_ITEM drop-glue transitive_drop_glue::Leaf[0] -//~ TRANS_ITEM drop-glue-contents transitive_drop_glue::Leaf[0] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue.cgu-0[Internal] struct Leaf; impl Drop for Leaf { @@ -39,17 +38,15 @@ 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 drop-glue-contents transitive_drop_glue::LeafGen[0] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[1]::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 drop-glue-contents transitive_drop_glue::LeafGen[0] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0] let _ = RootGen(IntermediateGen(LeafGen(0i16))); } diff --git a/src/test/codegen-units/item-collection/tuple-drop-glue.rs b/src/test/codegen-units/item-collection/tuple-drop-glue.rs index ef4bc1dca594c..39043cf87cbec 100644 --- a/src/test/codegen-units/item-collection/tuple-drop-glue.rs +++ b/src/test/codegen-units/item-collection/tuple-drop-glue.rs @@ -13,8 +13,7 @@ #![deny(dead_code)] -//~ TRANS_ITEM drop-glue tuple_drop_glue::Dropped[0] -//~ TRANS_ITEM drop-glue-contents tuple_drop_glue::Dropped[0] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ tuple_drop_glue.cgu-0[Internal] struct Dropped; impl Drop for Dropped { @@ -24,10 +23,10 @@ impl Drop for Dropped { //~ TRANS_ITEM fn tuple_drop_glue::main[0] fn main() { - //~ TRANS_ITEM drop-glue (u32, tuple_drop_glue::Dropped[0]) + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, tuple_drop_glue::Dropped[0])> @@ tuple_drop_glue.cgu-0[Internal] 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) + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(i16, (tuple_drop_glue::Dropped[0], bool))> @@ tuple_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(tuple_drop_glue::Dropped[0], bool)> @@ tuple_drop_glue.cgu-0[Internal] let x = (0i16, (Dropped, true)); } diff --git a/src/test/codegen-units/item-collection/unsizing.rs b/src/test/codegen-units/item-collection/unsizing.rs index cd4cc258f7a68..de7613741b27b 100644 --- a/src/test/codegen-units/item-collection/unsizing.rs +++ b/src/test/codegen-units/item-collection/unsizing.rs @@ -57,11 +57,13 @@ fn main() { // simple case let bool_sized = &true; - //~ TRANS_ITEM drop-glue i8 + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing.cgu-0[Internal] //~ TRANS_ITEM fn unsizing::{{impl}}[0]::foo[0] let _bool_unsized = bool_sized as &Trait; - let char_sized = &true; + let char_sized = &'a'; + + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing.cgu-0[Internal] //~ TRANS_ITEM fn unsizing::{{impl}}[1]::foo[0] let _char_unsized = char_sized as &Trait; @@ -71,11 +73,13 @@ fn main() _b: 2, _c: 3.0f64 }; + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing.cgu-0[Internal] //~ TRANS_ITEM fn unsizing::{{impl}}[2]::foo[0] let _struct_unsized = struct_sized as &Struct; // custom coercion let wrapper_sized = Wrapper(&0u32); + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing.cgu-0[Internal] //~ TRANS_ITEM fn unsizing::{{impl}}[3]::foo[0] let _wrapper_sized = wrapper_sized as Wrapper; } diff --git a/src/test/codegen-units/item-collection/unused-traits-and-generics.rs b/src/test/codegen-units/item-collection/unused-traits-and-generics.rs index 8689beb3fb77e..ce85c4fc13ce2 100644 --- a/src/test/codegen-units/item-collection/unused-traits-and-generics.rs +++ b/src/test/codegen-units/item-collection/unused-traits-and-generics.rs @@ -86,4 +86,3 @@ impl NonGeneric { // Only the non-generic methods should be instantiated: //~ TRANS_ITEM fn unused_traits_and_generics::{{impl}}[3]::foo[0] -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/partitioning/extern-drop-glue.rs b/src/test/codegen-units/partitioning/extern-drop-glue.rs index 910ffd2959ed0..f28c4872111c9 100644 --- a/src/test/codegen-units/partitioning/extern-drop-glue.rs +++ b/src/test/codegen-units/partitioning/extern-drop-glue.rs @@ -20,15 +20,14 @@ // aux-build:cgu_extern_drop_glue.rs extern crate cgu_extern_drop_glue; -//~ TRANS_ITEM drop-glue cgu_extern_drop_glue::Struct[0] @@ extern_drop_glue[Internal] extern_drop_glue-mod1[Internal] -//~ TRANS_ITEM drop-glue-contents cgu_extern_drop_glue::Struct[0] @@ extern_drop_glue[Internal] extern_drop_glue-mod1[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue[Internal] extern_drop_glue-mod1[Internal] struct LocalStruct(cgu_extern_drop_glue::Struct); //~ TRANS_ITEM fn extern_drop_glue::user[0] @@ extern_drop_glue[External] fn user() { - //~ TRANS_ITEM drop-glue extern_drop_glue::LocalStruct[0] @@ extern_drop_glue[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue[Internal] let _ = LocalStruct(cgu_extern_drop_glue::Struct(0)); } @@ -40,7 +39,7 @@ mod mod1 { //~ TRANS_ITEM fn extern_drop_glue::mod1[0]::user[0] @@ extern_drop_glue-mod1[External] fn user() { - //~ TRANS_ITEM drop-glue extern_drop_glue::mod1[0]::LocalStruct[0] @@ extern_drop_glue-mod1[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue-mod1[Internal] let _ = LocalStruct(cgu_extern_drop_glue::Struct(0)); } } diff --git a/src/test/codegen-units/partitioning/extern-generic.rs b/src/test/codegen-units/partitioning/extern-generic.rs index db36b50702a43..e32c946f8554f 100644 --- a/src/test/codegen-units/partitioning/extern-generic.rs +++ b/src/test/codegen-units/partitioning/extern-generic.rs @@ -60,5 +60,3 @@ mod mod3 { // once for the current crate //~ TRANS_ITEM fn cgu_generic_function::foo[0]<&str> @@ cgu_generic_function.volatile[External] //~ TRANS_ITEM fn cgu_generic_function::bar[0]<&str> @@ cgu_generic_function.volatile[External] - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/partitioning/local-drop-glue.rs b/src/test/codegen-units/partitioning/local-drop-glue.rs index f61e3fe12931e..64f4f854c2d7f 100644 --- a/src/test/codegen-units/partitioning/local-drop-glue.rs +++ b/src/test/codegen-units/partitioning/local-drop-glue.rs @@ -16,8 +16,7 @@ #![allow(dead_code)] #![crate_type="lib"] -//~ TRANS_ITEM drop-glue local_drop_glue::Struct[0] @@ local_drop_glue[Internal] local_drop_glue-mod1[Internal] -//~ TRANS_ITEM drop-glue-contents local_drop_glue::Struct[0] @@ local_drop_glue[Internal] local_drop_glue-mod1[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue[Internal] local_drop_glue-mod1[Internal] struct Struct { _a: u32 } @@ -27,7 +26,7 @@ impl Drop for Struct { fn drop(&mut self) {} } -//~ TRANS_ITEM drop-glue local_drop_glue::Outer[0] @@ local_drop_glue[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue[Internal] struct Outer { _a: Struct } @@ -46,10 +45,10 @@ mod mod1 { use super::Struct; - //~ TRANS_ITEM drop-glue local_drop_glue::mod1[0]::Struct2[0] @@ local_drop_glue-mod1[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue-mod1[Internal] struct Struct2 { _a: Struct, - //~ TRANS_ITEM drop-glue (u32, local_drop_glue::Struct[0]) @@ local_drop_glue-mod1[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, local_drop_glue::Struct[0])> @@ local_drop_glue-mod1[Internal] _b: (u32, Struct), } diff --git a/src/test/codegen-units/partitioning/regular-modules.rs b/src/test/codegen-units/partitioning/regular-modules.rs index 4da6411032168..07c341203f9e2 100644 --- a/src/test/codegen-units/partitioning/regular-modules.rs +++ b/src/test/codegen-units/partitioning/regular-modules.rs @@ -80,5 +80,3 @@ mod mod2 { static BAZ: u64 = 0; } } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/partitioning/statics.rs b/src/test/codegen-units/partitioning/statics.rs index ffe1ec278b8dd..d06b3ac407a7f 100644 --- a/src/test/codegen-units/partitioning/statics.rs +++ b/src/test/codegen-units/partitioning/statics.rs @@ -46,5 +46,3 @@ mod mod1 { static BAR: u32 = 0; } } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/partitioning/vtable-through-const.rs b/src/test/codegen-units/partitioning/vtable-through-const.rs index 7a0217072f32c..c4594bb547ef5 100644 --- a/src/test/codegen-units/partitioning/vtable-through-const.rs +++ b/src/test/codegen-units/partitioning/vtable-through-const.rs @@ -69,7 +69,7 @@ mod mod1 { //~ TRANS_ITEM fn vtable_through_const::main[0] @@ vtable_through_const[External] fn main() { - //~ TRANS_ITEM drop-glue i8 @@ vtable_through_const[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ vtable_through_const[Internal] // Since Trait1::do_something() is instantiated via its default implementation, // it is considered a generic and is instantiated here only because it is