From 68bf8d2487103f90c0427701cf2247d758de9d13 Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Tue, 26 May 2020 19:45:30 -0400 Subject: [PATCH 01/15] expand `env!` with def-site context --- src/librustc_builtin_macros/env.rs | 1 + src/test/ui/extenv/extenv-not-defined-custom.stderr | 2 ++ src/test/ui/extenv/extenv-not-defined-default.stderr | 2 ++ src/test/ui/extenv/issue-55897.stderr | 2 ++ src/test/ui/macros/macros-nonfatal-errors.stderr | 2 ++ 5 files changed, 9 insertions(+) diff --git a/src/librustc_builtin_macros/env.rs b/src/librustc_builtin_macros/env.rs index 21e1889513b01..d769ebb1f5520 100644 --- a/src/librustc_builtin_macros/env.rs +++ b/src/librustc_builtin_macros/env.rs @@ -77,6 +77,7 @@ pub fn expand_env<'cx>( return DummyResult::any(sp); } + let sp = cx.with_def_site_ctxt(sp); let e = match env::var(&*var.as_str()) { Err(_) => { cx.span_err(sp, &msg.as_str()); diff --git a/src/test/ui/extenv/extenv-not-defined-custom.stderr b/src/test/ui/extenv/extenv-not-defined-custom.stderr index 523982dd0196b..56415fd1f0dd0 100644 --- a/src/test/ui/extenv/extenv-not-defined-custom.stderr +++ b/src/test/ui/extenv/extenv-not-defined-custom.stderr @@ -3,6 +3,8 @@ error: my error message | LL | fn main() { env!("__HOPEFULLY_NOT_DEFINED__", "my error message"); } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/extenv/extenv-not-defined-default.stderr b/src/test/ui/extenv/extenv-not-defined-default.stderr index 4bfe330f59235..1a9332c4f1c9f 100644 --- a/src/test/ui/extenv/extenv-not-defined-default.stderr +++ b/src/test/ui/extenv/extenv-not-defined-default.stderr @@ -3,6 +3,8 @@ error: environment variable `__HOPEFULLY_NOT_DEFINED__` not defined | LL | env!("__HOPEFULLY_NOT_DEFINED__"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/extenv/issue-55897.stderr b/src/test/ui/extenv/issue-55897.stderr index c57a467cdba56..b62f06e33e531 100644 --- a/src/test/ui/extenv/issue-55897.stderr +++ b/src/test/ui/extenv/issue-55897.stderr @@ -3,6 +3,8 @@ error: environment variable `NON_EXISTENT` not defined | LL | include!(concat!(env!("NON_EXISTENT"), "/data.rs")); | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: suffixes on a string literal are invalid --> $DIR/issue-55897.rs:16:22 diff --git a/src/test/ui/macros/macros-nonfatal-errors.stderr b/src/test/ui/macros/macros-nonfatal-errors.stderr index 6ef757a55b8fb..42954ebcdc1cd 100644 --- a/src/test/ui/macros/macros-nonfatal-errors.stderr +++ b/src/test/ui/macros/macros-nonfatal-errors.stderr @@ -47,6 +47,8 @@ error: environment variable `RUST_HOPEFULLY_THIS_DOESNT_EXIST` not defined | LL | env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: format argument must be a string literal --> $DIR/macros-nonfatal-errors.rs:23:13 From 709ddba9fea4a4ba84883793df4f63c2f64a690a Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 27 May 2020 16:56:57 +0200 Subject: [PATCH 02/15] Allow types (with lifetimes/generics) in impl_lint_pass --- src/librustc_session/lint.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustc_session/lint.rs b/src/librustc_session/lint.rs index b16d513d9239f..8a66fac1e3634 100644 --- a/src/librustc_session/lint.rs +++ b/src/librustc_session/lint.rs @@ -347,14 +347,14 @@ pub trait LintPass { fn name(&self) -> &'static str; } -/// Implements `LintPass for $name` with the given list of `Lint` statics. +/// Implements `LintPass for $ty` with the given list of `Lint` statics. #[macro_export] macro_rules! impl_lint_pass { - ($name:ident => [$($lint:expr),* $(,)?]) => { - impl $crate::lint::LintPass for $name { - fn name(&self) -> &'static str { stringify!($name) } + ($ty:ty => [$($lint:expr),* $(,)?]) => { + impl $crate::lint::LintPass for $ty { + fn name(&self) -> &'static str { stringify!($ty) } } - impl $name { + impl $ty { pub fn get_lints() -> $crate::lint::LintArray { $crate::lint_array!($($lint),*) } } }; From b12489248084f1ea33008e9b77ad2bbd41ee0f7f Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 27 May 2020 16:57:31 +0200 Subject: [PATCH 03/15] Add test for {impl,declare}_lint_pass macros --- src/test/ui-fulldeps/lint-pass-macros.rs | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/test/ui-fulldeps/lint-pass-macros.rs diff --git a/src/test/ui-fulldeps/lint-pass-macros.rs b/src/test/ui-fulldeps/lint-pass-macros.rs new file mode 100644 index 0000000000000..b3c2a542792f0 --- /dev/null +++ b/src/test/ui-fulldeps/lint-pass-macros.rs @@ -0,0 +1,26 @@ +// compile-flags: -Z unstable-options +// check-pass + +#![feature(rustc_private)] + +extern crate rustc_session; + +use rustc_session::lint::{LintArray, LintPass}; +use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; + +declare_lint! { + pub TEST_LINT, + Allow, + "test" +} + +struct Foo; + +struct Bar<'a>(&'a u32); + +impl_lint_pass!(Foo => [TEST_LINT]); +impl_lint_pass!(Bar<'_> => [TEST_LINT]); + +declare_lint_pass!(Baz => [TEST_LINT]); + +fn main() {} From 0c1ef853bb6ff87c2179b5e41b7e044b6c5b79cb Mon Sep 17 00:00:00 2001 From: Ivan Lozano Date: Tue, 26 May 2020 13:41:40 -0400 Subject: [PATCH 04/15] Add -Z profile-emit= for Gcov gcda output. Adds a -Z flag to control the file path that the Gcov gcda output is written to during runtime. This flag expects a path and filename, e.g. -Z profile-emit=gcov/out/lib.gcda. This works similar to GCC/Clang's -fprofile-dir flag which allows control over the output path for gcda coverage files. --- src/librustc_codegen_llvm/debuginfo/metadata.rs | 8 ++++---- src/librustc_interface/tests.rs | 1 + src/librustc_session/options.rs | 3 +++ src/test/run-make-fulldeps/profile/Makefile | 3 +++ 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 0cce0b25e5893..333eb805221ff 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -959,16 +959,16 @@ pub fn compile_unit_metadata( if tcx.sess.opts.debugging_opts.profile { let cu_desc_metadata = llvm::LLVMRustMetadataAsValue(debug_context.llcontext, unit_metadata); + let default_gcda_path = &tcx.output_filenames(LOCAL_CRATE).with_extension("gcda"); + let gcda_path = + tcx.sess.opts.debugging_opts.profile_emit.as_ref().unwrap_or(default_gcda_path); let gcov_cu_info = [ path_to_mdstring( debug_context.llcontext, &tcx.output_filenames(LOCAL_CRATE).with_extension("gcno"), ), - path_to_mdstring( - debug_context.llcontext, - &tcx.output_filenames(LOCAL_CRATE).with_extension("gcda"), - ), + path_to_mdstring(debug_context.llcontext, &gcda_path), cu_desc_metadata, ]; let gcov_metadata = llvm::LLVMMDNodeInContext( diff --git a/src/librustc_interface/tests.rs b/src/librustc_interface/tests.rs index d573e11fc4b24..91a4bc2b86856 100644 --- a/src/librustc_interface/tests.rs +++ b/src/librustc_interface/tests.rs @@ -557,6 +557,7 @@ fn test_debugging_options_tracking_hash() { tracked!(plt, Some(true)); tracked!(print_fuel, Some("abc".to_string())); tracked!(profile, true); + tracked!(profile_emit, Some(PathBuf::from("abc"))); tracked!(relro_level, Some(RelroLevel::Full)); tracked!(report_delayed_bugs, true); tracked!(run_dsymutil, false); diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index 95f9ff00fb8d4..a38e7f063d79a 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -955,6 +955,9 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "print layout information for each type encountered (default: no)"), profile: bool = (false, parse_bool, [TRACKED], "insert profiling code (default: no)"), + profile_emit: Option = (None, parse_opt_pathbuf, [TRACKED], + "file path to emit profiling data at runtime when using 'profile' \ + (default based on relative source path)"), query_dep_graph: bool = (false, parse_bool, [UNTRACKED], "enable queries of the dependency graph for regression testing (default: no)"), query_stats: bool = (false, parse_bool, [UNTRACKED], diff --git a/src/test/run-make-fulldeps/profile/Makefile b/src/test/run-make-fulldeps/profile/Makefile index c12712590e48f..04d382b475ed2 100644 --- a/src/test/run-make-fulldeps/profile/Makefile +++ b/src/test/run-make-fulldeps/profile/Makefile @@ -7,3 +7,6 @@ all: $(call RUN,test) || exit 1 [ -e "$(TMPDIR)/test.gcno" ] || (echo "No .gcno file"; exit 1) [ -e "$(TMPDIR)/test.gcda" ] || (echo "No .gcda file"; exit 1) + $(RUSTC) -g -Z profile -Z profile-emit=$(TMPDIR)/abc/abc.gcda test.rs + $(call RUN,test) || exit 1 + [ -e "$(TMPDIR)/abc/abc.gcda" ] || (echo "gcda file not emitted to defined path"; exit 1) From 5369f4aa57fc72c6f58368bb0f4977975ec2f1e6 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 27 May 2020 21:34:17 +0300 Subject: [PATCH 05/15] rustc_session: Cleanup session creation --- src/librustc_interface/interface.rs | 9 ++--- src/librustc_interface/tests.rs | 12 +++++-- src/librustc_interface/util.rs | 8 ++--- src/librustc_session/parse.rs | 4 +++ src/librustc_session/session.rs | 55 ++++++++--------------------- src/librustdoc/test.rs | 2 +- 6 files changed, 35 insertions(+), 55 deletions(-) diff --git a/src/librustc_interface/interface.rs b/src/librustc_interface/interface.rs index 55f825e150e5e..f127a239eea2c 100644 --- a/src/librustc_interface/interface.rs +++ b/src/librustc_interface/interface.rs @@ -18,7 +18,7 @@ use rustc_session::lint; use rustc_session::parse::{CrateConfig, ParseSess}; use rustc_session::{DiagnosticOutput, Session}; use rustc_span::edition; -use rustc_span::source_map::{FileLoader, FileName, SourceMap}; +use rustc_span::source_map::{FileLoader, FileName}; use std::path::PathBuf; use std::result; use std::sync::{Arc, Mutex}; @@ -31,7 +31,6 @@ pub type Result = result::Result; pub struct Compiler { pub(crate) sess: Lrc, codegen_backend: Lrc>, - source_map: Lrc, pub(crate) input: Input, pub(crate) input_path: Option, pub(crate) output_dir: Option, @@ -49,9 +48,6 @@ impl Compiler { pub fn codegen_backend(&self) -> &Lrc> { &self.codegen_backend } - pub fn source_map(&self) -> &Lrc { - &self.source_map - } pub fn input(&self) -> &Input { &self.input } @@ -168,7 +164,7 @@ pub fn run_compiler_in_existing_thread_pool( f: impl FnOnce(&Compiler) -> R, ) -> R { let registry = &config.registry; - let (sess, codegen_backend, source_map) = util::create_session( + let (sess, codegen_backend) = util::create_session( config.opts, config.crate_cfg, config.diagnostic_output, @@ -181,7 +177,6 @@ pub fn run_compiler_in_existing_thread_pool( let compiler = Compiler { sess, codegen_backend, - source_map, input: config.input, input_path: config.input_path, output_dir: config.output_dir, diff --git a/src/librustc_interface/tests.rs b/src/librustc_interface/tests.rs index d573e11fc4b24..9390bbb34d03a 100644 --- a/src/librustc_interface/tests.rs +++ b/src/librustc_interface/tests.rs @@ -7,11 +7,10 @@ use rustc_session::config::{build_configuration, build_session_options, to_crate use rustc_session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes}; use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; use rustc_session::config::{Externs, OutputType, OutputTypes, Sanitizer, SymbolManglingVersion}; -use rustc_session::getopts; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; use rustc_session::utils::NativeLibKind; -use rustc_session::{build_session, Session}; +use rustc_session::{build_session, getopts, DiagnosticOutput, Session}; use rustc_span::edition::{Edition, DEFAULT_EDITION}; use rustc_span::symbol::sym; use rustc_span::SourceFileHashAlgorithm; @@ -32,7 +31,14 @@ fn build_session_options_and_crate_config(matches: getopts::Matches) -> (Options fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) { let registry = registry::Registry::new(&[]); let (sessopts, cfg) = build_session_options_and_crate_config(matches); - let sess = build_session(sessopts, None, registry); + let sess = build_session( + sessopts, + None, + registry, + DiagnosticOutput::Default, + Default::default(), + None, + ); (sess, cfg) } diff --git a/src/librustc_interface/util.rs b/src/librustc_interface/util.rs index 7eaaff05fb5f0..924908e572487 100644 --- a/src/librustc_interface/util.rs +++ b/src/librustc_interface/util.rs @@ -23,7 +23,7 @@ use rustc_session::parse::CrateConfig; use rustc_session::CrateDisambiguator; use rustc_session::{early_error, filesearch, output, DiagnosticOutput, Session}; use rustc_span::edition::Edition; -use rustc_span::source_map::{FileLoader, SourceMap}; +use rustc_span::source_map::FileLoader; use rustc_span::symbol::{sym, Symbol}; use smallvec::SmallVec; use std::env; @@ -65,8 +65,8 @@ pub fn create_session( input_path: Option, lint_caps: FxHashMap, descriptions: Registry, -) -> (Lrc, Lrc>, Lrc) { - let (mut sess, source_map) = session::build_session_with_source_map( +) -> (Lrc, Lrc>) { + let mut sess = session::build_session( sopts, input_path, descriptions, @@ -81,7 +81,7 @@ pub fn create_session( add_configuration(&mut cfg, &mut sess, &*codegen_backend); sess.parse_sess.config = cfg; - (Lrc::new(sess), Lrc::new(codegen_backend), source_map) + (Lrc::new(sess), Lrc::new(codegen_backend)) } const STACK_SIZE: usize = 8 * 1024 * 1024; diff --git a/src/librustc_session/parse.rs b/src/librustc_session/parse.rs index 746e3536ce908..233761dbed7de 100644 --- a/src/librustc_session/parse.rs +++ b/src/librustc_session/parse.rs @@ -174,6 +174,10 @@ impl ParseSess { &self.source_map } + pub fn clone_source_map(&self) -> Lrc { + self.source_map.clone() + } + pub fn buffer_lint( &self, lint: &'static Lint, diff --git a/src/librustc_session/session.rs b/src/librustc_session/session.rs index f2f02cb649463..ba9741b1890f6 100644 --- a/src/librustc_session/session.rs +++ b/src/librustc_session/session.rs @@ -21,7 +21,7 @@ use rustc_errors::json::JsonEmitter; use rustc_errors::registry::Registry; use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId, ErrorReported}; use rustc_span::edition::Edition; -use rustc_span::source_map::{self, FileLoader, MultiSpan, RealFileLoader, SourceMap, Span}; +use rustc_span::source_map::{FileLoader, MultiSpan, RealFileLoader, SourceMap, Span}; use rustc_span::{SourceFileHashAlgorithm, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel}; @@ -481,7 +481,7 @@ impl Session { } #[inline] - pub fn source_map(&self) -> &source_map::SourceMap { + pub fn source_map(&self) -> &SourceMap { self.parse_sess.source_map() } pub fn verbose(&self) -> bool { @@ -984,26 +984,10 @@ impl Session { } } -pub fn build_session( - sopts: config::Options, - local_crate_source_file: Option, - registry: rustc_errors::registry::Registry, -) -> Session { - build_session_with_source_map( - sopts, - local_crate_source_file, - registry, - DiagnosticOutput::Default, - Default::default(), - None, - ) - .0 -} - fn default_emitter( sopts: &config::Options, registry: rustc_errors::registry::Registry, - source_map: &Lrc, + source_map: Lrc, emitter_dest: Option>, ) -> Box { let macro_backtrace = sopts.debugging_opts.macro_backtrace; @@ -1012,17 +996,14 @@ fn default_emitter( let (short, color_config) = kind.unzip(); if let HumanReadableErrorType::AnnotateSnippet(_) = kind { - let emitter = AnnotateSnippetEmitterWriter::new( - Some(source_map.clone()), - short, - macro_backtrace, - ); + let emitter = + AnnotateSnippetEmitterWriter::new(Some(source_map), short, macro_backtrace); Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing)) } else { let emitter = match dst { None => EmitterWriter::stderr( color_config, - Some(source_map.clone()), + Some(source_map), short, sopts.debugging_opts.teach, sopts.debugging_opts.terminal_width, @@ -1030,7 +1011,7 @@ fn default_emitter( ), Some(dst) => EmitterWriter::new( dst, - Some(source_map.clone()), + Some(source_map), short, false, // no teach messages when writing to a buffer false, // no colors when writing to a buffer @@ -1042,20 +1023,14 @@ fn default_emitter( } } (config::ErrorOutputType::Json { pretty, json_rendered }, None) => Box::new( - JsonEmitter::stderr( - Some(registry), - source_map.clone(), - pretty, - json_rendered, - macro_backtrace, - ) - .ui_testing(sopts.debugging_opts.ui_testing), + JsonEmitter::stderr(Some(registry), source_map, pretty, json_rendered, macro_backtrace) + .ui_testing(sopts.debugging_opts.ui_testing), ), (config::ErrorOutputType::Json { pretty, json_rendered }, Some(dst)) => Box::new( JsonEmitter::new( dst, Some(registry), - source_map.clone(), + source_map, pretty, json_rendered, macro_backtrace, @@ -1070,14 +1045,14 @@ pub enum DiagnosticOutput { Raw(Box), } -pub fn build_session_with_source_map( +pub fn build_session( sopts: config::Options, local_crate_source_file: Option, registry: rustc_errors::registry::Registry, diagnostics_output: DiagnosticOutput, driver_lint_caps: FxHashMap, file_loader: Option>, -) -> (Session, Lrc) { +) -> 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 // later via the source code. @@ -1115,7 +1090,7 @@ pub fn build_session_with_source_map( sopts.file_path_mapping(), hash_kind, )); - let emitter = default_emitter(&sopts, registry, &source_map, write_dest); + let emitter = default_emitter(&sopts, registry, source_map.clone(), write_dest); let span_diagnostic = rustc_errors::Handler::with_emitter_and_flags( emitter, @@ -1143,7 +1118,7 @@ pub fn build_session_with_source_map( None }; - let parse_sess = ParseSess::with_span_handler(span_diagnostic, source_map.clone()); + let parse_sess = ParseSess::with_span_handler(span_diagnostic, source_map); let sysroot = match &sopts.maybe_sysroot { Some(sysroot) => sysroot.clone(), None => filesearch::get_or_default_sysroot(), @@ -1266,7 +1241,7 @@ pub fn build_session_with_source_map( validate_commandline_args_with_session_available(&sess); - (sess, source_map) + sess } // If it is useful to have a Session available already for validating a diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 284e6d421ee2f..85ba4cbdc7e1b 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -114,7 +114,7 @@ pub fn run(options: Options) -> Result<(), String> { options, false, opts, - Some(compiler.source_map().clone()), + Some(compiler.session().parse_sess.clone_source_map()), None, enable_per_target_ignores, ); From d5e6bdac638a05432eb17b11c0dcbd2f71acbdaa Mon Sep 17 00:00:00 2001 From: Dennis Duda Date: Wed, 27 May 2020 21:58:12 +0200 Subject: [PATCH 06/15] Make remote-test-client and remote-test-server compatible with windows `compiletest` and `remote-test-client`: The command line for `remote-test-client` was changed slightly to allow cross-platform compatible paths. The old way of supplying the support libs was by joining their paths with the executable path with `:`. This caused Windows-style paths to be split after the directory letter. Now, the number of support libs is provided as a parameter as well, and the support lib paths are split off from the regular args in the client. `remote-test-server`: - Marked Unix-only parts as such and implemented Windows alternatives - On Windows `LD_LIBRARY_PATH` doesn't exist. Libraries are loaded from `PATH` though, so that's the way around it. - Tiny cleanup: `Command::args`/`envs` instead of manually looping over them - The temp path for Windows has to be set via environment variable, since there isn't a global temp directory that would work on every machine (as a static string) --- src/tools/compiletest/src/runtest.rs | 22 ++++--- src/tools/remote-test-client/src/main.rs | 14 ++-- src/tools/remote-test-server/src/main.rs | 83 +++++++++++++++++------- 3 files changed, 83 insertions(+), 36 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 63fd052a5560d..a6995eb820a78 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1584,29 +1584,35 @@ impl<'test> TestCx<'test> { // // into // - // remote-test-client run program:support-lib.so arg1 arg2 + // remote-test-client run program 2 support-lib.so support-lib2.so arg1 arg2 // // The test-client program will upload `program` to the emulator // along with all other support libraries listed (in this case - // `support-lib.so`. It will then execute the program on the - // emulator with the arguments specified (in the environment we give - // the process) and then report back the same result. + // `support-lib.so` and `support-lib2.so`. It will then execute + // the program on the emulator with the arguments specified + // (in the environment we give the process) and then report back + // the same result. _ if self.config.remote_test_client.is_some() => { let aux_dir = self.aux_output_dir_name(); - let ProcArgs { mut prog, args } = self.make_run_args(); + let ProcArgs { prog, args } = self.make_run_args(); + let mut support_libs = Vec::new(); if let Ok(entries) = aux_dir.read_dir() { for entry in entries { let entry = entry.unwrap(); if !entry.path().is_file() { continue; } - prog.push_str(":"); - prog.push_str(entry.path().to_str().unwrap()); + support_libs.push(entry.path()); } } let mut test_client = Command::new(self.config.remote_test_client.as_ref().unwrap()); - test_client.args(&["run", &prog]).args(args).envs(env.clone()); + test_client + .args(&["run", &prog]) + .arg(support_libs.len().to_string()) + .args(support_libs) + .args(args) + .envs(env.clone()); self.compose_and_run( test_client, self.config.run_lib_path.to_str().unwrap(), diff --git a/src/tools/remote-test-client/src/main.rs b/src/tools/remote-test-client/src/main.rs index 3379d82eda829..dea8bb933c9d9 100644 --- a/src/tools/remote-test-client/src/main.rs +++ b/src/tools/remote-test-client/src/main.rs @@ -44,7 +44,11 @@ fn main() { args.next().map(|s| s.into()), ), "push" => push(Path::new(&args.next().unwrap())), - "run" => run(args.next().unwrap(), args.collect()), + "run" => run( + args.next().unwrap(), + args.next().and_then(|count| count.parse().ok()).unwrap(), + args.collect(), + ), "help" | "-h" | "--help" => help(), cmd => { println!("unknown command: {}", cmd); @@ -197,12 +201,14 @@ fn push(path: &Path) { println!("done pushing {:?}", path); } -fn run(files: String, args: Vec) { +fn run(exe: String, support_lib_count: usize, all_args: Vec) { let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or(DEFAULT_ADDR.to_string()); let client = t!(TcpStream::connect(device_address)); let mut client = BufWriter::new(client); t!(client.write_all(b"run ")); + let (support_libs, args) = all_args.split_at(support_lib_count); + // Send over the args for arg in args { t!(client.write_all(arg.as_bytes())); @@ -227,9 +233,7 @@ fn run(files: String, args: Vec) { t!(client.write_all(&[0])); // Send over support libraries - let mut files = files.split(':'); - let exe = files.next().unwrap(); - for file in files.map(Path::new) { + for file in support_libs.iter().map(Path::new) { send(&file, &mut client); } t!(client.write_all(&[0])); diff --git a/src/tools/remote-test-server/src/main.rs b/src/tools/remote-test-server/src/main.rs index 826e3d05111ae..d9f65cf502da6 100644 --- a/src/tools/remote-test-server/src/main.rs +++ b/src/tools/remote-test-server/src/main.rs @@ -12,15 +12,19 @@ #![deny(warnings)] +#[cfg(not(windows))] +use std::fs::Permissions; +#[cfg(not(windows))] +use std::os::unix::prelude::*; + use std::cmp; use std::env; -use std::fs::{self, File, Permissions}; +use std::fs::{self, File}; use std::io::prelude::*; use std::io::{self, BufReader}; use std::net::{TcpListener, TcpStream}; -use std::os::unix::prelude::*; use std::path::{Path, PathBuf}; -use std::process::{Command, Stdio}; +use std::process::{Command, ExitStatus, Stdio}; use std::str; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; @@ -72,21 +76,23 @@ fn main() { let config = Config::parse_args(); - let bind_addr = if cfg!(target_os = "android") || config.remote { + let bind_addr = if cfg!(target_os = "android") || cfg!(windows) || config.remote { "0.0.0.0:12345" } else { "10.0.2.15:12345" }; - let (listener, work) = if cfg!(target_os = "android") { - (t!(TcpListener::bind(bind_addr)), "/data/tmp/work") + let listener = t!(TcpListener::bind(bind_addr)); + let work: PathBuf = if cfg!(windows) { + env::var_os("RUST_TEMP").expect("Set RUST_TEMP to your preferred temp folder").into() + } else if cfg!(target_os = "android") { + "/data/tmp/work".into() } else { - (t!(TcpListener::bind(bind_addr)), "/tmp/work") + "/tmp/work".into() }; println!("listening!"); - let work = Path::new(work); - t!(fs::create_dir_all(work)); + t!(fs::create_dir_all(&work)); let lock = Arc::new(Mutex::new(())); @@ -99,10 +105,11 @@ fn main() { if &buf[..] == b"ping" { t!(socket.write_all(b"pong")); } else if &buf[..] == b"push" { - handle_push(socket, work); + handle_push(socket, &work); } else if &buf[..] == b"run " { let lock = lock.clone(); - thread::spawn(move || handle_run(socket, work, &lock)); + let work = work.clone(); + thread::spawn(move || handle_run(socket, &work, &lock)); } else { panic!("unknown command {:?}", buf); } @@ -196,17 +203,28 @@ fn handle_run(socket: TcpStream, work: &Path, lock: &Mutex<()>) { let exe = recv(&path, &mut reader); let mut cmd = Command::new(&exe); - for arg in args { - cmd.arg(arg); - } - for (k, v) in env { - cmd.env(k, v); - } + cmd.args(args); + cmd.envs(env); // Support libraries were uploaded to `work` earlier, so make sure that's // in `LD_LIBRARY_PATH`. Also include our own current dir which may have // had some libs uploaded. - cmd.env("LD_LIBRARY_PATH", format!("{}:{}", work.display(), path.display())); + if cfg!(windows) { + // On windows, libraries are just searched in the executable directory, + // system directories, PWD, and PATH, in that order. PATH is the only one + // we can change for this. + cmd.env( + "PATH", + env::join_paths( + std::iter::once(work.to_owned()) + .chain(std::iter::once(path.clone())) + .chain(env::split_paths(&env::var_os("PATH").unwrap())), + ) + .unwrap(), + ); + } else { + cmd.env("LD_LIBRARY_PATH", format!("{}:{}", work.display(), path.display())); + } // Spawn the child and ferry over stdout/stderr to the socket in a framed // fashion (poor man's style) @@ -223,10 +241,9 @@ fn handle_run(socket: TcpStream, work: &Path, lock: &Mutex<()>) { // Finally send over the exit status. let status = t!(child.wait()); - let (which, code) = match status.code() { - Some(n) => (0, n), - None => (1, status.signal().unwrap()), - }; + + let (which, code) = get_status_code(&status); + t!(socket.lock().unwrap().write_all(&[ which, (code >> 24) as u8, @@ -236,6 +253,19 @@ fn handle_run(socket: TcpStream, work: &Path, lock: &Mutex<()>) { ])); } +#[cfg(not(windows))] +fn get_status_code(status: &ExitStatus) -> (u8, i32) { + match status.code() { + Some(n) => (0, n), + None => (1, status.signal().unwrap()), + } +} + +#[cfg(windows)] +fn get_status_code(status: &ExitStatus) -> (u8, i32) { + (0, status.code().unwrap()) +} + fn recv(dir: &Path, io: &mut B) -> PathBuf { let mut filename = Vec::new(); t!(io.read_until(0, &mut filename)); @@ -253,10 +283,17 @@ fn recv(dir: &Path, io: &mut B) -> PathBuf { let dst = dir.join(t!(str::from_utf8(&filename[..len]))); let amt = read_u32(io) as u64; t!(io::copy(&mut io.take(amt), &mut t!(File::create(&dst)))); - t!(fs::set_permissions(&dst, Permissions::from_mode(0o755))); + set_permissions(&dst); dst } +#[cfg(not(windows))] +fn set_permissions(path: &Path) { + t!(fs::set_permissions(&dst, Permissions::from_mode(0o755))); +} +#[cfg(windows)] +fn set_permissions(_path: &Path) {} + fn my_copy(src: &mut dyn Read, which: u8, dst: &Mutex) { let mut b = [0; 1024]; loop { From 2adbfa9139cb126e96240d8f669acf61d288a064 Mon Sep 17 00:00:00 2001 From: Dennis Duda Date: Thu, 28 May 2020 10:04:43 +0200 Subject: [PATCH 07/15] Unify temp path generation for non-android --- src/tools/remote-test-server/src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/remote-test-server/src/main.rs b/src/tools/remote-test-server/src/main.rs index d9f65cf502da6..9112985e140a7 100644 --- a/src/tools/remote-test-server/src/main.rs +++ b/src/tools/remote-test-server/src/main.rs @@ -83,12 +83,12 @@ fn main() { }; let listener = t!(TcpListener::bind(bind_addr)); - let work: PathBuf = if cfg!(windows) { - env::var_os("RUST_TEMP").expect("Set RUST_TEMP to your preferred temp folder").into() - } else if cfg!(target_os = "android") { + let work: PathBuf = if cfg!(target_os = "android") { "/data/tmp/work".into() } else { - "/tmp/work".into() + let mut temp_dir = env::temp_dir(); + temp_dir.push("work"); + temp_dir }; println!("listening!"); From 36d679129bad9cf05814cf1d954091f2d5c89bdd Mon Sep 17 00:00:00 2001 From: Dennis Duda Date: Fri, 29 May 2020 11:57:23 +0200 Subject: [PATCH 08/15] Fix `set_permissions` call for non-windows --- src/tools/remote-test-server/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/remote-test-server/src/main.rs b/src/tools/remote-test-server/src/main.rs index 9112985e140a7..e7eff35e55725 100644 --- a/src/tools/remote-test-server/src/main.rs +++ b/src/tools/remote-test-server/src/main.rs @@ -289,7 +289,7 @@ fn recv(dir: &Path, io: &mut B) -> PathBuf { #[cfg(not(windows))] fn set_permissions(path: &Path) { - t!(fs::set_permissions(&dst, Permissions::from_mode(0o755))); + t!(fs::set_permissions(&path, Permissions::from_mode(0o755))); } #[cfg(windows)] fn set_permissions(_path: &Path) {} From ce722999ea7a150d57504222e16107ef8a6556e6 Mon Sep 17 00:00:00 2001 From: O01eg Date: Fri, 29 May 2020 16:50:03 +0300 Subject: [PATCH 09/15] Fix lld detection if stage0 rustc built with custom libdir --- src/bootstrap/lib.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 15bf831a14835..5328ccb64df7e 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -344,13 +344,19 @@ impl Build { // we always try to use git for LLVM builds let in_tree_llvm_info = channel::GitInfo::new(false, &src.join("src/llvm-project")); - let initial_sysroot = config.initial_rustc.parent().unwrap().parent().unwrap(); - let initial_lld = initial_sysroot - .join("lib") - .join("rustlib") - .join(config.build) - .join("bin") - .join("rust-lld"); + let initial_target_libdir = if config.dry_run { + "/dummy/path/to/lib/".to_string() + } else { + output( + Command::new(&config.initial_rustc) + .arg("--target") + .arg(config.build) + .arg("--print") + .arg("target-libdir"), + ) + }; + let initial_lld = + Path::new(&initial_target_libdir).parent().unwrap().join("bin").join("rust-lld"); let mut build = Build { initial_rustc: config.initial_rustc.clone(), From c0ac2e86f9d7545594fdb1be27f2fe9336699ca7 Mon Sep 17 00:00:00 2001 From: O01eg Date: Fri, 29 May 2020 10:15:46 +0300 Subject: [PATCH 10/15] Get libdir from stage0 compiler --- src/bootstrap/builder.rs | 1 + src/bootstrap/lib.rs | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 5489b1bc66b64..c8a85eae252ff 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -648,6 +648,7 @@ impl<'a> Builder<'a> { pub fn sysroot_libdir_relative(&self, compiler: Compiler) -> &Path { match self.config.libdir_relative() { Some(relative_libdir) if compiler.stage >= 1 => relative_libdir, + _ if compiler.stage == 0 => &self.build.initial_libdir, _ => Path::new("lib"), } } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 5328ccb64df7e..c46c68e4d5682 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -243,6 +243,7 @@ pub struct Build { initial_rustc: PathBuf, initial_cargo: PathBuf, initial_lld: PathBuf, + initial_libdir: PathBuf, // Runtime state filled in later on // C/C++ compilers and archiver for all targets @@ -344,8 +345,8 @@ impl Build { // we always try to use git for LLVM builds let in_tree_llvm_info = channel::GitInfo::new(false, &src.join("src/llvm-project")); - let initial_target_libdir = if config.dry_run { - "/dummy/path/to/lib/".to_string() + let initial_target_libdir_str = if config.dry_run { + "/dummy/lib/path/to/lib/".to_string() } else { output( Command::new(&config.initial_rustc) @@ -355,13 +356,28 @@ impl Build { .arg("target-libdir"), ) }; - let initial_lld = - Path::new(&initial_target_libdir).parent().unwrap().join("bin").join("rust-lld"); + let initial_target_dir = Path::new(&initial_target_libdir_str).parent().unwrap(); + let initial_lld = initial_target_dir.join("bin").join("rust-lld"); + + let initial_sysroot = if config.dry_run { + "/dummy".to_string() + } else { + output(Command::new(&config.initial_rustc).arg("--print").arg("sysroot")) + }; + let initial_libdir = initial_target_dir + .parent() + .unwrap() + .parent() + .unwrap() + .strip_prefix(initial_sysroot.trim()) + .unwrap() + .to_path_buf(); let mut build = Build { initial_rustc: config.initial_rustc.clone(), initial_cargo: config.initial_cargo.clone(), initial_lld, + initial_libdir, local_rebuild: config.local_rebuild, fail_fast: config.cmd.fail_fast(), doc_tests: config.cmd.doc_tests(), From b78b15665b622cc37b25e9bd971537296403b83d Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Tue, 26 May 2020 20:07:59 +0100 Subject: [PATCH 11/15] Improve inline asm error diagnostics --- src/libfmt_macros/lib.rs | 24 +++++- src/librustc_ast/ast.rs | 3 +- src/librustc_ast_lowering/expr.rs | 3 +- src/librustc_builtin_macros/asm.rs | 10 ++- src/librustc_codegen_llvm/asm.rs | 32 ++++++-- src/librustc_codegen_llvm/back/write.rs | 51 +++++++++++-- src/librustc_codegen_llvm/llvm/ffi.rs | 9 ++- src/librustc_codegen_ssa/back/write.rs | 39 ++++++++-- src/librustc_codegen_ssa/mir/block.rs | 12 ++- src/librustc_codegen_ssa/traits/asm.rs | 2 +- src/librustc_hir/hir.rs | 1 + src/librustc_middle/mir/mod.rs | 6 +- src/librustc_middle/mir/type_foldable.rs | 10 ++- src/librustc_middle/mir/visit.rs | 1 + src/librustc_mir/borrow_check/invalidation.rs | 8 +- src/librustc_mir/borrow_check/mod.rs | 8 +- .../dataflow/framework/direction.rs | 2 +- .../dataflow/move_paths/builder.rs | 8 +- src/librustc_mir_build/build/expr/into.rs | 3 +- src/librustc_mir_build/hair/cx/expr.rs | 1 + src/librustc_mir_build/hair/mod.rs | 1 + src/librustc_span/lib.rs | 12 ++- src/rustllvm/RustWrapper.cpp | 31 +++++++- src/test/ui/asm/srcloc.rs | 41 ++++++++++ src/test/ui/asm/srcloc.stderr | 74 +++++++++++++++++++ src/test/ui/issues/issue-23458.stderr | 15 ++-- src/test/ui/llvm-asm/issue-69092.rs | 2 +- src/test/ui/llvm-asm/issue-69092.stderr | 13 ++-- 28 files changed, 365 insertions(+), 57 deletions(-) create mode 100644 src/test/ui/asm/srcloc.rs create mode 100644 src/test/ui/asm/srcloc.stderr diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs index 677c027f17b54..23bf7b35419db 100644 --- a/src/libfmt_macros/lib.rs +++ b/src/libfmt_macros/lib.rs @@ -191,6 +191,11 @@ pub struct Parser<'a> { append_newline: bool, /// Whether this formatting string is a literal or it comes from a macro. is_literal: bool, + /// Start position of the current line. + cur_line_start: usize, + /// Start and end byte offset of every line of the format string. Excludes + /// newline characters and leading whitespace. + pub line_spans: Vec, } impl<'a> Iterator for Parser<'a> { @@ -235,10 +240,15 @@ impl<'a> Iterator for Parser<'a> { None } } - '\n' => Some(String(self.string(pos))), _ => Some(String(self.string(pos))), } } else { + if self.is_literal && self.cur_line_start != self.input.len() { + let start = self.to_span_index(self.cur_line_start); + let end = self.to_span_index(self.input.len()); + self.line_spans.push(start.to(end)); + self.cur_line_start = self.input.len(); + } None } } @@ -266,6 +276,8 @@ impl<'a> Parser<'a> { last_opening_brace: None, append_newline, is_literal, + cur_line_start: 0, + line_spans: vec![], } } @@ -433,7 +445,17 @@ impl<'a> Parser<'a> { '{' | '}' => { return &self.input[start..pos]; } + '\n' if self.is_literal => { + let start = self.to_span_index(self.cur_line_start); + let end = self.to_span_index(pos); + self.line_spans.push(start.to(end)); + self.cur_line_start = pos + 1; + self.cur.next(); + } _ => { + if self.is_literal && pos == self.cur_line_start && c.is_whitespace() { + self.cur_line_start = pos + c.len_utf8(); + } self.cur.next(); } } diff --git a/src/librustc_ast/ast.rs b/src/librustc_ast/ast.rs index 30bb5c0bffa6a..efcf95ec706b8 100644 --- a/src/librustc_ast/ast.rs +++ b/src/librustc_ast/ast.rs @@ -1252,7 +1252,7 @@ pub enum ExprKind { Ret(Option>), /// Output of the `asm!()` macro. - InlineAsm(InlineAsm), + InlineAsm(P), /// Output of the `llvm_asm!()` macro. LlvmInlineAsm(P), @@ -1971,6 +1971,7 @@ pub struct InlineAsm { pub template: Vec, pub operands: Vec<(InlineAsmOperand, Span)>, pub options: InlineAsmOptions, + pub line_spans: Vec, } /// Inline assembly dialect. diff --git a/src/librustc_ast_lowering/expr.rs b/src/librustc_ast_lowering/expr.rs index 5bcd111706f35..fd69f5c1e5f89 100644 --- a/src/librustc_ast_lowering/expr.rs +++ b/src/librustc_ast_lowering/expr.rs @@ -1265,7 +1265,8 @@ impl<'hir> LoweringContext<'_, 'hir> { let operands = self.arena.alloc_from_iter(operands); let template = self.arena.alloc_from_iter(asm.template.iter().cloned()); - let hir_asm = hir::InlineAsm { template, operands, options: asm.options }; + let line_spans = self.arena.alloc_slice(&asm.line_spans[..]); + let hir_asm = hir::InlineAsm { template, operands, options: asm.options, line_spans }; hir::ExprKind::InlineAsm(self.arena.alloc(hir_asm)) } diff --git a/src/librustc_builtin_macros/asm.rs b/src/librustc_builtin_macros/asm.rs index 224b52b239f43..19fae63557289 100644 --- a/src/librustc_builtin_macros/asm.rs +++ b/src/librustc_builtin_macros/asm.rs @@ -513,10 +513,16 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P for Builder<'a, 'll, 'tcx> { ia.volatile, ia.alignstack, ia.dialect, - span, + &[span], ); if r.is_none() { return false; @@ -119,7 +119,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { template: &[InlineAsmTemplatePiece], operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, - span: Span, + line_spans: &[Span], ) { let asm_arch = self.tcx.sess.asm_arch.unwrap(); @@ -286,9 +286,9 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { volatile, alignstack, dialect, - span, + line_spans, ) - .unwrap_or_else(|| span_bug!(span, "LLVM asm constraint validation failed")); + .unwrap_or_else(|| span_bug!(line_spans[0], "LLVM asm constraint validation failed")); if options.contains(InlineAsmOptions::PURE) { if options.contains(InlineAsmOptions::NOMEM) { @@ -340,7 +340,7 @@ fn inline_asm_call( volatile: bool, alignstack: bool, dia: LlvmAsmDialect, - span: Span, + line_spans: &[Span], ) -> Option<&'ll Value> { let volatile = if volatile { llvm::True } else { llvm::False }; let alignstack = if alignstack { llvm::True } else { llvm::False }; @@ -381,8 +381,24 @@ fn inline_asm_call( key.len() as c_uint, ); - let val: &'ll Value = bx.const_i32(span.ctxt().outer_expn().as_u32() as i32); - llvm::LLVMSetMetadata(call, kind, llvm::LLVMMDNodeInContext(bx.llcx, &val, 1)); + // srcloc contains one integer for each line of assembly code. + // Unfortunately this isn't enough to encode a full span so instead + // we just encode the start position of each line. + // FIXME: Figure out a way to pass the entire line spans. + let mut srcloc = vec![]; + if dia == LlvmAsmDialect::Intel && line_spans.len() > 1 { + // LLVM inserts an extra line to add the ".intel_syntax", so add + // a dummy srcloc entry for it. + // + // Don't do this if we only have 1 line span since that may be + // due to the asm template string coming from a macro. LLVM will + // default to the first srcloc for lines that don't have an + // associated srcloc. + srcloc.push(bx.const_i32(0)); + } + srcloc.extend(line_spans.iter().map(|span| bx.const_i32(span.lo().to_u32() as i32))); + let md = llvm::LLVMMDNodeInContext(bx.llcx, srcloc.as_ptr(), srcloc.len() as u32); + llvm::LLVMSetMetadata(call, kind, md); Some(call) } else { diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs index 57e018bba6a1a..02a9294930d2b 100644 --- a/src/librustc_codegen_llvm/back/write.rs +++ b/src/librustc_codegen_llvm/back/write.rs @@ -23,6 +23,7 @@ use rustc_middle::bug; use rustc_middle::ty::TyCtxt; use rustc_session::config::{self, Lto, OutputType, Passes, Sanitizer, SwitchWithOptPath}; use rustc_session::Session; +use rustc_span::InnerSpan; use rustc_target::spec::{CodeModel, RelocModel}; use libc::{c_char, c_int, c_uint, c_void, size_t}; @@ -238,12 +239,19 @@ impl<'a> Drop for DiagnosticHandlers<'a> { } } -unsafe extern "C" fn report_inline_asm( +fn report_inline_asm( cgcx: &CodegenContext, - msg: &str, - cookie: c_uint, + msg: String, + mut cookie: c_uint, + source: Option<(String, Vec)>, ) { - cgcx.diag_emitter.inline_asm_error(cookie as u32, msg.to_owned()); + // In LTO build we may get srcloc values from other crates which are invalid + // since they use a different source map. To be safe we just suppress these + // in LTO builds. + if matches!(cgcx.lto, Lto::Fat | Lto::Thin) { + cookie = 0; + } + cgcx.diag_emitter.inline_asm_error(cookie as u32, msg, source); } unsafe extern "C" fn inline_asm_handler(diag: &SMDiagnostic, user: *const c_void, cookie: c_uint) { @@ -252,10 +260,37 @@ unsafe extern "C" fn inline_asm_handler(diag: &SMDiagnostic, user: *const c_void } let (cgcx, _) = *(user as *const (&CodegenContext, &Handler)); - let msg = llvm::build_string(|s| llvm::LLVMRustWriteSMDiagnosticToString(diag, s)) - .expect("non-UTF8 SMDiagnostic"); + // Recover the post-substitution assembly code from LLVM for better + // diagnostics. + let mut have_source = false; + let mut buffer = String::new(); + let mut loc = 0; + let mut ranges = [0; 8]; + let mut num_ranges = ranges.len() / 2; + let msg = llvm::build_string(|msg| { + buffer = llvm::build_string(|buffer| { + have_source = llvm::LLVMRustUnpackSMDiagnostic( + diag, + msg, + buffer, + &mut loc, + ranges.as_mut_ptr(), + &mut num_ranges, + ); + }) + .expect("non-UTF8 inline asm"); + }) + .expect("non-UTF8 SMDiagnostic"); + + let source = have_source.then(|| { + let mut spans = vec![InnerSpan::new(loc as usize, loc as usize)]; + for i in 0..num_ranges { + spans.push(InnerSpan::new(ranges[i * 2] as usize, ranges[i * 2 + 1] as usize)); + } + (buffer, spans) + }); - report_inline_asm(cgcx, &msg, cookie); + report_inline_asm(cgcx, msg, cookie, source); } unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void) { @@ -266,7 +301,7 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void match llvm::diagnostic::Diagnostic::unpack(info) { llvm::diagnostic::InlineAsm(inline) => { - report_inline_asm(cgcx, &llvm::twine_to_string(inline.message), inline.cookie); + report_inline_asm(cgcx, llvm::twine_to_string(inline.message), inline.cookie, None); } llvm::diagnostic::Optimization(opt) => { diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index 3fb7ff3cb8dfd..759c2bf1b85f4 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -2070,7 +2070,14 @@ extern "C" { ); #[allow(improper_ctypes)] - pub fn LLVMRustWriteSMDiagnosticToString(d: &SMDiagnostic, s: &RustString); + pub fn LLVMRustUnpackSMDiagnostic( + d: &SMDiagnostic, + message_out: &RustString, + buffer_out: &RustString, + loc_out: &mut c_uint, + ranges_out: *mut c_uint, + num_ranges: &mut usize, + ) -> bool; pub fn LLVMRustWriteArchive( Dst: *const c_char, diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs index 9e03c283cfb5c..cb5c95c11fad8 100644 --- a/src/librustc_codegen_ssa/back/write.rs +++ b/src/librustc_codegen_ssa/back/write.rs @@ -31,9 +31,9 @@ use rustc_session::cgu_reuse_tracker::CguReuseTracker; use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType}; use rustc_session::config::{Passes, Sanitizer, SwitchWithOptPath}; use rustc_session::Session; -use rustc_span::hygiene::ExpnId; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{sym, Symbol}; +use rustc_span::{BytePos, FileName, InnerSpan, Pos, Span}; use rustc_target::spec::{MergeFunctions, PanicStrategy}; use std::any::Any; @@ -1551,7 +1551,7 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem enum SharedEmitterMessage { Diagnostic(Diagnostic), - InlineAsmError(u32, String), + InlineAsmError(u32, String, Option<(String, Vec)>), AbortIfErrors, Fatal(String), } @@ -1572,8 +1572,13 @@ impl SharedEmitter { (SharedEmitter { sender }, SharedEmitterMain { receiver }) } - pub fn inline_asm_error(&self, cookie: u32, msg: String) { - drop(self.sender.send(SharedEmitterMessage::InlineAsmError(cookie, msg))); + pub fn inline_asm_error( + &self, + cookie: u32, + msg: String, + source: Option<(String, Vec)>, + ) { + drop(self.sender.send(SharedEmitterMessage::InlineAsmError(cookie, msg, source))); } pub fn fatal(&self, msg: &str) { @@ -1626,8 +1631,30 @@ impl SharedEmitterMain { } handler.emit_diagnostic(&d); } - Ok(SharedEmitterMessage::InlineAsmError(cookie, msg)) => { - sess.span_err(ExpnId::from_u32(cookie).expn_data().call_site, &msg) + Ok(SharedEmitterMessage::InlineAsmError(cookie, msg, source)) => { + let msg = msg.strip_prefix("error: ").unwrap_or(&msg); + + // If the cookie is 0 then we don't have span information. + let mut err = if cookie == 0 { + sess.struct_err(&msg) + } else { + let pos = BytePos::from_u32(cookie); + let span = Span::with_root_ctxt(pos, pos); + sess.struct_span_err(span, &msg) + }; + + // Point to the generated assembly if it is available. + if let Some((buffer, spans)) = source { + let source = sess + .source_map() + .new_source_file(FileName::inline_asm_source_code(&buffer), buffer); + let source_span = Span::with_root_ctxt(source.start_pos, source.end_pos); + let spans: Vec<_> = + spans.iter().map(|sp| source_span.from_inner(*sp)).collect(); + err.span_note(spans, "instantiated into assembly here"); + } + + err.emit(); } Ok(SharedEmitterMessage::AbortIfErrors) => { sess.abort_if_errors(); diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index b487ed8dea8b6..e0c6fb451fc41 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -831,6 +831,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { template: &[ast::InlineAsmTemplatePiece], operands: &[mir::InlineAsmOperand<'tcx>], options: ast::InlineAsmOptions, + line_spans: &[Span], destination: Option, ) { let span = terminator.source_info.span; @@ -931,7 +932,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }) .collect(); - bx.codegen_inline_asm(template, &operands, options, span); + bx.codegen_inline_asm(template, &operands, options, line_spans); if let Some(target) = destination { helper.funclet_br(self, &mut bx, target); @@ -1034,7 +1035,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bug!("borrowck false edges in codegen") } - mir::TerminatorKind::InlineAsm { template, ref operands, options, destination } => { + mir::TerminatorKind::InlineAsm { + template, + ref operands, + options, + line_spans, + destination, + } => { self.codegen_asm_terminator( helper, bx, @@ -1042,6 +1049,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { template, operands, options, + line_spans, destination, ); } diff --git a/src/librustc_codegen_ssa/traits/asm.rs b/src/librustc_codegen_ssa/traits/asm.rs index 0abfdfde7801b..b6b57744f95b6 100644 --- a/src/librustc_codegen_ssa/traits/asm.rs +++ b/src/librustc_codegen_ssa/traits/asm.rs @@ -52,7 +52,7 @@ pub trait AsmBuilderMethods<'tcx>: BackendTypes { template: &[InlineAsmTemplatePiece], operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, - span: Span, + line_spans: &[Span], ); } diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs index 35cff668581dd..0194dc9f90bb8 100644 --- a/src/librustc_hir/hir.rs +++ b/src/librustc_hir/hir.rs @@ -2106,6 +2106,7 @@ pub struct InlineAsm<'hir> { pub template: &'hir [InlineAsmTemplatePiece], pub operands: &'hir [InlineAsmOperand<'hir>], pub options: InlineAsmOptions, + pub line_spans: &'hir [Span], } #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic, PartialEq)] diff --git a/src/librustc_middle/mir/mod.rs b/src/librustc_middle/mir/mod.rs index 47cfa62abb14d..f6a236d38ecc3 100644 --- a/src/librustc_middle/mir/mod.rs +++ b/src/librustc_middle/mir/mod.rs @@ -1193,6 +1193,10 @@ pub enum TerminatorKind<'tcx> { /// Miscellaneous options for the inline assembly. options: InlineAsmOptions, + /// Source spans for each line of the inline assembly code. These are + /// used to map assembler errors back to the line in the source code. + line_spans: &'tcx [Span], + /// Destination block after the inline assembly returns, unless it is /// diverging (InlineAsmOptions::NORETURN). destination: Option, @@ -1595,7 +1599,7 @@ impl<'tcx> TerminatorKind<'tcx> { } FalseEdges { .. } => write!(fmt, "falseEdges"), FalseUnwind { .. } => write!(fmt, "falseUnwind"), - InlineAsm { template, ref operands, options, destination: _ } => { + InlineAsm { template, ref operands, options, .. } => { write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?; for op in operands { write!(fmt, ", ")?; diff --git a/src/librustc_middle/mir/type_foldable.rs b/src/librustc_middle/mir/type_foldable.rs index bb7001c1207bf..b0207b469fa64 100644 --- a/src/librustc_middle/mir/type_foldable.rs +++ b/src/librustc_middle/mir/type_foldable.rs @@ -78,9 +78,13 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { FalseEdges { real_target, imaginary_target } } FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind }, - InlineAsm { template, ref operands, options, destination } => { - InlineAsm { template, operands: operands.fold_with(folder), options, destination } - } + InlineAsm { template, ref operands, options, line_spans, destination } => InlineAsm { + template, + operands: operands.fold_with(folder), + options, + line_spans, + destination, + }, }; Terminator { source_info: self.source_info, kind } } diff --git a/src/librustc_middle/mir/visit.rs b/src/librustc_middle/mir/visit.rs index a29b7b75294b7..035e6e55a975d 100644 --- a/src/librustc_middle/mir/visit.rs +++ b/src/librustc_middle/mir/visit.rs @@ -535,6 +535,7 @@ macro_rules! make_mir_visitor { template: _, operands, options: _, + line_spans: _, destination: _, } => { for op in operands { diff --git a/src/librustc_mir/borrow_check/invalidation.rs b/src/librustc_mir/borrow_check/invalidation.rs index 178e3db17cd32..0b59e29b66c74 100644 --- a/src/librustc_mir/borrow_check/invalidation.rs +++ b/src/librustc_mir/borrow_check/invalidation.rs @@ -183,7 +183,13 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { } } } - TerminatorKind::InlineAsm { template: _, ref operands, options: _, destination: _ } => { + TerminatorKind::InlineAsm { + template: _, + ref operands, + options: _, + line_spans: _, + destination: _, + } => { for op in operands { match *op { InlineAsmOperand::In { reg: _, ref value } diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index a0c1d96bb4743..525c054a7665a 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -724,7 +724,13 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc self.mutate_place(loc, (resume_arg, span), Deep, JustWrite, flow_state); } - TerminatorKind::InlineAsm { template: _, ref operands, options: _, destination: _ } => { + TerminatorKind::InlineAsm { + template: _, + ref operands, + options: _, + line_spans: _, + destination: _, + } => { for op in operands { match *op { InlineAsmOperand::In { reg: _, ref value } diff --git a/src/librustc_mir/dataflow/framework/direction.rs b/src/librustc_mir/dataflow/framework/direction.rs index 97b14ea771b2f..9e2a28853e151 100644 --- a/src/librustc_mir/dataflow/framework/direction.rs +++ b/src/librustc_mir/dataflow/framework/direction.rs @@ -482,7 +482,7 @@ impl Direction for Forward { } } - InlineAsm { template: _, operands: _, options: _, destination } => { + InlineAsm { template: _, operands: _, options: _, line_spans: _, destination } => { if let Some(target) = destination { propagate(target, exit_state); } diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 427ab1ca5cd22..e35d853c92815 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -411,7 +411,13 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { self.gather_init(destination.as_ref(), InitKind::NonPanicPathOnly); } } - TerminatorKind::InlineAsm { template: _, ref operands, options: _, destination: _ } => { + TerminatorKind::InlineAsm { + template: _, + ref operands, + options: _, + line_spans: _, + destination: _ + } => { for op in operands { match *op { InlineAsmOperand::In { reg: _, ref value } diff --git a/src/librustc_mir_build/build/expr/into.rs b/src/librustc_mir_build/build/expr/into.rs index ff3c7ee3ee823..e7733deee4dd3 100644 --- a/src/librustc_mir_build/build/expr/into.rs +++ b/src/librustc_mir_build/build/expr/into.rs @@ -310,7 +310,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); block.unit() } - ExprKind::InlineAsm { template, operands, options } => { + ExprKind::InlineAsm { template, operands, options, line_spans } => { use crate::hair; use rustc_middle::mir; let operands = operands @@ -368,6 +368,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { template, operands, options, + line_spans, destination: if options.contains(InlineAsmOptions::NORETURN) { None } else { diff --git a/src/librustc_mir_build/hair/cx/expr.rs b/src/librustc_mir_build/hair/cx/expr.rs index 114bf5710402f..056cb9d98ce3b 100644 --- a/src/librustc_mir_build/hair/cx/expr.rs +++ b/src/librustc_mir_build/hair/cx/expr.rs @@ -513,6 +513,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( }) .collect(), options: asm.options, + line_spans: asm.line_spans, }, hir::ExprKind::LlvmInlineAsm(ref asm) => ExprKind::LlvmInlineAsm { diff --git a/src/librustc_mir_build/hair/mod.rs b/src/librustc_mir_build/hair/mod.rs index aba7a7a1b420c..0a1c68e83a94c 100644 --- a/src/librustc_mir_build/hair/mod.rs +++ b/src/librustc_mir_build/hair/mod.rs @@ -283,6 +283,7 @@ crate enum ExprKind<'tcx> { template: &'tcx [InlineAsmTemplatePiece], operands: Vec>, options: InlineAsmOptions, + line_spans: &'tcx [Span], }, LlvmInlineAsm { asm: &'tcx hir::LlvmInlineAsmInner, diff --git a/src/librustc_span/lib.rs b/src/librustc_span/lib.rs index 58cdb87158afe..616876d4b02a8 100644 --- a/src/librustc_span/lib.rs +++ b/src/librustc_span/lib.rs @@ -101,6 +101,8 @@ pub enum FileName { /// Custom sources for explicit parser calls from plugins and drivers. Custom(String), DocTest(PathBuf, isize), + /// Post-substitution inline assembly from LLVM + InlineAsm(u64), } impl std::fmt::Display for FileName { @@ -116,6 +118,7 @@ impl std::fmt::Display for FileName { CliCrateAttr(_) => write!(fmt, ""), Custom(ref s) => write!(fmt, "<{}>", s), DocTest(ref path, _) => write!(fmt, "{}", path.display()), + InlineAsm(_) => write!(fmt, ""), } } } @@ -139,7 +142,8 @@ impl FileName { | CliCrateAttr(_) | Custom(_) | QuoteExpansion(_) - | DocTest(_, _) => false, + | DocTest(_, _) + | InlineAsm(_) => false, } } @@ -182,6 +186,12 @@ impl FileName { pub fn doc_test_source_code(path: PathBuf, line: isize) -> FileName { FileName::DocTest(path, line) } + + pub fn inline_asm_source_code(src: &str) -> FileName { + let mut hasher = StableHasher::new(); + src.hash(&mut hasher); + FileName::InlineAsm(hasher.finish()) + } } /// Spans represent a region of code, used for error reporting. Positions in spans diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 24f35627d10e5..6fac2662506a1 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -1216,10 +1216,33 @@ extern "C" void LLVMRustSetInlineAsmDiagnosticHandler( unwrap(C)->setInlineAsmDiagnosticHandler(H, CX); } -extern "C" void LLVMRustWriteSMDiagnosticToString(LLVMSMDiagnosticRef D, - RustStringRef Str) { - RawRustStringOstream OS(Str); - unwrap(D)->print("", OS); +extern "C" bool LLVMRustUnpackSMDiagnostic(LLVMSMDiagnosticRef DRef, + RustStringRef MessageOut, + RustStringRef BufferOut, + unsigned* LocOut, + unsigned* RangesOut, + size_t* NumRanges) { + SMDiagnostic& D = *unwrap(DRef); + RawRustStringOstream MessageOS(MessageOut); + MessageOS << D.getMessage(); + + if (D.getLoc() == SMLoc()) + return false; + + const SourceMgr &LSM = *D.getSourceMgr(); + const MemoryBuffer *LBuf = LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc())); + LLVMRustStringWriteImpl(BufferOut, LBuf->getBufferStart(), LBuf->getBufferSize()); + + *LocOut = D.getLoc().getPointer() - LBuf->getBufferStart(); + + *NumRanges = std::min(*NumRanges, D.getRanges().size()); + size_t LineStart = *LocOut - (size_t)D.getColumnNo(); + for (size_t i = 0; i < *NumRanges; i++) { + RangesOut[i * 2] = LineStart + D.getRanges()[i].first; + RangesOut[i * 2 + 1] = LineStart + D.getRanges()[i].second; + } + + return true; } extern "C" LLVMValueRef LLVMRustBuildCleanupPad(LLVMBuilderRef B, diff --git a/src/test/ui/asm/srcloc.rs b/src/test/ui/asm/srcloc.rs new file mode 100644 index 0000000000000..7af6f620a9858 --- /dev/null +++ b/src/test/ui/asm/srcloc.rs @@ -0,0 +1,41 @@ +// no-system-llvm +// only-x86_64 +// build-fail + +#![feature(asm)] + +// Checks that inline asm errors are mapped to the correct line in the source code. + +fn main() { + unsafe { + asm!("invalid_instruction"); + //~^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!(" + invalid_instruction + "); + //~^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!(r#" + invalid_instruction + "#); + //~^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!(" + mov eax, eax + invalid_instruction + mov eax, eax + "); + //~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!(r#" + mov eax, eax + invalid_instruction + mov eax, eax + "#); + //~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!(concat!("invalid", "_", "instruction")); + //~^ ERROR: invalid instruction mnemonic 'invalid_instruction' + } +} diff --git a/src/test/ui/asm/srcloc.stderr b/src/test/ui/asm/srcloc.stderr new file mode 100644 index 0000000000000..57a4fbb974228 --- /dev/null +++ b/src/test/ui/asm/srcloc.stderr @@ -0,0 +1,74 @@ +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:11:15 + | +LL | asm!("invalid_instruction"); + | ^ + | +note: instantiated into assembly here + --> :2:2 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:15:13 + | +LL | invalid_instruction + | ^ + | +note: instantiated into assembly here + --> :3:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:20:13 + | +LL | invalid_instruction + | ^ + | +note: instantiated into assembly here + --> :3:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:26:13 + | +LL | invalid_instruction + | ^ + | +note: instantiated into assembly here + --> :4:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:33:13 + | +LL | invalid_instruction + | ^ + | +note: instantiated into assembly here + --> :4:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:38:14 + | +LL | asm!(concat!("invalid", "_", "instruction")); + | ^ + | +note: instantiated into assembly here + --> :2:2 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/issues/issue-23458.stderr b/src/test/ui/issues/issue-23458.stderr index 81f06e6397542..a6500b9bb4c24 100644 --- a/src/test/ui/issues/issue-23458.stderr +++ b/src/test/ui/issues/issue-23458.stderr @@ -2,16 +2,19 @@ error: invalid operand in inline asm: 'int $3' --> $DIR/issue-23458.rs:8:9 | LL | llvm_asm!("int $3"); - | ^^^^^^^^^^^^^^^^^^^^ - -error: :1:2: error: too few operands for instruction - int - ^ + | ^ +error: too few operands for instruction --> $DIR/issue-23458.rs:8:9 | LL | llvm_asm!("int $3"); - | ^^^^^^^^^^^^^^^^^^^^ + | ^ + | +note: instantiated into assembly here + --> :1:2 + | +LL | int + | ^ error: aborting due to 2 previous errors diff --git a/src/test/ui/llvm-asm/issue-69092.rs b/src/test/ui/llvm-asm/issue-69092.rs index ecce7bfdf5bba..96c019b760e95 100644 --- a/src/test/ui/llvm-asm/issue-69092.rs +++ b/src/test/ui/llvm-asm/issue-69092.rs @@ -6,5 +6,5 @@ fn main() { unsafe { llvm_asm!(".ascii \"Xen\0\""); } - //~^ ERROR: :1:9: error: expected string in '.ascii' directive + //~^ ERROR: expected string in '.ascii' directive } diff --git a/src/test/ui/llvm-asm/issue-69092.stderr b/src/test/ui/llvm-asm/issue-69092.stderr index 35f77edc3c402..2ca86cf7c1b99 100644 --- a/src/test/ui/llvm-asm/issue-69092.stderr +++ b/src/test/ui/llvm-asm/issue-69092.stderr @@ -1,11 +1,14 @@ -error: :1:9: error: expected string in '.ascii' directive - .ascii "Xen - ^ - +error: expected string in '.ascii' directive --> $DIR/issue-69092.rs:8:14 | LL | unsafe { llvm_asm!(".ascii \"Xen\0\""); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ + | +note: instantiated into assembly here + --> :1:9 + | +LL | .ascii "Xen + | ^ error: aborting due to previous error From fc497f79b3a60e23da08680d66e6cfc00a716bcc Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Tue, 26 May 2020 22:44:43 +0100 Subject: [PATCH 12/15] Add Span to arena_types! for decoding &'tcx [Span] --- src/librustc_middle/arena.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustc_middle/arena.rs b/src/librustc_middle/arena.rs index 9b9207312e8dd..d5212ec5c3081 100644 --- a/src/librustc_middle/arena.rs +++ b/src/librustc_middle/arena.rs @@ -82,6 +82,8 @@ macro_rules! arena_types { // (during lowering) and the `librustc_middle` arena (for decoding MIR) [decode] asm_template: rustc_ast::ast::InlineAsmTemplatePiece, + // This is used to decode the &'tcx [Span] for InlineAsm's line_spans. + [decode] span: rustc_span::Span, ], $tcx); ) } From 21755b58c900cd5d14422ebd980fe11390e021fe Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 29 May 2020 21:51:46 +0300 Subject: [PATCH 13/15] rustc_lexer: Optimize shebang detection slightly --- src/librustc_lexer/src/lib.rs | 37 +++++++++---------- .../ui/parser/shebang/shebang-doc-comment.rs | 6 +++ .../parser/shebang/shebang-doc-comment.stderr | 8 ++++ 3 files changed, 32 insertions(+), 19 deletions(-) create mode 100644 src/test/ui/parser/shebang/shebang-doc-comment.rs create mode 100644 src/test/ui/parser/shebang/shebang-doc-comment.stderr diff --git a/src/librustc_lexer/src/lib.rs b/src/librustc_lexer/src/lib.rs index fe6785de009a1..c2139d07f378a 100644 --- a/src/librustc_lexer/src/lib.rs +++ b/src/librustc_lexer/src/lib.rs @@ -238,26 +238,25 @@ pub enum Base { /// `rustc` allows files to have a shebang, e.g. "#!/usr/bin/rustrun", /// but shebang isn't a part of rust syntax. pub fn strip_shebang(input: &str) -> Option { - let first_line = input.lines().next()?; - // A shebang is intentionally loosely defined as `#! [non whitespace]` on the first line. - let could_be_shebang = - first_line.starts_with("#!") && first_line[2..].contains(|c| !is_whitespace(c)); - if !could_be_shebang { - return None; - } - let non_whitespace_tokens = tokenize(input).map(|tok| tok.kind).filter(|tok| - !matches!(tok, TokenKind::LineComment | TokenKind::BlockComment { .. } | TokenKind::Whitespace) - ); - let prefix = [TokenKind::Pound, TokenKind::Not, TokenKind::OpenBracket]; - let starts_with_attribute = non_whitespace_tokens.take(3).eq(prefix.iter().copied()); - if starts_with_attribute { - // If the file starts with #![ then it's definitely not a shebang -- it couldn't be - // a rust program since a Rust program can't start with `[` - None - } else { - // It's a #!... and there isn't a `[` in sight, must be a shebang - Some(first_line.len()) + // Shebang must start with `#!` literally, without any preceding whitespace. + if input.starts_with("#!") { + let input_tail = &input[2..]; + // Shebang must have something non-whitespace after `#!` on the first line. + let first_line_tail = input_tail.lines().next()?; + if first_line_tail.contains(|c| !is_whitespace(c)) { + // Ok, this is a shebang but if the next non-whitespace token is `[` or maybe + // a doc comment (due to `TokenKind::(Line,Block)Comment` ambiguity at lexer level), + // then it may be valid Rust code, so consider it Rust code. + let next_non_whitespace_token = tokenize(input_tail).map(|tok| tok.kind).filter(|tok| + !matches!(tok, TokenKind::Whitespace | TokenKind::LineComment | TokenKind::BlockComment { .. }) + ).next(); + if next_non_whitespace_token != Some(TokenKind::OpenBracket) { + // No other choice than to consider this a shebang. + return Some(2 + first_line_tail.len()); + } + } } + None } /// Parses the first token from the provided input string. diff --git a/src/test/ui/parser/shebang/shebang-doc-comment.rs b/src/test/ui/parser/shebang/shebang-doc-comment.rs new file mode 100644 index 0000000000000..7dbb9eebc7571 --- /dev/null +++ b/src/test/ui/parser/shebang/shebang-doc-comment.rs @@ -0,0 +1,6 @@ +#!///bin/bash +[allow(unused_variables)] +//~^^ ERROR expected `[`, found doc comment + +// Doc comment is misinterpreted as a whitespace (regular comment) during shebang detection. +// Even if it wasn't, it would still result in an error, just a different one. diff --git a/src/test/ui/parser/shebang/shebang-doc-comment.stderr b/src/test/ui/parser/shebang/shebang-doc-comment.stderr new file mode 100644 index 0000000000000..f524f556837fb --- /dev/null +++ b/src/test/ui/parser/shebang/shebang-doc-comment.stderr @@ -0,0 +1,8 @@ +error: expected `[`, found doc comment `///bin/bash` + --> $DIR/shebang-doc-comment.rs:1:3 + | +LL | #!///bin/bash + | ^^^^^^^^^^^ expected `[` + +error: aborting due to previous error + From f8d38057335ff6c760fabb53b62d87938d8fb17f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 30 May 2020 09:33:05 +0200 Subject: [PATCH 14/15] miri validation: clarify valid values of 'char' --- src/librustc_mir/interpret/validity.rs | 2 +- src/test/ui/consts/const-eval/ub-enum.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index c83555d65faf0..f2ce46e9740f9 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -485,7 +485,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' try_validation!( value.to_char(), self.path, - err_ub!(InvalidChar(..)) => { "{}", value } expected { "a valid unicode codepoint" }, + err_ub!(InvalidChar(..)) => { "{}", value } expected { "a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`)" }, ); Ok(true) } diff --git a/src/test/ui/consts/const-eval/ub-enum.stderr b/src/test/ui/consts/const-eval/ub-enum.stderr index d8dafac3e70a1..e49fd3e0b970b 100644 --- a/src/test/ui/consts/const-eval/ub-enum.stderr +++ b/src/test/ui/consts/const-eval/ub-enum.stderr @@ -82,7 +82,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:87:1 | LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0xffffffff at ..0.1, but expected a valid unicode codepoint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0xffffffff at ..0.1, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. From 4b7e44f89317ecd8162db8ab0286003205c3f886 Mon Sep 17 00:00:00 2001 From: marmeladema Date: Sat, 30 May 2020 12:30:58 +0100 Subject: [PATCH 15/15] rustdoc: remove calls to `local_def_id_from_node_id` --- src/librustdoc/clean/inline.rs | 8 ++------ src/librustdoc/clean/mod.rs | 6 +++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 4bf3649dcc2e2..08e04f719e9ba 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -6,7 +6,7 @@ use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_hir::Mutability; use rustc_metadata::creader::LoadedMacro; use rustc_middle::ty; @@ -454,11 +454,7 @@ fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet) name: None, attrs: clean::Attributes::default(), source: clean::Span::empty(), - def_id: cx - .tcx - .hir() - .local_def_id_from_node_id(ast::CRATE_NODE_ID) - .to_def_id(), + def_id: DefId::local(CRATE_DEF_INDEX), visibility: clean::Public, stability: None, deprecation: None, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 702c7d1e0f120..371df7444b004 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1549,11 +1549,11 @@ impl<'tcx> Clean for Ty<'tcx> { ty::FnDef(..) | ty::FnPtr(_) => { let ty = cx.tcx.lift(self).expect("FnPtr lift failed"); let sig = ty.fn_sig(cx.tcx); - let local_def_id = cx.tcx.hir().local_def_id_from_node_id(ast::CRATE_NODE_ID); + let def_id = DefId::local(CRATE_DEF_INDEX); BareFunction(box BareFunctionDecl { unsafety: sig.unsafety(), generic_params: Vec::new(), - decl: (local_def_id.to_def_id(), sig).clean(cx), + decl: (def_id, sig).clean(cx), abi: sig.abi(), }) } @@ -2255,7 +2255,7 @@ impl Clean> for doctree::Import<'_> { name: None, attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.tcx.hir().local_def_id_from_node_id(ast::CRATE_NODE_ID).to_def_id(), + def_id: DefId::local(CRATE_DEF_INDEX), visibility: self.vis.clean(cx), stability: None, deprecation: None,