diff --git a/Cargo.lock b/Cargo.lock index a36df24359df7..e34d737eb91bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2716,6 +2716,7 @@ dependencies = [ "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", "rustc_incremental 0.0.0", + "rustc_interface 0.0.0", "rustc_lint 0.0.0", "rustc_metadata 0.0.0", "rustc_mir 0.0.0", @@ -2768,6 +2769,36 @@ dependencies = [ "syntax_pos 0.0.0", ] +[[package]] +name = "rustc_interface" +version = "0.0.0" +dependencies = [ + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc 0.0.0", + "rustc-rayon 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_allocator 0.0.0", + "rustc_borrowck 0.0.0", + "rustc_codegen_utils 0.0.0", + "rustc_data_structures 0.0.0", + "rustc_errors 0.0.0", + "rustc_incremental 0.0.0", + "rustc_lint 0.0.0", + "rustc_metadata 0.0.0", + "rustc_mir 0.0.0", + "rustc_passes 0.0.0", + "rustc_plugin 0.0.0", + "rustc_privacy 0.0.0", + "rustc_resolve 0.0.0", + "rustc_traits 0.0.0", + "rustc_typeck 0.0.0", + "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serialize 0.0.0", + "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "syntax 0.0.0", + "syntax_ext 0.0.0", + "syntax_pos 0.0.0", +] + [[package]] name = "rustc_lint" version = "0.0.0" diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index 7a765973e20f8..03a7535eef125 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -109,6 +109,8 @@ fn main() { // actually downloaded, so we just always pass the `--sysroot` option. cmd.arg("--sysroot").arg(&sysroot); + cmd.arg("-Zexternal-macro-backtrace"); + // When we build Rust dylibs they're all intended for intermediate // usage, so make sure we pass the -Cprefer-dynamic flag instead of // linking all deps statically into the dylib. diff --git a/src/doc/guide-error-handling.md b/src/doc/guide-error-handling.md index 54fa529f3aa8e..fd71d3e3c8e79 100644 --- a/src/doc/guide-error-handling.md +++ b/src/doc/guide-error-handling.md @@ -1,4 +1,4 @@ % Error Handling in Rust This content has moved into -[the Rust Programming Language book](book/error-handling.html). +[the Rust Programming Language book](book/ch09-00-error-handling.html). diff --git a/src/doc/guide-ownership.md b/src/doc/guide-ownership.md index 884f14726ca87..767dafc5baf92 100644 --- a/src/doc/guide-ownership.md +++ b/src/doc/guide-ownership.md @@ -1,4 +1,4 @@ % The (old) Rust Ownership Guide This content has moved into -[the Rust Programming Language book](book/ownership.html). +[the Rust Programming Language book](book/ch04-00-understanding-ownership.html). diff --git a/src/doc/guide-pointers.md b/src/doc/guide-pointers.md index dc80ec4399131..bafdb2fe0bbc3 100644 --- a/src/doc/guide-pointers.md +++ b/src/doc/guide-pointers.md @@ -2,6 +2,6 @@ This content has been removed, with no direct replacement. Rust only has two built-in pointer types now, -[references](book/references-and-borrowing.html) and [raw +[references](book/ch04-02-references-and-borrowing.html) and [raw pointers](book/raw-pointers.html). Older Rusts had many more pointer types, they’re gone now. diff --git a/src/doc/guide-testing.md b/src/doc/guide-testing.md index 67bcb0a5e546a..28d9fb48b73e7 100644 --- a/src/doc/guide-testing.md +++ b/src/doc/guide-testing.md @@ -1,4 +1,4 @@ % The (old) Rust Testing Guide This content has moved into -[the Rust Programming Language book](book/testing.html). +[the Rust Programming Language book](book/ch11-00-testing.html). diff --git a/src/libcore/convert.rs b/src/libcore/convert.rs index 4a7c6e15a4df1..b8d751cfbb6df 100644 --- a/src/libcore/convert.rs +++ b/src/libcore/convert.rs @@ -116,9 +116,6 @@ pub const fn identity(x: T) -> T { x } /// - Use `Borrow` when the goal is related to writing code that is agnostic to /// the type of borrow and whether it is a reference or value /// -/// See [the book][book] for a more detailed comparison. -/// -/// [book]: ../../book/first-edition/borrow-and-asref.html /// [`Borrow`]: ../../std/borrow/trait.Borrow.html /// /// **Note: this trait must not fail**. If the conversion can fail, use a @@ -351,7 +348,7 @@ pub trait Into: Sized { /// [`String`]: ../../std/string/struct.String.html /// [`Into`]: trait.Into.html /// [`from`]: trait.From.html#tymethod.from -/// [book]: ../../book/first-edition/error-handling.html +/// [book]: ../../book/ch09-00-error-handling.html #[stable(feature = "rust1", since = "1.0.0")] pub trait From: Sized { /// Performs the conversion. diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 457d556e4fa6d..c4b41f1a3090c 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -78,7 +78,7 @@ impl !Send for *mut T { } /// // be made into an object /// ``` /// -/// [trait object]: ../../book/first-edition/trait-objects.html +/// [trait object]: ../../book/ch17-02-trait-objects.html #[stable(feature = "rust1", since = "1.0.0")] #[lang = "sized"] #[rustc_on_unimplemented( @@ -518,7 +518,7 @@ macro_rules! impls{ /// types. We track the Rust type using a phantom type parameter on /// the struct `ExternalResource` which wraps a handle. /// -/// [FFI]: ../../book/first-edition/ffi.html +/// [FFI]: ../../book/ch19-01-unsafe-rust.html#using-extern-functions-to-call-external-code /// /// ``` /// # #![allow(dead_code)] diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 2a493e88fe896..3f7455aeb59b9 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -299,7 +299,7 @@ pub const fn size_of() -> usize { /// then `size_of_val` can be used to get the dynamically-known size. /// /// [slice]: ../../std/primitive.slice.html -/// [trait object]: ../../book/first-edition/trait-objects.html +/// [trait object]: ../../book/ch17-02-trait-objects.html /// /// # Examples /// diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 238f8f635415b..6c061189d00d7 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -5,7 +5,9 @@ //! function-like macros `#[proc_macro]`, macro attributes `#[proc_macro_attribute]` and //! custom derive attributes`#[proc_macro_derive]`. //! -//! See [the book](../book/first-edition/procedural-macros.html) for more. +//! See [the book] for more. +//! +//! [the book]: ../book/ch19-06-macros.html#procedural-macros-for-generating-code-from-attributes #![stable(feature = "proc_macro_lib", since = "1.15.0")] #![deny(missing_docs)] diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 796739c872174..d8546a678f549 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -456,6 +456,7 @@ define_dep_nodes!( <'tcx> [eval_always] CoherenceInherentImplOverlapCheck, [] CoherenceCheckTrait(DefId), [eval_always] PrivacyAccessLevels(CrateNum), + [eval_always] Analysis(CrateNum), // Represents the MIR for a fn; also used as the task node for // things read/modify that MIR. diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 6644175bbd8a8..f46ff6f6062c2 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1,3 +1,4 @@ +// ignore-tidy-linelength #![allow(non_snake_case)] // Error messages for EXXXX errors. @@ -410,7 +411,7 @@ fn baz<'a>(x: &'a str, y: &str) -> &str { } Lifetime elision in implementation headers was part of the lifetime elision RFC. It is, however, [currently unimplemented][iss15872]. -[book-le]: https://doc.rust-lang.org/nightly/book/first-edition/lifetimes.html#lifetime-elision +[book-le]: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#lifetime-elision [iss15872]: https://github.com/rust-lang/rust/issues/15872 "##, @@ -646,7 +647,9 @@ attributes: #![no_std] ``` -See also https://doc.rust-lang.org/book/first-edition/no-stdlib.html +See also the [unstable book][1]. + +[1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html#writing-an-executable-without-stdlib "##, E0214: r##" @@ -1713,7 +1716,7 @@ fn main() { ``` To understand better how closures work in Rust, read: -https://doc.rust-lang.org/book/first-edition/closures.html +https://doc.rust-lang.org/book/ch13-01-closures.html "##, E0580: r##" diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index cc5b105bad0d4..84487c40f8745 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -3859,7 +3859,7 @@ impl<'a> LoweringContext<'a> { hir::ExprKind::Call(f, args.iter().map(|x| self.lower_expr(x)).collect()) } ExprKind::MethodCall(ref seg, ref args) => { - let hir_seg = self.lower_path_segment( + let hir_seg = P(self.lower_path_segment( e.span, seg, ParamMode::Optional, @@ -3867,7 +3867,7 @@ impl<'a> LoweringContext<'a> { ParenthesizedGenericArgs::Err, ImplTraitContext::disallowed(), None, - ); + )); let args = args.iter().map(|x| self.lower_expr(x)).collect(); hir::ExprKind::MethodCall(hir_seg, seg.ident.span, args) } @@ -4148,7 +4148,7 @@ impl<'a> LoweringContext<'a> { node: if is_unit { hir::ExprKind::Path(struct_path) } else { - hir::ExprKind::Struct(struct_path, fields, None) + hir::ExprKind::Struct(P(struct_path), fields, None) }, span: e.span, attrs: e.attrs.clone(), @@ -4220,13 +4220,13 @@ impl<'a> LoweringContext<'a> { hir::ExprKind::InlineAsm(P(hir_asm), outputs, inputs) } ExprKind::Struct(ref path, ref fields, ref maybe_expr) => hir::ExprKind::Struct( - self.lower_qpath( + P(self.lower_qpath( e.id, &None, path, ParamMode::Optional, ImplTraitContext::disallowed(), - ), + )), fields.iter().map(|x| self.lower_field(x)).collect(), maybe_expr.as_ref().map(|x| P(self.lower_expr(x))), ), diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 0bf9857b49777..d774359fa79ec 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1319,6 +1319,10 @@ pub struct Expr { pub hir_id: HirId, } +// `Expr` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +static_assert!(MEM_SIZE_OF_EXPR: std::mem::size_of::() == 72); + impl Expr { pub fn precedence(&self) -> ExprPrecedence { match self.node { @@ -1438,7 +1442,7 @@ pub enum ExprKind { /// and the remaining elements are the rest of the arguments. /// Thus, `x.foo::(a, b, c, d)` is represented as /// `ExprKind::MethodCall(PathSegment { foo, [Bar, Baz] }, [x, a, b, c, d])`. - MethodCall(PathSegment, Span, HirVec), + MethodCall(P, Span, HirVec), /// A tuple (e.g., `(a, b, c ,d)`). Tup(HirVec), /// A binary operation (e.g., `a + b`, `a * b`). @@ -1506,7 +1510,7 @@ pub enum ExprKind { /// /// For example, `Foo {x: 1, y: 2}`, or /// `Foo {x: 1, .. base}`, where `base` is the `Option`. - Struct(QPath, HirVec, Option>), + Struct(P, HirVec, Option>), /// An array literal constructed from one repeated element. /// diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index ff4e520d8e08e..705d9c4cf93ea 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -508,22 +508,31 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } } - ObligationCauseCode::MatchExpressionArm { arm_span, source } => match source { + ObligationCauseCode::MatchExpressionArm { + source, + ref prior_arms, + last_ty, + .. + } => match source { hir::MatchSource::IfLetDesugar { .. } => { - let msg = "`if let` arm with an incompatible type"; - if self.tcx.sess.source_map().is_multiline(arm_span) { - err.span_note(arm_span, msg); - } else { - err.span_label(arm_span, msg); - } + let msg = "`if let` arms have incompatible types"; + err.span_label(cause.span, msg); } hir::MatchSource::TryDesugar => {} _ => { - let msg = "match arm with an incompatible type"; - if self.tcx.sess.source_map().is_multiline(arm_span) { - err.span_note(arm_span, msg); - } else { - err.span_label(arm_span, msg); + let msg = "`match` arms have incompatible types"; + err.span_label(cause.span, msg); + if prior_arms.len() <= 4 { + for sp in prior_arms { + err.span_label(*sp, format!( + "this is found to be of type `{}`", + last_ty, + )); + } + } else if let Some(sp) = prior_arms.last() { + err.span_label(*sp, format!( + "this and all prior arms are found to be of type `{}`", last_ty, + )); } } }, diff --git a/src/librustc/infer/error_reporting/nice_region_error/mod.rs b/src/librustc/infer/error_reporting/nice_region_error/mod.rs index dad1e3ba80da3..d995fe92337c4 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/mod.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/mod.rs @@ -1,9 +1,10 @@ use crate::infer::InferCtxt; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::lexical_region_resolve::RegionResolutionError::*; -use syntax::source_map::Span; use crate::ty::{self, TyCtxt}; use crate::util::common::ErrorReported; +use errors::DiagnosticBuilder; +use syntax::source_map::Span; mod different_lifetimes; mod find_anon_type; @@ -59,7 +60,7 @@ impl<'cx, 'gcx, 'tcx> NiceRegionError<'cx, 'gcx, 'tcx> { self.infcx.tcx } - pub fn try_report_from_nll(&self) -> Option { + pub fn try_report_from_nll(&self) -> Option> { // Due to the improved diagnostics returned by the MIR borrow checker, only a subset of // the nice region errors are required when running under the MIR borrow checker. self.try_report_named_anon_conflict() @@ -68,6 +69,7 @@ impl<'cx, 'gcx, 'tcx> NiceRegionError<'cx, 'gcx, 'tcx> { pub fn try_report(&self) -> Option { self.try_report_from_nll() + .map(|mut diag| { diag.emit(); ErrorReported }) .or_else(|| self.try_report_anon_anon_conflict()) .or_else(|| self.try_report_outlives_closure()) .or_else(|| self.try_report_static_impl_trait()) diff --git a/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs index b10af400f2b6c..3821484d38e5f 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs @@ -2,13 +2,12 @@ //! where one region is named and the other is anonymous. use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::ty; -use crate::util::common::ErrorReported; -use errors::Applicability; +use errors::{Applicability, DiagnosticBuilder}; impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> { /// When given a `ConcreteFailure` for a function with arguments containing a named region and /// an anonymous region, emit an descriptive diagnostic error. - pub(super) fn try_report_named_anon_conflict(&self) -> Option { + pub(super) fn try_report_named_anon_conflict(&self) -> Option> { let (span, sub, sup) = self.get_regions(); debug!( @@ -96,21 +95,23 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> { ("parameter type".to_owned(), "type".to_owned()) }; - struct_span_err!( + let mut diag = struct_span_err!( self.tcx().sess, span, E0621, "explicit lifetime required in {}", error_var - ).span_suggestion( - new_ty_span, - &format!("add explicit lifetime `{}` to {}", named, span_label_var), - new_ty.to_string(), - Applicability::Unspecified, - ) - .span_label(span, format!("lifetime `{}` required", named)) - .emit(); - return Some(ErrorReported); + ); + + diag.span_suggestion( + new_ty_span, + &format!("add explicit lifetime `{}` to {}", named, span_label_var), + new_ty.to_string(), + Applicability::Unspecified, + ) + .span_label(span, format!("lifetime `{}` required", named)); + + Some(diag) } // This method returns whether the given Region is Named diff --git a/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs b/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs index 843fa8b0182e2..3b2fb7d41008e 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs @@ -8,13 +8,12 @@ use crate::traits::{ObligationCause, ObligationCauseCode}; use crate::ty; use crate::ty::error::ExpectedFound; use crate::ty::subst::Substs; -use crate::util::common::ErrorReported; use crate::util::ppaux::RegionHighlightMode; impl NiceRegionError<'me, 'gcx, 'tcx> { /// When given a `ConcreteFailure` for a function with arguments containing a named region and /// an anonymous region, emit a descriptive diagnostic error. - pub(super) fn try_report_placeholder_conflict(&self) -> Option { + pub(super) fn try_report_placeholder_conflict(&self) -> Option> { match &self.error { /////////////////////////////////////////////////////////////////////////// // NB. The ordering of cases in this match is very @@ -178,7 +177,7 @@ impl NiceRegionError<'me, 'gcx, 'tcx> { trait_def_id: DefId, expected_substs: &'tcx Substs<'tcx>, actual_substs: &'tcx Substs<'tcx>, - ) -> ErrorReported { + ) -> DiagnosticBuilder<'me> { debug!( "try_report_placeholders_trait(\ vid={:?}, \ @@ -295,8 +294,7 @@ impl NiceRegionError<'me, 'gcx, 'tcx> { any_self_ty_has_vid, ); - err.emit(); - ErrorReported + err } /// Add notes with details about the expected and actual trait refs, with attention to cases diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 6f10b0e2c0e67..655707ff9bd0d 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -557,7 +557,7 @@ impl BuiltinLintDiagnostics { } BuiltinLintDiagnostics::UnusedImports(message, replaces) => { if !replaces.is_empty() { - db.multipart_suggestion( + db.tool_only_multipart_suggestion( &message, replaces, Applicability::MachineApplicable, diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 22b1c3f18acb2..5d2a56597fbb7 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -499,6 +499,13 @@ impl Input { Input::Str { ref mut input, .. } => Some(input), } } + + pub fn source_name(&self) -> FileName { + match *self { + Input::File(ref ifile) => ifile.clone().into(), + Input::Str { ref name, .. } => name.clone(), + } + } } #[derive(Clone, Hash)] diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 58bd4782b2143..6080e7eb1b133 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -899,14 +899,14 @@ impl Session { /// Returns the number of query threads that should be used for this /// compilation - pub fn threads_from_opts(opts: &config::Options) -> usize { - opts.debugging_opts.threads.unwrap_or(::num_cpus::get()) + pub fn threads_from_count(query_threads: Option) -> usize { + query_threads.unwrap_or(::num_cpus::get()) } /// Returns the number of query threads that should be used for this /// compilation pub fn threads(&self) -> usize { - Self::threads_from_opts(&self.opts) + Self::threads_from_count(self.opts.debugging_opts.threads) } /// Returns the number of codegen units that should be used for this @@ -1023,16 +1023,67 @@ pub fn build_session( local_crate_source_file, registry, Lrc::new(source_map::SourceMap::new(file_path_mapping)), - None, + DiagnosticOutput::Default, + Default::default(), ) } +fn default_emitter( + sopts: &config::Options, + registry: errors::registry::Registry, + source_map: &Lrc, + emitter_dest: Option>, +) -> Box { + match (sopts.error_format, emitter_dest) { + (config::ErrorOutputType::HumanReadable(color_config), None) => Box::new( + EmitterWriter::stderr( + color_config, + Some(source_map.clone()), + false, + sopts.debugging_opts.teach, + ).ui_testing(sopts.debugging_opts.ui_testing), + ), + (config::ErrorOutputType::HumanReadable(_), Some(dst)) => Box::new( + EmitterWriter::new(dst, Some(source_map.clone()), false, false) + .ui_testing(sopts.debugging_opts.ui_testing), + ), + (config::ErrorOutputType::Json(pretty), None) => Box::new( + JsonEmitter::stderr( + Some(registry), + source_map.clone(), + pretty, + ).ui_testing(sopts.debugging_opts.ui_testing), + ), + (config::ErrorOutputType::Json(pretty), Some(dst)) => Box::new( + JsonEmitter::new( + dst, + Some(registry), + source_map.clone(), + pretty, + ).ui_testing(sopts.debugging_opts.ui_testing), + ), + (config::ErrorOutputType::Short(color_config), None) => Box::new( + EmitterWriter::stderr(color_config, Some(source_map.clone()), true, false), + ), + (config::ErrorOutputType::Short(_), Some(dst)) => { + Box::new(EmitterWriter::new(dst, Some(source_map.clone()), true, false)) + } + } +} + +pub enum DiagnosticOutput { + Default, + Raw(Box), + Emitter(Box) +} + pub fn build_session_with_source_map( sopts: config::Options, local_crate_source_file: Option, registry: errors::registry::Registry, source_map: Lrc, - emitter_dest: Option>, + diagnostics_output: DiagnosticOutput, + lint_caps: FxHashMap, ) -> Session { // FIXME: This is not general enough to make the warning lint completely override // normal diagnostic warnings, since the warning lint can also be denied and changed @@ -1054,42 +1105,13 @@ pub fn build_session_with_source_map( let external_macro_backtrace = sopts.debugging_opts.external_macro_backtrace; - let emitter: Box = - match (sopts.error_format, emitter_dest) { - (config::ErrorOutputType::HumanReadable(color_config), None) => Box::new( - EmitterWriter::stderr( - color_config, - Some(source_map.clone()), - false, - sopts.debugging_opts.teach, - ).ui_testing(sopts.debugging_opts.ui_testing), - ), - (config::ErrorOutputType::HumanReadable(_), Some(dst)) => Box::new( - EmitterWriter::new(dst, Some(source_map.clone()), false, false) - .ui_testing(sopts.debugging_opts.ui_testing), - ), - (config::ErrorOutputType::Json(pretty), None) => Box::new( - JsonEmitter::stderr( - Some(registry), - source_map.clone(), - pretty, - ).ui_testing(sopts.debugging_opts.ui_testing), - ), - (config::ErrorOutputType::Json(pretty), Some(dst)) => Box::new( - JsonEmitter::new( - dst, - Some(registry), - source_map.clone(), - pretty, - ).ui_testing(sopts.debugging_opts.ui_testing), - ), - (config::ErrorOutputType::Short(color_config), None) => Box::new( - EmitterWriter::stderr(color_config, Some(source_map.clone()), true, false), - ), - (config::ErrorOutputType::Short(_), Some(dst)) => { - Box::new(EmitterWriter::new(dst, Some(source_map.clone()), true, false)) - } - }; + let emitter = match diagnostics_output { + DiagnosticOutput::Default => default_emitter(&sopts, registry, &source_map, None), + DiagnosticOutput::Raw(write) => { + default_emitter(&sopts, registry, &source_map, Some(write)) + } + DiagnosticOutput::Emitter(emitter) => emitter, + }; let diagnostic_handler = errors::Handler::with_emitter_and_flags( emitter, @@ -1103,7 +1125,7 @@ pub fn build_session_with_source_map( }, ); - build_session_(sopts, local_crate_source_file, diagnostic_handler, source_map) + build_session_(sopts, local_crate_source_file, diagnostic_handler, source_map, lint_caps) } pub fn build_session_( @@ -1111,6 +1133,7 @@ pub fn build_session_( local_crate_source_file: Option, span_diagnostic: errors::Handler, source_map: Lrc, + driver_lint_caps: FxHashMap, ) -> Session { let host_triple = TargetTriple::from_triple(config::host_triple()); let host = Target::search(&host_triple).unwrap_or_else(|e| @@ -1235,7 +1258,7 @@ pub fn build_session_( }, has_global_allocator: Once::new(), has_panic_handler: Once::new(), - driver_lint_caps: Default::default(), + driver_lint_caps, }; validate_commandline_args_with_session_available(&sess); diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 459ff4db9e957..99d1e32d52398 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -145,6 +145,7 @@ impl<'tcx> ObligationCause<'tcx> { ObligationCauseCode::StartFunctionType => { tcx.sess.source_map().def_span(self.span) } + ObligationCauseCode::MatchExpressionArm { arm_span, .. } => arm_span, _ => self.span, } } @@ -223,6 +224,8 @@ pub enum ObligationCauseCode<'tcx> { MatchExpressionArm { arm_span: Span, source: hir::MatchSource, + prior_arms: Vec, + last_ty: Ty<'tcx>, }, /// Computing common supertype in the pattern guard for the arms of a match expression diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index c5cc9e8b40182..b5be1777fa0d8 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -513,10 +513,21 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { trait_item_def_id, }), super::ExprAssignable => Some(super::ExprAssignable), - super::MatchExpressionArm { arm_span, source } => Some(super::MatchExpressionArm { + super::MatchExpressionArm { arm_span, - source: source, - }), + source, + ref prior_arms, + last_ty, + } => { + tcx.lift(&last_ty).map(|last_ty| { + super::MatchExpressionArm { + arm_span, + source, + prior_arms: prior_arms.clone(), + last_ty, + } + }) + } super::MatchExpressionArmPattern { span, ty } => { tcx.lift(&ty).map(|ty| super::MatchExpressionArmPattern { span, ty }) } diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 18b0afe1fd91e..5e819be901954 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -50,7 +50,7 @@ use rustc_data_structures::stable_hasher::{HashStable, hash_stable_hashmap, StableVec}; use arena::{TypedArena, SyncDroplessArena}; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use rustc_data_structures::sync::{self, Lrc, Lock, WorkerLocal}; +use rustc_data_structures::sync::{Lrc, Lock, WorkerLocal}; use std::any::Any; use std::borrow::Borrow; use std::cmp::Ordering; @@ -1274,8 +1274,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { let gcx = arenas.global_ctxt.as_ref().unwrap(); - sync::assert_send_val(&gcx); - let r = tls::enter_global(gcx, f); gcx.queries.record_computed_queries(s); diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs index a3ee92f8e1263..353a93e78ef2a 100644 --- a/src/librustc/ty/query/config.rs +++ b/src/librustc/ty/query/config.rs @@ -610,6 +610,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::extern_crate<'tcx> { } } +impl<'tcx> QueryDescription<'tcx> for queries::analysis<'tcx> { + fn describe(_tcx: TyCtxt<'_, '_, '_>, _: CrateNum) -> Cow<'static, str> { + "running analysis passes on this crate".into() + } +} + impl<'tcx> QueryDescription<'tcx> for queries::lint_levels<'tcx> { fn describe(_tcx: TyCtxt<'_, '_, '_>, _: CrateNum) -> Cow<'static, str> { "computing the lint levels for items in this crate".into() diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs index 3b191d4201fbf..56495d0bf6225 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs @@ -19,7 +19,7 @@ use crate::mir::interpret::{ConstEvalRawResult, ConstEvalResult}; use crate::mir::mono::CodegenUnit; use crate::mir; use crate::mir::interpret::GlobalId; -use crate::session::{CompileResult, CrateDisambiguator}; +use crate::session::CrateDisambiguator; use crate::session::config::{EntryFnType, OutputFilenames, OptLevel}; use crate::traits::{self, Vtable}; use crate::traits::query::{ @@ -99,6 +99,9 @@ pub use self::on_disk_cache::OnDiskCache; // as they will raise an fatal error on query cycles instead. define_queries! { <'tcx> Other { + /// Run analysis passes on the crate + [] fn analysis: Analysis(CrateNum) -> Result<(), ErrorReported>, + /// Records the type of every item. [] fn type_of: TypeOfItem(DefId) -> Ty<'tcx>, @@ -281,7 +284,8 @@ define_queries! { <'tcx> }, TypeChecking { - [] fn typeck_item_bodies: typeck_item_bodies_dep_node(CrateNum) -> CompileResult, + [] fn typeck_item_bodies: + typeck_item_bodies_dep_node(CrateNum) -> Result<(), ErrorReported>, [] fn typeck_tables_of: TypeckTables(DefId) -> &'tcx ty::TypeckTables<'tcx>, }, diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs index 233aff9de243f..5c7da0168f66e 100644 --- a/src/librustc/ty/query/plumbing.rs +++ b/src/librustc/ty/query/plumbing.rs @@ -1365,6 +1365,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::CrateHash => { force!(crate_hash, krate!()); } DepKind::OriginalCrateName => { force!(original_crate_name, krate!()); } DepKind::ExtraFileName => { force!(extra_filename, krate!()); } + DepKind::Analysis => { force!(analysis, krate!()); } DepKind::AllTraitImplementations => { force!(all_trait_implementations, krate!()); diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index 8bcda409e6663..281030eec494d 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -33,6 +33,7 @@ rustc_save_analysis = { path = "../librustc_save_analysis" } rustc_traits = { path = "../librustc_traits" } rustc_codegen_utils = { path = "../librustc_codegen_utils" } rustc_typeck = { path = "../librustc_typeck" } +rustc_interface = { path = "../librustc_interface" } serialize = { path = "../libserialize" } syntax = { path = "../libsyntax" } smallvec = { version = "0.6.7", features = ["union", "may_dangle"] } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index e5b290b55c22e..f414b23d46e45 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -2,42 +2,39 @@ use rustc::dep_graph::DepGraph; use rustc::hir; use rustc::hir::lowering::lower_crate; use rustc::hir::map as hir_map; +use rustc::hir::def_id::LOCAL_CRATE; use rustc::lint; use rustc::middle::{self, reachable, resolve_lifetime, stability}; use rustc::ty::{self, AllArenas, Resolutions, TyCtxt}; use rustc::traits; use rustc::util::common::{install_panic_hook, time, ErrorReported}; use rustc::util::profiling::ProfileCategory; -use rustc::session::{CompileResult, CrateDisambiguator, Session}; +use rustc::session::{CompileResult, Session}; use rustc::session::CompileIncomplete; use rustc::session::config::{self, Input, OutputFilenames, OutputType}; use rustc::session::search_paths::PathKind; use rustc_allocator as allocator; use rustc_borrowck as borrowck; use rustc_codegen_utils::codegen_backend::CodegenBackend; -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{self, Lock}; use rustc_incremental; use rustc_metadata::creader::CrateLoader; use rustc_metadata::cstore::{self, CStore}; use rustc_mir as mir; -use rustc_passes::{self, ast_validation, hir_stats, loops, rvalue_promotion, layout_test}; +use rustc_passes::{self, ast_validation, hir_stats}; use rustc_plugin as plugin; use rustc_plugin::registry::Registry; use rustc_privacy; use rustc_resolve::{Resolver, ResolverArenas}; use rustc_traits; use rustc_typeck as typeck; -use syntax::{self, ast, attr, diagnostics, visit}; +use syntax::{self, ast, diagnostics, visit}; use syntax::early_buffered_lints::BufferedEarlyLint; use syntax::ext::base::ExtCtxt; use syntax::mut_visit::MutVisitor; use syntax::parse::{self, PResult}; use syntax::util::node_count::NodeCounter; -use syntax::util::lev_distance::find_best_match_for_name; -use syntax::symbol::Symbol; -use syntax_pos::{FileName, hygiene}; +use syntax_pos::hygiene; use syntax_ext; use serialize::json; @@ -46,14 +43,11 @@ use std::any::Any; use std::env; use std::ffi::OsString; use std::fs; -use std::io::{self, Write}; use std::iter; use std::path::{Path, PathBuf}; use std::sync::mpsc; -use pretty::ReplaceBodyWithLoop; -use proc_macro_decls; -use profile; +use rustc_interface::{util, profile, passes}; use super::Compilation; #[cfg(not(parallel_compiler))] @@ -78,7 +72,7 @@ pub fn spawn_thread_pool R + sync::Send, R: sync:: let gcx_ptr = &Lock::new(0); let config = ThreadPoolBuilder::new() - .num_threads(Session::threads_from_opts(&opts)) + .num_threads(Session::threads_from_count(opts.debugging_opts.threads)) .deadlock_handler(|| unsafe { ty::query::handle_deadlock() }) .stack_size(::STACK_SIZE); @@ -160,7 +154,7 @@ pub fn compile_input( (compile_state.krate.unwrap(), compile_state.registry) }; - let outputs = build_output_filenames(input, outdir, output, &krate.attrs, sess); + let outputs = util::build_output_filenames(input, outdir, output, &krate.attrs, sess); let crate_name = ::rustc_codegen_utils::link::find_crate_name(Some(sess), &krate.attrs, input); install_panic_hook(); @@ -194,12 +188,17 @@ pub fn compile_input( )? }; - let output_paths = generated_output_paths(sess, &outputs, output.is_some(), &crate_name); + let output_paths = passes::generated_output_paths( + sess, + &outputs, + output.is_some(), + &crate_name + ); // Ensure the source file isn't accidentally overwritten during compilation. if let Some(ref input_path) = *input_path { if sess.opts.will_create_output_file() { - if output_contains_path(&output_paths, input_path) { + if passes::output_contains_path(&output_paths, input_path) { sess.err(&format!( "the input file \"{}\" would be overwritten by the generated \ executable", @@ -207,7 +206,7 @@ pub fn compile_input( )); return Err(CompileIncomplete::Stopped); } - if let Some(dir_path) = output_conflicts_with_dir(&output_paths) { + if let Some(dir_path) = passes::output_conflicts_with_dir(&output_paths) { sess.err(&format!( "the generated executable for the input file \"{}\" conflicts with the \ existing directory \"{}\"", @@ -219,7 +218,7 @@ pub fn compile_input( } } - write_out_deps(sess, &outputs, &output_paths); + passes::write_out_deps(sess, &outputs, &output_paths); if sess.opts.output_types.contains_key(&OutputType::DepInfo) && sess.opts.output_types.len() == 1 { @@ -333,7 +332,7 @@ pub fn compile_input( Ok((outputs.clone(), ongoing_codegen, tcx.dep_graph.clone())) }, - )?? + )? }; if sess.opts.debugging_opts.print_type_sizes { @@ -364,13 +363,6 @@ pub fn compile_input( Ok(()) } -pub fn source_name(input: &Input) -> FileName { - match *input { - Input::File(ref ifile) => ifile.clone().into(), - Input::Str { ref name, .. } => name.clone(), - } -} - /// CompileController is used to customize compilation, it allows compilation to /// be stopped and/or to call arbitrary code at various points in compilation. /// It also allows for various flags to be set to influence what information gets @@ -806,10 +798,10 @@ where // these need to be set "early" so that expansion sees `quote` if enabled. sess.init_features(features); - let crate_types = collect_crate_types(sess, &krate.attrs); + let crate_types = util::collect_crate_types(sess, &krate.attrs); sess.crate_types.set(crate_types); - let disambiguator = compute_crate_disambiguator(sess); + let disambiguator = util::compute_crate_disambiguator(sess); sess.crate_disambiguator.set(disambiguator); rustc_incremental::prepare_session_directory(sess, &crate_name, disambiguator); @@ -1019,7 +1011,7 @@ where // If we're actually rustdoc then there's no need to actually compile // anything, so switch everything to just looping if sess.opts.actually_rustdoc { - ReplaceBodyWithLoop::new(sess).visit_crate(&mut krate); + util::ReplaceBodyWithLoop::new(sess).visit_crate(&mut krate); } let (has_proc_macro_decls, has_global_allocator) = time(sess, "AST validation", || { @@ -1145,7 +1137,7 @@ where } pub fn default_provide(providers: &mut ty::query::Providers) { - proc_macro_decls::provide(providers); + rustc_interface::passes::provide(providers); plugin::build::provide(providers); hir::provide(providers); borrowck::provide(providers); @@ -1186,7 +1178,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>( name: &str, output_filenames: &OutputFilenames, f: F, -) -> Result +) -> R where F: for<'a> FnOnce( TyCtxt<'a, 'tcx, 'tcx>, @@ -1227,114 +1219,9 @@ where // tcx available. time(sess, "dep graph tcx init", || rustc_incremental::dep_graph_tcx_init(tcx)); - parallel!({ - time(sess, "looking for entry point", || { - middle::entry::find_entry_point(tcx) - }); - - time(sess, "looking for plugin registrar", || { - plugin::build::find_plugin_registrar(tcx) - }); - - time(sess, "looking for derive registrar", || { - proc_macro_decls::find(tcx) - }); - }, { - time(sess, "loop checking", || loops::check_crate(tcx)); - }, { - time(sess, "attribute checking", || { - hir::check_attr::check_crate(tcx) - }); - }, { - time(sess, "stability checking", || { - stability::check_unstable_api_usage(tcx) - }); - }); - - // passes are timed inside typeck - match typeck::check_crate(tcx) { - Ok(x) => x, - Err(x) => { - f(tcx, rx, Err(x)); - return Err(x); - } - } + tcx.analysis(LOCAL_CRATE).ok(); - time(sess, "misc checking", || { - parallel!({ - time(sess, "rvalue promotion", || { - rvalue_promotion::check_crate(tcx) - }); - }, { - time(sess, "intrinsic checking", || { - middle::intrinsicck::check_crate(tcx) - }); - }, { - time(sess, "match checking", || mir::matchck_crate(tcx)); - }, { - // this must run before MIR dump, because - // "not all control paths return a value" is reported here. - // - // maybe move the check to a MIR pass? - time(sess, "liveness checking", || { - middle::liveness::check_crate(tcx) - }); - }); - }); - - // Abort so we don't try to construct MIR with liveness errors. - // We also won't want to continue with errors from rvalue promotion - tcx.sess.abort_if_errors(); - - time(sess, "borrow checking", || { - if tcx.use_ast_borrowck() { - borrowck::check_crate(tcx); - } - }); - - time(sess, - "MIR borrow checking", - || tcx.par_body_owners(|def_id| { tcx.ensure().mir_borrowck(def_id); })); - - time(sess, "dumping chalk-like clauses", || { - rustc_traits::lowering::dump_program_clauses(tcx); - }); - - time(sess, "MIR effect checking", || { - for def_id in tcx.body_owners() { - mir::transform::check_unsafety::check_unsafety(tcx, def_id) - } - }); - - time(sess, "layout testing", || layout_test::test_layout(tcx)); - - // Avoid overwhelming user with errors if borrow checking failed. - // I'm not sure how helpful this is, to be honest, but it avoids - // a - // lot of annoying errors in the compile-fail tests (basically, - // lint warnings and so on -- kindck used to do this abort, but - // kindck is gone now). -nmatsakis - if sess.err_count() > 0 { - return Ok(f(tcx, rx, sess.compile_status())); - } - - time(sess, "misc checking", || { - parallel!({ - time(sess, "privacy checking", || { - rustc_privacy::check_crate(tcx) - }); - }, { - time(sess, "death checking", || middle::dead::check_crate(tcx)); - }, { - time(sess, "unused lib feature checking", || { - stability::check_unused_or_stable_features(tcx) - }); - }, { - time(sess, "lint checking", || lint::check_crate(tcx)); - }); - }); - - return Ok(f(tcx, rx, tcx.sess.compile_status())); + f(tcx, rx, tcx.sess.compile_status()) }, ) } @@ -1359,328 +1246,3 @@ pub fn phase_4_codegen<'a, 'tcx>( codegen } - -fn escape_dep_filename(filename: &FileName) -> String { - // Apparently clang and gcc *only* escape spaces: - // http://llvm.org/klaus/clang/commit/9d50634cfc268ecc9a7250226dd5ca0e945240d4 - filename.to_string().replace(" ", "\\ ") -} - -// Returns all the paths that correspond to generated files. -fn generated_output_paths( - sess: &Session, - outputs: &OutputFilenames, - exact_name: bool, - crate_name: &str, -) -> Vec { - let mut out_filenames = Vec::new(); - for output_type in sess.opts.output_types.keys() { - let file = outputs.path(*output_type); - match *output_type { - // If the filename has been overridden using `-o`, it will not be modified - // by appending `.rlib`, `.exe`, etc., so we can skip this transformation. - OutputType::Exe if !exact_name => for crate_type in sess.crate_types.borrow().iter() { - let p = ::rustc_codegen_utils::link::filename_for_input( - sess, - *crate_type, - crate_name, - outputs, - ); - out_filenames.push(p); - }, - OutputType::DepInfo if sess.opts.debugging_opts.dep_info_omit_d_target => { - // Don't add the dep-info output when omitting it from dep-info targets - } - _ => { - out_filenames.push(file); - } - } - } - out_filenames -} - -// Runs `f` on every output file path and returns the first non-None result, or None if `f` -// returns None for every file path. -fn check_output(output_paths: &[PathBuf], f: F) -> Option -where - F: Fn(&PathBuf) -> Option, -{ - for output_path in output_paths { - if let Some(result) = f(output_path) { - return Some(result); - } - } - None -} - -pub fn output_contains_path(output_paths: &[PathBuf], input_path: &PathBuf) -> bool { - let input_path = input_path.canonicalize().ok(); - if input_path.is_none() { - return false; - } - let check = |output_path: &PathBuf| { - if output_path.canonicalize().ok() == input_path { - Some(()) - } else { - None - } - }; - check_output(output_paths, check).is_some() -} - -pub fn output_conflicts_with_dir(output_paths: &[PathBuf]) -> Option { - let check = |output_path: &PathBuf| { - if output_path.is_dir() { - Some(output_path.clone()) - } else { - None - } - }; - check_output(output_paths, check) -} - -fn write_out_deps(sess: &Session, outputs: &OutputFilenames, out_filenames: &[PathBuf]) { - // Write out dependency rules to the dep-info file if requested - if !sess.opts.output_types.contains_key(&OutputType::DepInfo) { - return; - } - let deps_filename = outputs.path(OutputType::DepInfo); - - let result = (|| -> io::Result<()> { - // Build a list of files used to compile the output and - // write Makefile-compatible dependency rules - let files: Vec = sess.source_map() - .files() - .iter() - .filter(|fmap| fmap.is_real_file()) - .filter(|fmap| !fmap.is_imported()) - .map(|fmap| escape_dep_filename(&fmap.name)) - .collect(); - let mut file = fs::File::create(&deps_filename)?; - for path in out_filenames { - writeln!(file, "{}: {}\n", path.display(), files.join(" "))?; - } - - // Emit a fake target for each input file to the compilation. This - // prevents `make` from spitting out an error if a file is later - // deleted. For more info see #28735 - for path in files { - writeln!(file, "{}:", path)?; - } - Ok(()) - })(); - - if let Err(e) = result { - sess.fatal(&format!( - "error writing dependencies to `{}`: {}", - deps_filename.display(), - e - )); - } -} - -pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec { - // Unconditionally collect crate types from attributes to make them used - let attr_types: Vec = attrs - .iter() - .filter_map(|a| { - if a.check_name("crate_type") { - match a.value_str() { - Some(ref n) if *n == "rlib" => Some(config::CrateType::Rlib), - Some(ref n) if *n == "dylib" => Some(config::CrateType::Dylib), - Some(ref n) if *n == "cdylib" => Some(config::CrateType::Cdylib), - Some(ref n) if *n == "lib" => Some(config::default_lib_output()), - Some(ref n) if *n == "staticlib" => Some(config::CrateType::Staticlib), - Some(ref n) if *n == "proc-macro" => Some(config::CrateType::ProcMacro), - Some(ref n) if *n == "bin" => Some(config::CrateType::Executable), - Some(ref n) => { - let crate_types = vec![ - Symbol::intern("rlib"), - Symbol::intern("dylib"), - Symbol::intern("cdylib"), - Symbol::intern("lib"), - Symbol::intern("staticlib"), - Symbol::intern("proc-macro"), - Symbol::intern("bin") - ]; - - if let ast::MetaItemKind::NameValue(spanned) = a.meta().unwrap().node { - let span = spanned.span; - let lev_candidate = find_best_match_for_name( - crate_types.iter(), - &n.as_str(), - None - ); - if let Some(candidate) = lev_candidate { - session.buffer_lint_with_diagnostic( - lint::builtin::UNKNOWN_CRATE_TYPES, - ast::CRATE_NODE_ID, - span, - "invalid `crate_type` value", - lint::builtin::BuiltinLintDiagnostics:: - UnknownCrateTypes( - span, - "did you mean".to_string(), - format!("\"{}\"", candidate) - ) - ); - } else { - session.buffer_lint( - lint::builtin::UNKNOWN_CRATE_TYPES, - ast::CRATE_NODE_ID, - span, - "invalid `crate_type` value" - ); - } - } - None - } - None => None - } - } else { - None - } - }) - .collect(); - - // If we're generating a test executable, then ignore all other output - // styles at all other locations - if session.opts.test { - return vec![config::CrateType::Executable]; - } - - // Only check command line flags if present. If no types are specified by - // command line, then reuse the empty `base` Vec to hold the types that - // will be found in crate attributes. - let mut base = session.opts.crate_types.clone(); - if base.is_empty() { - base.extend(attr_types); - if base.is_empty() { - base.push(::rustc_codegen_utils::link::default_output_for_target( - session, - )); - } else { - base.sort(); - base.dedup(); - } - } - - base.retain(|crate_type| { - let res = !::rustc_codegen_utils::link::invalid_output_for_target(session, *crate_type); - - if !res { - session.warn(&format!( - "dropping unsupported crate type `{}` for target `{}`", - *crate_type, session.opts.target_triple - )); - } - - res - }); - - base -} - -pub fn compute_crate_disambiguator(session: &Session) -> CrateDisambiguator { - use std::hash::Hasher; - - // The crate_disambiguator is a 128 bit hash. The disambiguator is fed - // into various other hashes quite a bit (symbol hashes, incr. comp. hashes, - // debuginfo type IDs, etc), so we don't want it to be too wide. 128 bits - // should still be safe enough to avoid collisions in practice. - let mut hasher = StableHasher::::new(); - - let mut metadata = session.opts.cg.metadata.clone(); - // We don't want the crate_disambiguator to dependent on the order - // -C metadata arguments, so sort them: - metadata.sort(); - // Every distinct -C metadata value is only incorporated once: - metadata.dedup(); - - hasher.write(b"metadata"); - for s in &metadata { - // Also incorporate the length of a metadata string, so that we generate - // different values for `-Cmetadata=ab -Cmetadata=c` and - // `-Cmetadata=a -Cmetadata=bc` - hasher.write_usize(s.len()); - hasher.write(s.as_bytes()); - } - - // Also incorporate crate type, so that we don't get symbol conflicts when - // linking against a library of the same name, if this is an executable. - let is_exe = session - .crate_types - .borrow() - .contains(&config::CrateType::Executable); - hasher.write(if is_exe { b"exe" } else { b"lib" }); - - CrateDisambiguator::from(hasher.finish()) -} - -pub fn build_output_filenames( - input: &Input, - odir: &Option, - ofile: &Option, - attrs: &[ast::Attribute], - sess: &Session, -) -> OutputFilenames { - match *ofile { - None => { - // "-" as input file will cause the parser to read from stdin so we - // have to make up a name - // We want to toss everything after the final '.' - let dirpath = (*odir).as_ref().cloned().unwrap_or_default(); - - // If a crate name is present, we use it as the link name - let stem = sess.opts - .crate_name - .clone() - .or_else(|| attr::find_crate_name(attrs).map(|n| n.to_string())) - .unwrap_or_else(|| input.filestem().to_owned()); - - OutputFilenames { - out_directory: dirpath, - out_filestem: stem, - single_output_file: None, - extra: sess.opts.cg.extra_filename.clone(), - outputs: sess.opts.output_types.clone(), - } - } - - Some(ref out_file) => { - let unnamed_output_types = sess.opts - .output_types - .values() - .filter(|a| a.is_none()) - .count(); - let ofile = if unnamed_output_types > 1 { - sess.warn( - "due to multiple output types requested, the explicitly specified \ - output file name will be adapted for each output type", - ); - None - } else { - Some(out_file.clone()) - }; - if *odir != None { - sess.warn("ignoring --out-dir flag due to -o flag"); - } - if !sess.opts.cg.extra_filename.is_empty() { - sess.warn("ignoring -C extra-filename flag due to -o flag"); - } - - OutputFilenames { - out_directory: out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(), - out_filestem: out_file - .file_stem() - .unwrap_or_default() - .to_str() - .unwrap() - .to_string(), - single_output_file: ofile, - extra: sess.opts.cg.extra_filename.clone(), - outputs: sess.opts.output_types.clone(), - } - } - } -} diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 990ad4ada01a2..ed0d60242a887 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -28,7 +28,6 @@ extern crate rustc; extern crate rustc_allocator; extern crate rustc_target; extern crate rustc_borrowck; -#[macro_use] extern crate rustc_data_structures; extern crate rustc_errors as errors; extern crate rustc_passes; @@ -43,6 +42,7 @@ extern crate rustc_save_analysis; extern crate rustc_traits; extern crate rustc_codegen_utils; extern crate rustc_typeck; +extern crate rustc_interface; extern crate scoped_tls; extern crate serialize; extern crate smallvec; @@ -59,19 +59,18 @@ use rustc_save_analysis as save; use rustc_save_analysis::DumpHandler; use rustc_data_structures::sync::{self, Lrc, Ordering::SeqCst}; use rustc_data_structures::OnDrop; -use rustc::session::{self, config, Session, build_session, CompileResult}; +use rustc::session::{self, config, Session, build_session, CompileResult, DiagnosticOutput}; use rustc::session::CompileIncomplete; use rustc::session::config::{Input, PrintRequest, ErrorOutputType}; use rustc::session::config::nightly_options; -use rustc::session::filesearch; use rustc::session::{early_error, early_warn}; use rustc::lint::Lint; use rustc::lint; use rustc_metadata::locator; use rustc_metadata::cstore::CStore; -use rustc_metadata::dynamic_lib::DynamicLibrary; use rustc::util::common::{time, ErrorReported}; use rustc_codegen_utils::codegen_backend::CodegenBackend; +use rustc_interface::util::{self, get_codegen_sysroot}; use serialize::json::ToJson; @@ -79,19 +78,15 @@ use std::any::Any; use std::borrow::Cow; use std::cmp::max; use std::default::Default; -use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; use std::env; use std::error::Error; use std::ffi::OsString; use std::fmt::{self, Display}; use std::io::{self, Read, Write}; -use std::mem; use std::panic; -use std::path::{PathBuf, Path}; +use std::path::PathBuf; use std::process::{self, Command, Stdio}; use std::str; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Once, ONCE_INIT}; use std::thread; use syntax::ast; @@ -103,34 +98,8 @@ use syntax_pos::{DUMMY_SP, MultiSpan, FileName}; #[cfg(test)] mod test; -pub mod profile; pub mod driver; pub mod pretty; -mod proc_macro_decls; - -pub mod target_features { - use syntax::ast; - use syntax::symbol::Symbol; - use rustc::session::Session; - use rustc_codegen_utils::codegen_backend::CodegenBackend; - - /// Adds `target_feature = "..."` cfgs for a variety of platform - /// specific features (SSE, NEON etc.). - /// - /// This is performed by checking whether a whitelisted set of - /// features is available on the target machine, by querying LLVM. - pub fn add_configuration(cfg: &mut ast::CrateConfig, - sess: &Session, - codegen_backend: &dyn CodegenBackend) { - let tf = Symbol::intern("target_feature"); - - cfg.extend(codegen_backend.target_features(sess).into_iter().map(|feat| (tf, Some(feat)))); - - if sess.crt_static_feature() { - cfg.insert((tf, Some(Symbol::intern("crt-static")))); - } - } -} /// Exit status code used for successful compilation and help output. pub const EXIT_SUCCESS: isize = 0; @@ -197,235 +166,6 @@ pub fn run(run_compiler: F) -> isize } } -fn load_backend_from_dylib(path: &Path) -> fn() -> Box { - let lib = DynamicLibrary::open(Some(path)).unwrap_or_else(|err| { - let err = format!("couldn't load codegen backend {:?}: {:?}", path, err); - early_error(ErrorOutputType::default(), &err); - }); - unsafe { - match lib.symbol("__rustc_codegen_backend") { - Ok(f) => { - mem::forget(lib); - mem::transmute::<*mut u8, _>(f) - } - Err(e) => { - let err = format!("couldn't load codegen backend as it \ - doesn't export the `__rustc_codegen_backend` \ - symbol: {:?}", e); - early_error(ErrorOutputType::default(), &err); - } - } - } -} - -pub fn get_codegen_backend(sess: &Session) -> Box { - static INIT: Once = ONCE_INIT; - - #[allow(deprecated)] - #[no_debug] - static mut LOAD: fn() -> Box = || unreachable!(); - - INIT.call_once(|| { - let codegen_name = sess.opts.debugging_opts.codegen_backend.as_ref() - .unwrap_or(&sess.target.target.options.codegen_backend); - let backend = match &codegen_name[..] { - "metadata_only" => { - rustc_codegen_utils::codegen_backend::MetadataOnlyCodegenBackend::boxed - } - filename if filename.contains(".") => { - load_backend_from_dylib(filename.as_ref()) - } - codegen_name => get_codegen_sysroot(codegen_name), - }; - - unsafe { - LOAD = backend; - } - }); - let backend = unsafe { LOAD() }; - backend.init(sess); - backend -} - -fn get_codegen_sysroot(backend_name: &str) -> fn() -> Box { - // For now we only allow this function to be called once as it'll dlopen a - // few things, which seems to work best if we only do that once. In - // general this assertion never trips due to the once guard in `get_codegen_backend`, - // but there's a few manual calls to this function in this file we protect - // against. - static LOADED: AtomicBool = AtomicBool::new(false); - assert!(!LOADED.fetch_or(true, Ordering::SeqCst), - "cannot load the default codegen backend twice"); - - // When we're compiling this library with `--test` it'll run as a binary but - // not actually exercise much functionality. As a result most of the logic - // here is defunkt (it assumes we're a dynamic library in a sysroot) so - // let's just return a dummy creation function which won't be used in - // general anyway. - if cfg!(test) { - return rustc_codegen_utils::codegen_backend::MetadataOnlyCodegenBackend::boxed - } - - let target = session::config::host_triple(); - let mut sysroot_candidates = vec![filesearch::get_or_default_sysroot()]; - let path = current_dll_path() - .and_then(|s| s.canonicalize().ok()); - if let Some(dll) = path { - // use `parent` twice to chop off the file name and then also the - // directory containing the dll which should be either `lib` or `bin`. - if let Some(path) = dll.parent().and_then(|p| p.parent()) { - // The original `path` pointed at the `rustc_driver` crate's dll. - // Now that dll should only be in one of two locations. The first is - // in the compiler's libdir, for example `$sysroot/lib/*.dll`. The - // other is the target's libdir, for example - // `$sysroot/lib/rustlib/$target/lib/*.dll`. - // - // We don't know which, so let's assume that if our `path` above - // ends in `$target` we *could* be in the target libdir, and always - // assume that we may be in the main libdir. - sysroot_candidates.push(path.to_owned()); - - if path.ends_with(target) { - sysroot_candidates.extend(path.parent() // chop off `$target` - .and_then(|p| p.parent()) // chop off `rustlib` - .and_then(|p| p.parent()) // chop off `lib` - .map(|s| s.to_owned())); - } - } - } - - let sysroot = sysroot_candidates.iter() - .map(|sysroot| { - let libdir = filesearch::relative_target_lib_path(&sysroot, &target); - sysroot.join(libdir).with_file_name( - option_env!("CFG_CODEGEN_BACKENDS_DIR").unwrap_or("codegen-backends")) - }) - .filter(|f| { - info!("codegen backend candidate: {}", f.display()); - f.exists() - }) - .next(); - let sysroot = sysroot.unwrap_or_else(|| { - let candidates = sysroot_candidates.iter() - .map(|p| p.display().to_string()) - .collect::>() - .join("\n* "); - let err = format!("failed to find a `codegen-backends` folder \ - in the sysroot candidates:\n* {}", candidates); - early_error(ErrorOutputType::default(), &err); - }); - info!("probing {} for a codegen backend", sysroot.display()); - - let d = sysroot.read_dir().unwrap_or_else(|e| { - let err = format!("failed to load default codegen backend, couldn't \ - read `{}`: {}", sysroot.display(), e); - early_error(ErrorOutputType::default(), &err); - }); - - let mut file: Option = None; - - let expected_name = format!("rustc_codegen_llvm-{}", backend_name); - for entry in d.filter_map(|e| e.ok()) { - let path = entry.path(); - let filename = match path.file_name().and_then(|s| s.to_str()) { - Some(s) => s, - None => continue, - }; - if !(filename.starts_with(DLL_PREFIX) && filename.ends_with(DLL_SUFFIX)) { - continue - } - let name = &filename[DLL_PREFIX.len() .. filename.len() - DLL_SUFFIX.len()]; - if name != expected_name { - continue - } - if let Some(ref prev) = file { - let err = format!("duplicate codegen backends found\n\ - first: {}\n\ - second: {}\n\ - ", prev.display(), path.display()); - early_error(ErrorOutputType::default(), &err); - } - file = Some(path.clone()); - } - - match file { - Some(ref s) => return load_backend_from_dylib(s), - None => { - let err = format!("failed to load default codegen backend for `{}`, \ - no appropriate codegen dylib found in `{}`", - backend_name, sysroot.display()); - early_error(ErrorOutputType::default(), &err); - } - } - - #[cfg(unix)] - fn current_dll_path() -> Option { - use std::ffi::{OsStr, CStr}; - use std::os::unix::prelude::*; - - unsafe { - let addr = current_dll_path as usize as *mut _; - let mut info = mem::zeroed(); - if libc::dladdr(addr, &mut info) == 0 { - info!("dladdr failed"); - return None - } - if info.dli_fname.is_null() { - info!("dladdr returned null pointer"); - return None - } - let bytes = CStr::from_ptr(info.dli_fname).to_bytes(); - let os = OsStr::from_bytes(bytes); - Some(PathBuf::from(os)) - } - } - - #[cfg(windows)] - fn current_dll_path() -> Option { - use std::ffi::OsString; - use std::os::windows::prelude::*; - - extern "system" { - fn GetModuleHandleExW(dwFlags: u32, - lpModuleName: usize, - phModule: *mut usize) -> i32; - fn GetModuleFileNameW(hModule: usize, - lpFilename: *mut u16, - nSize: u32) -> u32; - } - - const GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS: u32 = 0x00000004; - - unsafe { - let mut module = 0; - let r = GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - current_dll_path as usize, - &mut module); - if r == 0 { - info!("GetModuleHandleExW failed: {}", io::Error::last_os_error()); - return None - } - let mut space = Vec::with_capacity(1024); - let r = GetModuleFileNameW(module, - space.as_mut_ptr(), - space.capacity() as u32); - if r == 0 { - info!("GetModuleFileNameW failed: {}", io::Error::last_os_error()); - return None - } - let r = r as usize; - if r >= space.capacity() { - info!("our buffer was too small? {}", - io::Error::last_os_error()); - return None - } - space.set_len(r); - let os = OsString::from_wide(&space); - Some(PathBuf::from(os)) - } - } -} - // Parse args and run the compiler. This is the primary entry point for rustc. // See comments on CompilerCalls below for details about the callbacks argument. // The FileLoader provides a way to load files from sources other than the file system. @@ -486,7 +226,12 @@ fn run_compiler_with_pool<'a>( let loader = file_loader.unwrap_or(box RealFileLoader); let source_map = Lrc::new(SourceMap::with_file_loader(loader, sopts.file_path_mapping())); let mut sess = session::build_session_with_source_map( - sopts, input_file_path.clone(), descriptions, source_map, emitter_dest, + sopts, + input_file_path.clone(), + descriptions, + source_map, + emitter_dest.map(|e| DiagnosticOutput::Raw(e)).unwrap_or(DiagnosticOutput::Default), + Default::default(), ); if let Some(err) = input_err { @@ -496,12 +241,12 @@ fn run_compiler_with_pool<'a>( return (Err(CompileIncomplete::Stopped), Some(sess)); } - let codegen_backend = get_codegen_backend(&sess); + let codegen_backend = util::get_codegen_backend(&sess); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, cfg); - target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); + util::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let result = { @@ -711,8 +456,8 @@ fn stdout_isatty() -> bool { } fn handle_explain(code: &str, - descriptions: &errors::registry::Registry, output: ErrorOutputType) { + let descriptions = rustc_interface::util::diagnostics_registry(); let normalised = if code.starts_with("E") { code.to_string() } else { @@ -789,11 +534,11 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { matches: &getopts::Matches, _: &config::Options, _: &ast::CrateConfig, - descriptions: &errors::registry::Registry, + _: &errors::registry::Registry, output: ErrorOutputType) -> Compilation { if let Some(ref code) = matches.opt_str("explain") { - handle_explain(code, descriptions, output); + handle_explain(code, output); return Compilation::Stop; } @@ -821,8 +566,8 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { } rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, cfg.clone()); - let codegen_backend = get_codegen_backend(&sess); - target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); + let codegen_backend = util::get_codegen_backend(&sess); + util::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let should_stop = RustcDefaultCalls::print_crate_info( &*codegen_backend, @@ -1025,13 +770,19 @@ impl RustcDefaultCalls { let input = input.unwrap_or_else(|| early_error(ErrorOutputType::default(), "no input file provided")); let attrs = attrs.as_ref().unwrap(); - let t_outputs = driver::build_output_filenames(input, odir, ofile, attrs, sess); + let t_outputs = rustc_interface::util::build_output_filenames( + input, + odir, + ofile, + attrs, + sess + ); let id = rustc_codegen_utils::link::find_crate_name(Some(sess), attrs, input); if *req == PrintRequest::CrateName { println!("{}", id); continue; } - let crate_types = driver::collect_crate_types(sess, attrs); + let crate_types = rustc_interface::util::collect_crate_types(sess, attrs); for &style in &crate_types { let fname = rustc_codegen_utils::link::filename_for_input( sess, diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 4caf2ec676f07..2d41c6361a5ce 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -9,36 +9,33 @@ use rustc::hir::print as pprust_hir; use rustc::session::Session; use rustc::session::config::{Input, OutputFilenames}; use rustc::ty::{self, TyCtxt, Resolutions, AllArenas}; +use rustc_interface::util; use rustc_borrowck as borrowck; use rustc_borrowck::graphviz as borrowck_dot; -use rustc_data_structures::thin_vec::ThinVec; use rustc_metadata::cstore::CStore; use rustc_mir::util::{write_mir_pretty, write_mir_graphviz}; -use syntax::ast::{self, BlockCheckMode}; -use syntax::mut_visit::{*, MutVisitor, visit_clobber}; +use syntax::ast; +use syntax::mut_visit::MutVisitor; use syntax::print::{pprust}; use syntax::print::pprust::PrintState; -use syntax::ptr::P; -use syntax_pos::{self, FileName}; +use syntax_pos::FileName; use graphviz as dot; -use smallvec::SmallVec; use std::cell::Cell; use std::fs::File; use std::io::{self, Write}; -use std::ops::DerefMut; use std::option; use std::path::Path; use std::str::FromStr; -use std::mem; pub use self::UserIdentifiedItem::*; pub use self::PpSourceMode::*; pub use self::PpMode::*; use self::NodesMatchingUII::*; -use {abort_on_err, driver}; +use abort_on_err; +use driver; #[derive(Copy, Clone, PartialEq, Debug)] pub enum PpSourceMode { @@ -217,18 +214,19 @@ impl PpSourceMode { } PpmTyped => { let control = &driver::CompileController::basic(); - let codegen_backend = ::get_codegen_backend(sess); + let codegen_backend = util::get_codegen_backend(sess); let mut arenas = AllArenas::new(); - abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend, - control, - sess, - cstore, - hir_map.clone(), - resolutions.clone(), - &mut arenas, - id, - output_filenames, - |tcx, _, _| { + driver::phase_3_run_analysis_passes(&*codegen_backend, + control, + sess, + cstore, + hir_map.clone(), + resolutions.clone(), + &mut arenas, + id, + output_filenames, + |tcx, _, result| { + abort_on_err(result, tcx.sess); let empty_tables = ty::TypeckTables::empty(None); let annotation = TypedAnnotation { tcx, @@ -237,8 +235,7 @@ impl PpSourceMode { tcx.dep_graph.with_ignore(|| { f(&annotation, hir_map.forest.krate()) }) - }), - sess) + }) } _ => panic!("Should use call_with_pp_support"), } @@ -627,204 +624,6 @@ impl UserIdentifiedItem { } } -// Note: Also used by librustdoc, see PR #43348. Consider moving this struct elsewhere. -// -// FIXME: Currently the `everybody_loops` transformation is not applied to: -// * `const fn`, due to issue #43636 that `loop` is not supported for const evaluation. We are -// waiting for miri to fix that. -// * `impl Trait`, due to issue #43869 that functions returning impl Trait cannot be diverging. -// Solving this may require `!` to implement every trait, which relies on the an even more -// ambitious form of the closed RFC #1637. See also [#34511]. -// -// [#34511]: https://github.com/rust-lang/rust/issues/34511#issuecomment-322340401 -pub struct ReplaceBodyWithLoop<'a> { - within_static_or_const: bool, - nested_blocks: Option>, - sess: &'a Session, -} - -impl<'a> ReplaceBodyWithLoop<'a> { - pub fn new(sess: &'a Session) -> ReplaceBodyWithLoop<'a> { - ReplaceBodyWithLoop { - within_static_or_const: false, - nested_blocks: None, - sess - } - } - - fn run R>(&mut self, is_const: bool, action: F) -> R { - let old_const = mem::replace(&mut self.within_static_or_const, is_const); - let old_blocks = self.nested_blocks.take(); - let ret = action(self); - self.within_static_or_const = old_const; - self.nested_blocks = old_blocks; - ret - } - - fn should_ignore_fn(ret_ty: &ast::FnDecl) -> bool { - if let ast::FunctionRetTy::Ty(ref ty) = ret_ty.output { - fn involves_impl_trait(ty: &ast::Ty) -> bool { - match ty.node { - ast::TyKind::ImplTrait(..) => true, - ast::TyKind::Slice(ref subty) | - ast::TyKind::Array(ref subty, _) | - ast::TyKind::Ptr(ast::MutTy { ty: ref subty, .. }) | - ast::TyKind::Rptr(_, ast::MutTy { ty: ref subty, .. }) | - ast::TyKind::Paren(ref subty) => involves_impl_trait(subty), - ast::TyKind::Tup(ref tys) => any_involves_impl_trait(tys.iter()), - ast::TyKind::Path(_, ref path) => path.segments.iter().any(|seg| { - match seg.args.as_ref().map(|generic_arg| &**generic_arg) { - None => false, - Some(&ast::GenericArgs::AngleBracketed(ref data)) => { - let types = data.args.iter().filter_map(|arg| match arg { - ast::GenericArg::Type(ty) => Some(ty), - _ => None, - }); - any_involves_impl_trait(types.into_iter()) || - any_involves_impl_trait(data.bindings.iter().map(|b| &b.ty)) - }, - Some(&ast::GenericArgs::Parenthesized(ref data)) => { - any_involves_impl_trait(data.inputs.iter()) || - any_involves_impl_trait(data.output.iter()) - } - } - }), - _ => false, - } - } - - fn any_involves_impl_trait<'a, I: Iterator>>(mut it: I) -> bool { - it.any(|subty| involves_impl_trait(subty)) - } - - involves_impl_trait(ty) - } else { - false - } - } -} - -impl<'a> MutVisitor for ReplaceBodyWithLoop<'a> { - fn visit_item_kind(&mut self, i: &mut ast::ItemKind) { - let is_const = match i { - ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => true, - ast::ItemKind::Fn(ref decl, ref header, _, _) => - header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), - _ => false, - }; - self.run(is_const, |s| noop_visit_item_kind(i, s)) - } - - fn flat_map_trait_item(&mut self, i: ast::TraitItem) -> SmallVec<[ast::TraitItem; 1]> { - let is_const = match i.node { - ast::TraitItemKind::Const(..) => true, - ast::TraitItemKind::Method(ast::MethodSig { ref decl, ref header, .. }, _) => - header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), - _ => false, - }; - self.run(is_const, |s| noop_flat_map_trait_item(i, s)) - } - - fn flat_map_impl_item(&mut self, i: ast::ImplItem) -> SmallVec<[ast::ImplItem; 1]> { - let is_const = match i.node { - ast::ImplItemKind::Const(..) => true, - ast::ImplItemKind::Method(ast::MethodSig { ref decl, ref header, .. }, _) => - header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), - _ => false, - }; - self.run(is_const, |s| noop_flat_map_impl_item(i, s)) - } - - fn visit_anon_const(&mut self, c: &mut ast::AnonConst) { - self.run(true, |s| noop_visit_anon_const(c, s)) - } - - fn visit_block(&mut self, b: &mut P) { - fn stmt_to_block(rules: ast::BlockCheckMode, - s: Option, - sess: &Session) -> ast::Block { - ast::Block { - stmts: s.into_iter().collect(), - rules, - id: sess.next_node_id(), - span: syntax_pos::DUMMY_SP, - } - } - - fn block_to_stmt(b: ast::Block, sess: &Session) -> ast::Stmt { - let expr = P(ast::Expr { - id: sess.next_node_id(), - node: ast::ExprKind::Block(P(b), None), - span: syntax_pos::DUMMY_SP, - attrs: ThinVec::new(), - }); - - ast::Stmt { - id: sess.next_node_id(), - node: ast::StmtKind::Expr(expr), - span: syntax_pos::DUMMY_SP, - } - } - - let empty_block = stmt_to_block(BlockCheckMode::Default, None, self.sess); - let loop_expr = P(ast::Expr { - node: ast::ExprKind::Loop(P(empty_block), None), - id: self.sess.next_node_id(), - span: syntax_pos::DUMMY_SP, - attrs: ThinVec::new(), - }); - - let loop_stmt = ast::Stmt { - id: self.sess.next_node_id(), - span: syntax_pos::DUMMY_SP, - node: ast::StmtKind::Expr(loop_expr), - }; - - if self.within_static_or_const { - noop_visit_block(b, self) - } else { - visit_clobber(b.deref_mut(), |b| { - let mut stmts = vec![]; - for s in b.stmts { - let old_blocks = self.nested_blocks.replace(vec![]); - - stmts.extend(self.flat_map_stmt(s).into_iter().filter(|s| s.is_item())); - - // we put a Some in there earlier with that replace(), so this is valid - let new_blocks = self.nested_blocks.take().unwrap(); - self.nested_blocks = old_blocks; - stmts.extend(new_blocks.into_iter().map(|b| block_to_stmt(b, &self.sess))); - } - - let mut new_block = ast::Block { - stmts, - ..b - }; - - if let Some(old_blocks) = self.nested_blocks.as_mut() { - //push our fresh block onto the cache and yield an empty block with `loop {}` - if !new_block.stmts.is_empty() { - old_blocks.push(new_block); - } - - stmt_to_block(b.rules, Some(loop_stmt), self.sess) - } else { - //push `loop {}` onto the end of our fresh block and yield that - new_block.stmts.push(loop_stmt); - - new_block - } - }) - } - } - - // in general the pretty printer processes unexpanded code, so - // we override the default `visit_mac` method which panics. - fn visit_mac(&mut self, mac: &mut ast::Mac) { - noop_visit_mac(mac, self) - } -} - fn print_flowgraph<'a, 'tcx, W: Write>(variants: Vec, tcx: TyCtxt<'a, 'tcx, 'tcx>, code: blocks::Code<'tcx>, @@ -892,12 +691,12 @@ fn print_flowgraph<'a, 'tcx, W: Write>(variants: Vec, pub fn visit_crate(sess: &Session, krate: &mut ast::Crate, ppm: PpMode) { if let PpmSource(PpmEveryBodyLoops) = ppm { - ReplaceBodyWithLoop::new(sess).visit_crate(krate); + util::ReplaceBodyWithLoop::new(sess).visit_crate(krate); } } fn get_source(input: &Input, sess: &Session) -> (Vec, FileName) { - let src_name = driver::source_name(input); + let src_name = input.source_name(); let src = sess.source_map() .get_source_file(&src_name) .unwrap() @@ -1117,18 +916,19 @@ fn print_with_analysis<'tcx, 'a: 'tcx>(sess: &'a Session, let mut out = Vec::new(); let control = &driver::CompileController::basic(); - let codegen_backend = ::get_codegen_backend(sess); + let codegen_backend = util::get_codegen_backend(sess); let mut arenas = AllArenas::new(); - abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend, - control, - sess, - cstore, - hir_map.clone(), - resolutions.clone(), - &mut arenas, - crate_name, - output_filenames, - |tcx, _, _| { + driver::phase_3_run_analysis_passes(&*codegen_backend, + control, + sess, + cstore, + hir_map.clone(), + resolutions.clone(), + &mut arenas, + crate_name, + output_filenames, + |tcx, _, result| { + abort_on_err(result, tcx.sess); match ppm { PpmMir | PpmMirCFG => { if let Some(nodeid) = nodeid { @@ -1174,9 +974,7 @@ fn print_with_analysis<'tcx, 'a: 'tcx>(sess: &'a Session, } _ => unreachable!(), } - }), - sess) - .unwrap(); + }).unwrap(); write_output(out, ofile); } diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 2ec755bd62691..309a9f7b52522 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -16,6 +16,7 @@ use rustc::ty::query::OnDiskCache; use rustc::ty::subst::Subst; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::sync::{self, Lrc}; +use rustc_interface::util; use rustc_lint; use rustc_metadata::cstore::CStore; use rustc_target::spec::abi::Abi; @@ -91,6 +92,13 @@ where options.debugging_opts.verbose = true; options.unstable_features = UnstableFeatures::Allow; + // When we're compiling this library with `--test` it'll run as a binary but + // not actually exercise much functionality. + // As a result most of the logic loading the codegen backend is defunkt + // (it assumes we're a dynamic library in a sysroot) + // so let's just use the metadata only backend which doesn't need to load any libraries. + options.debugging_opts.codegen_backend = Some("metadata_only".to_owned()); + driver::spawn_thread_pool(options, |options| { test_env_with_pool(options, source_string, args, body) }) @@ -111,8 +119,9 @@ fn test_env_with_pool( None, diagnostic_handler, Lrc::new(SourceMap::new(FilePathMapping::empty())), + Default::default(), ); - let cstore = CStore::new(::get_codegen_backend(&sess).metadata_loader()); + let cstore = CStore::new(util::get_codegen_backend(&sess).metadata_loader()); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let input = config::Input::Str { name: FileName::anon_source_code(&source_string), diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs index 2c410f69bfc66..851b19e8177b5 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/src/librustc_errors/diagnostic.rs @@ -1,4 +1,5 @@ use crate::CodeSuggestion; +use crate::SuggestionStyle; use crate::SubstitutionPart; use crate::Substitution; use crate::Applicability; @@ -243,7 +244,33 @@ impl Diagnostic { .collect(), }], msg: msg.to_owned(), - show_code_when_inline: true, + style: SuggestionStyle::ShowCode, + applicability, + }); + self + } + + /// Prints out a message with for a multipart suggestion without showing the suggested code. + /// + /// This is intended to be used for suggestions that are obvious in what the changes need to + /// be from the message, showing the span label inline would be visually unpleasant + /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't + /// improve understandability. + pub fn tool_only_multipart_suggestion( + &mut self, + msg: &str, + suggestion: Vec<(Span, String)>, + applicability: Applicability, + ) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitutions: vec![Substitution { + parts: suggestion + .into_iter() + .map(|(span, snippet)| SubstitutionPart { snippet, span }) + .collect(), + }], + msg: msg.to_owned(), + style: SuggestionStyle::CompletelyHidden, applicability, }); self @@ -277,7 +304,7 @@ impl Diagnostic { }], }], msg: msg.to_owned(), - show_code_when_inline: true, + style: SuggestionStyle::ShowCode, applicability, }); self @@ -295,7 +322,7 @@ impl Diagnostic { }], }).collect(), msg: msg.to_owned(), - show_code_when_inline: true, + style: SuggestionStyle::ShowCode, applicability, }); self @@ -316,7 +343,51 @@ impl Diagnostic { }], }], msg: msg.to_owned(), - show_code_when_inline: false, + style: SuggestionStyle::HideCodeInline, + applicability, + }); + self + } + + /// Prints out a message with for a suggestion without showing the suggested code. + /// + /// This is intended to be used for suggestions that are obvious in what the changes need to + /// be from the message, showing the span label inline would be visually unpleasant + /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't + /// improve understandability. + pub fn span_suggestion_hidden( + &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability + ) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitutions: vec![Substitution { + parts: vec![SubstitutionPart { + snippet: suggestion, + span: sp, + }], + }], + msg: msg.to_owned(), + style: SuggestionStyle::HideCodeInline, + applicability, + }); + self + } + + /// Adds a suggestion to the json output, but otherwise remains silent/undisplayed in the cli. + /// + /// This is intended to be used for suggestions that are *very* obvious in what the changes + /// need to be from the message, but we still want other tools to be able to apply them. + pub fn tool_only_span_suggestion( + &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability + ) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitutions: vec![Substitution { + parts: vec![SubstitutionPart { + snippet: suggestion, + span: sp, + }], + }], + msg: msg.to_owned(), + style: SuggestionStyle::CompletelyHidden, applicability: applicability, }); self diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs index 9d5e8d10b1772..8a30790174d45 100644 --- a/src/librustc_errors/diagnostic_builder.rs +++ b/src/librustc_errors/diagnostic_builder.rs @@ -205,6 +205,24 @@ impl<'a> DiagnosticBuilder<'a> { self } + pub fn tool_only_multipart_suggestion( + &mut self, + msg: &str, + suggestion: Vec<(Span, String)>, + applicability: Applicability, + ) -> &mut Self { + if !self.allow_suggestions { + return self + } + self.diagnostic.tool_only_multipart_suggestion( + msg, + suggestion, + applicability, + ); + self + } + + pub fn span_suggestion( &mut self, sp: Span, @@ -261,6 +279,45 @@ impl<'a> DiagnosticBuilder<'a> { ); self } + + pub fn span_suggestion_hidden( + &mut self, + sp: Span, + msg: &str, + suggestion: String, + applicability: Applicability, + ) -> &mut Self { + if !self.allow_suggestions { + return self + } + self.diagnostic.span_suggestion_hidden( + sp, + msg, + suggestion, + applicability, + ); + self + } + + pub fn tool_only_span_suggestion( + &mut self, + sp: Span, + msg: &str, + suggestion: String, + applicability: Applicability, + ) -> &mut Self { + if !self.allow_suggestions { + return self + } + self.diagnostic.tool_only_span_suggestion( + sp, + msg, + suggestion, + applicability, + ); + self + } + forward!(pub fn set_span>(&mut self, sp: S) -> &mut Self); forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self); diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 1c0c9d137e40f..e9f269b6e2410 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -2,7 +2,10 @@ use Destination::*; use syntax_pos::{SourceFile, Span, MultiSpan}; -use crate::{Level, CodeSuggestion, DiagnosticBuilder, SubDiagnostic, SourceMapperDyn, DiagnosticId}; +use crate::{ + Level, CodeSuggestion, DiagnosticBuilder, SubDiagnostic, + SuggestionStyle, SourceMapperDyn, DiagnosticId, +}; use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, StyledString, Style}; use crate::styled_buffer::StyledBuffer; @@ -43,9 +46,14 @@ impl Emitter for EmitterWriter { // don't display long messages as labels sugg.msg.split_whitespace().count() < 10 && // don't display multiline suggestions as labels - !sugg.substitutions[0].parts[0].snippet.contains('\n') { + !sugg.substitutions[0].parts[0].snippet.contains('\n') && + // when this style is set we want the suggestion to be a message, not inline + sugg.style != SuggestionStyle::HideCodeAlways && + // trivial suggestion for tooling's sake, never shown + sugg.style != SuggestionStyle::CompletelyHidden + { let substitution = &sugg.substitutions[0].parts[0].snippet.trim(); - let msg = if substitution.len() == 0 || !sugg.show_code_when_inline { + let msg = if substitution.len() == 0 || sugg.style.hide_inline() { // This substitution is only removal or we explicitly don't want to show the // code inline, don't show it format!("help: {}", sugg.msg) @@ -942,14 +950,15 @@ impl EmitterWriter { } } - fn emit_message_default(&mut self, - msp: &MultiSpan, - msg: &[(String, Style)], - code: &Option, - level: &Level, - max_line_num_len: usize, - is_secondary: bool) - -> io::Result<()> { + fn emit_message_default( + &mut self, + msp: &MultiSpan, + msg: &[(String, Style)], + code: &Option, + level: &Level, + max_line_num_len: usize, + is_secondary: bool, + ) -> io::Result<()> { let mut buffer = StyledBuffer::new(); let header_style = if is_secondary { Style::HeaderMsg @@ -1184,11 +1193,12 @@ impl EmitterWriter { } - fn emit_suggestion_default(&mut self, - suggestion: &CodeSuggestion, - level: &Level, - max_line_num_len: usize) - -> io::Result<()> { + fn emit_suggestion_default( + &mut self, + suggestion: &CodeSuggestion, + level: &Level, + max_line_num_len: usize, + ) -> io::Result<()> { if let Some(ref sm) = self.sm { let mut buffer = StyledBuffer::new(); @@ -1198,11 +1208,13 @@ impl EmitterWriter { buffer.append(0, &level_str, Style::Level(level.clone())); buffer.append(0, ": ", Style::HeaderMsg); } - self.msg_to_buffer(&mut buffer, - &[(suggestion.msg.to_owned(), Style::NoStyle)], - max_line_num_len, - "suggestion", - Some(Style::HeaderMsg)); + self.msg_to_buffer( + &mut buffer, + &[(suggestion.msg.to_owned(), Style::NoStyle)], + max_line_num_len, + "suggestion", + Some(Style::HeaderMsg), + ); // Render the replacements for each suggestion let suggestions = suggestion.splice_lines(&**sm); @@ -1340,22 +1352,42 @@ impl EmitterWriter { if !self.short_message { for child in children { let span = child.render_span.as_ref().unwrap_or(&child.span); - match self.emit_message_default(&span, - &child.styled_message(), - &None, - &child.level, - max_line_num_len, - true) { + match self.emit_message_default( + &span, + &child.styled_message(), + &None, + &child.level, + max_line_num_len, + true, + ) { Err(e) => panic!("failed to emit error: {}", e), _ => () } } for sugg in suggestions { - match self.emit_suggestion_default(sugg, - &Level::Help, - max_line_num_len) { - Err(e) => panic!("failed to emit error: {}", e), - _ => () + if sugg.style == SuggestionStyle::CompletelyHidden { + // do not display this suggestion, it is meant only for tools + } else if sugg.style == SuggestionStyle::HideCodeAlways { + match self.emit_message_default( + &MultiSpan::new(), + &[(sugg.msg.to_owned(), Style::HeaderMsg)], + &None, + &Level::Help, + max_line_num_len, + true, + ) { + Err(e) => panic!("failed to emit error: {}", e), + _ => () + } + } else { + match self.emit_suggestion_default( + sugg, + &Level::Help, + max_line_num_len, + ) { + Err(e) => panic!("failed to emit error: {}", e), + _ => () + } } } } diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 93d33d9936187..87b4751526835 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -69,6 +69,29 @@ pub enum Applicability { Unspecified, } +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, RustcEncodable, RustcDecodable)] +pub enum SuggestionStyle { + /// Hide the suggested code when displaying this suggestion inline. + HideCodeInline, + /// Always hide the suggested code but display the message. + HideCodeAlways, + /// Do not display this suggestion in the cli output, it is only meant for tools. + CompletelyHidden, + /// Always show the suggested code. + /// This will *not* show the code if the suggestion is inline *and* the suggested code is + /// empty. + ShowCode, +} + +impl SuggestionStyle { + fn hide_inline(&self) -> bool { + match *self { + SuggestionStyle::ShowCode => false, + _ => true, + } + } +} + #[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub struct CodeSuggestion { /// Each substitute can have multiple variants due to multiple @@ -94,7 +117,8 @@ pub struct CodeSuggestion { /// ``` pub substitutions: Vec, pub msg: String, - pub show_code_when_inline: bool, + /// Visual representation of this suggestion. + pub style: SuggestionStyle, /// Whether or not the suggestion is approximate /// /// Sometimes we may show suggestions with placeholders, diff --git a/src/librustc_interface/Cargo.toml b/src/librustc_interface/Cargo.toml new file mode 100644 index 0000000000000..1acd3dfc7656a --- /dev/null +++ b/src/librustc_interface/Cargo.toml @@ -0,0 +1,35 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_interface" +version = "0.0.0" + +[lib] +name = "rustc_interface" +path = "lib.rs" +crate-type = ["dylib"] + +[dependencies] +log = "0.4" +rustc-rayon = "0.1.1" +smallvec = { version = "0.6.7", features = ["union", "may_dangle"] } +scoped-tls = { version = "0.1.1", features = ["nightly"] } +syntax = { path = "../libsyntax" } +syntax_ext = { path = "../libsyntax_ext" } +syntax_pos = { path = "../libsyntax_pos" } +serialize = { path = "../libserialize" } +rustc = { path = "../librustc" } +rustc_allocator = { path = "../librustc_allocator" } +rustc_borrowck = { path = "../librustc_borrowck" } +rustc_incremental = { path = "../librustc_incremental" } +rustc_traits = { path = "../librustc_traits" } +rustc_data_structures = { path = "../librustc_data_structures" } +rustc_codegen_utils = { path = "../librustc_codegen_utils" } +rustc_metadata = { path = "../librustc_metadata" } +rustc_mir = { path = "../librustc_mir" } +rustc_passes = { path = "../librustc_passes" } +rustc_typeck = { path = "../librustc_typeck" } +rustc_lint = { path = "../librustc_lint" } +rustc_errors = { path = "../librustc_errors" } +rustc_plugin = { path = "../librustc_plugin" } +rustc_privacy = { path = "../librustc_privacy" } +rustc_resolve = { path = "../librustc_resolve" } diff --git a/src/librustc_interface/lib.rs b/src/librustc_interface/lib.rs new file mode 100644 index 0000000000000..e5c7c35a36d75 --- /dev/null +++ b/src/librustc_interface/lib.rs @@ -0,0 +1,43 @@ +#![feature(box_syntax)] +#![feature(set_stdio)] +#![feature(nll)] +#![feature(arbitrary_self_types)] +#![feature(generator_trait)] +#![cfg_attr(unix, feature(libc))] + +#![allow(unused_imports)] + +#![recursion_limit="256"] + +#[cfg(unix)] +extern crate libc; +#[macro_use] +extern crate log; +extern crate rustc; +extern crate rustc_codegen_utils; +extern crate rustc_allocator; +extern crate rustc_borrowck; +extern crate rustc_incremental; +extern crate rustc_traits; +#[macro_use] +extern crate rustc_data_structures; +extern crate rustc_errors; +extern crate rustc_lint; +extern crate rustc_metadata; +extern crate rustc_mir; +extern crate rustc_passes; +extern crate rustc_plugin; +extern crate rustc_privacy; +extern crate rustc_rayon as rayon; +extern crate rustc_resolve; +extern crate rustc_typeck; +extern crate smallvec; +extern crate serialize; +extern crate syntax; +extern crate syntax_pos; +extern crate syntax_ext; + +pub mod passes; +pub mod profile; +pub mod util; +pub mod proc_macro_decls; diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs new file mode 100644 index 0000000000000..16ced6956380b --- /dev/null +++ b/src/librustc_interface/passes.rs @@ -0,0 +1,296 @@ +use util; +use proc_macro_decls; + +use rustc::dep_graph::DepGraph; +use rustc::hir; +use rustc::hir::lowering::lower_crate; +use rustc::hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc::lint; +use rustc::middle::{self, reachable, resolve_lifetime, stability}; +use rustc::middle::privacy::AccessLevels; +use rustc::ty::{self, AllArenas, Resolutions, TyCtxt}; +use rustc::ty::steal::Steal; +use rustc::traits; +use rustc::util::common::{time, ErrorReported}; +use rustc::util::profiling::ProfileCategory; +use rustc::session::{CompileResult, CrateDisambiguator, Session}; +use rustc::session::config::{self, Input, OutputFilenames, OutputType}; +use rustc::session::search_paths::PathKind; +use rustc_allocator as allocator; +use rustc_borrowck as borrowck; +use rustc_codegen_utils::codegen_backend::CodegenBackend; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::stable_hasher::StableHasher; +use rustc_data_structures::sync::Lrc; +use rustc_incremental; +use rustc_metadata::creader::CrateLoader; +use rustc_metadata::cstore::{self, CStore}; +use rustc_mir as mir; +use rustc_passes::{self, ast_validation, hir_stats, loops, rvalue_promotion, layout_test}; +use rustc_plugin as plugin; +use rustc_plugin::registry::Registry; +use rustc_privacy; +use rustc_resolve::{Resolver, ResolverArenas}; +use rustc_traits; +use rustc_typeck as typeck; +use syntax::{self, ast, attr, diagnostics, visit}; +use syntax::early_buffered_lints::BufferedEarlyLint; +use syntax::ext::base::ExtCtxt; +use syntax::mut_visit::MutVisitor; +use syntax::parse::{self, PResult}; +use syntax::util::node_count::NodeCounter; +use syntax::util::lev_distance::find_best_match_for_name; +use syntax::symbol::Symbol; +use syntax_pos::{FileName, hygiene}; +use syntax_ext; + +use serialize::json; + +use std::any::Any; +use std::env; +use std::ffi::OsString; +use std::fs; +use std::io::{self, Write}; +use std::iter; +use std::path::{Path, PathBuf}; +use std::sync::mpsc; +use std::cell::RefCell; +use std::rc::Rc; +use std::mem; +use std::ops::Generator; + +/// Returns all the paths that correspond to generated files. +pub fn generated_output_paths( + sess: &Session, + outputs: &OutputFilenames, + exact_name: bool, + crate_name: &str, +) -> Vec { + let mut out_filenames = Vec::new(); + for output_type in sess.opts.output_types.keys() { + let file = outputs.path(*output_type); + match *output_type { + // If the filename has been overridden using `-o`, it will not be modified + // by appending `.rlib`, `.exe`, etc., so we can skip this transformation. + OutputType::Exe if !exact_name => for crate_type in sess.crate_types.borrow().iter() { + let p = ::rustc_codegen_utils::link::filename_for_input( + sess, + *crate_type, + crate_name, + outputs, + ); + out_filenames.push(p); + }, + OutputType::DepInfo if sess.opts.debugging_opts.dep_info_omit_d_target => { + // Don't add the dep-info output when omitting it from dep-info targets + } + _ => { + out_filenames.push(file); + } + } + } + out_filenames +} + +// Runs `f` on every output file path and returns the first non-None result, or None if `f` +// returns None for every file path. +fn check_output(output_paths: &[PathBuf], f: F) -> Option +where + F: Fn(&PathBuf) -> Option, +{ + for output_path in output_paths { + if let Some(result) = f(output_path) { + return Some(result); + } + } + None +} + +pub fn output_contains_path(output_paths: &[PathBuf], input_path: &PathBuf) -> bool { + let input_path = input_path.canonicalize().ok(); + if input_path.is_none() { + return false; + } + let check = |output_path: &PathBuf| { + if output_path.canonicalize().ok() == input_path { + Some(()) + } else { + None + } + }; + check_output(output_paths, check).is_some() +} + +pub fn output_conflicts_with_dir(output_paths: &[PathBuf]) -> Option { + let check = |output_path: &PathBuf| { + if output_path.is_dir() { + Some(output_path.clone()) + } else { + None + } + }; + check_output(output_paths, check) +} + +fn escape_dep_filename(filename: &FileName) -> String { + // Apparently clang and gcc *only* escape spaces: + // http://llvm.org/klaus/clang/commit/9d50634cfc268ecc9a7250226dd5ca0e945240d4 + filename.to_string().replace(" ", "\\ ") +} + +pub fn write_out_deps(sess: &Session, outputs: &OutputFilenames, out_filenames: &[PathBuf]) { + // Write out dependency rules to the dep-info file if requested + if !sess.opts.output_types.contains_key(&OutputType::DepInfo) { + return; + } + let deps_filename = outputs.path(OutputType::DepInfo); + + let result = (|| -> io::Result<()> { + // Build a list of files used to compile the output and + // write Makefile-compatible dependency rules + let files: Vec = sess.source_map() + .files() + .iter() + .filter(|fmap| fmap.is_real_file()) + .filter(|fmap| !fmap.is_imported()) + .map(|fmap| escape_dep_filename(&fmap.name)) + .collect(); + let mut file = fs::File::create(&deps_filename)?; + for path in out_filenames { + writeln!(file, "{}: {}\n", path.display(), files.join(" "))?; + } + + // Emit a fake target for each input file to the compilation. This + // prevents `make` from spitting out an error if a file is later + // deleted. For more info see #28735 + for path in files { + writeln!(file, "{}:", path)?; + } + Ok(()) + })(); + + if let Err(e) = result { + sess.fatal(&format!( + "error writing dependencies to `{}`: {}", + deps_filename.display(), + e + )); + } +} + +pub fn provide(providers: &mut ty::query::Providers) { + providers.analysis = analysis; + proc_macro_decls::provide(providers); +} + +fn analysis<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + cnum: CrateNum, +) -> Result<(), ErrorReported> { + assert_eq!(cnum, LOCAL_CRATE); + + let sess = tcx.sess; + + parallel!({ + time(sess, "looking for entry point", || { + middle::entry::find_entry_point(tcx) + }); + + time(sess, "looking for plugin registrar", || { + plugin::build::find_plugin_registrar(tcx) + }); + + time(sess, "looking for derive registrar", || { + proc_macro_decls::find(tcx) + }); + }, { + time(sess, "loop checking", || loops::check_crate(tcx)); + }, { + time(sess, "attribute checking", || { + hir::check_attr::check_crate(tcx) + }); + }, { + time(sess, "stability checking", || { + stability::check_unstable_api_usage(tcx) + }); + }); + + // passes are timed inside typeck + typeck::check_crate(tcx)?; + + time(sess, "misc checking", || { + parallel!({ + time(sess, "rvalue promotion", || { + rvalue_promotion::check_crate(tcx) + }); + }, { + time(sess, "intrinsic checking", || { + middle::intrinsicck::check_crate(tcx) + }); + }, { + time(sess, "match checking", || mir::matchck_crate(tcx)); + }, { + // this must run before MIR dump, because + // "not all control paths return a value" is reported here. + // + // maybe move the check to a MIR pass? + time(sess, "liveness checking", || { + middle::liveness::check_crate(tcx) + }); + }); + }); + + // Abort so we don't try to construct MIR with liveness errors. + // We also won't want to continue with errors from rvalue promotion + tcx.sess.abort_if_errors(); + + time(sess, "borrow checking", || { + if tcx.use_ast_borrowck() { + borrowck::check_crate(tcx); + } + }); + + time(sess, + "MIR borrow checking", + || tcx.par_body_owners(|def_id| { tcx.ensure().mir_borrowck(def_id); })); + + time(sess, "dumping chalk-like clauses", || { + rustc_traits::lowering::dump_program_clauses(tcx); + }); + + time(sess, "MIR effect checking", || { + for def_id in tcx.body_owners() { + mir::transform::check_unsafety::check_unsafety(tcx, def_id) + } + }); + + time(sess, "layout testing", || layout_test::test_layout(tcx)); + + // Avoid overwhelming user with errors if borrow checking failed. + // I'm not sure how helpful this is, to be honest, but it avoids + // a + // lot of annoying errors in the compile-fail tests (basically, + // lint warnings and so on -- kindck used to do this abort, but + // kindck is gone now). -nmatsakis + if sess.err_count() > 0 { + return Err(ErrorReported); + } + + time(sess, "misc checking", || { + parallel!({ + time(sess, "privacy checking", || { + rustc_privacy::check_crate(tcx) + }); + }, { + time(sess, "death checking", || middle::dead::check_crate(tcx)); + }, { + time(sess, "unused lib feature checking", || { + stability::check_unused_or_stable_features(tcx) + }); + }, { + time(sess, "lint checking", || lint::check_crate(tcx)); + }); + }); + + Ok(()) +} diff --git a/src/librustc_driver/proc_macro_decls.rs b/src/librustc_interface/proc_macro_decls.rs similarity index 100% rename from src/librustc_driver/proc_macro_decls.rs rename to src/librustc_interface/proc_macro_decls.rs diff --git a/src/librustc_driver/profile/mod.rs b/src/librustc_interface/profile/mod.rs similarity index 100% rename from src/librustc_driver/profile/mod.rs rename to src/librustc_interface/profile/mod.rs diff --git a/src/librustc_driver/profile/trace.rs b/src/librustc_interface/profile/trace.rs similarity index 100% rename from src/librustc_driver/profile/trace.rs rename to src/librustc_interface/profile/trace.rs diff --git a/src/librustc_interface/util.rs b/src/librustc_interface/util.rs new file mode 100644 index 0000000000000..6f92c30446215 --- /dev/null +++ b/src/librustc_interface/util.rs @@ -0,0 +1,702 @@ +use rustc::session::config::{Input, OutputFilenames, ErrorOutputType}; +use rustc::session::{self, config, early_error, filesearch, Session, DiagnosticOutput}; +use rustc::session::CrateDisambiguator; +use rustc::ty; +use rustc::lint; +use rustc_codegen_utils::codegen_backend::CodegenBackend; +use rustc_data_structures::sync::{Lock, Lrc}; +use rustc_data_structures::stable_hasher::StableHasher; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::thin_vec::ThinVec; +use rustc_data_structures::fx::{FxHashSet, FxHashMap}; +use rustc_errors::registry::Registry; +use rustc_lint; +use rustc_metadata::dynamic_lib::DynamicLibrary; +use rustc_mir; +use rustc_passes; +use rustc_plugin; +use rustc_privacy; +use rustc_resolve; +use rustc_typeck; +use std::collections::HashSet; +use std::env; +use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; +use std::io::{self, Write}; +use std::mem; +use std::path::{Path, PathBuf}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex, Once}; +use std::ops::DerefMut; +use smallvec::SmallVec; +use syntax::ptr::P; +use syntax::mut_visit::{*, MutVisitor, visit_clobber}; +use syntax::ast::BlockCheckMode; +use syntax::util::lev_distance::find_best_match_for_name; +use syntax::source_map::{FileLoader, RealFileLoader, SourceMap}; +use syntax::symbol::Symbol; +use syntax::{self, ast, attr}; +#[cfg(not(parallel_compiler))] +use std::{thread, panic}; + +pub fn diagnostics_registry() -> Registry { + let mut all_errors = Vec::new(); + all_errors.extend_from_slice(&rustc::DIAGNOSTICS); + all_errors.extend_from_slice(&rustc_typeck::DIAGNOSTICS); + all_errors.extend_from_slice(&rustc_resolve::DIAGNOSTICS); + all_errors.extend_from_slice(&rustc_privacy::DIAGNOSTICS); + // FIXME: need to figure out a way to get these back in here + // all_errors.extend_from_slice(get_codegen_backend(sess).diagnostics()); + all_errors.extend_from_slice(&rustc_metadata::DIAGNOSTICS); + all_errors.extend_from_slice(&rustc_passes::DIAGNOSTICS); + all_errors.extend_from_slice(&rustc_plugin::DIAGNOSTICS); + all_errors.extend_from_slice(&rustc_mir::DIAGNOSTICS); + all_errors.extend_from_slice(&syntax::DIAGNOSTICS); + + Registry::new(&all_errors) +} + +/// Adds `target_feature = "..."` cfgs for a variety of platform +/// specific features (SSE, NEON etc.). +/// +/// This is performed by checking whether a whitelisted set of +/// features is available on the target machine, by querying LLVM. +pub fn add_configuration( + cfg: &mut ast::CrateConfig, + sess: &Session, + codegen_backend: &dyn CodegenBackend, +) { + let tf = Symbol::intern("target_feature"); + + cfg.extend( + codegen_backend + .target_features(sess) + .into_iter() + .map(|feat| (tf, Some(feat))), + ); + + if sess.crt_static_feature() { + cfg.insert((tf, Some(Symbol::intern("crt-static")))); + } +} + +fn load_backend_from_dylib(path: &Path) -> fn() -> Box { + let lib = DynamicLibrary::open(Some(path)).unwrap_or_else(|err| { + let err = format!("couldn't load codegen backend {:?}: {:?}", path, err); + early_error(ErrorOutputType::default(), &err); + }); + unsafe { + match lib.symbol("__rustc_codegen_backend") { + Ok(f) => { + mem::forget(lib); + mem::transmute::<*mut u8, _>(f) + } + Err(e) => { + let err = format!("couldn't load codegen backend as it \ + doesn't export the `__rustc_codegen_backend` \ + symbol: {:?}", e); + early_error(ErrorOutputType::default(), &err); + } + } + } +} + +pub fn get_codegen_backend(sess: &Session) -> Box { + static INIT: Once = Once::new(); + + static mut LOAD: fn() -> Box = || unreachable!(); + + INIT.call_once(|| { + let codegen_name = sess.opts.debugging_opts.codegen_backend.as_ref() + .unwrap_or(&sess.target.target.options.codegen_backend); + let backend = match &codegen_name[..] { + "metadata_only" => { + rustc_codegen_utils::codegen_backend::MetadataOnlyCodegenBackend::boxed + } + filename if filename.contains(".") => { + load_backend_from_dylib(filename.as_ref()) + } + codegen_name => get_codegen_sysroot(codegen_name), + }; + + unsafe { + LOAD = backend; + } + }); + let backend = unsafe { LOAD() }; + backend.init(sess); + backend +} + +pub fn get_codegen_sysroot(backend_name: &str) -> fn() -> Box { + // For now we only allow this function to be called once as it'll dlopen a + // few things, which seems to work best if we only do that once. In + // general this assertion never trips due to the once guard in `get_codegen_backend`, + // but there's a few manual calls to this function in this file we protect + // against. + static LOADED: AtomicBool = AtomicBool::new(false); + assert!(!LOADED.fetch_or(true, Ordering::SeqCst), + "cannot load the default codegen backend twice"); + + let target = session::config::host_triple(); + let mut sysroot_candidates = vec![filesearch::get_or_default_sysroot()]; + let path = current_dll_path() + .and_then(|s| s.canonicalize().ok()); + if let Some(dll) = path { + // use `parent` twice to chop off the file name and then also the + // directory containing the dll which should be either `lib` or `bin`. + if let Some(path) = dll.parent().and_then(|p| p.parent()) { + // The original `path` pointed at the `rustc_driver` crate's dll. + // Now that dll should only be in one of two locations. The first is + // in the compiler's libdir, for example `$sysroot/lib/*.dll`. The + // other is the target's libdir, for example + // `$sysroot/lib/rustlib/$target/lib/*.dll`. + // + // We don't know which, so let's assume that if our `path` above + // ends in `$target` we *could* be in the target libdir, and always + // assume that we may be in the main libdir. + sysroot_candidates.push(path.to_owned()); + + if path.ends_with(target) { + sysroot_candidates.extend(path.parent() // chop off `$target` + .and_then(|p| p.parent()) // chop off `rustlib` + .and_then(|p| p.parent()) // chop off `lib` + .map(|s| s.to_owned())); + } + } + } + + let sysroot = sysroot_candidates.iter() + .map(|sysroot| { + let libdir = filesearch::relative_target_lib_path(&sysroot, &target); + sysroot.join(libdir).with_file_name( + option_env!("CFG_CODEGEN_BACKENDS_DIR").unwrap_or("codegen-backends")) + }) + .filter(|f| { + info!("codegen backend candidate: {}", f.display()); + f.exists() + }) + .next(); + let sysroot = sysroot.unwrap_or_else(|| { + let candidates = sysroot_candidates.iter() + .map(|p| p.display().to_string()) + .collect::>() + .join("\n* "); + let err = format!("failed to find a `codegen-backends` folder \ + in the sysroot candidates:\n* {}", candidates); + early_error(ErrorOutputType::default(), &err); + }); + info!("probing {} for a codegen backend", sysroot.display()); + + let d = sysroot.read_dir().unwrap_or_else(|e| { + let err = format!("failed to load default codegen backend, couldn't \ + read `{}`: {}", sysroot.display(), e); + early_error(ErrorOutputType::default(), &err); + }); + + let mut file: Option = None; + + let expected_name = format!("rustc_codegen_llvm-{}", backend_name); + for entry in d.filter_map(|e| e.ok()) { + let path = entry.path(); + let filename = match path.file_name().and_then(|s| s.to_str()) { + Some(s) => s, + None => continue, + }; + if !(filename.starts_with(DLL_PREFIX) && filename.ends_with(DLL_SUFFIX)) { + continue + } + let name = &filename[DLL_PREFIX.len() .. filename.len() - DLL_SUFFIX.len()]; + if name != expected_name { + continue + } + if let Some(ref prev) = file { + let err = format!("duplicate codegen backends found\n\ + first: {}\n\ + second: {}\n\ + ", prev.display(), path.display()); + early_error(ErrorOutputType::default(), &err); + } + file = Some(path.clone()); + } + + match file { + Some(ref s) => return load_backend_from_dylib(s), + None => { + let err = format!("failed to load default codegen backend for `{}`, \ + no appropriate codegen dylib found in `{}`", + backend_name, sysroot.display()); + early_error(ErrorOutputType::default(), &err); + } + } + + #[cfg(unix)] + fn current_dll_path() -> Option { + use std::ffi::{OsStr, CStr}; + use std::os::unix::prelude::*; + + unsafe { + let addr = current_dll_path as usize as *mut _; + let mut info = mem::zeroed(); + if libc::dladdr(addr, &mut info) == 0 { + info!("dladdr failed"); + return None + } + if info.dli_fname.is_null() { + info!("dladdr returned null pointer"); + return None + } + let bytes = CStr::from_ptr(info.dli_fname).to_bytes(); + let os = OsStr::from_bytes(bytes); + Some(PathBuf::from(os)) + } + } + + #[cfg(windows)] + fn current_dll_path() -> Option { + use std::ffi::OsString; + use std::os::windows::prelude::*; + + extern "system" { + fn GetModuleHandleExW(dwFlags: u32, + lpModuleName: usize, + phModule: *mut usize) -> i32; + fn GetModuleFileNameW(hModule: usize, + lpFilename: *mut u16, + nSize: u32) -> u32; + } + + const GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS: u32 = 0x00000004; + + unsafe { + let mut module = 0; + let r = GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + current_dll_path as usize, + &mut module); + if r == 0 { + info!("GetModuleHandleExW failed: {}", io::Error::last_os_error()); + return None + } + let mut space = Vec::with_capacity(1024); + let r = GetModuleFileNameW(module, + space.as_mut_ptr(), + space.capacity() as u32); + if r == 0 { + info!("GetModuleFileNameW failed: {}", io::Error::last_os_error()); + return None + } + let r = r as usize; + if r >= space.capacity() { + info!("our buffer was too small? {}", + io::Error::last_os_error()); + return None + } + space.set_len(r); + let os = OsString::from_wide(&space); + Some(PathBuf::from(os)) + } + } +} + +pub fn compute_crate_disambiguator(session: &Session) -> CrateDisambiguator { + use std::hash::Hasher; + + // The crate_disambiguator is a 128 bit hash. The disambiguator is fed + // into various other hashes quite a bit (symbol hashes, incr. comp. hashes, + // debuginfo type IDs, etc), so we don't want it to be too wide. 128 bits + // should still be safe enough to avoid collisions in practice. + let mut hasher = StableHasher::::new(); + + let mut metadata = session.opts.cg.metadata.clone(); + // We don't want the crate_disambiguator to dependent on the order + // -C metadata arguments, so sort them: + metadata.sort(); + // Every distinct -C metadata value is only incorporated once: + metadata.dedup(); + + hasher.write(b"metadata"); + for s in &metadata { + // Also incorporate the length of a metadata string, so that we generate + // different values for `-Cmetadata=ab -Cmetadata=c` and + // `-Cmetadata=a -Cmetadata=bc` + hasher.write_usize(s.len()); + hasher.write(s.as_bytes()); + } + + // Also incorporate crate type, so that we don't get symbol conflicts when + // linking against a library of the same name, if this is an executable. + let is_exe = session + .crate_types + .borrow() + .contains(&config::CrateType::Executable); + hasher.write(if is_exe { b"exe" } else { b"lib" }); + + CrateDisambiguator::from(hasher.finish()) +} + +pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec { + // Unconditionally collect crate types from attributes to make them used + let attr_types: Vec = attrs + .iter() + .filter_map(|a| { + if a.check_name("crate_type") { + match a.value_str() { + Some(ref n) if *n == "rlib" => Some(config::CrateType::Rlib), + Some(ref n) if *n == "dylib" => Some(config::CrateType::Dylib), + Some(ref n) if *n == "cdylib" => Some(config::CrateType::Cdylib), + Some(ref n) if *n == "lib" => Some(config::default_lib_output()), + Some(ref n) if *n == "staticlib" => Some(config::CrateType::Staticlib), + Some(ref n) if *n == "proc-macro" => Some(config::CrateType::ProcMacro), + Some(ref n) if *n == "bin" => Some(config::CrateType::Executable), + Some(ref n) => { + let crate_types = vec![ + Symbol::intern("rlib"), + Symbol::intern("dylib"), + Symbol::intern("cdylib"), + Symbol::intern("lib"), + Symbol::intern("staticlib"), + Symbol::intern("proc-macro"), + Symbol::intern("bin") + ]; + + if let ast::MetaItemKind::NameValue(spanned) = a.meta().unwrap().node { + let span = spanned.span; + let lev_candidate = find_best_match_for_name( + crate_types.iter(), + &n.as_str(), + None + ); + if let Some(candidate) = lev_candidate { + session.buffer_lint_with_diagnostic( + lint::builtin::UNKNOWN_CRATE_TYPES, + ast::CRATE_NODE_ID, + span, + "invalid `crate_type` value", + lint::builtin::BuiltinLintDiagnostics:: + UnknownCrateTypes( + span, + "did you mean".to_string(), + format!("\"{}\"", candidate) + ) + ); + } else { + session.buffer_lint( + lint::builtin::UNKNOWN_CRATE_TYPES, + ast::CRATE_NODE_ID, + span, + "invalid `crate_type` value" + ); + } + } + None + } + None => None + } + } else { + None + } + }) + .collect(); + + // If we're generating a test executable, then ignore all other output + // styles at all other locations + if session.opts.test { + return vec![config::CrateType::Executable]; + } + + // Only check command line flags if present. If no types are specified by + // command line, then reuse the empty `base` Vec to hold the types that + // will be found in crate attributes. + let mut base = session.opts.crate_types.clone(); + if base.is_empty() { + base.extend(attr_types); + if base.is_empty() { + base.push(::rustc_codegen_utils::link::default_output_for_target( + session, + )); + } else { + base.sort(); + base.dedup(); + } + } + + base.retain(|crate_type| { + let res = !::rustc_codegen_utils::link::invalid_output_for_target(session, *crate_type); + + if !res { + session.warn(&format!( + "dropping unsupported crate type `{}` for target `{}`", + *crate_type, session.opts.target_triple + )); + } + + res + }); + + base +} + +pub fn build_output_filenames( + input: &Input, + odir: &Option, + ofile: &Option, + attrs: &[ast::Attribute], + sess: &Session, +) -> OutputFilenames { + match *ofile { + None => { + // "-" as input file will cause the parser to read from stdin so we + // have to make up a name + // We want to toss everything after the final '.' + let dirpath = (*odir).as_ref().cloned().unwrap_or_default(); + + // If a crate name is present, we use it as the link name + let stem = sess.opts + .crate_name + .clone() + .or_else(|| attr::find_crate_name(attrs).map(|n| n.to_string())) + .unwrap_or_else(|| input.filestem().to_owned()); + + OutputFilenames { + out_directory: dirpath, + out_filestem: stem, + single_output_file: None, + extra: sess.opts.cg.extra_filename.clone(), + outputs: sess.opts.output_types.clone(), + } + } + + Some(ref out_file) => { + let unnamed_output_types = sess.opts + .output_types + .values() + .filter(|a| a.is_none()) + .count(); + let ofile = if unnamed_output_types > 1 { + sess.warn( + "due to multiple output types requested, the explicitly specified \ + output file name will be adapted for each output type", + ); + None + } else { + Some(out_file.clone()) + }; + if *odir != None { + sess.warn("ignoring --out-dir flag due to -o flag"); + } + if !sess.opts.cg.extra_filename.is_empty() { + sess.warn("ignoring -C extra-filename flag due to -o flag"); + } + + OutputFilenames { + out_directory: out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(), + out_filestem: out_file + .file_stem() + .unwrap_or_default() + .to_str() + .unwrap() + .to_string(), + single_output_file: ofile, + extra: sess.opts.cg.extra_filename.clone(), + outputs: sess.opts.output_types.clone(), + } + } + } +} + +// Note: Also used by librustdoc, see PR #43348. Consider moving this struct elsewhere. +// +// FIXME: Currently the `everybody_loops` transformation is not applied to: +// * `const fn`, due to issue #43636 that `loop` is not supported for const evaluation. We are +// waiting for miri to fix that. +// * `impl Trait`, due to issue #43869 that functions returning impl Trait cannot be diverging. +// Solving this may require `!` to implement every trait, which relies on the an even more +// ambitious form of the closed RFC #1637. See also [#34511]. +// +// [#34511]: https://github.com/rust-lang/rust/issues/34511#issuecomment-322340401 +pub struct ReplaceBodyWithLoop<'a> { + within_static_or_const: bool, + nested_blocks: Option>, + sess: &'a Session, +} + +impl<'a> ReplaceBodyWithLoop<'a> { + pub fn new(sess: &'a Session) -> ReplaceBodyWithLoop<'a> { + ReplaceBodyWithLoop { + within_static_or_const: false, + nested_blocks: None, + sess + } + } + + fn run R>(&mut self, is_const: bool, action: F) -> R { + let old_const = mem::replace(&mut self.within_static_or_const, is_const); + let old_blocks = self.nested_blocks.take(); + let ret = action(self); + self.within_static_or_const = old_const; + self.nested_blocks = old_blocks; + ret + } + + fn should_ignore_fn(ret_ty: &ast::FnDecl) -> bool { + if let ast::FunctionRetTy::Ty(ref ty) = ret_ty.output { + fn involves_impl_trait(ty: &ast::Ty) -> bool { + match ty.node { + ast::TyKind::ImplTrait(..) => true, + ast::TyKind::Slice(ref subty) | + ast::TyKind::Array(ref subty, _) | + ast::TyKind::Ptr(ast::MutTy { ty: ref subty, .. }) | + ast::TyKind::Rptr(_, ast::MutTy { ty: ref subty, .. }) | + ast::TyKind::Paren(ref subty) => involves_impl_trait(subty), + ast::TyKind::Tup(ref tys) => any_involves_impl_trait(tys.iter()), + ast::TyKind::Path(_, ref path) => path.segments.iter().any(|seg| { + match seg.args.as_ref().map(|generic_arg| &**generic_arg) { + None => false, + Some(&ast::GenericArgs::AngleBracketed(ref data)) => { + let types = data.args.iter().filter_map(|arg| match arg { + ast::GenericArg::Type(ty) => Some(ty), + _ => None, + }); + any_involves_impl_trait(types.into_iter()) || + any_involves_impl_trait(data.bindings.iter().map(|b| &b.ty)) + }, + Some(&ast::GenericArgs::Parenthesized(ref data)) => { + any_involves_impl_trait(data.inputs.iter()) || + any_involves_impl_trait(data.output.iter()) + } + } + }), + _ => false, + } + } + + fn any_involves_impl_trait<'a, I: Iterator>>(mut it: I) -> bool { + it.any(|subty| involves_impl_trait(subty)) + } + + involves_impl_trait(ty) + } else { + false + } + } +} + +impl<'a> MutVisitor for ReplaceBodyWithLoop<'a> { + fn visit_item_kind(&mut self, i: &mut ast::ItemKind) { + let is_const = match i { + ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => true, + ast::ItemKind::Fn(ref decl, ref header, _, _) => + header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), + _ => false, + }; + self.run(is_const, |s| noop_visit_item_kind(i, s)) + } + + fn flat_map_trait_item(&mut self, i: ast::TraitItem) -> SmallVec<[ast::TraitItem; 1]> { + let is_const = match i.node { + ast::TraitItemKind::Const(..) => true, + ast::TraitItemKind::Method(ast::MethodSig { ref decl, ref header, .. }, _) => + header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), + _ => false, + }; + self.run(is_const, |s| noop_flat_map_trait_item(i, s)) + } + + fn flat_map_impl_item(&mut self, i: ast::ImplItem) -> SmallVec<[ast::ImplItem; 1]> { + let is_const = match i.node { + ast::ImplItemKind::Const(..) => true, + ast::ImplItemKind::Method(ast::MethodSig { ref decl, ref header, .. }, _) => + header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), + _ => false, + }; + self.run(is_const, |s| noop_flat_map_impl_item(i, s)) + } + + fn visit_anon_const(&mut self, c: &mut ast::AnonConst) { + self.run(true, |s| noop_visit_anon_const(c, s)) + } + + fn visit_block(&mut self, b: &mut P) { + fn stmt_to_block(rules: ast::BlockCheckMode, + s: Option, + sess: &Session) -> ast::Block { + ast::Block { + stmts: s.into_iter().collect(), + rules, + id: sess.next_node_id(), + span: syntax_pos::DUMMY_SP, + } + } + + fn block_to_stmt(b: ast::Block, sess: &Session) -> ast::Stmt { + let expr = P(ast::Expr { + id: sess.next_node_id(), + node: ast::ExprKind::Block(P(b), None), + span: syntax_pos::DUMMY_SP, + attrs: ThinVec::new(), + }); + + ast::Stmt { + id: sess.next_node_id(), + node: ast::StmtKind::Expr(expr), + span: syntax_pos::DUMMY_SP, + } + } + + let empty_block = stmt_to_block(BlockCheckMode::Default, None, self.sess); + let loop_expr = P(ast::Expr { + node: ast::ExprKind::Loop(P(empty_block), None), + id: self.sess.next_node_id(), + span: syntax_pos::DUMMY_SP, + attrs: ThinVec::new(), + }); + + let loop_stmt = ast::Stmt { + id: self.sess.next_node_id(), + span: syntax_pos::DUMMY_SP, + node: ast::StmtKind::Expr(loop_expr), + }; + + if self.within_static_or_const { + noop_visit_block(b, self) + } else { + visit_clobber(b.deref_mut(), |b| { + let mut stmts = vec![]; + for s in b.stmts { + let old_blocks = self.nested_blocks.replace(vec![]); + + stmts.extend(self.flat_map_stmt(s).into_iter().filter(|s| s.is_item())); + + // we put a Some in there earlier with that replace(), so this is valid + let new_blocks = self.nested_blocks.take().unwrap(); + self.nested_blocks = old_blocks; + stmts.extend(new_blocks.into_iter().map(|b| block_to_stmt(b, &self.sess))); + } + + let mut new_block = ast::Block { + stmts, + ..b + }; + + if let Some(old_blocks) = self.nested_blocks.as_mut() { + //push our fresh block onto the cache and yield an empty block with `loop {}` + if !new_block.stmts.is_empty() { + old_blocks.push(new_block); + } + + stmt_to_block(b.rules, Some(loop_stmt), self.sess) + } else { + //push `loop {}` onto the end of our fresh block and yield that + new_block.stmts.push(loop_stmt); + + new_block + } + }) + } + } + + // in general the pretty printer processes unexpanded code, so + // we override the default `visit_mac` method which panics. + fn visit_mac(&mut self, mac: &mut ast::Mac) { + noop_visit_mac(mac, self) + } +} diff --git a/src/librustc_metadata/diagnostics.rs b/src/librustc_metadata/diagnostics.rs index c27d13be49358..9ac582ebc42da 100644 --- a/src/librustc_metadata/diagnostics.rs +++ b/src/librustc_metadata/diagnostics.rs @@ -37,7 +37,7 @@ extern {} ``` See more: -https://doc.rust-lang.org/book/first-edition/conditional-compilation.html +https://doc.rust-lang.org/reference/attributes.html#conditional-compilation "##, E0458: r##" diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index f741af0b228b5..081c458bfc17a 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -244,7 +244,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { let tables = infcx.tcx.typeck_tables_of(mir_def_id); let nice = NiceRegionError::new_from_span(infcx, span, o, f, Some(tables)); - if let Some(_error_reported) = nice.try_report_from_nll() { + if let Some(diag) = nice.try_report_from_nll() { + diag.buffer(errors_buffer); return; } } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 6de05777fe888..cbeb5dc206ee6 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -15,7 +15,7 @@ use rustc::mir::{ ConstraintCategory, Local, Location, Mir, }; use rustc::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable}; -use rustc::util::common; +use rustc::util::common::{self, ErrorReported}; use rustc_data_structures::bit_set::BitSet; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::graph::scc::Sccs; @@ -763,20 +763,26 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("try_promote_type_test: ur={:?}", ur); - let non_local_ub = self.universal_region_relations.non_local_upper_bound(ur); + let non_local_ub = self.universal_region_relations.non_local_upper_bounds(&ur); debug!("try_promote_type_test: non_local_ub={:?}", non_local_ub); - assert!(self.universal_regions.is_universal_region(non_local_ub)); - assert!(!self.universal_regions.is_local_free_region(non_local_ub)); - - let requirement = ClosureOutlivesRequirement { - subject, - outlived_free_region: non_local_ub, - blame_span: locations.span(mir), - category: ConstraintCategory::Boring, - }; - debug!("try_promote_type_test: pushing {:#?}", requirement); - propagated_outlives_requirements.push(requirement); + // This is slightly too conservative. To show T: '1, given `'2: '1` + // and `'3: '1` we only need to prove that T: '2 *or* T: '3, but to + // avoid potential non-determinism we approximate this by requiring + // T: '1 and T: '2. + for &upper_bound in non_local_ub { + debug_assert!(self.universal_regions.is_universal_region(upper_bound)); + debug_assert!(!self.universal_regions.is_local_free_region(upper_bound)); + + let requirement = ClosureOutlivesRequirement { + subject, + outlived_free_region: upper_bound, + blame_span: locations.span(mir), + category: ConstraintCategory::Boring, + }; + debug!("try_promote_type_test: pushing {:#?}", requirement); + propagated_outlives_requirements.push(requirement); + } } true } @@ -1157,63 +1163,109 @@ impl<'tcx> RegionInferenceContext<'tcx> { .is_none() ); + // Only check all of the relations for the main representative of each + // SCC, otherwise just check that we outlive said representative. This + // reduces the number of redundant relations propagated out of + // closures. + // Note that the representative will be a universal region if there is + // one in this SCC, so we will always check the representative here. + let representative = self.scc_representatives[longer_fr_scc]; + if representative != longer_fr { + self.check_universal_region_relation( + longer_fr, + representative, + infcx, + mir, + mir_def_id, + propagated_outlives_requirements, + errors_buffer, + ); + return; + } + // Find every region `o` such that `fr: o` // (because `fr` includes `end(o)`). for shorter_fr in self.scc_values.universal_regions_outlived_by(longer_fr_scc) { - // If it is known that `fr: o`, carry on. - if self.universal_region_relations - .outlives(longer_fr, shorter_fr) - { - continue; + if let Some(ErrorReported) = self.check_universal_region_relation( + longer_fr, + shorter_fr, + infcx, + mir, + mir_def_id, + propagated_outlives_requirements, + errors_buffer, + ) { + // continuing to iterate just reports more errors than necessary + return; } + } + } - debug!( - "check_universal_region: fr={:?} does not outlive shorter_fr={:?}", - longer_fr, shorter_fr, - ); + fn check_universal_region_relation( + &self, + longer_fr: RegionVid, + shorter_fr: RegionVid, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + mir_def_id: DefId, + propagated_outlives_requirements: &mut Option<&mut Vec>>, + errors_buffer: &mut Vec, + ) -> Option { + // If it is known that `fr: o`, carry on. + if self.universal_region_relations + .outlives(longer_fr, shorter_fr) + { + return None; + } - let blame_span_category = self.find_outlives_blame_span(mir, longer_fr, shorter_fr); - - if let Some(propagated_outlives_requirements) = propagated_outlives_requirements { - // Shrink `fr` until we find a non-local region (if we do). - // We'll call that `fr-` -- it's ever so slightly smaller than `fr`. - if let Some(fr_minus) = self.universal_region_relations - .non_local_lower_bound(longer_fr) - { - debug!("check_universal_region: fr_minus={:?}", fr_minus); - - // Grow `shorter_fr` until we find a non-local - // region. (We always will.) We'll call that - // `shorter_fr+` -- it's ever so slightly larger than - // `fr`. - let shorter_fr_plus = self.universal_region_relations - .non_local_upper_bound(shorter_fr); - debug!( - "check_universal_region: shorter_fr_plus={:?}", - shorter_fr_plus - ); + debug!( + "check_universal_region_relation: fr={:?} does not outlive shorter_fr={:?}", + longer_fr, shorter_fr, + ); + if let Some(propagated_outlives_requirements) = propagated_outlives_requirements { + // Shrink `longer_fr` until we find a non-local region (if we do). + // We'll call it `fr-` -- it's ever so slightly smaller than + // `longer_fr`. + + if let Some(fr_minus) = self + .universal_region_relations + .non_local_lower_bound(longer_fr) + { + debug!("check_universal_region: fr_minus={:?}", fr_minus); + + let blame_span_category = self.find_outlives_blame_span(mir, longer_fr, shorter_fr); + + // Grow `shorter_fr` until we find some non-local regions. (We + // always will.) We'll call them `shorter_fr+` -- they're ever + // so slightly larger than `shorter_fr`. + let shorter_fr_plus = self.universal_region_relations + .non_local_upper_bounds(&shorter_fr); + debug!( + "check_universal_region: shorter_fr_plus={:?}", + shorter_fr_plus + ); + for &&fr in &shorter_fr_plus { // Push the constraint `fr-: shorter_fr+` propagated_outlives_requirements.push(ClosureOutlivesRequirement { subject: ClosureOutlivesSubject::Region(fr_minus), - outlived_free_region: shorter_fr_plus, + outlived_free_region: fr, blame_span: blame_span_category.1, category: blame_span_category.0, }); - continue; } + return None; } - - // If we are not in a context where we can propagate - // errors, or we could not shrink `fr` to something - // smaller, then just report an error. - // - // Note: in this case, we use the unapproximated regions - // to report the error. This gives better error messages - // in some cases. - self.report_error(mir, infcx, mir_def_id, longer_fr, shorter_fr, errors_buffer); - return; // continuing to iterate just reports more errors than necessary } + + // If we are not in a context where we can't propagate errors, or we + // could not shrink `fr` to something smaller, then just report an + // error. + // + // Note: in this case, we use the unapproximated regions to report the + // error. This gives better error messages in some cases. + self.report_error(mir, infcx, mir_def_id, longer_fr, shorter_fr, errors_buffer); + Some(ErrorReported) } fn check_bound_universal_region<'gcx>( diff --git a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs index 7ddfd55dbbb94..3b663ef6dad61 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs @@ -105,44 +105,89 @@ impl UniversalRegionRelations<'tcx> { /// Finds an "upper bound" for `fr` that is not local. In other /// words, returns the smallest (*) known region `fr1` that (a) - /// outlives `fr` and (b) is not local. This cannot fail, because - /// we will always find `'static` at worst. + /// outlives `fr` and (b) is not local. /// - /// (*) If there are multiple competing choices, we pick the "postdominating" - /// one. See `TransitiveRelation::postdom_upper_bound` for details. - crate fn non_local_upper_bound(&self, fr: RegionVid) -> RegionVid { + /// (*) If there are multiple competing choices, we return all of them. + crate fn non_local_upper_bounds(&'a self, fr: &'a RegionVid) -> Vec<&'a RegionVid> { debug!("non_local_upper_bound(fr={:?})", fr); - self.non_local_bound(&self.inverse_outlives, fr) + let res = self.non_local_bounds(&self.inverse_outlives, fr); + assert!(!res.is_empty(), "can't find an upper bound!?"); + res + } + + /// Returns the "postdominating" bound of the set of + /// `non_local_upper_bounds` for the given region. + crate fn non_local_upper_bound(&self, fr: RegionVid) -> RegionVid { + let upper_bounds = self.non_local_upper_bounds(&fr); + + // In case we find more than one, reduce to one for + // convenience. This is to prevent us from generating more + // complex constraints, but it will cause spurious errors. + let post_dom = self + .inverse_outlives + .mutual_immediate_postdominator(upper_bounds); + + debug!("non_local_bound: post_dom={:?}", post_dom); + + post_dom + .and_then(|&post_dom| { + // If the mutual immediate postdom is not local, then + // there is no non-local result we can return. + if !self.universal_regions.is_local_free_region(post_dom) { + Some(post_dom) + } else { + None + } + }) .unwrap_or(self.universal_regions.fr_static) } + /// Finds a "lower bound" for `fr` that is not local. In other /// words, returns the largest (*) known region `fr1` that (a) is - /// outlived by `fr` and (b) is not local. This cannot fail, - /// because we will always find `'static` at worst. + /// outlived by `fr` and (b) is not local. /// /// (*) If there are multiple competing choices, we pick the "postdominating" /// one. See `TransitiveRelation::postdom_upper_bound` for details. crate fn non_local_lower_bound(&self, fr: RegionVid) -> Option { debug!("non_local_lower_bound(fr={:?})", fr); - self.non_local_bound(&self.outlives, fr) + let lower_bounds = self.non_local_bounds(&self.outlives, &fr); + + // In case we find more than one, reduce to one for + // convenience. This is to prevent us from generating more + // complex constraints, but it will cause spurious errors. + let post_dom = self + .outlives + .mutual_immediate_postdominator(lower_bounds); + + debug!("non_local_bound: post_dom={:?}", post_dom); + + post_dom + .and_then(|&post_dom| { + // If the mutual immediate postdom is not local, then + // there is no non-local result we can return. + if !self.universal_regions.is_local_free_region(post_dom) { + Some(post_dom) + } else { + None + } + }) } - /// Helper for `non_local_upper_bound` and - /// `non_local_lower_bound`. Repeatedly invokes `postdom_parent` - /// until we find something that is not local. Returns `None` if we - /// never do so. - fn non_local_bound( + /// Helper for `non_local_upper_bounds` and `non_local_lower_bounds`. + /// Repeatedly invokes `postdom_parent` until we find something that is not + /// local. Returns `None` if we never do so. + fn non_local_bounds<'a>( &self, - relation: &TransitiveRelation, - fr0: RegionVid, - ) -> Option { + relation: &'a TransitiveRelation, + fr0: &'a RegionVid, + ) -> Vec<&'a RegionVid> { // This method assumes that `fr0` is one of the universally // quantified region variables. - assert!(self.universal_regions.is_universal_region(fr0)); + assert!(self.universal_regions.is_universal_region(*fr0)); let mut external_parents = vec![]; - let mut queue = vec![&fr0]; + let mut queue = vec![fr0]; // Keep expanding `fr` into its parents until we reach // non-local regions. @@ -157,24 +202,7 @@ impl UniversalRegionRelations<'tcx> { debug!("non_local_bound: external_parents={:?}", external_parents); - // In case we find more than one, reduce to one for - // convenience. This is to prevent us from generating more - // complex constraints, but it will cause spurious errors. - let post_dom = relation - .mutual_immediate_postdominator(external_parents) - .cloned(); - - debug!("non_local_bound: post_dom={:?}", post_dom); - - post_dom.and_then(|post_dom| { - // If the mutual immediate postdom is not local, then - // there is no non-local result we can return. - if !self.universal_regions.is_local_free_region(post_dom) { - Some(post_dom) - } else { - None - } - }) + external_parents } /// Returns `true` if fr1 is known to outlive fr2. diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index fb0c19f764c13..7be7f4b439289 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -11,7 +11,7 @@ use rustc::hir::def::Def; use rustc::mir::interpret::{ConstEvalErr, ErrorHandled}; use rustc::mir; use rustc::ty::{self, TyCtxt, query::TyCtxtAt}; -use rustc::ty::layout::{self, LayoutOf, TyLayout, VariantIdx}; +use rustc::ty::layout::{self, LayoutOf, VariantIdx}; use rustc::ty::subst::Subst; use rustc::traits::Reveal; use rustc_data_structures::fx::FxHashMap; @@ -21,7 +21,8 @@ use syntax::ast::Mutability; use syntax::source_map::{Span, DUMMY_SP}; use crate::interpret::{self, - PlaceTy, MPlaceTy, MemPlace, OpTy, Operand, Immediate, Scalar, RawConst, ConstValue, Pointer, + PlaceTy, MPlaceTy, MemPlace, OpTy, ImmTy, Operand, Immediate, Scalar, Pointer, + RawConst, ConstValue, EvalResult, EvalError, EvalErrorKind, GlobalId, EvalContext, StackPopCleanup, Allocation, AllocId, MemoryKind, snapshot, RefTracking, @@ -77,7 +78,7 @@ pub fn op_to_const<'tcx>( let normalized_op = if normalize { ecx.try_read_immediate(op)? } else { - match op.op { + match *op { Operand::Indirect(mplace) => Err(mplace), Operand::Immediate(val) => Ok(val) } @@ -105,15 +106,6 @@ pub fn op_to_const<'tcx>( Ok(ty::Const { val, ty: op.layout.ty }) } -pub fn lazy_const_to_op<'tcx>( - ecx: &CompileTimeEvalContext<'_, '_, 'tcx>, - cnst: ty::LazyConst<'tcx>, - ty: ty::Ty<'tcx>, -) -> EvalResult<'tcx, OpTy<'tcx>> { - let op = ecx.const_value_to_op(cnst)?; - Ok(OpTy { op, layout: ecx.layout_of(ty)? }) -} - fn eval_body_and_ecx<'a, 'mir, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, cid: GlobalId<'tcx>, @@ -388,10 +380,8 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx> fn ptr_op( _ecx: &EvalContext<'a, 'mir, 'tcx, Self>, _bin_op: mir::BinOp, - _left: Scalar, - _left_layout: TyLayout<'tcx>, - _right: Scalar, - _right_layout: TyLayout<'tcx>, + _left: ImmTy<'tcx>, + _right: ImmTy<'tcx>, ) -> EvalResult<'tcx, (Scalar, bool)> { Err( ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into(), @@ -486,7 +476,7 @@ pub fn const_field<'a, 'tcx>( let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env); let result = (|| { // get the operand again - let op = lazy_const_to_op(&ecx, ty::LazyConst::Evaluated(value), value.ty)?; + let op = ecx.lazy_const_to_op(ty::LazyConst::Evaluated(value), value.ty)?; // downcast let down = match variant { None => op, @@ -512,7 +502,7 @@ pub fn const_variant_index<'a, 'tcx>( ) -> EvalResult<'tcx, VariantIdx> { trace!("const_variant_index: {:?}", val); let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env); - let op = lazy_const_to_op(&ecx, ty::LazyConst::Evaluated(val), val.ty)?; + let op = ecx.lazy_const_to_op(ty::LazyConst::Evaluated(val), val.ty)?; Ok(ecx.read_discriminant(op)?.1) } diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs index eb72175421638..31aa3c2782697 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -690,7 +690,7 @@ fn main() { } ``` -See also https://doc.rust-lang.org/book/first-edition/unsafe.html +See also https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html "##, E0373: r##" @@ -873,7 +873,7 @@ that at most one writer or multiple readers can access the data at any one time. If you wish to learn more about ownership in Rust, start with the chapter in the Book: -https://doc.rust-lang.org/book/first-edition/ownership.html +https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html "##, E0383: r##" @@ -1207,7 +1207,7 @@ let mut a = &mut i; Please note that in rust, you can either have many immutable references, or one mutable reference. Take a look at -https://doc.rust-lang.org/stable/book/references-and-borrowing.html for more +https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html for more information. Example: @@ -1374,7 +1374,7 @@ fn foo(a: &mut i32) { ``` For more information on the rust ownership system, take a look at -https://doc.rust-lang.org/stable/book/references-and-borrowing.html. +https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html. "##, E0503: r##" @@ -1430,7 +1430,7 @@ fn main() { ``` You can find more information about borrowing in the rust-book: -http://doc.rust-lang.org/stable/book/references-and-borrowing.html +http://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html "##, E0504: r##" @@ -1614,7 +1614,7 @@ fn main() { ``` You can find more information about borrowing in the rust-book: -http://doc.rust-lang.org/stable/book/references-and-borrowing.html +http://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html "##, E0506: r##" @@ -1825,7 +1825,7 @@ mem::replace(&mut borrowed.knight, TheDarkKnight).nothing_is_true(); // ok! ``` You can find more information about borrowing in the rust-book: -http://doc.rust-lang.org/book/first-edition/references-and-borrowing.html +http://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html "##, E0508: r##" diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index c3b71be8354da..ce62d79e585a8 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -9,7 +9,7 @@ use rustc::mir::interpret::{ use rustc::mir::CastKind; use rustc_apfloat::Float; -use super::{EvalContext, Machine, PlaceTy, OpTy, Immediate}; +use super::{EvalContext, Machine, PlaceTy, OpTy, ImmTy, Immediate}; impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool { @@ -372,7 +372,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> assert_eq!(src.layout.fields.offset(i).bytes(), 0); assert_eq!(src_field_layout.size, src.layout.size); // just sawp out the layout - OpTy { op: src.op, layout: src_field_layout } + OpTy::from(ImmTy { imm: src.to_immediate(), layout: src_field_layout }) } }; if src_field.layout.ty == dst_field.layout.ty { diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 78c5c0a6d751c..e002c3fd511d6 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -126,7 +126,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let l = self.read_immediate(args[0])?; let r = self.read_immediate(args[1])?; let is_add = intrinsic_name == "saturating_add"; - let (val, overflowed) = self.binary_op_imm(if is_add { + let (val, overflowed) = self.binary_op(if is_add { BinOp::Add } else { BinOp::Sub @@ -173,7 +173,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> "unchecked_shr" => BinOp::Shr, _ => bug!("Already checked for int ops") }; - let (val, overflowed) = self.binary_op_imm(bin_op, l, r)?; + let (val, overflowed) = self.binary_op(bin_op, l, r)?; if overflowed { let layout = self.layout_of(substs.type_at(0))?; let r_val = r.to_scalar()?.to_bits(layout.size)?; diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 8f34b832f0b41..7fb4c47d92acb 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -7,11 +7,11 @@ use std::hash::Hash; use rustc::hir::{self, def_id::DefId}; use rustc::mir; -use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt}; +use rustc::ty::{self, query::TyCtxtAt}; use super::{ Allocation, AllocId, EvalResult, Scalar, AllocationExtra, - EvalContext, PlaceTy, MPlaceTy, OpTy, Pointer, MemoryKind, + EvalContext, PlaceTy, MPlaceTy, OpTy, ImmTy, Pointer, MemoryKind, }; /// Whether this kind of memory is allowed to leak @@ -158,10 +158,8 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized { fn ptr_op( ecx: &EvalContext<'a, 'mir, 'tcx, Self>, bin_op: mir::BinOp, - left: Scalar, - left_layout: TyLayout<'tcx>, - right: Scalar, - right_layout: TyLayout<'tcx>, + left: ImmTy<'tcx, Self::PointerTag>, + right: ImmTy<'tcx, Self::PointerTag>, ) -> EvalResult<'tcx, (Scalar, bool)>; /// Heap allocations via the `box` keyword. diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index c0b26442dd918..7da907028eebf 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -11,7 +11,10 @@ use rustc::mir::interpret::{ ConstValue, Pointer, Scalar, EvalResult, EvalErrorKind, }; -use super::{EvalContext, Machine, MemPlace, MPlaceTy, MemoryKind}; +use super::{ + EvalContext, Machine, AllocMap, Allocation, AllocationExtra, + MemPlace, MPlaceTy, PlaceTy, Place, MemoryKind, +}; pub use rustc::mir::interpret::ScalarMaybeUndef; /// A `Value` represents a single immediate self-contained Rust value. @@ -41,6 +44,11 @@ impl Immediate { } impl<'tcx, Tag> Immediate { + #[inline] + pub fn from_scalar(val: Scalar) -> Self { + Immediate::Scalar(ScalarMaybeUndef::Scalar(val)) + } + #[inline] pub fn erase_tag(self) -> Immediate { @@ -112,7 +120,7 @@ impl<'tcx, Tag> Immediate { // as input for binary and cast operations. #[derive(Copy, Clone, Debug)] pub struct ImmTy<'tcx, Tag=()> { - immediate: Immediate, + pub imm: Immediate, pub layout: TyLayout<'tcx>, } @@ -120,7 +128,7 @@ impl<'tcx, Tag> ::std::ops::Deref for ImmTy<'tcx, Tag> { type Target = Immediate; #[inline(always)] fn deref(&self) -> &Immediate { - &self.immediate + &self.imm } } @@ -180,7 +188,7 @@ impl Operand { #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct OpTy<'tcx, Tag=()> { - crate op: Operand, // ideally we'd make this private, but const_prop needs this + op: Operand, pub layout: TyLayout<'tcx>, } @@ -206,12 +214,25 @@ impl<'tcx, Tag> From> for OpTy<'tcx, Tag> { #[inline(always)] fn from(val: ImmTy<'tcx, Tag>) -> Self { OpTy { - op: Operand::Immediate(val.immediate), + op: Operand::Immediate(val.imm), layout: val.layout } } } +impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag> +{ + #[inline] + pub fn from_scalar(val: Scalar, layout: TyLayout<'tcx>) -> Self { + ImmTy { imm: Immediate::from_scalar(val), layout } + } + + #[inline] + pub fn to_bits(self) -> EvalResult<'tcx, u128> { + self.to_scalar()?.to_bits(self.layout.size) + } +} + impl<'tcx, Tag> OpTy<'tcx, Tag> { #[inline] @@ -324,8 +345,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> &self, op: OpTy<'tcx, M::PointerTag> ) -> EvalResult<'tcx, ImmTy<'tcx, M::PointerTag>> { - if let Ok(immediate) = self.try_read_immediate(op)? { - Ok(ImmTy { immediate, layout: op.layout }) + if let Ok(imm) = self.try_read_immediate(op)? { + Ok(ImmTy { imm, layout: op.layout }) } else { bug!("primitive read failed for type: {:?}", op.layout.ty); } @@ -469,6 +490,22 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> Ok(OpTy { op, layout }) } + /// Every place can be read from, so we can turm them into an operand + #[inline(always)] + pub fn place_to_op( + &self, + place: PlaceTy<'tcx, M::PointerTag> + ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> { + let op = match *place { + Place::Ptr(mplace) => { + Operand::Indirect(mplace) + } + Place::Local { frame, local } => + *self.stack[frame].locals[local].access()? + }; + Ok(OpTy { op, layout: place.layout }) + } + // Evaluate a place with the goal of reading from it. This lets us sometimes // avoid allocations. fn eval_place_to_op( @@ -531,10 +568,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> .collect() } - // Used when miri runs into a constant, and by CTFE. - // FIXME: CTFE should use allocations, then we can make this private (embed it into - // `eval_operand`, ideally). - pub(crate) fn const_value_to_op( + // Used when Miri runs into a constant, and (indirectly through lazy_const_to_op) by CTFE. + fn const_value_to_op( &self, val: ty::LazyConst<'tcx>, ) -> EvalResult<'tcx, Operand> { @@ -666,3 +701,21 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> } } + +impl<'a, 'mir, 'tcx, M> EvalContext<'a, 'mir, 'tcx, M> +where + M: Machine<'a, 'mir, 'tcx, PointerTag=()>, + // FIXME: Working around https://github.com/rust-lang/rust/issues/24159 + M::MemoryMap: AllocMap, Allocation<(), M::AllocExtra>)>, + M::AllocExtra: AllocationExtra<(), M::MemoryExtra>, +{ + // FIXME: CTFE should use allocations, then we can remove this. + pub(crate) fn lazy_const_to_op( + &self, + cnst: ty::LazyConst<'tcx>, + ty: ty::Ty<'tcx>, + ) -> EvalResult<'tcx, OpTy<'tcx>> { + let op = self.const_value_to_op(cnst)?; + Ok(OpTy { op, layout: self.layout_of(ty)? }) + } +} diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index 5e3335f4c7219..b3b9c742d6c28 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -18,7 +18,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> right: ImmTy<'tcx, M::PointerTag>, dest: PlaceTy<'tcx, M::PointerTag>, ) -> EvalResult<'tcx> { - let (val, overflowed) = self.binary_op_imm(op, left, right)?; + let (val, overflowed) = self.binary_op(op, left, right)?; let val = Immediate::ScalarPair(val.into(), Scalar::from_bool(overflowed).into()); self.write_immediate(val, dest) } @@ -32,7 +32,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> right: ImmTy<'tcx, M::PointerTag>, dest: PlaceTy<'tcx, M::PointerTag>, ) -> EvalResult<'tcx> { - let (val, _overflowed) = self.binary_op_imm(op, left, right)?; + let (val, _overflowed) = self.binary_op(op, left, right)?; self.write_scalar(val, dest) } } @@ -272,69 +272,55 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> Ok((val, false)) } - /// Convenience wrapper that's useful when keeping the layout together with the - /// immediate value. + /// Returns the result of the specified operation and whether it overflowed. #[inline] - pub fn binary_op_imm( + pub fn binary_op( &self, bin_op: mir::BinOp, left: ImmTy<'tcx, M::PointerTag>, right: ImmTy<'tcx, M::PointerTag>, - ) -> EvalResult<'tcx, (Scalar, bool)> { - self.binary_op( - bin_op, - left.to_scalar()?, left.layout, - right.to_scalar()?, right.layout, - ) - } - - /// Returns the result of the specified operation and whether it overflowed. - pub fn binary_op( - &self, - bin_op: mir::BinOp, - left: Scalar, - left_layout: TyLayout<'tcx>, - right: Scalar, - right_layout: TyLayout<'tcx>, ) -> EvalResult<'tcx, (Scalar, bool)> { trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", - bin_op, left, left_layout.ty, right, right_layout.ty); + bin_op, *left, left.layout.ty, *right, right.layout.ty); - match left_layout.ty.sty { + match left.layout.ty.sty { ty::Char => { - assert_eq!(left_layout.ty, right_layout.ty); - let left = left.to_char()?; - let right = right.to_char()?; + assert_eq!(left.layout.ty, right.layout.ty); + let left = left.to_scalar()?.to_char()?; + let right = right.to_scalar()?.to_char()?; self.binary_char_op(bin_op, left, right) } ty::Bool => { - assert_eq!(left_layout.ty, right_layout.ty); - let left = left.to_bool()?; - let right = right.to_bool()?; + assert_eq!(left.layout.ty, right.layout.ty); + let left = left.to_scalar()?.to_bool()?; + let right = right.to_scalar()?.to_bool()?; self.binary_bool_op(bin_op, left, right) } ty::Float(fty) => { - assert_eq!(left_layout.ty, right_layout.ty); - let left = left.to_bits(left_layout.size)?; - let right = right.to_bits(right_layout.size)?; + assert_eq!(left.layout.ty, right.layout.ty); + let left = left.to_bits()?; + let right = right.to_bits()?; self.binary_float_op(bin_op, fty, left, right) } _ => { // Must be integer(-like) types. Don't forget about == on fn pointers. - assert!(left_layout.ty.is_integral() || left_layout.ty.is_unsafe_ptr() || - left_layout.ty.is_fn()); - assert!(right_layout.ty.is_integral() || right_layout.ty.is_unsafe_ptr() || - right_layout.ty.is_fn()); + assert!(left.layout.ty.is_integral() || left.layout.ty.is_unsafe_ptr() || + left.layout.ty.is_fn()); + assert!(right.layout.ty.is_integral() || right.layout.ty.is_unsafe_ptr() || + right.layout.ty.is_fn()); // Handle operations that support pointer values - if left.is_ptr() || right.is_ptr() || bin_op == mir::BinOp::Offset { - return M::ptr_op(self, bin_op, left, left_layout, right, right_layout); + if left.to_scalar_ptr()?.is_ptr() || + right.to_scalar_ptr()?.is_ptr() || + bin_op == mir::BinOp::Offset + { + return M::ptr_op(self, bin_op, left, right); } // Everything else only works with "proper" bits - let left = left.to_bits(left_layout.size).expect("we checked is_ptr"); - let right = right.to_bits(right_layout.size).expect("we checked is_ptr"); - self.binary_int_op(bin_op, left, left_layout, right, right_layout) + let l = left.to_bits().expect("we checked is_ptr"); + let r = right.to_bits().expect("we checked is_ptr"); + self.binary_int_op(bin_op, l, left.layout, r, right.layout) } } } @@ -342,13 +328,14 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> pub fn unary_op( &self, un_op: mir::UnOp, - val: Scalar, - layout: TyLayout<'tcx>, + val: ImmTy<'tcx, M::PointerTag>, ) -> EvalResult<'tcx, Scalar> { use rustc::mir::UnOp::*; use rustc_apfloat::ieee::{Single, Double}; use rustc_apfloat::Float; + let layout = val.layout; + let val = val.to_scalar()?; trace!("Running unary op {:?}: {:?} ({:?})", un_op, val, layout.ty.sty); match layout.ty.sty { diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 004a11e34d6e1..b29e09900f6b1 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -244,10 +244,10 @@ impl<'tcx, Tag> MPlaceTy<'tcx, Tag> { } } -impl<'tcx, Tag: ::std::fmt::Debug> OpTy<'tcx, Tag> { +impl<'tcx, Tag: ::std::fmt::Debug + Copy> OpTy<'tcx, Tag> { #[inline(always)] pub fn try_as_mplace(self) -> Result, Immediate> { - match self.op { + match *self { Operand::Indirect(mplace) => Ok(MPlaceTy { mplace, layout: self.layout }), Operand::Immediate(imm) => Err(imm), } @@ -487,9 +487,9 @@ where Deref => self.deref_operand(base.into())?, Index(local) => { - let n = *self.frame().locals[local].access()?; - let n_layout = self.layout_of(self.tcx.types.usize)?; - let n = self.read_scalar(OpTy { op: n, layout: n_layout })?; + let layout = self.layout_of(self.tcx.types.usize)?; + let n = self.access_local(self.frame(), local, Some(layout))?; + let n = self.read_scalar(n)?; let n = n.to_bits(self.tcx.data_layout.pointer_size)?; self.mplace_field(base, u64::try_from(n).unwrap())? } @@ -991,22 +991,6 @@ where Ok(()) } - /// Every place can be read from, so we can turm them into an operand - #[inline(always)] - pub fn place_to_op( - &self, - place: PlaceTy<'tcx, M::PointerTag> - ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> { - let op = match place.place { - Place::Ptr(mplace) => { - Operand::Indirect(mplace) - } - Place::Local { frame, local } => - *self.stack[frame].locals[local].access()? - }; - Ok(OpTy { op, layout: place.layout }) - } - pub fn raw_const_to_mplace( &self, raw: RawConst<'tcx>, diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 0c988eb681083..97ef2b5fa3485 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -176,7 +176,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> UnaryOp(un_op, ref operand) => { // The operand always has the same type as the result. let val = self.read_immediate(self.eval_operand(operand, Some(dest.layout))?)?; - let val = self.unary_op(un_op, val.to_scalar()?, dest.layout)?; + let val = self.unary_op(un_op, val)?; self.write_scalar(val, dest)?; } diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index be50daa17092f..c2ee3f5715bd3 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -7,7 +7,7 @@ use rustc_target::spec::abi::Abi; use rustc::mir::interpret::{EvalResult, PointerArithmetic, EvalErrorKind, Scalar}; use super::{ - EvalContext, Machine, Immediate, OpTy, PlaceTy, MPlaceTy, Operand, StackPopCleanup + EvalContext, Machine, Immediate, OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup }; impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { @@ -51,8 +51,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> // Compare using binary_op, to also support pointer values let const_int = Scalar::from_uint(const_int, discr.layout.size); let (res, _) = self.binary_op(mir::BinOp::Eq, - discr.to_scalar()?, discr.layout, - const_int, discr.layout, + discr, + ImmTy::from_scalar(const_int, discr.layout), )?; if res.to_bool()? { target_block = targets[index]; @@ -418,8 +418,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let mut args = args.to_vec(); let pointee = args[0].layout.ty.builtin_deref(true).unwrap().ty; let fake_fat_ptr_ty = self.tcx.mk_mut_ptr(pointee); - args[0].layout = self.layout_of(fake_fat_ptr_ty)?.field(self, 0)?; - args[0].op = Operand::Immediate(Immediate::Scalar(ptr.ptr.into())); // strip vtable + args[0] = OpTy::from(ImmTy { // strip vtable + layout: self.layout_of(fake_fat_ptr_ty)?.field(self, 0)?, + imm: Immediate::Scalar(ptr.ptr.into()) + }); trace!("Patched self operand to {:#?}", args[0]); // recurse with concrete function self.eval_fn_call(instance, span, caller_abi, &args, dest, ret) @@ -448,8 +450,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> _ => (instance, place), }; - let arg = OpTy { - op: Operand::Immediate(place.to_ref()), + let arg = ImmTy { + imm: place.to_ref(), layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?, }; @@ -460,7 +462,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> instance, span, Abi::Rust, - &[arg], + &[arg.into()], Some(dest.into()), Some(target), ) diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 65d7060b544d6..1b0a9b17d3686 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -22,6 +22,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let (ty, poly_trait_ref) = self.tcx.erase_regions(&(ty, poly_trait_ref)); if let Some(&vtable) = self.vtables.get(&(ty, poly_trait_ref)) { + // This means we guarantee that there are no duplicate vtables, we will + // always use the same vtable for the same (Type, Trait) combination. + // That's not what happens in rustc, but emulating per-crate deduplication + // does not sound like it actually makes anything any better. return Ok(Pointer::from(vtable).with_default_tag()); } diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 018f71c39e513..7da00c4ea0c36 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -18,10 +18,9 @@ use rustc::ty::layout::{ HasTyCtxt, TargetDataLayout, HasDataLayout, }; -use crate::interpret::{self, EvalContext, ScalarMaybeUndef, Immediate, OpTy, MemoryKind}; +use crate::interpret::{EvalContext, ScalarMaybeUndef, Immediate, OpTy, ImmTy, MemoryKind}; use crate::const_eval::{ CompileTimeInterpreter, error_to_const_error, eval_promoted, mk_eval_cx, - lazy_const_to_op, }; use crate::transform::{MirPass, MirSource}; @@ -254,7 +253,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { source_info: SourceInfo, ) -> Option> { self.ecx.tcx.span = source_info.span; - match lazy_const_to_op(&self.ecx, *c.literal, c.ty) { + match self.ecx.lazy_const_to_op(*c.literal, c.ty) { Ok(op) => { Some((op, c.span)) }, @@ -345,15 +344,15 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { Rvalue::Len(_) => None, Rvalue::NullaryOp(NullOp::SizeOf, ty) => { type_size_of(self.tcx, self.param_env, ty).and_then(|n| Some(( - OpTy { - op: interpret::Operand::Immediate(Immediate::Scalar( + ImmTy { + imm: Immediate::Scalar( Scalar::Bits { bits: n as u128, size: self.tcx.data_layout.pointer_size.bytes() as u8, }.into() - )), + ), layout: self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).ok()?, - }, + }.into(), span, ))) } @@ -371,13 +370,12 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { let (arg, _) = self.eval_operand(arg, source_info)?; let val = self.use_ecx(source_info, |this| { - let prim = this.ecx.read_scalar(arg)?.not_undef()?; + let prim = this.ecx.read_immediate(arg)?; match op { UnOp::Neg => { // Need to do overflow check here: For actual CTFE, MIR // generation emits code that does this before calling the op. - let size = arg.layout.size; - if prim.to_bits(size)? == (1 << (size.bits() - 1)) { + if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) { return err!(OverflowNeg); } } @@ -386,13 +384,13 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { } } // Now run the actual operation. - this.ecx.unary_op(op, prim, arg.layout) + this.ecx.unary_op(op, prim) })?; - let res = OpTy { - op: interpret::Operand::Immediate(Immediate::Scalar(val.into())), + let res = ImmTy { + imm: Immediate::Scalar(val.into()), layout: place_layout, }; - Some((res, span)) + Some((res.into(), span)) } Rvalue::CheckedBinaryOp(op, ref left, ref right) | Rvalue::BinaryOp(op, ref left, ref right) => { @@ -447,7 +445,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { })?; trace!("const evaluating {:?} for {:?} and {:?}", op, left, right); let (val, overflow) = self.use_ecx(source_info, |this| { - this.ecx.binary_op_imm(op, l, r) + this.ecx.binary_op(op, l, r) })?; let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue { Immediate::ScalarPair( @@ -462,11 +460,11 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { } Immediate::Scalar(val.into()) }; - let res = OpTy { - op: interpret::Operand::Immediate(val), + let res = ImmTy { + imm: val, layout: place_layout, }; - Some((res, span)) + Some((res.into(), span)) }, } } diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 8d20c44a5a4ef..8ab9a8e8dda86 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -614,11 +614,16 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { Some(def) if def != HirDef::Err => def, _ => self.get_path_def(self.tcx.hir().get_parent_node(id)), } - }, + } + Node::Expr(&hir::Expr { node: hir::ExprKind::Struct(ref qpath, ..), .. - }) | + }) => { + let hir_id = self.tcx.hir().node_to_hir_id(id); + self.tables.qpath_def(qpath, hir_id) + } + Node::Expr(&hir::Expr { node: hir::ExprKind::Path(ref qpath), .. diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 3a670c8e2f15e..0e6ab5b1eb3b2 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -689,6 +689,8 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); CoerceMany::with_coercion_sites(coerce_first, arms) }; + let mut other_arms = vec![]; // used only for diagnostics + let mut prior_arm_ty = None; for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() { if let Some(ref g) = arm.guard { self.diverges.set(pats_diverge); @@ -709,17 +711,36 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); _ => false }; + let arm_span = if let hir::ExprKind::Block(ref blk, _) = arm.body.node { + // Point at the block expr instead of the entire block + blk.expr.as_ref().map(|e| e.span).unwrap_or(arm.body.span) + } else { + arm.body.span + }; if is_if_let_fallback { let cause = self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse); assert!(arm_ty.is_unit()); coercion.coerce_forced_unit(self, &cause, &mut |_| (), true); } else { - let cause = self.cause(expr.span, ObligationCauseCode::MatchExpressionArm { - arm_span: arm.body.span, - source: match_src - }); + let cause = if i == 0 { + // The reason for the first arm to fail is not that the match arms diverge, + // but rather that there's a prior obligation that doesn't hold. + self.cause(arm_span, ObligationCauseCode::BlockTailExpression(arm.body.id)) + } else { + self.cause(expr.span, ObligationCauseCode::MatchExpressionArm { + arm_span, + source: match_src, + prior_arms: other_arms.clone(), + last_ty: prior_arm_ty.unwrap(), + }) + }; coercion.coerce(self, &cause, &arm.body, arm_ty); } + other_arms.push(arm_span); + if other_arms.len() > 5 { + other_arms.remove(0); + } + prior_arm_ty = Some(arm_ty); } // We won't diverge unless the discriminant or all arms diverge. diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 33e93b582e540..8b80fba4d19d0 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -210,7 +210,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// ``` /// opt.map(|arg| { takes_ref(arg) }); /// ``` - fn can_use_as_ref(&self, expr: &hir::Expr) -> Option<(Span, &'static str, String)> { + fn can_use_as_ref( + &self, + expr: &hir::Expr, + ) -> Option<(Span, &'static str, String)> { if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.node { if let hir::def::Def::Local(id) = path.def { let parent = self.tcx.hir().get_parent_node(id); @@ -233,10 +236,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self_ty.starts_with("std::option::Option") || self_ty.starts_with("std::result::Result") ) && (name == "map" || name == "and_then"); - if is_as_ref_able { - return Some((span.shrink_to_lo(), - "consider using `as_ref` instead", - "as_ref().".into())); + match (is_as_ref_able, self.sess().source_map().span_to_snippet(*span)) { + (true, Ok(src)) => { + return Some((*span, "consider using `as_ref` instead", + format!("as_ref().{}", src))); + }, + _ => () } } } @@ -430,7 +435,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { match expr.node { // All built-in range literals but `..=` and `..` desugar to Structs - ExprKind::Struct(QPath::Resolved(None, ref path), _, _) | + ExprKind::Struct(ref qpath, _, _) => { + if let QPath::Resolved(None, ref path) = **qpath { + return is_range_path(&path) && span_is_range_literal(&expr.span); + } + } // `..` desugars to its struct path ExprKind::Path(QPath::Resolved(None, ref path)) => { return is_range_path(&path) && span_is_range_literal(&expr.span); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 91e44a1588268..4bbbc9959e1a8 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -131,7 +131,7 @@ use std::ops::{self, Deref}; use std::slice; use crate::require_c_abi_if_variadic; -use crate::session::{CompileIncomplete, Session}; +use crate::session::Session; use crate::session::config::EntryFnType; use crate::TypeAndSubsts; use crate::lint; @@ -711,12 +711,12 @@ fn check_mod_item_types<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>, module_def_id: DefId) tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckItemTypesVisitor { tcx }); } -pub fn check_item_bodies<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Result<(), CompileIncomplete> { +pub fn check_item_bodies<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Result<(), ErrorReported> { tcx.typeck_item_bodies(LOCAL_CRATE) } fn typeck_item_bodies<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, crate_num: CrateNum) - -> Result<(), CompileIncomplete> + -> Result<(), ErrorReported> { debug_assert!(crate_num == LOCAL_CRATE); Ok(tcx.sess.track_errors(|| { diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index e6533ac4b7559..71767fcfd4933 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -1,3 +1,4 @@ +// ignore-tidy-linelength #![allow(non_snake_case)] register_long_diagnostics! { @@ -1544,7 +1545,9 @@ fn f() {} It is not possible to declare type parameters on a function that has the `start` attribute. Such a function must have the following type signature (for more -information: http://doc.rust-lang.org/stable/book/first-edition/no-stdlib.html): +information, view [the unstable book][1]): + +[1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html#writing-an-executable-without-stdlib ``` # let _: @@ -2918,10 +2921,11 @@ impl Baz for Bar { } // Note: This is OK E0374: r##" A struct without a field containing an unsized type cannot implement -`CoerceUnsized`. An -[unsized type](https://doc.rust-lang.org/book/first-edition/unsized-types.html) -is any type that the compiler doesn't know the length or alignment of at -compile time. Any struct containing an unsized type is also unsized. +`CoerceUnsized`. An [unsized type][1] is any type that the compiler +doesn't know the length or alignment of at compile time. Any struct +containing an unsized type is also unsized. + +[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait Example of erroneous code: @@ -2978,9 +2982,9 @@ A struct with more than one field containing an unsized type cannot implement `CoerceUnsized`. This only occurs when you are trying to coerce one of the types in your struct to another type in the struct. In this case we try to impl `CoerceUnsized` from `T` to `U` which are both types that the struct -takes. An [unsized type] is any type that the compiler doesn't know the length -or alignment of at compile time. Any struct containing an unsized type is also -unsized. +takes. An [unsized type][1] is any type that the compiler doesn't know the +length or alignment of at compile time. Any struct containing an unsized type +is also unsized. Example of erroneous code: @@ -3025,7 +3029,7 @@ fn coerce_foo, U>(t: T) -> Foo { } ``` -[unsized type]: https://doc.rust-lang.org/book/first-edition/unsized-types.html +[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait "##, E0376: r##" @@ -3033,11 +3037,12 @@ The type you are trying to impl `CoerceUnsized` for is not a struct. `CoerceUnsized` can only be implemented for a struct. Unsized types are already able to be coerced without an implementation of `CoerceUnsized` whereas a struct containing an unsized type needs to know the unsized type -field it's containing is able to be coerced. An -[unsized type](https://doc.rust-lang.org/book/first-edition/unsized-types.html) +field it's containing is able to be coerced. An [unsized type][1] is any type that the compiler doesn't know the length or alignment of at compile time. Any struct containing an unsized type is also unsized. +[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait + Example of erroneous code: ```compile_fail,E0376 @@ -3883,8 +3888,10 @@ let c = 86u8 as char; // ok! assert_eq!(c, 'V'); ``` -For more information about casts, take a look at The Book: -https://doc.rust-lang.org/book/first-edition/casting-between-types.html +For more information about casts, take a look at the Type cast section in +[The Reference Book][1]. + +[1]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions "##, E0605: r##" @@ -3912,8 +3919,10 @@ let v = 0 as *const u8; v as *const i8; // ok! ``` -For more information about casts, take a look at The Book: -https://doc.rust-lang.org/book/first-edition/casting-between-types.html +For more information about casts, take a look at the Type cast section in +[The Reference Book][1]. + +[1]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions "##, E0606: r##" @@ -3934,8 +3943,10 @@ let x = &0u8; let y: u32 = *x as u32; // We dereference it first and then cast it. ``` -For more information about casts, take a look at The Book: -https://doc.rust-lang.org/book/first-edition/casting-between-types.html +For more information about casts, take a look at the Type cast section in +[The Reference Book][1]. + +[1]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions "##, E0607: r##" @@ -3961,8 +3972,10 @@ pointer holds is their size. To fix this error, don't try to cast directly between thin and fat pointers. -For more information about casts, take a look at The Book: -https://doc.rust-lang.org/book/first-edition/casting-between-types.html +For more information about casts, take a look at the Type cast section in +[The Reference Book][1]. + +[1]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions "##, E0609: r##" @@ -4020,8 +4033,8 @@ println!("x: {}, y: {}", variable.x, variable.y); ``` For more information about primitives and structs, take a look at The Book: -https://doc.rust-lang.org/book/first-edition/primitive-types.html -https://doc.rust-lang.org/book/first-edition/structs.html +https://doc.rust-lang.org/book/ch03-02-data-types.html +https://doc.rust-lang.org/book/ch05-00-structs.html "##, E0614: r##" diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 7055218577c5c..ac5fa76c9ae1e 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -105,7 +105,7 @@ use rustc::infer::InferOk; use rustc::lint; use rustc::middle; use rustc::session; -use rustc::session::CompileIncomplete; +use rustc::util::common::ErrorReported; use rustc::session::config::{EntryFnType, nightly_options}; use rustc::traits::{ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt}; use rustc::ty::subst::Substs; @@ -318,7 +318,7 @@ pub fn provide(providers: &mut Providers<'_>) { } pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) - -> Result<(), CompileIncomplete> + -> Result<(), ErrorReported> { tcx.sess.profiler(|p| p.start_activity(ProfileCategory::TypeChecking)); @@ -327,7 +327,6 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) tcx.sess.track_errors(|| { time(tcx.sess, "type collecting", || collect::collect_item_types(tcx)); - })?; if tcx.features().rustc_attrs { @@ -365,7 +364,11 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) tcx.sess.profiler(|p| p.end_activity(ProfileCategory::TypeChecking)); - tcx.sess.compile_status() + if tcx.sess.err_count() == 0 { + Ok(()) + } else { + Err(ErrorReported) + } } /// A quasi-deprecated helper used in rustdoc and save-analysis to get diff --git a/src/librustc_typeck/structured_errors.rs b/src/librustc_typeck/structured_errors.rs index f75ab47e1ab70..3e3eab8cf4cfb 100644 --- a/src/librustc_typeck/structured_errors.rs +++ b/src/librustc_typeck/structured_errors.rs @@ -137,7 +137,7 @@ To fix this error, don't try to cast directly between thin and fat pointers. For more information about casts, take a look at The Book: -https://doc.rust-lang.org/book/first-edition/casting-between-types.html"); +https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions"); err } } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index e90127ca162d2..e3457a6b6c52f 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -1,5 +1,5 @@ use rustc_lint; -use rustc_driver::{self, driver, target_features, abort_on_err}; +use rustc_driver::{driver, abort_on_err}; use rustc::session::{self, config}; use rustc::hir::def_id::{DefId, DefIndex, DefIndexAddressSpace, CrateNum, LOCAL_CRATE}; use rustc::hir::def::Def; @@ -11,6 +11,7 @@ use rustc::hir::map as hir_map; use rustc::lint::{self, LintPass}; use rustc::session::config::ErrorOutputType; use rustc::util::nodemap::{FxHashMap, FxHashSet}; +use rustc_interface::util; use rustc_resolve as resolve; use rustc_metadata::creader::CrateLoader; use rustc_metadata::cstore::CStore; @@ -398,7 +399,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt debugging_options.ui_testing); let mut sess = session::build_session_( - sessopts, cpath, diagnostic_handler, source_map, + sessopts, cpath, diagnostic_handler, source_map, Default::default(), ); lint::builtin::HardwiredLints.get_lints() @@ -418,12 +419,12 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt lint::Allow); }); - let codegen_backend = rustc_driver::get_codegen_backend(&sess); + let codegen_backend = util::get_codegen_backend(&sess); let cstore = Rc::new(CStore::new(codegen_backend.metadata_loader())); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs)); - target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); + util::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let control = &driver::CompileController::basic(); @@ -477,23 +478,23 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let mut arenas = AllArenas::new(); let hir_map = hir_map::map_crate(&sess, &*cstore, &mut hir_forest, &defs); - let output_filenames = driver::build_output_filenames(&input, + let output_filenames = util::build_output_filenames(&input, &None, &None, &[], &sess); let resolver = RefCell::new(resolver); - abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend, - control, - &sess, - &*cstore, - hir_map, - resolutions, - &mut arenas, - &name, - &output_filenames, - |tcx, _, result| { + driver::phase_3_run_analysis_passes(&*codegen_backend, + control, + &sess, + &*cstore, + hir_map, + resolutions, + &mut arenas, + &name, + &output_filenames, + |tcx, _, result| { if result.is_err() { sess.fatal("Compilation failed, aborting rustdoc"); } @@ -608,6 +609,6 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt ctxt.sess().abort_if_errors(); (krate, ctxt.renderinfo.into_inner(), render_options, passes) - }), &sess) + }) }) } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index ddb730672d294..a7f8bacf09556 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -30,6 +30,7 @@ extern crate rustc_lint; extern crate rustc_metadata; extern crate rustc_target; extern crate rustc_typeck; +extern crate rustc_interface; extern crate serialize; extern crate syntax; extern crate syntax_pos; diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 0b9fbc81da626..2b7f5432359dd 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -2,10 +2,11 @@ use errors::{self, FatalError}; use errors::emitter::ColorConfig; use rustc_data_structures::sync::Lrc; use rustc_lint; -use rustc_driver::{self, driver, target_features, Compilation}; +use rustc_driver::{self, driver, Compilation}; use rustc_driver::driver::phase_2_configure_and_expand; use rustc_metadata::cstore::CStore; use rustc_metadata::dynamic_lib::DynamicLibrary; +use rustc_interface::util; use rustc::hir; use rustc::hir::intravisit; use rustc::session::{self, CompileIncomplete, config}; @@ -72,15 +73,15 @@ pub fn run(mut options: Options) -> isize { Some(source_map.clone())); let mut sess = session::build_session_( - sessopts, Some(options.input), handler, source_map.clone(), + sessopts, Some(options.input), handler, source_map.clone(), Default::default(), ); - let codegen_backend = rustc_driver::get_codegen_backend(&sess); + let codegen_backend = util::get_codegen_backend(&sess); let cstore = CStore::new(codegen_backend.metadata_loader()); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(options.cfgs.clone())); - target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); + util::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let krate = @@ -276,9 +277,9 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize, let diagnostic_handler = errors::Handler::with_emitter(true, false, box emitter); let mut sess = session::build_session_( - sessopts, None, diagnostic_handler, source_map, + sessopts, None, diagnostic_handler, source_map, Default::default(), ); - let codegen_backend = rustc_driver::get_codegen_backend(&sess); + let codegen_backend = util::get_codegen_backend(&sess); let cstore = CStore::new(codegen_backend.metadata_loader()); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); @@ -308,7 +309,7 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize, let mut control = driver::CompileController::basic(); let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs.clone())); - target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); + util::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let out = Some(outdir.lock().unwrap().path().join("rust_out")); diff --git a/src/libstd/prelude/mod.rs b/src/libstd/prelude/mod.rs index bf689bad559d3..551e982a3c685 100644 --- a/src/libstd/prelude/mod.rs +++ b/src/libstd/prelude/mod.rs @@ -129,10 +129,10 @@ //! [`std::string`]: ../string/index.html //! [`std::vec`]: ../vec/index.html //! [`to_owned`]: ../borrow/trait.ToOwned.html#tymethod.to_owned -//! [book-closures]: ../../book/first-edition/closures.html -//! [book-dtor]: ../../book/first-edition/drop.html -//! [book-enums]: ../../book/first-edition/enums.html -//! [book-iter]: ../../book/first-edition/iterators.html +//! [book-closures]: ../../book/ch13-01-closures.html +//! [book-dtor]: ../../book/ch15-03-drop.html +//! [book-enums]: ../../book/ch06-01-defining-an-enum.html +//! [book-iter]: ../../book/ch13-02-iterators.html #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index e753a74b92531..5c7fb1b804461 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -6,7 +6,7 @@ //! benchmarks themselves) should be done via the `#[test]` and //! `#[bench]` attributes. //! -//! See the [Testing Chapter](../book/first-edition/testing.html) of the book for more details. +//! See the [Testing Chapter](../book/ch11-00-testing.html) of the book for more details. // Currently, not much of this is meant for users. It is intended to // support the simplest interface possible for representing and diff --git a/src/test/run-make-fulldeps/issue-19371/foo.rs b/src/test/run-make-fulldeps/issue-19371/foo.rs index b8461c568c86d..e9d825df2a46e 100644 --- a/src/test/run-make-fulldeps/issue-19371/foo.rs +++ b/src/test/run-make-fulldeps/issue-19371/foo.rs @@ -6,6 +6,7 @@ extern crate rustc_lint; extern crate rustc_metadata; extern crate rustc_errors; extern crate rustc_codegen_utils; +extern crate rustc_interface; extern crate syntax; use rustc::session::{build_session, Session}; @@ -14,6 +15,7 @@ use rustc::session::config::{Input, Options, use rustc_driver::driver::{self, compile_input, CompileController}; use rustc_metadata::cstore::CStore; use rustc_errors::registry::Registry; +use rustc_interface::util; use syntax::source_map::FileName; use rustc_codegen_utils::codegen_backend::CodegenBackend; @@ -45,7 +47,7 @@ fn main() { fn basic_sess(opts: Options) -> (Session, Rc, Box) { let descriptions = Registry::new(&rustc::DIAGNOSTICS); let sess = build_session(opts, None, descriptions); - let codegen_backend = rustc_driver::get_codegen_backend(&sess); + let codegen_backend = util::get_codegen_backend(&sess); let cstore = Rc::new(CStore::new(codegen_backend.metadata_loader())); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); (sess, cstore, codegen_backend) diff --git a/src/test/rustdoc-ui/failed-doctest-output.stdout b/src/test/rustdoc-ui/failed-doctest-output.stdout index 8af05e9ca97b1..a67b2e6235e56 100644 --- a/src/test/rustdoc-ui/failed-doctest-output.stdout +++ b/src/test/rustdoc-ui/failed-doctest-output.stdout @@ -12,7 +12,7 @@ error[E0425]: cannot find value `no` in this scope 3 | no | ^^ not found in this scope -thread '$DIR/failed-doctest-output.rs - OtherStruct (line 17)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:354:13 +thread '$DIR/failed-doctest-output.rs - OtherStruct (line 17)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:355:13 note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace. ---- $DIR/failed-doctest-output.rs - SomeStruct (line 11) stdout ---- @@ -21,7 +21,7 @@ thread '$DIR/failed-doctest-output.rs - SomeStruct (line 11)' panicked at 'test thread 'main' panicked at 'oh no', $DIR/failed-doctest-output.rs:3:1 note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace. -', src/librustdoc/test.rs:389:17 +', src/librustdoc/test.rs:390:17 failures: diff --git a/src/test/ui/bad/bad-lint-cap2.stderr b/src/test/ui/bad/bad-lint-cap2.stderr index b9638722778ee..d7ec41489d156 100644 --- a/src/test/ui/bad/bad-lint-cap2.stderr +++ b/src/test/ui/bad/bad-lint-cap2.stderr @@ -2,7 +2,7 @@ error: unused import: `std::option` --> $DIR/bad-lint-cap2.rs:6:5 | LL | use std::option; //~ ERROR - | ----^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^ | note: lint level defined here --> $DIR/bad-lint-cap2.rs:4:9 diff --git a/src/test/ui/bad/bad-lint-cap3.stderr b/src/test/ui/bad/bad-lint-cap3.stderr index 21ed50b550afc..5bf0b089afa20 100644 --- a/src/test/ui/bad/bad-lint-cap3.stderr +++ b/src/test/ui/bad/bad-lint-cap3.stderr @@ -2,7 +2,7 @@ warning: unused import: `std::option` --> $DIR/bad-lint-cap3.rs:7:5 | LL | use std::option; //~ WARN - | ----^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^ | note: lint level defined here --> $DIR/bad-lint-cap3.rs:4:9 diff --git a/src/test/ui/if/if-let-arm-types.rs b/src/test/ui/if/if-let-arm-types.rs index 749c089ae9752..819f5dd1cfc35 100644 --- a/src/test/ui/if/if-let-arm-types.rs +++ b/src/test/ui/if/if-let-arm-types.rs @@ -1,10 +1,11 @@ fn main() { - if let Some(b) = None { //~ ERROR: `if let` arms have incompatible types - //~^ expected (), found integer - //~| expected type `()` - //~| found type `{integer}` + if let Some(b) = None { + //~^ NOTE if let` arms have incompatible types () } else { 1 }; + //~^^ ERROR: `if let` arms have incompatible types + //~| NOTE expected (), found integer + //~| NOTE expected type `()` } diff --git a/src/test/ui/if/if-let-arm-types.stderr b/src/test/ui/if/if-let-arm-types.stderr index fcf9e4695f675..6401a62c06ba2 100644 --- a/src/test/ui/if/if-let-arm-types.stderr +++ b/src/test/ui/if/if-let-arm-types.stderr @@ -1,25 +1,17 @@ error[E0308]: `if let` arms have incompatible types - --> $DIR/if-let-arm-types.rs:2:5 + --> $DIR/if-let-arm-types.rs:6:9 | -LL | / if let Some(b) = None { //~ ERROR: `if let` arms have incompatible types -LL | | //~^ expected (), found integer -LL | | //~| expected type `()` -LL | | //~| found type `{integer}` -... | +LL | / if let Some(b) = None { +LL | | //~^ NOTE if let` arms have incompatible types +LL | | () +LL | | } else { LL | | 1 + | | ^ expected (), found integer LL | | }; - | |_____^ expected (), found integer + | |_____- `if let` arms have incompatible types | = note: expected type `()` found type `{integer}` -note: `if let` arm with an incompatible type - --> $DIR/if-let-arm-types.rs:7:12 - | -LL | } else { - | ____________^ -LL | | 1 -LL | | }; - | |_____^ error: aborting due to previous error diff --git a/src/test/ui/imports/unused.stderr b/src/test/ui/imports/unused.stderr index fa82e974e1e29..b56e930158cc1 100644 --- a/src/test/ui/imports/unused.stderr +++ b/src/test/ui/imports/unused.stderr @@ -2,7 +2,7 @@ error: unused import: `super::f` --> $DIR/unused.rs:7:24 | LL | pub(super) use super::f; //~ ERROR unused - | ---------------^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^ | note: lint level defined here --> $DIR/unused.rs:1:9 diff --git a/src/test/ui/issues/issue-11319.rs b/src/test/ui/issues/issue-11319.rs index ea901205544b6..726c437355e53 100644 --- a/src/test/ui/issues/issue-11319.rs +++ b/src/test/ui/issues/issue-11319.rs @@ -1,12 +1,14 @@ fn main() { match Some(10) { - //~^ ERROR match arms have incompatible types - //~| expected type `bool` - //~| found type `()` - //~| expected bool, found () + //~^ NOTE `match` arms have incompatible types Some(5) => false, + //~^ NOTE this is found to be of type `bool` Some(2) => true, + //~^ NOTE this is found to be of type `bool` None => (), + //~^ ERROR match arms have incompatible types + //~| NOTE expected bool, found () + //~| NOTE expected type `bool` _ => true } } diff --git a/src/test/ui/issues/issue-11319.stderr b/src/test/ui/issues/issue-11319.stderr index 44d63ba3e6879..10db477b8ca7b 100644 --- a/src/test/ui/issues/issue-11319.stderr +++ b/src/test/ui/issues/issue-11319.stderr @@ -1,16 +1,20 @@ error[E0308]: match arms have incompatible types - --> $DIR/issue-11319.rs:2:5 + --> $DIR/issue-11319.rs:8:20 | LL | / match Some(10) { -LL | | //~^ ERROR match arms have incompatible types -LL | | //~| expected type `bool` -LL | | //~| found type `()` -... | +LL | | //~^ NOTE `match` arms have incompatible types +LL | | Some(5) => false, + | | ----- this is found to be of type `bool` +LL | | //~^ NOTE this is found to be of type `bool` +LL | | Some(2) => true, + | | ---- this is found to be of type `bool` +LL | | //~^ NOTE this is found to be of type `bool` LL | | None => (), - | | -- match arm with an incompatible type + | | ^^ expected bool, found () +... | LL | | _ => true LL | | } - | |_____^ expected bool, found () + | |_____- `match` arms have incompatible types | = note: expected type `bool` found type `()` diff --git a/src/test/ui/issues/issue-17728.rs b/src/test/ui/issues/issue-17728.rs index 0f13ae3304d6c..15cea1d609d53 100644 --- a/src/test/ui/issues/issue-17728.rs +++ b/src/test/ui/issues/issue-17728.rs @@ -97,7 +97,7 @@ impl Debug for Player { } fn str_to_direction(to_parse: &str) -> RoomDirection { - match to_parse { //~ ERROR match arms have incompatible types + match to_parse { "w" | "west" => RoomDirection::West, "e" | "east" => RoomDirection::East, "n" | "north" => RoomDirection::North, @@ -108,6 +108,7 @@ fn str_to_direction(to_parse: &str) -> RoomDirection { "down" => RoomDirection::Down, _ => None } + //~^^ ERROR match arms have incompatible types } fn main() { diff --git a/src/test/ui/issues/issue-17728.stderr b/src/test/ui/issues/issue-17728.stderr index 355868f05690f..2c2efad19f569 100644 --- a/src/test/ui/issues/issue-17728.stderr +++ b/src/test/ui/issues/issue-17728.stderr @@ -10,17 +10,19 @@ LL | Some(entry) => Ok(entry), | ^^^^^^^^^ ...but data from `room` is returned here error[E0308]: match arms have incompatible types - --> $DIR/issue-17728.rs:100:5 + --> $DIR/issue-17728.rs:109:14 | -LL | / match to_parse { //~ ERROR match arms have incompatible types +LL | / match to_parse { LL | | "w" | "west" => RoomDirection::West, LL | | "e" | "east" => RoomDirection::East, LL | | "n" | "north" => RoomDirection::North, ... | +LL | | "down" => RoomDirection::Down, + | | ------------------- this and all prior arms are found to be of type `RoomDirection` LL | | _ => None - | | ---- match arm with an incompatible type + | | ^^^^ expected enum `RoomDirection`, found enum `std::option::Option` LL | | } - | |_____^ expected enum `RoomDirection`, found enum `std::option::Option` + | |_____- `match` arms have incompatible types | = note: expected type `RoomDirection` found type `std::option::Option<_>` diff --git a/src/test/ui/issues/issue-23302-3.stderr b/src/test/ui/issues/issue-23302-3.stderr index 392769b4c565f..b3ca736622a2a 100644 --- a/src/test/ui/issues/issue-23302-3.stderr +++ b/src/test/ui/issues/issue-23302-3.stderr @@ -20,6 +20,7 @@ note: ...which requires checking which parts of `B` are promotable to static... LL | const B: i32 = A; | ^ = note: ...which again requires const checking if rvalue is promotable to static `A`, completing the cycle + = note: cycle used when running analysis passes on this crate error: aborting due to previous error diff --git a/src/test/ui/issues/issue-24036.rs b/src/test/ui/issues/issue-24036.rs index 3642085934abe..2f501b941b5a6 100644 --- a/src/test/ui/issues/issue-24036.rs +++ b/src/test/ui/issues/issue-24036.rs @@ -6,11 +6,11 @@ fn closure_to_loc() { fn closure_from_match() { let x = match 1usize { - //~^ ERROR match arms have incompatible types 1 => |c| c + 1, 2 => |c| c - 1, _ => |c| c - 1 }; + //~^^^ ERROR match arms have incompatible types } fn main() { } diff --git a/src/test/ui/issues/issue-24036.stderr b/src/test/ui/issues/issue-24036.stderr index 9f799c9b45069..fa9935fcf619d 100644 --- a/src/test/ui/issues/issue-24036.stderr +++ b/src/test/ui/issues/issue-24036.stderr @@ -10,20 +10,20 @@ LL | x = |c| c + 1; = help: consider boxing your closure and/or using it as a trait object error[E0308]: match arms have incompatible types - --> $DIR/issue-24036.rs:8:13 + --> $DIR/issue-24036.rs:10:14 | LL | let x = match 1usize { - | _____________^ -LL | | //~^ ERROR match arms have incompatible types + | _____________- LL | | 1 => |c| c + 1, + | | --------- this is found to be of type `[closure@$DIR/issue-24036.rs:9:14: 9:23]` LL | | 2 => |c| c - 1, - | | --------- match arm with an incompatible type + | | ^^^^^^^^^ expected closure, found a different closure LL | | _ => |c| c - 1 LL | | }; - | |_____^ expected closure, found a different closure + | |_____- `match` arms have incompatible types | - = note: expected type `[closure@$DIR/issue-24036.rs:10:14: 10:23]` - found type `[closure@$DIR/issue-24036.rs:11:14: 11:23]` + = note: expected type `[closure@$DIR/issue-24036.rs:9:14: 9:23]` + found type `[closure@$DIR/issue-24036.rs:10:14: 10:23]` = note: no two closures, even if identical, have the same type = help: consider boxing your closure and/or using it as a trait object diff --git a/src/test/ui/issues/issue-30730.stderr b/src/test/ui/issues/issue-30730.stderr index 3cfadd33b8fec..0a901076f467a 100644 --- a/src/test/ui/issues/issue-30730.stderr +++ b/src/test/ui/issues/issue-30730.stderr @@ -2,7 +2,7 @@ error: unused import: `std::thread` --> $DIR/issue-30730.rs:3:5 | LL | use std::thread; - | ----^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^ | note: lint level defined here --> $DIR/issue-30730.rs:2:9 diff --git a/src/test/ui/lint/lint-directives-on-use-items-issue-10534.stderr b/src/test/ui/lint/lint-directives-on-use-items-issue-10534.stderr index e588d24517c8c..170b98a12a848 100644 --- a/src/test/ui/lint/lint-directives-on-use-items-issue-10534.stderr +++ b/src/test/ui/lint/lint-directives-on-use-items-issue-10534.stderr @@ -2,7 +2,7 @@ error: unused import: `a::x` --> $DIR/lint-directives-on-use-items-issue-10534.rs:12:9 | LL | use a::x; //~ ERROR: unused import - | ----^^^^- help: remove the whole `use` item + | ^^^^ | note: lint level defined here --> $DIR/lint-directives-on-use-items-issue-10534.rs:1:9 @@ -14,7 +14,7 @@ error: unused import: `a::y` --> $DIR/lint-directives-on-use-items-issue-10534.rs:21:9 | LL | use a::y; //~ ERROR: unused import - | ----^^^^- help: remove the whole `use` item + | ^^^^ | note: lint level defined here --> $DIR/lint-directives-on-use-items-issue-10534.rs:20:12 diff --git a/src/test/ui/lint/lint-unused-imports.stderr b/src/test/ui/lint/lint-unused-imports.stderr index 7970b0201db70..18f2fae0067eb 100644 --- a/src/test/ui/lint/lint-unused-imports.stderr +++ b/src/test/ui/lint/lint-unused-imports.stderr @@ -2,7 +2,7 @@ error: unused import: `std::fmt::{}` --> $DIR/lint-unused-imports.rs:8:5 | LL | use std::fmt::{}; - | ----^^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^^ | note: lint level defined here --> $DIR/lint-unused-imports.rs:1:9 @@ -14,39 +14,37 @@ error: unused imports: `None`, `Some` --> $DIR/lint-unused-imports.rs:12:27 | LL | use std::option::Option::{Some, None}; - | --------------------------^^^^--^^^^-- help: remove the whole `use` item + | ^^^^ ^^^^ error: unused import: `test::A` --> $DIR/lint-unused-imports.rs:15:5 | LL | use test::A; //~ ERROR unused import: `test::A` - | ----^^^^^^^- help: remove the whole `use` item + | ^^^^^^^ error: unused import: `bar` --> $DIR/lint-unused-imports.rs:24:18 | LL | use test2::{foo, bar}; //~ ERROR unused import: `bar` - | --^^^ - | | - | help: remove the unused import + | ^^^ error: unused import: `foo::Square` --> $DIR/lint-unused-imports.rs:52:13 | LL | use foo::Square; //~ ERROR unused import: `foo::Square` - | ----^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^ error: unused import: `self::g` --> $DIR/lint-unused-imports.rs:68:9 | LL | use self::g; //~ ERROR unused import: `self::g` - | ----^^^^^^^- help: remove the whole `use` item + | ^^^^^^^ error: unused import: `test2::foo` --> $DIR/lint-unused-imports.rs:77:9 | LL | use test2::foo; //~ ERROR unused import: `test2::foo` - | ----^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^ error: unused import: `test::B2` --> $DIR/lint-unused-imports.rs:20:5 diff --git a/src/test/ui/lint/lints-in-foreign-macros.stderr b/src/test/ui/lint/lints-in-foreign-macros.stderr index b808ca708a311..8287ca5692bd9 100644 --- a/src/test/ui/lint/lints-in-foreign-macros.stderr +++ b/src/test/ui/lint/lints-in-foreign-macros.stderr @@ -2,7 +2,7 @@ warning: unused import: `std::string::ToString` --> $DIR/lints-in-foreign-macros.rs:11:16 | LL | () => {use std::string::ToString;} //~ WARN: unused import - | ----^^^^^^^^^^^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^^^^^^^^^^^ ... LL | mod a { foo!(); } | ------- in this macro invocation @@ -17,13 +17,13 @@ warning: unused import: `std::string::ToString` --> $DIR/lints-in-foreign-macros.rs:16:18 | LL | mod c { baz!(use std::string::ToString;); } //~ WARN: unused import - | ----^^^^^^^^^^^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^^^^^^^^^^^ warning: unused import: `std::string::ToString` --> $DIR/lints-in-foreign-macros.rs:17:19 | LL | mod d { baz2!(use std::string::ToString;); } //~ WARN: unused import - | ----^^^^^^^^^^^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^^^^^^^^^^^ warning: missing documentation for crate --> $DIR/lints-in-foreign-macros.rs:4:1 diff --git a/src/test/ui/match/match-type-err-first-arm.rs b/src/test/ui/match/match-type-err-first-arm.rs new file mode 100644 index 0000000000000..b4b84ef8f1cec --- /dev/null +++ b/src/test/ui/match/match-type-err-first-arm.rs @@ -0,0 +1,45 @@ +fn main() { + let _ = test_func1(1); + let _ = test_func2(1); +} + +fn test_func1(n: i32) -> i32 { + //~^ NOTE expected `i32` because of return type + match n { + 12 => 'b', + //~^ ERROR mismatched types + //~| NOTE expected i32, found char + _ => 42, + } +} + +fn test_func2(n: i32) -> i32 { + let x = match n { + //~^ NOTE `match` arms have incompatible types + 12 => 'b', + //~^ NOTE this is found to be of type `char` + _ => 42, + //~^ ERROR match arms have incompatible types + //~| NOTE expected char, found integer + //~| NOTE expected type `char` + }; + x +} + +fn test_func3(n: i32) -> i32 { + let x = match n { + //~^ NOTE `match` arms have incompatible types + 1 => 'b', + 2 => 'b', + 3 => 'b', + 4 => 'b', + 5 => 'b', + 6 => 'b', + //~^ NOTE this and all prior arms are found to be of type `char` + _ => 42, + //~^ ERROR match arms have incompatible types + //~| NOTE expected char, found integer + //~| NOTE expected type `char` + }; + x +} diff --git a/src/test/ui/match/match-type-err-first-arm.stderr b/src/test/ui/match/match-type-err-first-arm.stderr new file mode 100644 index 0000000000000..db8bef8dc7755 --- /dev/null +++ b/src/test/ui/match/match-type-err-first-arm.stderr @@ -0,0 +1,53 @@ +error[E0308]: mismatched types + --> $DIR/match-type-err-first-arm.rs:9:15 + | +LL | fn test_func1(n: i32) -> i32 { + | --- expected `i32` because of return type +... +LL | 12 => 'b', + | ^^^ expected i32, found char + +error[E0308]: match arms have incompatible types + --> $DIR/match-type-err-first-arm.rs:21:14 + | +LL | let x = match n { + | _____________- +LL | | //~^ NOTE `match` arms have incompatible types +LL | | 12 => 'b', + | | --- this is found to be of type `char` +LL | | //~^ NOTE this is found to be of type `char` +LL | | _ => 42, + | | ^^ expected char, found integer +... | +LL | | //~| NOTE expected type `char` +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected type `char` + found type `{integer}` + +error[E0308]: match arms have incompatible types + --> $DIR/match-type-err-first-arm.rs:39:14 + | +LL | let x = match n { + | _____________- +LL | | //~^ NOTE `match` arms have incompatible types +LL | | 1 => 'b', +LL | | 2 => 'b', +... | +LL | | 6 => 'b', + | | --- this and all prior arms are found to be of type `char` +LL | | //~^ NOTE this and all prior arms are found to be of type `char` +LL | | _ => 42, + | | ^^ expected char, found integer +... | +LL | | //~| NOTE expected type `char` +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected type `char` + found type `{integer}` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/nll/closure-requirements/issue-58127-mutliple-requirements.rs b/src/test/ui/nll/closure-requirements/issue-58127-mutliple-requirements.rs new file mode 100644 index 0000000000000..71d5d4053ee25 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/issue-58127-mutliple-requirements.rs @@ -0,0 +1,40 @@ +// revisions: migrate nll +//[migrate]compile-flags: -Z borrowck=migrate +#![cfg_attr(nll, feature(nll))] + +// compile-pass + +// Test that we propagate region relations from closures precisely when there is +// more than one non-local lower bound. + +// In this case the closure has signature +// |x: &'4 mut (&'5 (&'1 str, &'2 str), &'3 str)| -> .. +// We end up with a `'3: '5` constraint that we can propagate as +// `'3: '1`, `'3: '2`, but previously we approximated it as `'3: 'static`. + +// As an optimization, we primarily propagate bounds for the "representative" +// of each SCC. As such we have these two similar cases where hopefully one +// of them will test the case we want (case2, when this test was added). +mod case1 { + fn f(s: &str) { + g(s, |x| h(x)); + } + + fn g(_: T, _: F) + where F: Fn(&mut (&(T, T), T)) {} + + fn h(_: &mut (&(T, T), T)) {} +} + +mod case2 { + fn f(s: &str) { + g(s, |x| h(x)); + } + + fn g(_: T, _: F) + where F: Fn(&mut (T, &(T, T))) {} + + fn h(_: &mut (T, &(T, T))) {} +} + +fn main() {} diff --git a/src/test/ui/rfc-2166-underscore-imports/basic.stderr b/src/test/ui/rfc-2166-underscore-imports/basic.stderr index c7b36eaf2e76b..3080359192603 100644 --- a/src/test/ui/rfc-2166-underscore-imports/basic.stderr +++ b/src/test/ui/rfc-2166-underscore-imports/basic.stderr @@ -2,7 +2,7 @@ warning: unused import: `m::Tr1 as _` --> $DIR/basic.rs:26:9 | LL | use m::Tr1 as _; //~ WARN unused import - | ----^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^ | note: lint level defined here --> $DIR/basic.rs:4:9 @@ -14,5 +14,5 @@ warning: unused import: `S as _` --> $DIR/basic.rs:27:9 | LL | use S as _; //~ WARN unused import - | ----^^^^^^- help: remove the whole `use` item + | ^^^^^^ diff --git a/src/test/ui/rfc-2166-underscore-imports/unused-2018.stderr b/src/test/ui/rfc-2166-underscore-imports/unused-2018.stderr index 0bbc17276d98b..4163c2876074b 100644 --- a/src/test/ui/rfc-2166-underscore-imports/unused-2018.stderr +++ b/src/test/ui/rfc-2166-underscore-imports/unused-2018.stderr @@ -2,7 +2,7 @@ error: unused import: `core::any` --> $DIR/unused-2018.rs:6:9 | LL | use core::any; //~ ERROR unused import: `core::any` - | ----^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^ | note: lint level defined here --> $DIR/unused-2018.rs:3:9 @@ -14,7 +14,7 @@ error: unused import: `core` --> $DIR/unused-2018.rs:10:9 | LL | use core; //~ ERROR unused import: `core` - | ----^^^^- help: remove the whole `use` item + | ^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/span/multispan-import-lint.stderr b/src/test/ui/span/multispan-import-lint.stderr index 6bd0e9be81f5e..a730d081b7c03 100644 --- a/src/test/ui/span/multispan-import-lint.stderr +++ b/src/test/ui/span/multispan-import-lint.stderr @@ -10,8 +10,4 @@ note: lint level defined here LL | #![warn(unused)] | ^^^^^^ = note: #[warn(unused_imports)] implied by #[warn(unused)] -help: remove the unused imports - | -LL | use std::cmp::{min}; - | -- -- diff --git a/src/test/ui/suggestions/as-ref.stderr b/src/test/ui/suggestions/as-ref.stderr index 7273496a7ce06..8143acc957b4c 100644 --- a/src/test/ui/suggestions/as-ref.stderr +++ b/src/test/ui/suggestions/as-ref.stderr @@ -2,9 +2,9 @@ error[E0308]: mismatched types --> $DIR/as-ref.rs:6:27 | LL | opt.map(|arg| takes_ref(arg)); - | - ^^^ expected &Foo, found struct `Foo` + | --- ^^^ expected &Foo, found struct `Foo` | | - | help: consider using `as_ref` instead: `as_ref().` + | help: consider using `as_ref` instead: `as_ref().map` | = note: expected type `&Foo` found type `Foo` @@ -13,9 +13,9 @@ error[E0308]: mismatched types --> $DIR/as-ref.rs:8:37 | LL | opt.and_then(|arg| Some(takes_ref(arg))); - | - ^^^ expected &Foo, found struct `Foo` + | -------- ^^^ expected &Foo, found struct `Foo` | | - | help: consider using `as_ref` instead: `as_ref().` + | help: consider using `as_ref` instead: `as_ref().and_then` | = note: expected type `&Foo` found type `Foo` @@ -24,9 +24,9 @@ error[E0308]: mismatched types --> $DIR/as-ref.rs:11:27 | LL | opt.map(|arg| takes_ref(arg)); - | - ^^^ expected &Foo, found struct `Foo` + | --- ^^^ expected &Foo, found struct `Foo` | | - | help: consider using `as_ref` instead: `as_ref().` + | help: consider using `as_ref` instead: `as_ref().map` | = note: expected type `&Foo` found type `Foo` @@ -35,9 +35,9 @@ error[E0308]: mismatched types --> $DIR/as-ref.rs:13:35 | LL | opt.and_then(|arg| Ok(takes_ref(arg))); - | - ^^^ expected &Foo, found struct `Foo` + | -------- ^^^ expected &Foo, found struct `Foo` | | - | help: consider using `as_ref` instead: `as_ref().` + | help: consider using `as_ref` instead: `as_ref().and_then` | = note: expected type `&Foo` found type `Foo` diff --git a/src/test/ui/use/use-nested-groups-unused-imports.stderr b/src/test/ui/use/use-nested-groups-unused-imports.stderr index 6af6f449de5e6..c8df6cbc57dca 100644 --- a/src/test/ui/use/use-nested-groups-unused-imports.stderr +++ b/src/test/ui/use/use-nested-groups-unused-imports.stderr @@ -2,7 +2,7 @@ error: unused imports: `*`, `Foo`, `baz::{}`, `foobar::*` --> $DIR/use-nested-groups-unused-imports.rs:16:11 | LL | use foo::{Foo, bar::{baz::{}, foobar::*}, *}; - | ----------^^^--------^^^^^^^--^^^^^^^^^---^-- help: remove the whole `use` item + | ^^^ ^^^^^^^ ^^^^^^^^^ ^ | note: lint level defined here --> $DIR/use-nested-groups-unused-imports.rs:3:9 @@ -14,15 +14,13 @@ error: unused import: `*` --> $DIR/use-nested-groups-unused-imports.rs:18:24 | LL | use foo::bar::baz::{*, *}; - | --^ - | | - | help: remove the unused import + | ^ error: unused import: `foo::{}` --> $DIR/use-nested-groups-unused-imports.rs:20:5 | LL | use foo::{}; - | ----^^^^^^^- help: remove the whole `use` item + | ^^^^^^^ error: aborting due to 3 previous errors