From a47fd3df89c267829d96748b3bdff305f20d27d5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 20 Feb 2018 13:49:54 -0500 Subject: [PATCH 01/14] make `#[unwind]` attribute specify expectations more clearly You can now choose between the following: - `#[unwind(allowed)]` - `#[unwind(aborts)]` Per rust-lang/rust#48251, the default is `#[unwind(allowed)]`, though I think we should change this eventually. --- src/libcore/panicking.rs | 3 +- src/libpanic_unwind/gcc.rs | 3 +- src/libpanic_unwind/lib.rs | 3 +- src/libpanic_unwind/seh64_gnu.rs | 3 +- src/libpanic_unwind/windows.rs | 9 ++++-- src/librustc_mir/build/mod.rs | 19 ++++++++---- src/libstd/panicking.rs | 6 ++-- src/libsyntax/attr.rs | 45 ++++++++++++++++++++++++++++ src/libsyntax/diagnostic_list.rs | 27 +++++++++++++++++ src/libsyntax/feature_gate.rs | 2 +- src/libunwind/libunwind.rs | 9 ++++-- src/test/codegen/extern-functions.rs | 2 +- src/test/run-pass/abort-on-c-abi.rs | 1 + 13 files changed, 113 insertions(+), 19 deletions(-) diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index 4170d91e5fce2..94db0baa3f95f 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs @@ -64,7 +64,8 @@ pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) #[allow(improper_ctypes)] extern { #[lang = "panic_fmt"] - #[unwind] + #[cfg_attr(stage0, unwind)] + #[cfg_attr(not(stage0), unwind(allowed))] fn panic_impl(fmt: fmt::Arguments, file: &'static str, line: u32, col: u32) -> !; } let (file, line, col) = *file_line_col; diff --git a/src/libpanic_unwind/gcc.rs b/src/libpanic_unwind/gcc.rs index 63e44f71a3a8f..ca2fd561cadcf 100644 --- a/src/libpanic_unwind/gcc.rs +++ b/src/libpanic_unwind/gcc.rs @@ -286,7 +286,8 @@ unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) // See docs in the `unwind` module. #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] #[lang = "eh_unwind_resume"] -#[unwind] +#[cfg_attr(stage0, unwind)] +#[cfg_attr(not(stage0), unwind(allowed))] unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! { uw::_Unwind_Resume(panic_ctx as *mut uw::_Unwind_Exception); } diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs index 92e40e8f26d40..a5cebc3e4d04b 100644 --- a/src/libpanic_unwind/lib.rs +++ b/src/libpanic_unwind/lib.rs @@ -112,7 +112,8 @@ pub unsafe extern "C" fn __rust_maybe_catch_panic(f: fn(*mut u8), // Entry point for raising an exception, just delegates to the platform-specific // implementation. #[no_mangle] -#[unwind] +#[cfg_attr(stage0, unwind)] +#[cfg_attr(not(stage0), unwind(allowed))] pub unsafe extern "C" fn __rust_start_panic(data: usize, vtable: usize) -> u32 { imp::panic(mem::transmute(raw::TraitObject { data: data as *mut (), diff --git a/src/libpanic_unwind/seh64_gnu.rs b/src/libpanic_unwind/seh64_gnu.rs index 0a9fa7d9a80b4..090cd095380ee 100644 --- a/src/libpanic_unwind/seh64_gnu.rs +++ b/src/libpanic_unwind/seh64_gnu.rs @@ -108,7 +108,8 @@ unsafe extern "C" fn rust_eh_personality(exceptionRecord: *mut c::EXCEPTION_RECO } #[lang = "eh_unwind_resume"] -#[unwind] +#[cfg_attr(stage0, unwind)] +#[cfg_attr(not(stage0), unwind(allowed))] unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: c::LPVOID) -> ! { let params = [panic_ctx as c::ULONG_PTR]; c::RaiseException(RUST_PANIC, diff --git a/src/libpanic_unwind/windows.rs b/src/libpanic_unwind/windows.rs index a7e90071ceae8..50fba5faee747 100644 --- a/src/libpanic_unwind/windows.rs +++ b/src/libpanic_unwind/windows.rs @@ -79,18 +79,21 @@ pub enum EXCEPTION_DISPOSITION { pub use self::EXCEPTION_DISPOSITION::*; extern "system" { - #[unwind] + #[cfg_attr(stage0, unwind)] + #[cfg_attr(not(stage0), unwind(allowed))] pub fn RaiseException(dwExceptionCode: DWORD, dwExceptionFlags: DWORD, nNumberOfArguments: DWORD, lpArguments: *const ULONG_PTR); - #[unwind] + #[cfg_attr(stage0, unwind)] + #[cfg_attr(not(stage0), unwind(allowed))] pub fn RtlUnwindEx(TargetFrame: LPVOID, TargetIp: LPVOID, ExceptionRecord: *const EXCEPTION_RECORD, ReturnValue: LPVOID, OriginalContext: *const CONTEXT, HistoryTable: *const UNWIND_HISTORY_TABLE); - #[unwind] + #[cfg_attr(stage0, unwind)] + #[cfg_attr(not(stage0), unwind(allowed))] pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8); } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 57059cd31a1bc..a325cfe3eaae3 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -28,6 +28,7 @@ use std::mem; use std::u32; use syntax::abi::Abi; use syntax::ast; +use syntax::attr::{self, UnwindAttr}; use syntax::symbol::keywords; use syntax_pos::Span; use transform::MirSource; @@ -355,10 +356,9 @@ macro_rules! unpack { } fn should_abort_on_panic<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - fn_id: ast::NodeId, + fn_def_id: DefId, abi: Abi) -> bool { - // Not callable from C, so we can safely unwind through these if abi == Abi::Rust || abi == Abi::RustCall { return false; } @@ -370,9 +370,17 @@ fn should_abort_on_panic<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, // This is a special case: some functions have a C abi but are meant to // unwind anyway. Don't stop them. - if tcx.has_attr(tcx.hir.local_def_id(fn_id), "unwind") { return false; } + let attrs = &tcx.get_attrs(fn_def_id); + match attr::find_unwind_attr(Some(tcx.sess.diagnostic()), attrs) { + None => { + // FIXME(rust-lang/rust#48251) -- Had to disable + // abort-on-panic for backwards compatibility reasons. + false + } - return true; + Some(UnwindAttr::Allowed) => false, + Some(UnwindAttr::Aborts) => true, + } } /////////////////////////////////////////////////////////////////////////// @@ -399,13 +407,14 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>, safety, return_ty); + let fn_def_id = tcx.hir.local_def_id(fn_id); let call_site_scope = region::Scope::CallSite(body.value.hir_id.local_id); let arg_scope = region::Scope::Arguments(body.value.hir_id.local_id); let mut block = START_BLOCK; let source_info = builder.source_info(span); let call_site_s = (call_site_scope, source_info); unpack!(block = builder.in_scope(call_site_s, LintLevel::Inherited, block, |builder| { - if should_abort_on_panic(tcx, fn_id, abi) { + if should_abort_on_panic(tcx, fn_def_id, abi) { builder.schedule_abort(); } diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 161c3fc7113a7..454ac64735c67 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -55,7 +55,8 @@ extern { data: *mut u8, data_ptr: *mut usize, vtable_ptr: *mut usize) -> u32; - #[unwind] + #[cfg_attr(stage0, unwind)] + #[cfg_attr(not(stage0), unwind(allowed))] fn __rust_start_panic(data: usize, vtable: usize) -> u32; } @@ -315,7 +316,8 @@ pub fn panicking() -> bool { /// Entry point of panic from the libcore crate. #[cfg(not(test))] #[lang = "panic_fmt"] -#[unwind] +#[cfg_attr(stage0, unwind)] +#[cfg_attr(not(stage0), unwind(allowed))] pub extern fn rust_begin_panic(msg: fmt::Arguments, file: &'static str, line: u32, diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index d18d6f5e6bd0d..d0822b69aa692 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -565,6 +565,51 @@ pub fn find_inline_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> In }) } +#[derive(Copy, Clone, PartialEq)] +pub enum UnwindAttr { + Allowed, + Aborts, +} + +/// Determine what `#[unwind]` attribute is present in `attrs`, if any. +pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Option { + let syntax_error = |attr: &Attribute| { + mark_used(attr); + diagnostic.map(|d| { + span_err!(d, attr.span, E0633, "malformed `#[unwind]` attribute"); + }); + None + }; + + attrs.iter().fold(None, |ia, attr| { + if attr.path != "unwind" { + return ia; + } + let meta = match attr.meta() { + Some(meta) => meta.node, + None => return ia, + }; + match meta { + MetaItemKind::Word => { + syntax_error(attr) + } + MetaItemKind::List(ref items) => { + mark_used(attr); + if items.len() != 1 { + syntax_error(attr) + } else if list_contains_name(&items[..], "allowed") { + Some(UnwindAttr::Allowed) + } else if list_contains_name(&items[..], "aborts") { + Some(UnwindAttr::Aborts) + } else { + syntax_error(attr) + } + } + _ => ia, + } + }) +} + /// True if `#[inline]` or `#[inline(always)]` is present in `attrs`. pub fn requests_inline(attrs: &[Attribute]) -> bool { match find_inline_attr(None, attrs) { diff --git a/src/libsyntax/diagnostic_list.rs b/src/libsyntax/diagnostic_list.rs index d841281e48580..84ab0336f1671 100644 --- a/src/libsyntax/diagnostic_list.rs +++ b/src/libsyntax/diagnostic_list.rs @@ -342,6 +342,33 @@ fn main() { ``` "##, +E0633: r##" +The `unwind` attribute was malformed. + +Erroneous code example: + +```ignore (compile_fail not working here; see Issue #43707) +#[unwind()] // error: expected one argument +pub extern fn something() {} + +fn main() {} +``` + +The `#[unwind]` attribute should be used as follows: + +- `#[unwind(aborts)]` -- specifies that if a non-Rust ABI function + should abort the process if it attempts to unwind. This is the safer + and preferred option. + +- `#[unwind(allowed)]` -- specifies that a non-Rust ABI function + should be allowed to unwind. This can easily result in Undefined + Behavior (UB), so be careful. + +NB. The default behavior here is "allowed", but this is unspecified +and likely to change in the future. + +"##, + } register_diagnostics! { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 3b137f9570a39..f1d0a70a22cd0 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -233,7 +233,7 @@ declare_features! ( // allow `extern "platform-intrinsic" { ... }` (active, platform_intrinsics, "1.4.0", Some(27731)), - // allow `#[unwind]` + // allow `#[unwind(..)]` // rust runtime internal (active, unwind_attributes, "1.4.0", None), diff --git a/src/libunwind/libunwind.rs b/src/libunwind/libunwind.rs index e6fff7963f79c..aa73b11fb3813 100644 --- a/src/libunwind/libunwind.rs +++ b/src/libunwind/libunwind.rs @@ -83,7 +83,8 @@ pub enum _Unwind_Context {} pub type _Unwind_Exception_Cleanup_Fn = extern "C" fn(unwind_code: _Unwind_Reason_Code, exception: *mut _Unwind_Exception); extern "C" { - #[unwind] + #[cfg_attr(stage0, unwind)] + #[cfg_attr(not(stage0), unwind(allowed))] pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !; pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception); pub fn _Unwind_GetLanguageSpecificData(ctx: *mut _Unwind_Context) -> *mut c_void; @@ -220,7 +221,8 @@ if #[cfg(all(any(target_os = "ios", not(target_arch = "arm"))))] { if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] { // Not 32-bit iOS extern "C" { - #[unwind] + #[cfg_attr(stage0, unwind)] + #[cfg_attr(not(stage0), unwind(allowed))] pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code; pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, trace_argument: *mut c_void) @@ -229,7 +231,8 @@ if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] { } else { // 32-bit iOS uses SjLj and does not provide _Unwind_Backtrace() extern "C" { - #[unwind] + #[cfg_attr(stage0, unwind)] + #[cfg_attr(not(stage0), unwind(allowed))] pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code; } diff --git a/src/test/codegen/extern-functions.rs b/src/test/codegen/extern-functions.rs index 7ee31070b2635..90ee0c75680dc 100644 --- a/src/test/codegen/extern-functions.rs +++ b/src/test/codegen/extern-functions.rs @@ -19,7 +19,7 @@ extern { fn extern_fn(); // CHECK-NOT: Function Attrs: nounwind // CHECK: declare void @unwinding_extern_fn - #[unwind] + #[unwind(allowed)] fn unwinding_extern_fn(); } diff --git a/src/test/run-pass/abort-on-c-abi.rs b/src/test/run-pass/abort-on-c-abi.rs index 17661c0b12097..5039c334f26f5 100644 --- a/src/test/run-pass/abort-on-c-abi.rs +++ b/src/test/run-pass/abort-on-c-abi.rs @@ -19,6 +19,7 @@ use std::io::prelude::*; use std::io; use std::process::{Command, Stdio}; +#[unwind(aborts)] extern "C" fn panic_in_ffi() { panic!("Test"); } From 566c6ac6bac479d4977339fc91bb497cb96342c6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Feb 2018 20:39:01 -0500 Subject: [PATCH 02/14] add `unwind_attributes` feature --- src/test/run-pass/abort-on-c-abi.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/run-pass/abort-on-c-abi.rs b/src/test/run-pass/abort-on-c-abi.rs index 5039c334f26f5..ef368ed604bea 100644 --- a/src/test/run-pass/abort-on-c-abi.rs +++ b/src/test/run-pass/abort-on-c-abi.rs @@ -14,6 +14,8 @@ // ignore-cloudabi no env and process // ignore-emscripten no processes +#![feature(unwind_attributes)] + use std::{env, panic}; use std::io::prelude::*; use std::io; From 56a68285332000c858e9aeba7d66a4ec66ebff91 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sun, 18 Feb 2018 15:05:24 -0800 Subject: [PATCH 03/14] Implement --remap-path-prefix Remove experimental -Zremap-path-prefix-from/to, and replace it with the stabilized --remap-path-prefix=from=to variant. This is an implementation for issue of #41555. --- src/doc/man/rustc.1 | 10 ++++ .../src/compiler-flags/remap-path-prefix.md | 37 --------------- src/libproc_macro/lib.rs | 2 +- src/librustc/session/config.rs | 47 +++++++++---------- src/librustc_metadata/encoder.rs | 2 +- src/libsyntax/codemap.rs | 2 +- src/libsyntax_pos/lib.rs | 2 +- .../auxiliary/remap_path_prefix_aux.rs | 2 +- src/test/codegen/remap_path_prefix/main.rs | 2 +- .../auxiliary/extern_crate.rs | 2 +- 10 files changed, 38 insertions(+), 70 deletions(-) delete mode 100644 src/doc/unstable-book/src/compiler-flags/remap-path-prefix.md diff --git a/src/doc/man/rustc.1 b/src/doc/man/rustc.1 index 19f6cc9ac619d..39d1053995945 100644 --- a/src/doc/man/rustc.1 +++ b/src/doc/man/rustc.1 @@ -125,6 +125,16 @@ Print version info and exit. \fB\-v\fR, \fB\-\-verbose\fR Use verbose output. .TP +\fB\-\-remap\-path\-prefix\fR \fIfrom\fR=\fIto\fR +Remap source path prefixes in all output, including compiler diagnostics, debug information, +macro expansions, etc. The \fIfrom\fR=\fIto\fR parameter is scanned from right to left, so \fIfrom\fR +may contain '=', but \fIto\fR may not. + +This is useful for normalizing build products, for example by removing the current directory out of +pathnames emitted into the object files. The replacement is purely textual, with no consideration of +the current system's pathname syntax. For example \fI\-\-remap\-path\-prefix foo=bar\fR will +match \fBfoo/lib.rs\fR but not \fB./foo/lib.rs\fR. +.TP \fB\-\-extern\fR \fINAME\fR=\fIPATH\fR Specify where an external rust library is located. These should match \fIextern\fR declarations in the crate's source code. diff --git a/src/doc/unstable-book/src/compiler-flags/remap-path-prefix.md b/src/doc/unstable-book/src/compiler-flags/remap-path-prefix.md deleted file mode 100644 index 8ca04d2532592..0000000000000 --- a/src/doc/unstable-book/src/compiler-flags/remap-path-prefix.md +++ /dev/null @@ -1,37 +0,0 @@ -# `remap-path-prefix` - -The tracking issue for this feature is: [#41555](https://github.com/rust-lang/rust/issues/41555) - ------------------------- - -The `-Z remap-path-prefix-from`, `-Z remap-path-prefix-to` commandline option -pair allows to replace prefixes of any file paths the compiler emits in various -places. This is useful for bringing debuginfo paths into a well-known form and -for achieving reproducible builds independent of the directory the compiler was -executed in. All paths emitted by the compiler are affected, including those in -error messages. - -In order to map all paths starting with `/home/foo/my-project/src` to -`/sources/my-project`, one would invoke the compiler as follows: - -```text -rustc -Zremap-path-prefix-from="/home/foo/my-project/src" -Zremap-path-prefix-to="/sources/my-project" -``` - -Debuginfo for code from the file `/home/foo/my-project/src/foo/mod.rs`, -for example, would then point debuggers to `/sources/my-project/foo/mod.rs` -instead of the original file. - -The options can be specified multiple times when multiple prefixes should be -mapped: - -```text -rustc -Zremap-path-prefix-from="/home/foo/my-project/src" \ - -Zremap-path-prefix-to="/sources/my-project" \ - -Zremap-path-prefix-from="/home/foo/my-project/build-dir" \ - -Zremap-path-prefix-to="/stable-build-dir" -``` - -When the options are given multiple times, the nth `-from` will be matched up -with the nth `-to` and they can appear anywhere on the commandline. Mappings -specified later on the line will take precedence over earlier ones. diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 6768e0ade4304..0f31ac15a4545 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -316,7 +316,7 @@ impl SourceFile { /// If the code span associated with this `SourceFile` was generated by an external macro, this /// may not be an actual path on the filesystem. Use [`is_real`] to check. /// - /// Also note that even if `is_real` returns `true`, if `-Z remap-path-prefix-*` was passed on + /// Also note that even if `is_real` returns `true`, if `--remap-path-prefix` was passed on /// the command line, the path as given may not actually be valid. /// /// [`is_real`]: #method.is_real diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index cfbf233297cf8..1d554cb0d821d 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -435,6 +435,9 @@ top_level_options!( // if we otherwise use the defaults of rustc. cli_forced_codegen_units: Option [UNTRACKED], cli_forced_thinlto_off: bool [UNTRACKED], + + // Remap source path prefixes in all output (messages, object files, debug, etc) + remap_path_prefix: Vec<(PathBuf, PathBuf)> [UNTRACKED], } ); @@ -617,6 +620,7 @@ pub fn basic_options() -> Options { actually_rustdoc: false, cli_forced_codegen_units: None, cli_forced_thinlto_off: false, + remap_path_prefix: Vec::new(), } } @@ -635,11 +639,7 @@ impl Options { } pub fn file_path_mapping(&self) -> FilePathMapping { - FilePathMapping::new( - self.debugging_opts.remap_path_prefix_from.iter().zip( - self.debugging_opts.remap_path_prefix_to.iter() - ).map(|(src, dst)| (src.clone(), dst.clone())).collect() - ) + FilePathMapping::new(self.remap_path_prefix.clone()) } /// True if there will be an output file generated @@ -1269,10 +1269,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "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"), - remap_path_prefix_from: Vec = (vec![], parse_pathbuf_push, [UNTRACKED], - "add a source pattern to the file path remapping config"), - remap_path_prefix_to: Vec = (vec![], parse_pathbuf_push, [UNTRACKED], - "add a mapping target to the file path remapping config"), force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED], "force all crates to be `rustc_private` unstable"), pre_link_arg: Vec = (vec![], parse_string_push, [UNTRACKED], @@ -1595,6 +1591,7 @@ pub fn rustc_optgroups() -> Vec { `expanded` (crates expanded), or `expanded,identified` (fully parenthesized, AST nodes with IDs).", "TYPE"), + opt::multi_s("", "remap-path-prefix", "remap source names in output", "FROM=TO"), ]); opts } @@ -1718,23 +1715,6 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) output_types.insert(OutputType::Exe, None); } - let remap_path_prefix_sources = debugging_opts.remap_path_prefix_from.len(); - let remap_path_prefix_targets = debugging_opts.remap_path_prefix_to.len(); - - if remap_path_prefix_targets < remap_path_prefix_sources { - for source in &debugging_opts.remap_path_prefix_from[remap_path_prefix_targets..] { - early_error(error_format, - &format!("option `-Zremap-path-prefix-from='{}'` does not have \ - a corresponding `-Zremap-path-prefix-to`", source.display())) - } - } else if remap_path_prefix_targets > remap_path_prefix_sources { - for target in &debugging_opts.remap_path_prefix_to[remap_path_prefix_sources..] { - early_error(error_format, - &format!("option `-Zremap-path-prefix-to='{}'` does not have \ - a corresponding `-Zremap-path-prefix-from`", target.display())) - } - } - let mut cg = build_codegen_options(matches, error_format); let mut codegen_units = cg.codegen_units; let mut disable_thinlto = false; @@ -1968,6 +1948,20 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) let crate_name = matches.opt_str("crate-name"); + let remap_path_prefix = matches.opt_strs("remap-path-prefix") + .into_iter() + .map(|remap| { + let mut parts = remap.rsplitn(2, '='); // reverse iterator + let to = parts.next(); + let from = parts.next(); + match (from, to) { + (Some(from), Some(to)) => (PathBuf::from(from), PathBuf::from(to)), + _ => early_error(error_format, + "--remap-path-prefix must contain '=' between FROM and TO"), + } + }) + .collect(); + (Options { crate_types, optimize: opt_level, @@ -1995,6 +1989,7 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) actually_rustdoc: false, cli_forced_codegen_units: codegen_units, cli_forced_thinlto_off: disable_thinlto, + remap_path_prefix, }, cfg) } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 78578ca179c82..d140b135416c9 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -323,7 +323,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // paths because any relative paths are potentially relative to // a wrong directory. // However, if a path has been modified via - // `-Zremap-path-prefix` we assume the user has already set + // `--remap-path-prefix` we assume the user has already set // things up the way they want and don't touch the path values // anymore. match filemap.name { diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index df5845f6c217d..926548b60318b 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -129,7 +129,7 @@ pub struct CodeMap { pub(super) files: RefCell>>, file_loader: Box, // This is used to apply the file path remapping as specified via - // -Zremap-path-prefix to all FileMaps allocated within this CodeMap. + // --remap-path-prefix to all FileMaps allocated within this CodeMap. path_mapping: FilePathMapping, stable_id_to_filemap: RefCell>>, /// In case we are in a doctest, replace all file names with the PathBuf, diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 294506625bc05..e3f1417456c8a 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -664,7 +664,7 @@ pub struct FileMap { /// originate from files has names between angle brackets by convention, /// e.g. `` pub name: FileName, - /// True if the `name` field above has been modified by -Zremap-path-prefix + /// True if the `name` field above has been modified by --remap-path-prefix pub name_was_remapped: bool, /// The unmapped path of the file that the source came from. /// Set to `None` if the FileMap was imported from an external crate. diff --git a/src/test/codegen/remap_path_prefix/auxiliary/remap_path_prefix_aux.rs b/src/test/codegen/remap_path_prefix/auxiliary/remap_path_prefix_aux.rs index 5543a091680f1..3ef0ff9ef0687 100644 --- a/src/test/codegen/remap_path_prefix/auxiliary/remap_path_prefix_aux.rs +++ b/src/test/codegen/remap_path_prefix/auxiliary/remap_path_prefix_aux.rs @@ -10,7 +10,7 @@ // ignore-tidy-linelength -// compile-flags: -g -Zremap-path-prefix-from={{cwd}} -Zremap-path-prefix-to=/the/aux-cwd -Zremap-path-prefix-from={{src-base}}/remap_path_prefix/auxiliary -Zremap-path-prefix-to=/the/aux-src +// compile-flags: -g --remap-path-prefix={{cwd}}=/the/aux-cwd --remap-path-prefix={{src-base}}/remap_path_prefix/auxiliary=/the/aux-src #[inline] pub fn some_aux_function() -> i32 { diff --git a/src/test/codegen/remap_path_prefix/main.rs b/src/test/codegen/remap_path_prefix/main.rs index ea0c9ad2b8324..2f46b6c5d48da 100644 --- a/src/test/codegen/remap_path_prefix/main.rs +++ b/src/test/codegen/remap_path_prefix/main.rs @@ -11,7 +11,7 @@ // ignore-windows // ignore-tidy-linelength -// compile-flags: -g -C no-prepopulate-passes -Zremap-path-prefix-from={{cwd}} -Zremap-path-prefix-to=/the/cwd -Zremap-path-prefix-from={{src-base}} -Zremap-path-prefix-to=/the/src +// compile-flags: -g -C no-prepopulate-passes --remap-path-prefix={{cwd}}=/the/cwd --remap-path-prefix={{src-base}}=/the/src // aux-build:remap_path_prefix_aux.rs extern crate remap_path_prefix_aux; diff --git a/src/test/incremental/remapped_paths_cc/auxiliary/extern_crate.rs b/src/test/incremental/remapped_paths_cc/auxiliary/extern_crate.rs index 1483bf92c9708..c80b334623b43 100644 --- a/src/test/incremental/remapped_paths_cc/auxiliary/extern_crate.rs +++ b/src/test/incremental/remapped_paths_cc/auxiliary/extern_crate.rs @@ -12,7 +12,7 @@ //[rpass1] compile-flags: -g //[rpass2] compile-flags: -g -//[rpass3] compile-flags: -g -Zremap-path-prefix-from={{src-base}} -Zremap-path-prefix-to=/the/src +//[rpass3] compile-flags: -g --remap-path-prefix={{src-base}}=/the/src #![feature(rustc_attrs)] #![crate_type="rlib"] From d9438c30d59bbe7c4442e4a824a41a2bd00372ac Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 22 Feb 2018 15:25:31 -0800 Subject: [PATCH 04/14] Add ToString and FromStr impls for Epoch --- src/librustc/session/config.rs | 38 +++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index cfbf233297cf8..ef85ef8e37e70 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -41,7 +41,7 @@ use std::collections::btree_map::Iter as BTreeMapIter; use std::collections::btree_map::Keys as BTreeMapKeysIter; use std::collections::btree_map::Values as BTreeMapValuesIter; -use std::fmt; +use std::{fmt, str}; use std::hash::Hasher; use std::collections::hash_map::DefaultHasher; use std::collections::HashSet; @@ -137,6 +137,28 @@ pub enum Epoch { // as well as changing the default Cargo template. } +pub const ALL_EPOCHS: &[Epoch] = &[Epoch::Epoch2015, Epoch::Epoch2018]; + +impl ToString for Epoch { + fn to_string(&self) -> String { + match *self { + Epoch::Epoch2015 => "2015".into(), + Epoch::Epoch2018 => "2018".into(), + } + } +} + +impl str::FromStr for Epoch { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "2015" => Ok(Epoch::Epoch2015), + "2018" => Ok(Epoch::Epoch2018), + _ => Err(()) + } + } +} + impl_stable_hash_for!(enum self::OutputType { Bitcode, Assembly, @@ -1021,11 +1043,17 @@ macro_rules! options { fn parse_epoch(slot: &mut Epoch, v: Option<&str>) -> bool { match v { - Some("2015") => *slot = Epoch::Epoch2015, - Some("2018") => *slot = Epoch::Epoch2018, - _ => return false, + Some(s) => { + let epoch = s.parse(); + if let Ok(parsed) = epoch { + *slot = parsed; + true + } else { + false + } + } + _ => false, } - true } } ) } From da9dc0507bc86104db8bf7a99e849bfd995fb1ef Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 22 Feb 2018 16:51:42 -0800 Subject: [PATCH 05/14] Allow future-incompat lints to mention an epoch --- src/librustc/lint/context.rs | 27 ++++++++++++++++++++++----- src/librustc/lint/levels.rs | 17 ++++++++++------- src/librustc/lint/mod.rs | 29 +++++++++++++++++++++++++---- src/librustc/session/config.rs | 11 ++++++++++- src/librustc/session/mod.rs | 4 ++++ src/librustc/ty/context.rs | 2 +- src/librustc_driver/driver.rs | 2 +- src/librustc_driver/lib.rs | 20 ++++++++++---------- src/librustc_lint/lib.rs | 18 ++++++++++++++++++ 9 files changed, 101 insertions(+), 29 deletions(-) diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index ed937046e5ed7..870b52c221f37 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -99,7 +99,11 @@ pub struct BufferedEarlyLint { /// guidelines. pub struct FutureIncompatibleInfo { pub id: LintId, - pub reference: &'static str // e.g., a URL for an issue/PR/RFC or error code + /// e.g., a URL for an issue/PR/RFC or error code + pub reference: &'static str, + /// If this is an epoch fixing lint, the epoch in which + /// this lint becomes obsolete + pub epoch: Option, } /// The target of the `by_name` map, which accounts for renaming/deprecation. @@ -194,11 +198,24 @@ impl LintStore { pub fn register_future_incompatible(&mut self, sess: Option<&Session>, lints: Vec) { - let ids = lints.iter().map(|f| f.id).collect(); - self.register_group(sess, false, "future_incompatible", ids); - for info in lints { - self.future_incompatible.insert(info.id, info); + + for epoch in config::ALL_EPOCHS { + let lints = lints.iter().filter(|f| f.epoch == Some(*epoch)).map(|f| f.id) + .collect::>(); + if !lints.is_empty() { + self.register_group(sess, false, epoch.lint_name(), lints) + } + } + + let mut future_incompatible = vec![]; + for lint in lints { + future_incompatible.push(lint.id); + self.future_incompatible.insert(lint.id, lint); } + + self.register_group(sess, false, "future_incompatible", future_incompatible); + + } pub fn future_incompatible(&self, id: LintId) -> Option<&FutureIncompatibleInfo> { diff --git a/src/librustc/lint/levels.rs b/src/librustc/lint/levels.rs index 4bc37747f2a76..909904b4fc36c 100644 --- a/src/librustc/lint/levels.rs +++ b/src/librustc/lint/levels.rs @@ -89,14 +89,15 @@ impl LintLevelSets { fn get_lint_level(&self, lint: &'static Lint, idx: u32, - aux: Option<&FxHashMap>) + aux: Option<&FxHashMap>, + sess: &Session) -> (Level, LintSource) { let (level, mut src) = self.get_lint_id_level(LintId::of(lint), idx, aux); // If `level` is none then we actually assume the default level for this // lint. - let mut level = level.unwrap_or(lint.default_level); + let mut level = level.unwrap_or(lint.default_level(sess)); // If we're about to issue a warning, check at the last minute for any // directives against the warnings "lint". If, for example, there's an @@ -235,7 +236,8 @@ impl<'a> LintLevelsBuilder<'a> { let lint = builtin::RENAMED_AND_REMOVED_LINTS; let (level, src) = self.sets.get_lint_level(lint, self.cur, - Some(&specs)); + Some(&specs), + &sess); lint::struct_lint_level(self.sess, lint, level, @@ -248,7 +250,8 @@ impl<'a> LintLevelsBuilder<'a> { let lint = builtin::UNKNOWN_LINTS; let (level, src) = self.sets.get_lint_level(lint, self.cur, - Some(&specs)); + Some(&specs), + self.sess); let msg = format!("unknown lint: `{}`", name); let mut db = lint::struct_lint_level(self.sess, lint, @@ -342,7 +345,7 @@ impl<'a> LintLevelsBuilder<'a> { msg: &str) -> DiagnosticBuilder<'a> { - let (level, src) = self.sets.get_lint_level(lint, self.cur, None); + let (level, src) = self.sets.get_lint_level(lint, self.cur, None, self.sess); lint::struct_lint_level(self.sess, lint, level, src, span, msg) } @@ -377,11 +380,11 @@ impl LintLevelMap { /// If the `id` was not previously registered, returns `None`. If `None` is /// returned then the parent of `id` should be acquired and this function /// should be called again. - pub fn level_and_source(&self, lint: &'static Lint, id: HirId) + pub fn level_and_source(&self, lint: &'static Lint, id: HirId, session: &Session) -> Option<(Level, LintSource)> { self.id_to_set.get(&id).map(|idx| { - self.sets.get_lint_level(lint, *idx, None) + self.sets.get_lint_level(lint, *idx, None, session) }) } diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index b2a9859f68a3e..e28cc9c201d02 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -37,7 +37,7 @@ use errors::{DiagnosticBuilder, DiagnosticId}; use hir::def_id::{CrateNum, LOCAL_CRATE}; use hir::intravisit::{self, FnKind}; use hir; -use session::{Session, DiagnosticMessageId}; +use session::{config, Session, DiagnosticMessageId}; use std::hash; use syntax::ast; use syntax::codemap::MultiSpan; @@ -74,6 +74,9 @@ pub struct Lint { /// /// e.g. "imports that are never used" pub desc: &'static str, + + /// Deny lint after this epoch + pub epoch_deny: Option, } impl Lint { @@ -81,18 +84,36 @@ impl Lint { pub fn name_lower(&self) -> String { self.name.to_ascii_lowercase() } + + pub fn default_level(&self, session: &Session) -> Level { + if let Some(epoch_deny) = self.epoch_deny { + if session.epoch() >= epoch_deny { + return Level::Deny + } + } + self.default_level + } } /// Declare a static item of type `&'static Lint`. #[macro_export] macro_rules! declare_lint { + ($vis: vis $NAME: ident, $Level: ident, $desc: expr, $epoch: expr) => ( + $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint { + name: stringify!($NAME), + default_level: $crate::lint::$Level, + desc: $desc, + epoch_deny: Some($epoch) + }; + ); ($vis: vis $NAME: ident, $Level: ident, $desc: expr) => ( $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint { name: stringify!($NAME), default_level: $crate::lint::$Level, - desc: $desc + desc: $desc, + epoch_deny: None, }; - ) + ); } /// Declare a static `LintArray` and return it as an expression. @@ -304,7 +325,7 @@ impl LintId { /// Setting for how to handle a lint. #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] pub enum Level { - Allow, Warn, Deny, Forbid + Allow, Warn, Deny, Forbid, } impl_stable_hash_for!(enum self::Level { diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index ef85ef8e37e70..beb828ab91bf7 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -113,7 +113,7 @@ pub enum OutputType { } /// The epoch of the compiler (RFC 2052) -#[derive(Clone, Copy, Hash, PartialOrd, Ord, Eq, PartialEq)] +#[derive(Clone, Copy, Hash, PartialOrd, Ord, Eq, PartialEq, Debug)] #[non_exhaustive] pub enum Epoch { // epochs must be kept in order, newest to oldest @@ -148,6 +148,15 @@ impl ToString for Epoch { } } +impl Epoch { + pub fn lint_name(&self) -> &'static str { + match *self { + Epoch::Epoch2015 => "epoch_2015", + Epoch::Epoch2018 => "epoch_2018", + } + } +} + impl str::FromStr for Epoch { type Err = (); fn from_str(s: &str) -> Result { diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 9d7a9acc3d533..6901674a6cf8d 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -869,6 +869,10 @@ impl Session { pub fn rust_2018(&self) -> bool { self.opts.debugging_opts.epoch >= Epoch::Epoch2018 } + + pub fn epoch(&self) -> Epoch { + self.opts.debugging_opts.epoch + } } pub fn build_session(sopts: config::Options, diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index e4e07454c97ac..414ddb9efb2c2 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -2234,7 +2234,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { let sets = self.lint_levels(LOCAL_CRATE); loop { let hir_id = self.hir.definitions().node_to_hir_id(id); - if let Some(pair) = sets.level_and_source(lint, hir_id) { + if let Some(pair) = sets.level_and_source(lint, hir_id, self.sess) { return pair } let next = self.hir.get_parent_node(id); diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index b8a1fe9910540..943d690d76710 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -735,7 +735,7 @@ pub fn phase_2_configure_and_expand_inner<'a, F>(sess: &'a Session, // Lint plugins are registered; now we can process command line flags. if sess.opts.describe_lints { - super::describe_lints(&sess.lint_store.borrow(), true); + super::describe_lints(&sess, &sess.lint_store.borrow(), true); return Err(CompileIncomplete::Stopped); } diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 05dcaf731352a..5f0d106877c08 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -774,15 +774,15 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { -> Option<(Input, Option)> { match matches.free.len() { 0 => { + let mut sess = build_session(sopts.clone(), + None, + descriptions.clone()); if sopts.describe_lints { let mut ls = lint::LintStore::new(); - rustc_lint::register_builtins(&mut ls, None); - describe_lints(&ls, false); + rustc_lint::register_builtins(&mut ls, Some(&sess)); + describe_lints(&sess, &ls, false); return None; } - let mut sess = build_session(sopts.clone(), - None, - descriptions.clone()); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, cfg.clone()); let trans = get_trans(&sess); @@ -1121,7 +1121,7 @@ fn usage(verbose: bool, include_unstable_options: bool) { verbose_help); } -fn describe_lints(lint_store: &lint::LintStore, loaded_plugins: bool) { +fn describe_lints(sess: &Session, lint_store: &lint::LintStore, loaded_plugins: bool) { println!(" Available lint options: -W Warn about @@ -1133,10 +1133,10 @@ Available lint options: "); - fn sort_lints(lints: Vec<(&'static Lint, bool)>) -> Vec<&'static Lint> { + fn sort_lints(sess: &Session, lints: Vec<(&'static Lint, bool)>) -> Vec<&'static Lint> { let mut lints: Vec<_> = lints.into_iter().map(|(x, _)| x).collect(); lints.sort_by(|x: &&Lint, y: &&Lint| { - match x.default_level.cmp(&y.default_level) { + match x.default_level(sess).cmp(&y.default_level(sess)) { // The sort doesn't case-fold but it's doubtful we care. Equal => x.name.cmp(y.name), r => r, @@ -1159,8 +1159,8 @@ Available lint options: .iter() .cloned() .partition(|&(_, p)| p); - let plugin = sort_lints(plugin); - let builtin = sort_lints(builtin); + let plugin = sort_lints(sess, plugin); + let builtin = sort_lints(sess, builtin); let (plugin_groups, builtin_groups): (Vec<_>, _) = lint_store.get_lint_groups() .iter() diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 699765dde03ff..c35a3fbe419d1 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -185,74 +185,92 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { FutureIncompatibleInfo { id: LintId::of(PRIVATE_IN_PUBLIC), reference: "issue #34537 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(PUB_USE_OF_PRIVATE_EXTERN_CRATE), reference: "issue #34537 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(PATTERNS_IN_FNS_WITHOUT_BODY), reference: "issue #35203 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(SAFE_EXTERN_STATICS), reference: "issue #36247 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(INVALID_TYPE_PARAM_DEFAULT), reference: "issue #36887 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(LEGACY_DIRECTORY_OWNERSHIP), reference: "issue #37872 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(LEGACY_IMPORTS), reference: "issue #38260 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(LEGACY_CONSTRUCTOR_VISIBILITY), reference: "issue #39207 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(RESOLVE_TRAIT_ON_DEFAULTED_UNIT), reference: "issue #39216 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(MISSING_FRAGMENT_SPECIFIER), reference: "issue #40107 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(ILLEGAL_FLOATING_POINT_LITERAL_PATTERN), reference: "issue #41620 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(ANONYMOUS_PARAMETERS), reference: "issue #41686 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES), reference: "issue #42238 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(LATE_BOUND_LIFETIME_ARGUMENTS), reference: "issue #42868 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(SAFE_PACKED_BORROWS), reference: "issue #46043 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(INCOHERENT_FUNDAMENTAL_IMPLS), reference: "issue #46205 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(COERCE_NEVER), reference: "issue #46325 ", + epoch: None, }, FutureIncompatibleInfo { id: LintId::of(TYVAR_BEHIND_RAW_POINTER), reference: "issue #46906 ", + epoch: None, }, ]); From 3eeabe7b7d13f8ca2d29e117f45cd420d6f73da4 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 22 Feb 2018 22:14:08 -0800 Subject: [PATCH 06/14] Add hardwired lint for dyn trait --- src/librustc/lint/builtin.rs | 12 ++++++++++-- src/librustc_lint/lib.rs | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 0577800f3f411..2b1ff8a0e7881 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -15,6 +15,7 @@ //! lints are all available in `rustc_lint::builtin`. use lint::{LintPass, LateLintPass, LintArray}; +use session::config::Epoch; declare_lint! { pub CONST_ERR, @@ -252,6 +253,13 @@ declare_lint! { "hidden lifetime parameters are deprecated, try `Foo<'_>`" } +declare_lint! { + pub BARE_TRAIT_OBJECT, + Warn, + "suggest using `dyn Trait` for trait objects", + Epoch::Epoch2018 +} + /// Does nothing as a lint pass, but registers some `Lint`s /// which are used by other parts of the compiler. #[derive(Copy, Clone)] @@ -298,8 +306,8 @@ impl LintPass for HardwiredLints { COERCE_NEVER, SINGLE_USE_LIFETIME, TYVAR_BEHIND_RAW_POINTER, - ELIDED_LIFETIME_IN_PATH - + ELIDED_LIFETIME_IN_PATH, + BARE_TRAIT_OBJECT ) } } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index c35a3fbe419d1..c162b8409af6a 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -272,6 +272,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { reference: "issue #46906 ", epoch: None, }, + FutureIncompatibleInfo { + id: LintId::of(lint::builtin::BARE_TRAIT_OBJECT), + reference: "issue #48457 ", + epoch: Some(session::config::Epoch::Epoch2018), + } ]); // Register renamed and removed lints From bd29696218c9363d0e6dff7824c53f040eab76fc Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 22 Feb 2018 22:34:06 -0800 Subject: [PATCH 07/14] Add ability for hardwired lints to operate on the diagnostic builder --- src/librustc/lint/builtin.rs | 26 ++++++++++++++++++++++++++ src/librustc/lint/context.rs | 19 ++++++++++++++++--- src/librustc/lint/mod.rs | 5 ++++- src/librustc/session/mod.rs | 14 +++++++++++++- 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 2b1ff8a0e7881..cc7c5dc06660c 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -14,8 +14,11 @@ //! compiler code, rather than using their own custom pass. Those //! lints are all available in `rustc_lint::builtin`. +use errors::DiagnosticBuilder; use lint::{LintPass, LateLintPass, LintArray}; +use session::Session; use session::config::Epoch; +use syntax::codemap::Span; declare_lint! { pub CONST_ERR, @@ -312,4 +315,27 @@ impl LintPass for HardwiredLints { } } +// this could be a closure, but then implementing derive traits +// becomes hacky (and it gets allocated) +#[derive(PartialEq, RustcEncodable, RustcDecodable, Debug)] +pub enum BuiltinLintDiagnostics { + Normal, + BareTraitObject(Span) +} + +impl BuiltinLintDiagnostics { + pub fn run(self, sess: &Session, db: &mut DiagnosticBuilder) { + match self { + BuiltinLintDiagnostics::Normal => (), + BuiltinLintDiagnostics::BareTraitObject(span) => { + let sugg = match sess.codemap().span_to_snippet(span) { + Ok(s) => format!("dyn {}", s), + Err(_) => format!("dyn ") + }; + db.span_suggestion(span, "use `dyn`", sugg); + } + } + } +} + impl<'a, 'tcx> LateLintPass<'a, 'tcx> for HardwiredLints {} diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 870b52c221f37..a9c023d14309d 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -29,6 +29,7 @@ use self::TargetLint::*; use std::slice; use lint::{EarlyLintPassObject, LateLintPassObject}; use lint::{Level, Lint, LintId, LintPass, LintBuffer}; +use lint::builtin::BuiltinLintDiagnostics; use lint::levels::{LintLevelSets, LintLevelsBuilder}; use middle::privacy::AccessLevels; use rustc_serialize::{Decoder, Decodable, Encoder, Encodable}; @@ -92,6 +93,7 @@ pub struct BufferedEarlyLint { pub ast_id: ast::NodeId, pub span: MultiSpan, pub msg: String, + pub diagnostic: BuiltinLintDiagnostics, } /// Extra information for a future incompatibility lint. See the call @@ -446,6 +448,16 @@ pub trait LintContext<'tcx>: Sized { self.lookup(lint, span, msg).emit(); } + fn lookup_and_emit_with_diagnostics>(&self, + lint: &'static Lint, + span: Option, + msg: &str, + diagnostic: BuiltinLintDiagnostics) { + let mut db = self.lookup(lint, span, msg); + diagnostic.run(self.sess(), &mut db); + db.emit(); + } + fn lookup>(&self, lint: &'static Lint, span: Option, @@ -516,9 +528,10 @@ impl<'a> EarlyContext<'a> { fn check_id(&mut self, id: ast::NodeId) { for early_lint in self.buffered.take(id) { - self.lookup_and_emit(early_lint.lint_id.lint, - Some(early_lint.span.clone()), - &early_lint.msg); + self.lookup_and_emit_with_diagnostics(early_lint.lint_id.lint, + Some(early_lint.span.clone()), + &early_lint.msg, + early_lint.diagnostic); } } } diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index e28cc9c201d02..a51d06c06edd3 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -37,6 +37,7 @@ use errors::{DiagnosticBuilder, DiagnosticId}; use hir::def_id::{CrateNum, LOCAL_CRATE}; use hir::intravisit::{self, FnKind}; use hir; +use lint::builtin::BuiltinLintDiagnostics; use session::{config, Session, DiagnosticMessageId}; use std::hash; use syntax::ast; @@ -399,12 +400,14 @@ impl LintBuffer { lint: &'static Lint, id: ast::NodeId, sp: MultiSpan, - msg: &str) { + msg: &str, + diagnostic: BuiltinLintDiagnostics) { let early_lint = BufferedEarlyLint { lint_id: LintId::of(lint), ast_id: id, span: sp, msg: msg.to_string(), + diagnostic }; let arr = self.map.entry(id).or_insert(Vec::new()); if !arr.contains(&early_lint) { diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 6901674a6cf8d..7041efbd5bc5e 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -16,6 +16,7 @@ use ich::Fingerprint; use ich; use lint; +use lint::builtin::BuiltinLintDiagnostics; use middle::allocator::AllocatorKind; use middle::dependency_format; use session::search_paths::PathKind; @@ -341,7 +342,18 @@ impl Session { sp: S, msg: &str) { match *self.buffered_lints.borrow_mut() { - Some(ref mut buffer) => buffer.add_lint(lint, id, sp.into(), msg), + Some(ref mut buffer) => buffer.add_lint(lint, id, sp.into(), + msg, BuiltinLintDiagnostics::Normal), + None => bug!("can't buffer lints after HIR lowering"), + } + } + + pub fn buffer_lint_with_diagnostic>(&self, + lint: &'static lint::Lint, id: ast::NodeId, sp: S, + msg: &str, diagnostic: BuiltinLintDiagnostics) { + match *self.buffered_lints.borrow_mut() { + Some(ref mut buffer) => buffer.add_lint(lint, id, sp.into(), + msg, diagnostic), None => bug!("can't buffer lints after HIR lowering"), } } From 63168f72756da88c7488865160c7dfbd446bb0aa Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 22 Feb 2018 22:44:44 -0800 Subject: [PATCH 08/14] Lint bare traits --- src/librustc/hir/lowering.rs | 24 ++++++++++++++++++++---- src/librustc/lib.rs | 2 ++ src/librustc_mir/lib.rs | 1 + 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 55dcb16c3c95f..aeccf133fa86a 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -46,7 +46,7 @@ use hir::HirVec; use hir::map::{Definitions, DefKey, DefPathData}; use hir::def_id::{DefIndex, DefId, CRATE_DEF_INDEX, DefIndexAddressSpace}; use hir::def::{Def, PathResolution}; -use lint::builtin::PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES; +use lint::builtin::{self, PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES}; use middle::cstore::CrateStore; use rustc_data_structures::indexed_vec::IndexVec; use session::Session; @@ -912,7 +912,11 @@ impl<'a> LoweringContext<'a> { TyKind::Path(ref qself, ref path) => { let id = self.lower_node_id(t.id); let qpath = self.lower_qpath(t.id, qself, path, ParamMode::Explicit, itctx); - return self.ty_path(id, t.span, qpath); + let ty = self.ty_path(id, t.span, qpath); + if let hir::TyTraitObject(..) = ty.node { + self.maybe_lint_bare_trait(t.span, t.id); + } + return ty; } TyKind::ImplicitSelf => { hir::TyPath(hir::QPath::Resolved(None, P(hir::Path { @@ -931,7 +935,7 @@ impl<'a> LoweringContext<'a> { let expr = self.lower_body(None, |this| this.lower_expr(expr)); hir::TyTypeof(expr) } - TyKind::TraitObject(ref bounds, ..) => { + TyKind::TraitObject(ref bounds, kind) => { let mut lifetime_bound = None; let bounds = bounds.iter().filter_map(|bound| { match *bound { @@ -950,6 +954,9 @@ impl<'a> LoweringContext<'a> { let lifetime_bound = lifetime_bound.unwrap_or_else(|| { self.elided_lifetime(t.span) }); + if kind != TraitObjectSyntax::Dyn { + self.maybe_lint_bare_trait(t.span, t.id); + } hir::TyTraitObject(bounds, lifetime_bound) } TyKind::ImplTrait(ref bounds) => { @@ -3685,7 +3692,6 @@ impl<'a> LoweringContext<'a> { // The original ID is taken by the `PolyTraitRef`, // so the `Ty` itself needs a different one. id = self.next_id(); - hir::TyTraitObject(hir_vec![principal], self.elided_lifetime(span)) } else { hir::TyPath(hir::QPath::Resolved(None, path)) @@ -3703,6 +3709,16 @@ impl<'a> LoweringContext<'a> { name: hir::LifetimeName::Implicit, } } + + fn maybe_lint_bare_trait(&self, span: Span, id: NodeId) { + if self.sess.features.borrow().dyn_trait { + self.sess.buffer_lint_with_diagnostic( + builtin::BARE_TRAIT_OBJECT, id, span, + "trait objects without an explicit `dyn` are deprecated", + builtin::BuiltinLintDiagnostics::BareTraitObject(span) + ) + } + } } fn body_ids(bodies: &BTreeMap) -> Vec { diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index a7a2619505931..e1ca4665df8bb 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -40,6 +40,8 @@ html_root_url = "https://doc.rust-lang.org/nightly/")] #![deny(warnings)] +#![cfg_attr(not(stage0), allow(bare_trait_object))] + #![feature(box_patterns)] #![feature(box_syntax)] #![feature(conservative_impl_trait)] diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 1699ad0f19cf6..7905065bc1d8f 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -15,6 +15,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! */ #![deny(warnings)] +#![cfg_attr(not(stage0), allow(bare_trait_object))] #![feature(box_patterns)] #![feature(box_syntax)] From 177271f91401852841c647ae01b4072253d69234 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Fri, 23 Feb 2018 00:10:37 -0800 Subject: [PATCH 09/14] span_bug doesn't work well at this stage, use the session directly --- src/librustc/lint/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index a9c023d14309d..bfd2034dd6cfe 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -1084,7 +1084,7 @@ pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) { if !sess.opts.actually_rustdoc { for (_id, lints) in cx.buffered.map { for early_lint in lints { - span_bug!(early_lint.span, "failed to process buffered lint here"); + sess.delay_span_bug(early_lint.span, "failed to process buffered lint here"); } } } From dd67fe17276e4bf8cd054d2a8453dc58e0571447 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Fri, 23 Feb 2018 13:00:11 -0800 Subject: [PATCH 10/14] Silence warning in test --- src/test/compile-fail/trait-bounds-not-on-struct.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/compile-fail/trait-bounds-not-on-struct.rs b/src/test/compile-fail/trait-bounds-not-on-struct.rs index 6cd439167314b..0dd1a4e7d7335 100644 --- a/src/test/compile-fail/trait-bounds-not-on-struct.rs +++ b/src/test/compile-fail/trait-bounds-not-on-struct.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(dyn_trait)] +#![allow(bare_trait_object)] struct Foo; From 12c7e273309e7fb3c8309e0d6217d96baa7cbc89 Mon Sep 17 00:00:00 2001 From: Mikhail Modin Date: Mon, 19 Feb 2018 19:42:00 +0300 Subject: [PATCH 11/14] restore Subslice move out from array after elaborate drops and borrowck --- src/librustc_mir/transform/mod.rs | 1 + .../transform/uniform_array_move_out.rs | 198 +++++++++++++++++- src/test/mir-opt/uniform_array_move_out.rs | 25 +++ 3 files changed, 213 insertions(+), 11 deletions(-) diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 7ed250e94c52f..f721cdf714dca 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -257,6 +257,7 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx // Optimizations begin. + uniform_array_move_out::RestoreSubsliceArrayMoveOut, inline::Inline, instcombine::InstCombine, deaggregator::Deaggregator, diff --git a/src/librustc_mir/transform/uniform_array_move_out.rs b/src/librustc_mir/transform/uniform_array_move_out.rs index 0db5ecf0eb270..e46de17047986 100644 --- a/src/librustc_mir/transform/uniform_array_move_out.rs +++ b/src/librustc_mir/transform/uniform_array_move_out.rs @@ -34,15 +34,15 @@ // and mir statement _11 = move _2[-1 of 1]; replaced by: // _11 = move _2[2 of 3]; // -// FIXME: convert to Subslice back for performance reason // FIXME: integrate this transformation to the mir build use rustc::ty; use rustc::ty::TyCtxt; use rustc::mir::*; -use rustc::mir::visit::Visitor; +use rustc::mir::visit::{Visitor, PlaceContext}; use transform::{MirPass, MirSource}; use util::patch::MirPatch; +use rustc_data_structures::indexed_vec::{IndexVec}; pub struct UniformArrayMoveOut; @@ -67,12 +67,12 @@ struct UniformArrayMoveOutVisitor<'a, 'tcx: 'a> { } impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> { - fn visit_statement(&mut self, - block: BasicBlock, - statement: &Statement<'tcx>, - location: Location) { - if let StatementKind::Assign(ref dst_place, - Rvalue::Use(Operand::Move(ref src_place))) = statement.kind { + fn visit_assign(&mut self, + block: BasicBlock, + dst_place: &Place<'tcx>, + rvalue: &Rvalue<'tcx>, + location: Location) { + if let Rvalue::Use(Operand::Move(ref src_place)) = rvalue { if let Place::Projection(ref proj) = *src_place { if let ProjectionElem::ConstantIndex{offset: _, min_length: _, @@ -92,7 +92,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> { } } } - return self.super_statement(block, statement, location); + self.super_assign(block, dst_place, rvalue, location) } } @@ -104,7 +104,7 @@ impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> { item_ty: &'tcx ty::TyS<'tcx>, size: u32) { match proj.elem { - // uniform _10 = move _2[:-1]; + // uniforms statements like_10 = move _2[:-1]; ProjectionElem::Subslice{from, to} => { self.patch.make_nop(location); let temps : Vec<_> = (from..(size-to)).map(|i| { @@ -133,7 +133,7 @@ impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> { self.patch.add_statement(location, StatementKind::StorageDead(temp)); } } - // _11 = move _2[-1 of 1]; + // uniforms statements like _11 = move _2[-1 of 1]; ProjectionElem::ConstantIndex{offset, min_length: _, from_end: true} => { self.patch.make_nop(location); self.patch.add_assign(location, @@ -151,3 +151,179 @@ impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> { } } } + +// Restore Subslice move out after analysis +// Example: +// +// next statements: +// StorageLive(_12); +// _12 = move _2[0 of 3]; +// StorageLive(_13); +// _13 = move _2[1 of 3]; +// _10 = [move _12, move _13] +// StorageDead(_12); +// StorageDead(_13); +// +// replaced by _10 = move _2[:-1]; + +pub struct RestoreSubsliceArrayMoveOut; + +impl MirPass for RestoreSubsliceArrayMoveOut { + fn run_pass<'a, 'tcx>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + _src: MirSource, + mir: &mut Mir<'tcx>) { + let mut patch = MirPatch::new(mir); + { + let mut visitor = RestoreDataCollector { + locals_use: IndexVec::from_elem(LocalUse::new(), &mir.local_decls), + candidates: vec![], + }; + visitor.visit_mir(mir); + + for candidate in &visitor.candidates { + let statement = &mir[candidate.block].statements[candidate.statement_index]; + if let StatementKind::Assign(ref dst_place, ref rval) = statement.kind { + if let Rvalue::Aggregate(box AggregateKind::Array(_), ref items) = *rval { + let items : Vec<_> = items.iter().map(|item| { + if let Operand::Move(Place::Local(local)) = item { + let local_use = &visitor.locals_use[*local]; + let opt_index_and_place = Self::try_get_item_source(local_use, mir); + // each local should be used twice: + // in assign and in aggregate statments + if local_use.use_count == 2 && opt_index_and_place.is_some() { + let (index, src_place) = opt_index_and_place.unwrap(); + return Some((local_use, index, src_place)); + } + } + None + }).collect(); + + let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2); + let opt_size = opt_src_place.and_then(|src_place| { + let src_ty = src_place.ty(mir, tcx).to_ty(tcx); + if let ty::TyArray(_, ref size_o) = src_ty.sty { + size_o.val.to_const_int().and_then(|v| v.to_u64()) + } else { + None + } + }); + Self::check_and_patch(*candidate, &items, opt_size, &mut patch, dst_place); + } + } + } + } + patch.apply(mir); + } +} + +impl RestoreSubsliceArrayMoveOut { + // Checks that source has size, all locals are inited from same source place and + // indices is an integer interval. If all checks pass do the replacent. + // items are Vec> + fn check_and_patch<'tcx>(candidate: Location, + items: &Vec)>>, + opt_size: Option, + patch: &mut MirPatch<'tcx>, + dst_place: &Place<'tcx>) { + let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2); + + if opt_size.is_some() && items.iter().all( + |l| l.is_some() && l.unwrap().2 == opt_src_place.unwrap()) { + + let indicies: Vec<_> = items.iter().map(|x| x.unwrap().1).collect(); + for i in 1..indicies.len() { + if indicies[i - 1] + 1 != indicies[i] { + return; + } + } + + let min = *indicies.first().unwrap(); + let max = *indicies.last().unwrap(); + + for item in items { + let locals_use = item.unwrap().0; + patch.make_nop(locals_use.alive.unwrap()); + patch.make_nop(locals_use.dead.unwrap()); + patch.make_nop(locals_use.first_use.unwrap()); + } + patch.make_nop(candidate); + let size = opt_size.unwrap() as u32; + patch.add_assign(candidate, + dst_place.clone(), + Rvalue::Use( + Operand::Move( + Place::Projection(box PlaceProjection{ + base: opt_src_place.unwrap().clone(), + elem: ProjectionElem::Subslice{ + from: min, to: size - max - 1}})))); + } + } + + fn try_get_item_source<'a, 'tcx>(local_use: &LocalUse, + mir: &'a Mir<'tcx>) -> Option<(u32, &'a Place<'tcx>)> { + if let Some(location) = local_use.first_use { + let block = &mir[location.block]; + if block.statements.len() > location.statement_index { + let statement = &block.statements[location.statement_index]; + if let StatementKind::Assign( + Place::Local(_), + Rvalue::Use(Operand::Move(Place::Projection(box PlaceProjection{ + ref base, elem: ProjectionElem::ConstantIndex{ + offset, min_length: _, from_end: false}})))) = statement.kind { + return Some((offset, base)) + } + } + } + None + } +} + +#[derive(Copy, Clone, Debug)] +struct LocalUse { + alive: Option, + dead: Option, + use_count: u32, + first_use: Option, +} + +impl LocalUse { + pub fn new() -> Self { + LocalUse{alive: None, dead: None, use_count: 0, first_use: None} + } +} + +struct RestoreDataCollector { + locals_use: IndexVec, + candidates: Vec, +} + +impl<'tcx> Visitor<'tcx> for RestoreDataCollector { + fn visit_assign(&mut self, + block: BasicBlock, + place: &Place<'tcx>, + rvalue: &Rvalue<'tcx>, + location: Location) { + if let Rvalue::Aggregate(box AggregateKind::Array(_), _) = *rvalue { + self.candidates.push(location); + } + self.super_assign(block, place, rvalue, location) + } + + fn visit_local(&mut self, + local: &Local, + context: PlaceContext<'tcx>, + location: Location) { + let local_use = &mut self.locals_use[*local]; + match context { + PlaceContext::StorageLive => local_use.alive = Some(location), + PlaceContext::StorageDead => local_use.dead = Some(location), + _ => { + local_use.use_count += 1; + if local_use.first_use.is_none() { + local_use.first_use = Some(location); + } + } + } + } +} diff --git a/src/test/mir-opt/uniform_array_move_out.rs b/src/test/mir-opt/uniform_array_move_out.rs index 4a310255aac57..482b69a59ddbc 100644 --- a/src/test/mir-opt/uniform_array_move_out.rs +++ b/src/test/mir-opt/uniform_array_move_out.rs @@ -57,3 +57,28 @@ fn main() { // nop; // _0 = (); // END rustc.move_out_by_subslice.UniformArrayMoveOut.after.mir + +// START rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.before.mir +// StorageLive(_6); +// StorageLive(_7); +// _7 = move _1[0 of 2]; +// StorageLive(_8); +// _8 = move _1[1 of 2]; +// _6 = [move _7, move _8]; +// StorageDead(_7); +// StorageDead(_8); +// _0 = (); +// END rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.before.mir + +// START rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.after.mir +// StorageLive(_6); +// nop; +// nop; +// nop; +// nop; +// _6 = move _1[0:]; +// nop; +// nop; +// nop; +// _0 = (); +// END rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.after.mir From 0cb367266b0a336c0a18ae999beeadd3235961a2 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Fri, 23 Feb 2018 13:52:28 -0800 Subject: [PATCH 12/14] Emit parentheses in suggestion for global paths --- src/librustc/hir/lowering.rs | 8 ++++---- src/librustc/lint/builtin.rs | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index aeccf133fa86a..9d4011fa7ad9f 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -914,7 +914,7 @@ impl<'a> LoweringContext<'a> { let qpath = self.lower_qpath(t.id, qself, path, ParamMode::Explicit, itctx); let ty = self.ty_path(id, t.span, qpath); if let hir::TyTraitObject(..) = ty.node { - self.maybe_lint_bare_trait(t.span, t.id); + self.maybe_lint_bare_trait(t.span, t.id, qself.is_none() && path.is_global()); } return ty; } @@ -955,7 +955,7 @@ impl<'a> LoweringContext<'a> { self.elided_lifetime(t.span) }); if kind != TraitObjectSyntax::Dyn { - self.maybe_lint_bare_trait(t.span, t.id); + self.maybe_lint_bare_trait(t.span, t.id, false); } hir::TyTraitObject(bounds, lifetime_bound) } @@ -3710,12 +3710,12 @@ impl<'a> LoweringContext<'a> { } } - fn maybe_lint_bare_trait(&self, span: Span, id: NodeId) { + fn maybe_lint_bare_trait(&self, span: Span, id: NodeId, is_global: bool) { if self.sess.features.borrow().dyn_trait { self.sess.buffer_lint_with_diagnostic( builtin::BARE_TRAIT_OBJECT, id, span, "trait objects without an explicit `dyn` are deprecated", - builtin::BuiltinLintDiagnostics::BareTraitObject(span) + builtin::BuiltinLintDiagnostics::BareTraitObject(span, is_global) ) } } diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index cc7c5dc06660c..b68b7dc6c0672 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -320,15 +320,16 @@ impl LintPass for HardwiredLints { #[derive(PartialEq, RustcEncodable, RustcDecodable, Debug)] pub enum BuiltinLintDiagnostics { Normal, - BareTraitObject(Span) + BareTraitObject(Span, /* is_global */ bool) } impl BuiltinLintDiagnostics { pub fn run(self, sess: &Session, db: &mut DiagnosticBuilder) { match self { BuiltinLintDiagnostics::Normal => (), - BuiltinLintDiagnostics::BareTraitObject(span) => { + BuiltinLintDiagnostics::BareTraitObject(span, is_global) => { let sugg = match sess.codemap().span_to_snippet(span) { + Ok(ref s) if is_global => format!("dyn ({})", s), Ok(s) => format!("dyn {}", s), Err(_) => format!("dyn ") }; From db6a5ee1aa57ec5f1d41b8a2a8519b7ee7d3598f Mon Sep 17 00:00:00 2001 From: Alexander Ronald Altman Date: Tue, 27 Feb 2018 23:57:47 -0600 Subject: [PATCH 13/14] Minor grammatical/style fix in docs. --- src/libcore/iter/iterator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index 9d8a71250f88a..b06534c9c1ec9 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -1062,8 +1062,8 @@ pub trait Iterator { /// assert_eq!(merged, "alphabetagamma"); /// ``` /// - /// You can also rewrite this in terms of [`flat_map()`] which is preferable - /// in this case since that conveys intent clearer: + /// You can also rewrite this in terms of [`flat_map()`], which is preferable + /// in this case since it conveys intent more clearly: /// /// ``` /// let words = ["alpha", "beta", "gamma"]; From b9e9b4a1461e3a49b68db56413324dc1b6a2ed60 Mon Sep 17 00:00:00 2001 From: Tobias Stolzmann Date: Wed, 28 Feb 2018 15:29:16 +0100 Subject: [PATCH 14/14] Add std::path::Path::ancestors Squashed commit of the following: commit 1b5d55e26f667b1a25c83c5db0cbb072013a5122 Author: Tobias Stolzmann Date: Wed Feb 28 00:06:15 2018 +0100 Bugfix commit 4265c2db0b0aaa66fdeace5d329665fd2d13903a Author: Tobias Stolzmann Date: Tue Feb 27 22:59:12 2018 +0100 Rename std::path::Path::parents into std::path::Path::ancestors commit 2548e4b14d377d20adad0f08304a0dd6f8e48e23 Author: Tobias Stolzmann Date: Tue Feb 27 12:50:37 2018 +0100 Add tracking issue commit 3e2ce51a6eea0e39af05849f76dd2cefd5035e86 Author: Tobias Stolzmann Date: Mon Feb 26 15:05:15 2018 +0100 impl FusedIterator for Parents commit a7e096420809740311e19d963d4aba6df77be2f9 Author: Tobias Stolzmann Date: Mon Feb 26 14:38:41 2018 +0100 Clarify that the iterator returned will yield at least one value commit 796a36ea203cd197cc4c810eebd21c7e3433e6f1 Author: Tobias Stolzmann Date: Thu Feb 22 14:01:21 2018 +0100 Fix examples commit e279383b21f11c97269cb355a5b2a0ecdb65bb0c Author: Tobias Stolzmann Date: Thu Feb 22 04:47:24 2018 +0100 Add std::path::Path::parents --- src/libstd/path.rs | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/libstd/path.rs b/src/libstd/path.rs index 4bbad30a5a315..1608a752a463f 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -1035,6 +1035,50 @@ impl<'a> cmp::Ord for Components<'a> { } } +/// An iterator over [`Path`] and its ancestors. +/// +/// This `struct` is created by the [`ancestors`] method on [`Path`]. +/// See its documentation for more. +/// +/// # Examples +/// +/// ``` +/// #![feature(path_ancestors)] +/// +/// use std::path::Path; +/// +/// let path = Path::new("/foo/bar"); +/// +/// for ancestor in path.ancestors() { +/// println!("{}", ancestor.display()); +/// } +/// ``` +/// +/// [`ancestors`]: struct.Path.html#method.ancestors +/// [`Path`]: struct.Path.html +#[derive(Copy, Clone, Debug)] +#[unstable(feature = "path_ancestors", issue = "48581")] +pub struct Ancestors<'a> { + next: Option<&'a Path>, +} + +#[unstable(feature = "path_ancestors", issue = "48581")] +impl<'a> Iterator for Ancestors<'a> { + type Item = &'a Path; + + fn next(&mut self) -> Option { + let next = self.next; + self.next = match next { + Some(path) => path.parent(), + None => None, + }; + next + } +} + +#[unstable(feature = "fused", issue = "35602")] +impl<'a> FusedIterator for Ancestors<'a> {} + //////////////////////////////////////////////////////////////////////////////// // Basic types and traits //////////////////////////////////////////////////////////////////////////////// @@ -1820,6 +1864,37 @@ impl Path { }) } + /// Produces an iterator over `Path` and its ancestors. + /// + /// The iterator will yield the `Path` that is returned if the [`parent`] method is used zero + /// or more times. That means, the iterator will yield `&self`, `&self.parent().unwrap()`, + /// `&self.parent().unwrap().parent().unwrap()` and so on. If the [`parent`] method returns + /// [`None`], the iterator will do likewise. The iterator will always yield at least one value, + /// namely `&self`. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_ancestors)] + /// + /// use std::path::Path; + /// + /// let mut ancestors = Path::new("/foo/bar").ancestors(); + /// assert_eq!(ancestors.next(), Some(Path::new("/foo/bar"))); + /// assert_eq!(ancestors.next(), Some(Path::new("/foo"))); + /// assert_eq!(ancestors.next(), Some(Path::new("/"))); + /// assert_eq!(ancestors.next(), None); + /// ``` + /// + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// [`parent`]: struct.Path.html#method.parent + #[unstable(feature = "path_ancestors", issue = "48581")] + pub fn ancestors(&self) -> Ancestors { + Ancestors { + next: Some(&self), + } + } + /// Returns the final component of the `Path`, if there is one. /// /// If the path is a normal file, this is the file name. If it's the path of a directory, this