From dd50e18ff04888c13c4104c6fdf9953ed0ac66f4 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Mon, 10 Jun 2024 17:06:56 +0300 Subject: [PATCH 01/13] Revert "Update `rustc-perf` submodule before running tidy" This reverts commit faac70b66e5135717a651dd3f13e6504e7b0c8c9. --- src/bootstrap/src/core/build_steps/test.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 29cd90222b255..fb7b40b73212e 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1101,8 +1101,6 @@ impl Step for Tidy { /// Once tidy passes, this step also runs `fmt --check` if tests are being run /// for the `dev` or `nightly` channels. fn run(self, builder: &Builder<'_>) { - builder.build.update_submodule(Path::new("src/tools/rustc-perf")); - let mut cmd = builder.tool_cmd(Tool::Tidy); cmd.arg(&builder.src); cmd.arg(&builder.initial_cargo); From e9e3c38d01669d87d2d14b17d5c7e0b2188991df Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Mon, 10 Jun 2024 17:07:19 +0300 Subject: [PATCH 02/13] tidy: skip submodules if not present for non-CI environments Signed-off-by: onur-ozkan --- Cargo.lock | 1 + src/bootstrap/src/core/build_steps/dist.rs | 2 +- src/bootstrap/src/core/builder.rs | 28 ++-------------------- src/tools/build_helper/src/util.rs | 28 ++++++++++++++++++++++ src/tools/tidy/Cargo.toml | 1 + src/tools/tidy/src/deps.rs | 14 +++++++++++ src/tools/tidy/src/extdeps.rs | 15 +++++++++++- 7 files changed, 61 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58d95f927bf95..a9fdf8926ee03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5697,6 +5697,7 @@ dependencies = [ name = "tidy" version = "0.1.0" dependencies = [ + "build_helper", "cargo_metadata 0.15.4", "ignore", "miropt-test-tools", diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 60b1ff3e4413e..7adc8245d8661 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1014,7 +1014,7 @@ impl Step for PlainSourceTarball { // perhaps it should be removed in favor of making `dist` perform the `vendor` step? // Ensure we have all submodules from src and other directories checked out. - for submodule in builder.get_all_submodules() { + for submodule in build_helper::util::parse_gitmodules(&builder.src) { builder.update_submodule(Path::new(submodule)); } diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 12d2bb18ab7ca..d419a76282e29 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -4,13 +4,11 @@ use std::collections::BTreeSet; use std::env; use std::ffi::{OsStr, OsString}; use std::fmt::{Debug, Write}; -use std::fs::{self, File}; +use std::fs; use std::hash::Hash; -use std::io::{BufRead, BufReader}; use std::ops::Deref; use std::path::{Path, PathBuf}; use std::process::Command; -use std::sync::OnceLock; use std::time::{Duration, Instant}; use crate::core::build_steps::tool::{self, SourceType}; @@ -577,7 +575,7 @@ impl<'a> ShouldRun<'a> { /// /// [`path`]: ShouldRun::path pub fn paths(mut self, paths: &[&str]) -> Self { - let submodules_paths = self.builder.get_all_submodules(); + let submodules_paths = build_helper::util::parse_gitmodules(&self.builder.src); self.paths.insert(PathSet::Set( paths @@ -2238,28 +2236,6 @@ impl<'a> Builder<'a> { out } - /// Return paths of all submodules. - pub fn get_all_submodules(&self) -> &[String] { - static SUBMODULES_PATHS: OnceLock> = OnceLock::new(); - - let init_submodules_paths = |src: &PathBuf| { - let file = File::open(src.join(".gitmodules")).unwrap(); - - let mut submodules_paths = vec![]; - for line in BufReader::new(file).lines().map_while(Result::ok) { - let line = line.trim(); - if line.starts_with("path") { - let actual_path = line.split(' ').last().expect("Couldn't get value of path"); - submodules_paths.push(actual_path.to_owned()); - } - } - - submodules_paths - }; - - SUBMODULES_PATHS.get_or_init(|| init_submodules_paths(&self.src)) - } - /// Ensure that a given step is built *only if it's supposed to be built by default*, returning /// its output. This will cache the step, so it's safe (and good!) to call this as often as /// needed to ensure that all dependencies are build. diff --git a/src/tools/build_helper/src/util.rs b/src/tools/build_helper/src/util.rs index 5801a8648f227..72c05c4c48abf 100644 --- a/src/tools/build_helper/src/util.rs +++ b/src/tools/build_helper/src/util.rs @@ -1,4 +1,8 @@ +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::path::Path; use std::process::Command; +use std::sync::OnceLock; /// Invokes `build_helper::util::detail_exit` with `cfg!(test)` /// @@ -45,3 +49,27 @@ pub fn try_run(cmd: &mut Command, print_cmd_on_fail: bool) -> Result<(), ()> { Ok(()) } } + +/// Returns the submodule paths from the `.gitmodules` file in the given directory. +pub fn parse_gitmodules(target_dir: &Path) -> &[String] { + static SUBMODULES_PATHS: OnceLock> = OnceLock::new(); + let gitmodules = target_dir.join(".gitmodules"); + assert!(gitmodules.exists(), "'{}' file is missing.", gitmodules.display()); + + let init_submodules_paths = || { + let file = File::open(gitmodules).unwrap(); + + let mut submodules_paths = vec![]; + for line in BufReader::new(file).lines().map_while(Result::ok) { + let line = line.trim(); + if line.starts_with("path") { + let actual_path = line.split(' ').last().expect("Couldn't get value of path"); + submodules_paths.push(actual_path.to_owned()); + } + } + + submodules_paths + }; + + SUBMODULES_PATHS.get_or_init(|| init_submodules_paths()) +} diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml index 63963b0bd1ced..c5204a9fee728 100644 --- a/src/tools/tidy/Cargo.toml +++ b/src/tools/tidy/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" autobins = false [dependencies] +build_helper = { path = "../build_helper" } cargo_metadata = "0.15" regex = "1" miropt-test-tools = { path = "../miropt-test-tools" } diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 2dd3d17f9e3e7..cb47b23b0d05c 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -1,7 +1,9 @@ //! Checks the licenses of third-party dependencies. +use build_helper::ci::CiEnv; use cargo_metadata::{Metadata, Package, PackageId}; use std::collections::HashSet; +use std::fs::read_dir; use std::path::Path; /// These are licenses that are allowed for all crates, including the runtime, @@ -514,7 +516,19 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ pub fn check(root: &Path, cargo: &Path, bad: &mut bool) { let mut checked_runtime_licenses = false; + let submodules = build_helper::util::parse_gitmodules(root); for &(workspace, exceptions, permitted_deps) in WORKSPACES { + // Skip if it's a submodule, not in a CI environment, and not initialized. + // + // This prevents enforcing developers to fetch submodules for tidy. + if submodules.contains(&workspace.into()) + && !CiEnv::is_ci() + // If the directory is empty, we can consider it as an uninitialized submodule. + && read_dir(root.join(workspace)).unwrap().next().is_none() + { + continue; + } + if !root.join(workspace).join("Cargo.lock").exists() { tidy_error!(bad, "the `{workspace}` workspace doesn't have a Cargo.lock"); continue; diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs index 2118de5f20411..8bb80f1171184 100644 --- a/src/tools/tidy/src/extdeps.rs +++ b/src/tools/tidy/src/extdeps.rs @@ -1,6 +1,7 @@ //! Check for external package sources. Allow only vendorable packages. -use std::fs; +use build_helper::ci::CiEnv; +use std::fs::{self, read_dir}; use std::path::Path; /// List of allowed sources for packages. @@ -13,7 +14,19 @@ const ALLOWED_SOURCES: &[&str] = &[ /// Checks for external package sources. `root` is the path to the directory that contains the /// workspace `Cargo.toml`. pub fn check(root: &Path, bad: &mut bool) { + let submodules = build_helper::util::parse_gitmodules(root); for &(workspace, _, _) in crate::deps::WORKSPACES { + // Skip if it's a submodule, not in a CI environment, and not initialized. + // + // This prevents enforcing developers to fetch submodules for tidy. + if submodules.contains(&workspace.into()) + && !CiEnv::is_ci() + // If the directory is empty, we can consider it as an uninitialized submodule. + && read_dir(root.join(workspace)).unwrap().next().is_none() + { + continue; + } + // FIXME check other workspaces too // `Cargo.lock` of rust. let path = root.join(workspace).join("Cargo.lock"); From 455e2f9027bdc69bc2cbd9aa2d29fa2401b35219 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 17 Jun 2024 14:41:05 -0700 Subject: [PATCH 03/13] Update outdated README in build-manifest. I believe this was changed a while ago in https://github.com/rust-lang/promote-release/pull/14. --- src/tools/build-manifest/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/build-manifest/README.md b/src/tools/build-manifest/README.md index 9d30c554186be..2ea1bffb35f4d 100644 --- a/src/tools/build-manifest/README.md +++ b/src/tools/build-manifest/README.md @@ -4,7 +4,7 @@ This tool generates the manifests uploaded to static.rust-lang.org and used by r You can see a full list of all manifests at . This listing is updated by every 7 days. -This gets called by `promote-release` via `x.py dist hash-and-sign`. +This gets called by `promote-release` . `promote-release` downloads a pre-built binary of `build-manifest` which is generated in the dist-x86_64-linux builder and uploaded to s3. ## Adding a new component From f76c3b7fb971fb7d8a0909f107378416bb2d2029 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Mon, 17 Jun 2024 22:43:44 +0300 Subject: [PATCH 04/13] replace `remove_dir` with `remove_dir_all` in `helpers::symlink_dir` When using `symlink_dir`, it first removes the existing link with `remove_dir`. However, if the path isn't a link and contains files, `remove_dir` fails with "DirectoryNotEmpty", which causes the symbolic linking to fail as well. We have this problem on linking 'rustlib/rust' because it contains files as an actual directory. Signed-off-by: onur-ozkan --- src/bootstrap/src/utils/helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 13d1346b3d9a6..55cb05f864a76 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -135,7 +135,7 @@ pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result< if config.dry_run() { return Ok(()); } - let _ = fs::remove_dir(link); + let _ = fs::remove_dir_all(link); return symlink_dir_inner(original, link); #[cfg(not(windows))] From 521e707d7acf3f85389bbcfe53670638ef00c32a Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Tue, 18 Jun 2024 07:42:40 +0300 Subject: [PATCH 05/13] make codegen-backend config warning less noisy If `codegen-backends` is missing "cranelift" and "gcc" (which is common), bootstrap will now only show this warning during `dist` and `install` steps, or if codegen-backends was explicitly called for build. Signed-off-by: onur-ozkan --- src/bootstrap/src/core/build_steps/compile.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index cc5b007321350..5b18cb3f7e1e6 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1290,15 +1290,21 @@ fn needs_codegen_config(run: &RunConfig<'_>) -> bool { pub(crate) const CODEGEN_BACKEND_PREFIX: &str = "rustc_codegen_"; fn is_codegen_cfg_needed(path: &TaskPath, run: &RunConfig<'_>) -> bool { - if path.path.to_str().unwrap().contains(CODEGEN_BACKEND_PREFIX) { + let path = path.path.to_str().unwrap(); + + let is_explicitly_called = |p| -> bool { run.builder.paths.contains(p) }; + let should_enforce = run.builder.kind == Kind::Dist || run.builder.kind == Kind::Install; + + if path.contains(CODEGEN_BACKEND_PREFIX) { let mut needs_codegen_backend_config = true; for backend in run.builder.config.codegen_backends(run.target) { - if path.path.to_str().unwrap().ends_with(&(CODEGEN_BACKEND_PREFIX.to_owned() + backend)) - { + if path.ends_with(&(CODEGEN_BACKEND_PREFIX.to_owned() + backend)) { needs_codegen_backend_config = false; } } - if needs_codegen_backend_config { + if (is_explicitly_called(&PathBuf::from(path)) || should_enforce) + && needs_codegen_backend_config + { run.builder.info( "WARNING: no codegen-backends config matched the requested path to build a codegen backend. \ HELP: add backend to codegen-backends in config.toml.", From 279bf05ffbd363f7250e8c57667fdac0c629c7f7 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Wed, 19 Jun 2024 07:33:12 +0300 Subject: [PATCH 06/13] remove `GIT_DIR` handling in pre-push hook This is already handled from bootstrap at https://github.com/rust-lang/rust/blob/a1ca449981e3b8442e358026437b7bedb9a1458e/src/bootstrap/src/utils/helpers.rs#L504-L506. Signed-off-by: onur-ozkan --- src/etc/pre-push.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/etc/pre-push.sh b/src/etc/pre-push.sh index c9e1a2733fdc5..6f86c7ab8a448 100755 --- a/src/etc/pre-push.sh +++ b/src/etc/pre-push.sh @@ -7,8 +7,6 @@ set -Euo pipefail -# https://github.com/rust-lang/rust/issues/77620#issuecomment-705144570 -unset GIT_DIR ROOT_DIR="$(git rev-parse --show-toplevel)" echo "Running pre-push script $ROOT_DIR/x test tidy" From 093799693a6ad3307ed468323ed7a5b6d7853275 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 22 Jun 2024 15:34:50 +0200 Subject: [PATCH 07/13] make unsized_fn_params an internal feature --- compiler/rustc_feature/src/unstable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 45527bec2f2ef..79de2d60d7838 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -629,7 +629,7 @@ declare_features! ( /// Allows unsafe on extern declarations and safety qualifiers over internal items. (unstable, unsafe_extern_blocks, "1.80.0", Some(123743)), /// Allows unsized fn parameters. - (unstable, unsized_fn_params, "1.49.0", Some(48055)), + (internal, unsized_fn_params, "1.49.0", Some(48055)), /// Allows unsized rvalues at arguments and parameters. (incomplete, unsized_locals, "1.30.0", Some(48055)), /// Allows unsized tuple coercion. From 763e3131cc05d9d9b036ce991cc948aefe2cb8ff Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 22 Jun 2024 16:26:30 +0200 Subject: [PATCH 08/13] don't ICE when encountering an extern type field during validation --- compiler/rustc_const_eval/messages.ftl | 2 + .../src/const_eval/eval_queries.rs | 76 +++++++++++-------- compiler/rustc_const_eval/src/errors.rs | 8 +- .../src/interpret/projection.rs | 6 +- .../src/interpret/validity.rs | 10 ++- .../rustc_middle/src/mir/interpret/error.rs | 2 + src/tools/miri/src/diagnostics.rs | 4 +- .../fail/extern-type-field-offset.stderr | 4 +- ...sue-91827-extern-types-field-offset.stderr | 2 +- .../validation-ice-extern-type-field.rs | 15 ++++ .../validation-ice-extern-type-field.stderr | 9 +++ ...ck-overflow-trait-infer-98842.32bit.stderr | 6 +- ...ck-overflow-trait-infer-98842.64bit.stderr | 6 +- .../sized/stack-overflow-trait-infer-98842.rs | 2 +- .../stack-overflow-trait-infer-98842.stderr | 25 ------ 15 files changed, 96 insertions(+), 81 deletions(-) create mode 100644 tests/ui/consts/const-eval/validation-ice-extern-type-field.rs create mode 100644 tests/ui/consts/const-eval/validation-ice-extern-type-field.stderr delete mode 100644 tests/ui/sized/stack-overflow-trait-infer-98842.stderr diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 1476fe285ef5c..cd269810741e7 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -89,6 +89,8 @@ const_eval_exact_div_has_remainder = const_eval_extern_static = cannot access extern static ({$did}) +const_eval_extern_type_field = `extern type` field does not have a known offset + const_eval_fn_ptr_call = function pointers need an RFC before allowed to be called in {const_eval_const_context}s const_eval_for_loop_into_iter_non_const = diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index c60df06bb0efc..d8efaa66415fb 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -386,33 +386,8 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( CompileTimeMachine::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error), ); let res = ecx.load_mir(cid.instance.def, cid.promoted); - res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)).map_err(|error| { - let (error, backtrace) = error.into_parts(); - backtrace.print_backtrace(); - - let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) { - ("static", String::new()) - } else { - // If the current item has generics, we'd like to enrich the message with the - // instance and its args: to show the actual compile-time values, in addition to - // the expression, leading to the const eval error. - let instance = &cid.instance; - if !instance.args.is_empty() { - let instance = with_no_trimmed_paths!(instance.to_string()); - ("const_with_path", instance) - } else { - ("const", String::new()) - } - }; - - super::report( - *ecx.tcx, - error, - DUMMY_SP, - || super::get_span_and_frames(ecx.tcx, ecx.stack()), - |span, frames| ConstEvalError { span, error_kind: kind, instance, frame_notes: frames }, - ) - }) + res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)) + .map_err(|error| report_eval_error(&ecx, cid, error)) } #[inline(always)] @@ -438,24 +413,61 @@ fn const_validate_mplace<'tcx>( ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode) // Instead of just reporting the `InterpError` via the usual machinery, we give a more targeted // error about the validation failure. - .map_err(|error| report_validation_error(&ecx, error, alloc_id))?; + .map_err(|error| report_validation_error(&ecx, cid, error, alloc_id))?; inner = true; } Ok(()) } -#[inline(always)] +#[inline(never)] +fn report_eval_error<'tcx>( + ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>, + cid: GlobalId<'tcx>, + error: InterpErrorInfo<'tcx>, +) -> ErrorHandled { + let (error, backtrace) = error.into_parts(); + backtrace.print_backtrace(); + + let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) { + ("static", String::new()) + } else { + // If the current item has generics, we'd like to enrich the message with the + // instance and its args: to show the actual compile-time values, in addition to + // the expression, leading to the const eval error. + let instance = &cid.instance; + if !instance.args.is_empty() { + let instance = with_no_trimmed_paths!(instance.to_string()); + ("const_with_path", instance) + } else { + ("const", String::new()) + } + }; + + super::report( + *ecx.tcx, + error, + DUMMY_SP, + || super::get_span_and_frames(ecx.tcx, ecx.stack()), + |span, frames| ConstEvalError { span, error_kind: kind, instance, frame_notes: frames }, + ) +} + +#[inline(never)] fn report_validation_error<'tcx>( ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>, + cid: GlobalId<'tcx>, error: InterpErrorInfo<'tcx>, alloc_id: AllocId, ) -> ErrorHandled { + if !matches!(error.kind(), InterpError::UndefinedBehavior(_)) { + // Some other error happened during validation, e.g. an unsupported operation. + return report_eval_error(ecx, cid, error); + } + let (error, backtrace) = error.into_parts(); backtrace.print_backtrace(); - let ub_note = matches!(error, InterpError::UndefinedBehavior(_)).then(|| {}); - let bytes = ecx.print_alloc_bytes_for_diagnostics(alloc_id); let (size, align, _) = ecx.get_alloc_info(alloc_id); let raw_bytes = errors::RawBytesNote { size: size.bytes(), align: align.bytes(), bytes }; @@ -465,6 +477,6 @@ fn report_validation_error<'tcx>( error, DUMMY_SP, || crate::const_eval::get_span_and_frames(ecx.tcx, ecx.stack()), - move |span, frames| errors::ValidationFailure { span, ub_note, frames, raw_bytes }, + move |span, frames| errors::ValidationFailure { span, ub_note: (), frames, raw_bytes }, ) } diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 5fa48a59794be..292d6ba9d08a4 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -425,7 +425,7 @@ pub struct ValidationFailure { #[primary_span] pub span: Span, #[note(const_eval_validation_failure_note)] - pub ub_note: Option<()>, + pub ub_note: (), #[subdiagnostic] pub frames: Vec, #[subdiagnostic] @@ -825,6 +825,7 @@ impl ReportErrorExt for UnsupportedOpInfo { use crate::fluent_generated::*; match self { UnsupportedOpInfo::Unsupported(s) => s.clone().into(), + UnsupportedOpInfo::ExternTypeField => const_eval_extern_type_field, UnsupportedOpInfo::UnsizedLocal => const_eval_unsized_local, UnsupportedOpInfo::OverwritePartialPointer(_) => const_eval_partial_pointer_overwrite, UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_copy, @@ -845,7 +846,10 @@ impl ReportErrorExt for UnsupportedOpInfo { // `ReadPointerAsInt(Some(info))` is never printed anyway, it only serves as an error to // be further processed by validity checking which then turns it into something nice to // print. So it's not worth the effort of having diagnostics that can print the `info`. - UnsizedLocal | Unsupported(_) | ReadPointerAsInt(_) => {} + UnsizedLocal + | UnsupportedOpInfo::ExternTypeField + | Unsupported(_) + | ReadPointerAsInt(_) => {} OverwritePartialPointer(ptr) | ReadPartialPointer(ptr) => { diag.arg("ptr", ptr); } diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index efa01b5434260..cfa814c810aff 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -21,7 +21,7 @@ use rustc_target::abi::{self, VariantIdx}; use tracing::{debug, instrument}; use super::{ - throw_ub, throw_unsup_format, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, + throw_ub, throw_unsup, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Provenance, Scalar, }; @@ -186,8 +186,8 @@ where (base_meta, offset) } None => { - // We don't know the alignment of this field, so we cannot adjust. - throw_unsup_format!("`extern type` does not have a known offset") + // We cannot know the alignment of this field, so we cannot adjust. + throw_unsup!(ExternTypeField) } } } else { diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index ca8b98849338b..add48e1b186cb 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -5,6 +5,7 @@ //! to be const-safe. use std::fmt::Write; +use std::hash::Hash; use std::num::NonZero; use either::{Left, Right}; @@ -17,7 +18,8 @@ use rustc_hir as hir; use rustc_middle::bug; use rustc_middle::mir::interpret::{ ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance, - ValidationErrorInfo, ValidationErrorKind, ValidationErrorKind::*, + UnsupportedOpInfo, ValidationErrorInfo, + ValidationErrorKind::{self, *}, }; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty}; @@ -26,8 +28,6 @@ use rustc_target::abi::{ Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange, }; -use std::hash::Hash; - use super::{ err_ub, format_interp_error, machine::AllocMap, throw_ub, AllocId, AllocKind, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, @@ -1028,7 +1028,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Err(err) if matches!( err.kind(), - err_ub!(ValidationError { .. }) | InterpError::InvalidProgram(_) + err_ub!(ValidationError { .. }) + | InterpError::InvalidProgram(_) + | InterpError::Unsupported(UnsupportedOpInfo::ExternTypeField) ) => { Err(err) diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 23680f143970d..6a8498abaf933 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -520,6 +520,8 @@ pub enum UnsupportedOpInfo { Unsupported(String), /// Unsized local variables. UnsizedLocal, + /// Extern type field with an indeterminate offset. + ExternTypeField, // // The variants below are only reachable from CTFE/const prop, miri will never emit them. // diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 12fb76f397241..1b70a1a1cf08f 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -311,7 +311,9 @@ pub fn report_error<'tcx>( ResourceExhaustion(_) => "resource exhaustion", Unsupported( // We list only the ones that can actually happen. - UnsupportedOpInfo::Unsupported(_) | UnsupportedOpInfo::UnsizedLocal, + UnsupportedOpInfo::Unsupported(_) + | UnsupportedOpInfo::UnsizedLocal + | UnsupportedOpInfo::ExternTypeField, ) => "unsupported operation", InvalidProgram( // We list only the ones that can actually happen. diff --git a/src/tools/miri/tests/fail/extern-type-field-offset.stderr b/src/tools/miri/tests/fail/extern-type-field-offset.stderr index e0d6e9ebf1de7..3ed5732b4eb06 100644 --- a/src/tools/miri/tests/fail/extern-type-field-offset.stderr +++ b/src/tools/miri/tests/fail/extern-type-field-offset.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: `extern type` does not have a known offset +error: unsupported operation: `extern type` field does not have a known offset --> $DIR/extern-type-field-offset.rs:LL:CC | LL | let _field = &x.a; - | ^^^^ `extern type` does not have a known offset + | ^^^^ `extern type` field does not have a known offset | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support = note: BACKTRACE: diff --git a/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.stderr b/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.stderr index 99f37fedd3dd3..54d45ee8ffb24 100644 --- a/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.stderr +++ b/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/issue-91827-extern-types-field-offset.rs:38:17 | LL | let field = &x.a; - | ^^^^ `extern type` does not have a known offset + | ^^^^ `extern type` field does not have a known offset error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/validation-ice-extern-type-field.rs b/tests/ui/consts/const-eval/validation-ice-extern-type-field.rs new file mode 100644 index 0000000000000..3502409d576c2 --- /dev/null +++ b/tests/ui/consts/const-eval/validation-ice-extern-type-field.rs @@ -0,0 +1,15 @@ +#![feature(extern_types)] + +extern { + type Opaque; +} + +struct ThinDst { + x: u8, + tail: Opaque, +} + +const C1: &ThinDst = unsafe { std::mem::transmute(b"d".as_ptr()) }; +//~^ERROR: evaluation of constant value failed + +fn main() {} diff --git a/tests/ui/consts/const-eval/validation-ice-extern-type-field.stderr b/tests/ui/consts/const-eval/validation-ice-extern-type-field.stderr new file mode 100644 index 0000000000000..1ec36abc2eccf --- /dev/null +++ b/tests/ui/consts/const-eval/validation-ice-extern-type-field.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/validation-ice-extern-type-field.rs:12:1 + | +LL | const C1: &ThinDst = unsafe { std::mem::transmute(b"d".as_ptr()) }; + | ^^^^^^^^^^^^^^^^^^ `extern type` field does not have a known offset + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr b/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr index 3f8011d961ae7..6bbd81ae3e163 100644 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr @@ -9,15 +9,11 @@ LL | const _: *const Foo = 0 as _; | ^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0080]: it is undefined behavior to use this value +error[E0080]: evaluation of constant value failed --> $DIR/stack-overflow-trait-infer-98842.rs:15:1 | LL | const _: *const Foo = 0 as _; | ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation - | - = note: the raw bytes of the constant (size: 4, align: 4) { - 00 00 00 00 │ .... - } error: aborting due to 2 previous errors diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr b/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr index 04e2c4483bf64..6bbd81ae3e163 100644 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr @@ -9,15 +9,11 @@ LL | const _: *const Foo = 0 as _; | ^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0080]: it is undefined behavior to use this value +error[E0080]: evaluation of constant value failed --> $DIR/stack-overflow-trait-infer-98842.rs:15:1 | LL | const _: *const Foo = 0 as _; | ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation - | - = note: the raw bytes of the constant (size: 8, align: 8) { - 00 00 00 00 00 00 00 00 │ ........ - } error: aborting due to 2 previous errors diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.rs b/tests/ui/sized/stack-overflow-trait-infer-98842.rs index 54d50346cc831..be4807b2e4aa7 100644 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.rs +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.rs @@ -13,6 +13,6 @@ struct Foo(<&'static Foo as ::core::ops::Deref>::Target); // and it will infinitely recurse somewhere trying to figure out the // size of this pointer (is my guess): const _: *const Foo = 0 as _; -//~^ ERROR it is undefined behavior to use this value +//~^ ERROR evaluation of constant value failed pub fn main() {} diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.stderr b/tests/ui/sized/stack-overflow-trait-infer-98842.stderr deleted file mode 100644 index 8ddddeb5bf2d1..0000000000000 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error[E0391]: cycle detected when computing layout of `Foo` - | - = note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`... - = note: ...which again requires computing layout of `Foo`, completing the cycle -note: cycle used when const-evaluating + checking `_` - --> $DIR/stack-overflow-trait-infer-98842.rs:13:1 - | -LL | const _: *const Foo = 0 as _; - | ^^^^^^^^^^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -error[E0080]: it is undefined behavior to use this value - --> $DIR/stack-overflow-trait-infer-98842.rs:13:1 - | -LL | const _: *const Foo = 0 as _; - | ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation - | - = note: the raw bytes of the constant (size: 8, align: 8) { - 00 00 00 00 00 00 00 00 │ ........ - } - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0080, E0391. -For more information about an error, try `rustc --explain E0080`. From 0a265957ddebf3517e5d1105541ba7931d1974bb Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 22 Jun 2024 19:14:16 +0300 Subject: [PATCH 09/13] delegation: Do not crash on qpaths without a trait --- compiler/rustc_expand/messages.ftl | 3 +++ compiler/rustc_expand/src/errors.rs | 7 +++++++ compiler/rustc_expand/src/expand.rs | 12 ++++++++++-- tests/ui/delegation/glob-traitless-qpath.rs | 11 +++++++++++ tests/ui/delegation/glob-traitless-qpath.stderr | 14 ++++++++++++++ 5 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 tests/ui/delegation/glob-traitless-qpath.rs create mode 100644 tests/ui/delegation/glob-traitless-qpath.stderr diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 6113580491ef9..cc0b110d2bc6f 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -61,6 +61,9 @@ expand_feature_removed = expand_glob_delegation_outside_impls = glob delegation is only supported in impls +expand_glob_delegation_traitless_qpath = + qualified path without a trait in glob delegation + expand_helper_attribute_name_invalid = `{$name}` cannot be a name of derive helper attribute diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index c883121fb4065..0be7403f25b17 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -449,6 +449,13 @@ pub(crate) struct GlobDelegationOutsideImpls { pub span: Span, } +#[derive(Diagnostic)] +#[diag(expand_glob_delegation_traitless_qpath)] +pub(crate) struct GlobDelegationTraitlessQpath { + #[primary_span] + pub span: Span, +} + // This used to be the `proc_macro_back_compat` lint (#83125). It was later // turned into a hard error. #[derive(Diagnostic)] diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 716bfc8c26b1e..a331d83aab00a 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1,8 +1,9 @@ use crate::base::*; use crate::config::StripUnconfigured; use crate::errors::{ - EmptyDelegationMac, GlobDelegationOutsideImpls, IncompleteParse, RecursionLimitReached, - RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue, WrongFragmentKind, + EmptyDelegationMac, GlobDelegationOutsideImpls, GlobDelegationTraitlessQpath, IncompleteParse, + RecursionLimitReached, RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue, + WrongFragmentKind, }; use crate::mbe::diagnostics::annotate_err_with_kind; use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod}; @@ -1989,6 +1990,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } None if let Some((deleg, item)) = node.delegation() => { let Some(suffixes) = &deleg.suffixes else { + let traitless_qself = + matches!(&deleg.qself, Some(qself) if qself.position == 0); let item = match node.to_annotatable() { Annotatable::ImplItem(item) => item, ann @ (Annotatable::Item(_) @@ -2000,6 +2003,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } _ => unreachable!(), }; + if traitless_qself { + let span = item.span; + self.cx.dcx().emit_err(GlobDelegationTraitlessQpath { span }); + return Default::default(); + } return self.collect_glob_delegation(item, Node::KIND).make_ast::(); }; diff --git a/tests/ui/delegation/glob-traitless-qpath.rs b/tests/ui/delegation/glob-traitless-qpath.rs new file mode 100644 index 0000000000000..abf4b3180ed21 --- /dev/null +++ b/tests/ui/delegation/glob-traitless-qpath.rs @@ -0,0 +1,11 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +struct S; + +impl S { + reuse ::*; //~ ERROR qualified path without a trait in glob delegation + reuse <()>::*; //~ ERROR qualified path without a trait in glob delegation +} + +fn main() {} diff --git a/tests/ui/delegation/glob-traitless-qpath.stderr b/tests/ui/delegation/glob-traitless-qpath.stderr new file mode 100644 index 0000000000000..e3257de347a76 --- /dev/null +++ b/tests/ui/delegation/glob-traitless-qpath.stderr @@ -0,0 +1,14 @@ +error: qualified path without a trait in glob delegation + --> $DIR/glob-traitless-qpath.rs:7:5 + | +LL | reuse ::*; + | ^^^^^^^^^^^^^^ + +error: qualified path without a trait in glob delegation + --> $DIR/glob-traitless-qpath.rs:8:5 + | +LL | reuse <()>::*; + | ^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From 470b0e9c3ca0d08678a9acd38a7308d5291cd1ae Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 23 Jun 2024 08:07:55 +1000 Subject: [PATCH 10/13] Import `NonterminalKind` in `compiler/rustc_expand/src/mbe/quoted.rs`. So we can omit the `token::` qualifier, which gives more space to some cramped code. --- compiler/rustc_expand/src/mbe/quoted.rs | 55 +++++++++++-------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index fdf187438d3d7..e60e4d70fbb73 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -2,7 +2,7 @@ use crate::errors; use crate::mbe::macro_parser::count_metavar_decls; use crate::mbe::{Delimited, KleeneOp, KleeneToken, MetaVarExpr, SequenceRepetition, TokenTree}; -use rustc_ast::token::{self, Delimiter, IdentIsRaw, Token}; +use rustc_ast::token::{self, Delimiter, IdentIsRaw, NonterminalKind, Token}; use rustc_ast::{tokenstream, NodeId}; use rustc_ast_pretty::pprust; use rustc_feature::Features; @@ -85,36 +85,31 @@ pub(super) fn parse( span.edition() } }; - let kind = - token::NonterminalKind::from_symbol(fragment.name, edition) - .unwrap_or_else(|| { - let help = match fragment.name { - sym::expr_2021 => { - format!( - "fragment specifier `expr_2021` \ - requires Rust 2021 or later\n\ - {VALID_FRAGMENT_NAMES_MSG}" - ) - } - _ if edition().at_least_rust_2021() - && features - .expr_fragment_specifier_2024 => - { - VALID_FRAGMENT_NAMES_MSG_2021.into() - } - _ => VALID_FRAGMENT_NAMES_MSG.into(), - }; - sess.dcx().emit_err( - errors::InvalidFragmentSpecifier { - span, - fragment, - help, - }, - ); - token::NonterminalKind::Ident + let kind = NonterminalKind::from_symbol(fragment.name, edition) + .unwrap_or_else(|| { + let help = match fragment.name { + sym::expr_2021 => { + format!( + "fragment specifier `expr_2021` \ + requires Rust 2021 or later\n\ + {VALID_FRAGMENT_NAMES_MSG}" + ) + } + _ if edition().at_least_rust_2021() + && features.expr_fragment_specifier_2024 => + { + VALID_FRAGMENT_NAMES_MSG_2021.into() + } + _ => VALID_FRAGMENT_NAMES_MSG.into(), + }; + sess.dcx().emit_err(errors::InvalidFragmentSpecifier { + span, + fragment, + help, }); - if kind - == (token::NonterminalKind::Expr2021 { inferred: false }) + NonterminalKind::Ident + }); + if kind == (NonterminalKind::Expr2021 { inferred: false }) && !features.expr_fragment_specifier_2024 { rustc_session::parse::feature_err( From 70fa67c0b2551b68b3d54bdebbb6565c95f25ab7 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 23 Jun 2024 08:13:41 +1000 Subject: [PATCH 11/13] Tweak some ugly formatting. --- compiler/rustc_expand/src/mbe/macro_rules.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 0050ff10539a8..0045baf7c569b 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -1146,7 +1146,10 @@ fn check_matcher_core<'tt>( // whereas macros from an external crate have a dummy id. if def.id != DUMMY_NODE_ID && matches!(kind, NonterminalKind::PatParam { inferred: true }) - && matches!(next_token, TokenTree::Token(token) if token.kind == BinOp(token::BinOpToken::Or)) + && matches!( + next_token, + TokenTree::Token(token) if token.kind == BinOp(token::BinOpToken::Or) + ) { // It is suggestion to use pat_param, for example: $x:pat -> $x:pat_param. let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl( From e2aa38e6abf9c2ddd06fb2469628ee488dc49e30 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 23 Jun 2024 08:13:56 +1000 Subject: [PATCH 12/13] Rework pattern and expression nonterminal kinds. Merge `PatParam`/`PatWithOr`, and `Expr`/`Expr2021`, for a few reasons. - It's conceptually nice, because the two pattern kinds and the two expression kinds are very similar. - With expressions in particular, there are several places where both expression kinds get the same treatment. - It removes one unreachable match arm. - Most importantly, for #124141 I will need to introduce a new type `MetaVarKind` that is very similar to `NonterminalKind`, but records a couple of extra fields for expression metavars. It's nicer to have a single `MetaVarKind::Expr` expression variant to hold those extra fields instead of duplicating them across two variants `MetaVarKind::{Expr,Expr2021}`. And then it makes sense for patterns to be treated the same way, and for `NonterminalKind` to also be treated the same way. I also clarified the comments, because I have long found them a little hard to understand. --- compiler/rustc_ast/src/token.rs | 73 +++++++++++-------- compiler/rustc_expand/src/mbe/macro_rules.rs | 20 ++--- compiler/rustc_expand/src/mbe/quoted.rs | 4 +- .../rustc_parse/src/parser/nonterminal.rs | 28 +++---- src/tools/rustfmt/src/parse/macros/mod.rs | 6 +- 5 files changed, 71 insertions(+), 60 deletions(-) diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index cc66cc87652d0..efe1956615216 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -1,6 +1,8 @@ pub use BinOpToken::*; pub use LitKind::*; pub use Nonterminal::*; +pub use NtExprKind::*; +pub use NtPatKind::*; pub use TokenKind::*; use crate::ast; @@ -871,6 +873,27 @@ impl PartialEq for Token { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable)] +pub enum NtPatKind { + // Matches or-patterns. Was written using `pat` in edition 2021 or later. + PatWithOr, + // Doesn't match or-patterns. + // - `inferred`: was written using `pat` in edition 2015 or 2018. + // - `!inferred`: was written using `pat_param`. + PatParam { inferred: bool }, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable)] +pub enum NtExprKind { + // Matches expressions using the post-edition 2024. Was written using + // `expr` in edition 2024 or later. + Expr, + // Matches expressions using the pre-edition 2024 rules. + // - `inferred`: was written using `expr` in edition 2021 or earlier. + // - `!inferred`: was written using `expr_2021`. + Expr2021 { inferred: bool }, +} + #[derive(Clone, Encodable, Decodable)] /// For interpolation during macro expansion. pub enum Nonterminal { @@ -892,19 +915,8 @@ pub enum NonterminalKind { Item, Block, Stmt, - PatParam { - /// Keep track of whether the user used `:pat_param` or `:pat` and we inferred it from the - /// edition of the span. This is used for diagnostics. - inferred: bool, - }, - PatWithOr, - Expr, - /// Matches an expression using the rules from edition 2021 and earlier. - Expr2021 { - /// Keep track of whether the user used `:expr` or `:expr_2021` and we inferred it from the - /// edition of the span. This is used for diagnostics AND feature gating. - inferred: bool, - }, + Pat(NtPatKind), + Expr(NtExprKind), Ty, Ident, Lifetime, @@ -926,20 +938,22 @@ impl NonterminalKind { sym::item => NonterminalKind::Item, sym::block => NonterminalKind::Block, sym::stmt => NonterminalKind::Stmt, - sym::pat => match edition() { - Edition::Edition2015 | Edition::Edition2018 => { - NonterminalKind::PatParam { inferred: true } + sym::pat => { + if edition().at_least_rust_2021() { + NonterminalKind::Pat(PatWithOr) + } else { + NonterminalKind::Pat(PatParam { inferred: true }) } - Edition::Edition2021 | Edition::Edition2024 => NonterminalKind::PatWithOr, - }, - sym::pat_param => NonterminalKind::PatParam { inferred: false }, - sym::expr => match edition() { - Edition::Edition2015 | Edition::Edition2018 | Edition::Edition2021 => { - NonterminalKind::Expr2021 { inferred: true } + } + sym::pat_param => NonterminalKind::Pat(PatParam { inferred: false }), + sym::expr => { + if edition().at_least_rust_2024() { + NonterminalKind::Expr(Expr) + } else { + NonterminalKind::Expr(Expr2021 { inferred: true }) } - Edition::Edition2024 => NonterminalKind::Expr, - }, - sym::expr_2021 => NonterminalKind::Expr2021 { inferred: false }, + } + sym::expr_2021 => NonterminalKind::Expr(Expr2021 { inferred: false }), sym::ty => NonterminalKind::Ty, sym::ident => NonterminalKind::Ident, sym::lifetime => NonterminalKind::Lifetime, @@ -951,15 +965,16 @@ impl NonterminalKind { _ => return None, }) } + fn symbol(self) -> Symbol { match self { NonterminalKind::Item => sym::item, NonterminalKind::Block => sym::block, NonterminalKind::Stmt => sym::stmt, - NonterminalKind::PatParam { inferred: false } => sym::pat_param, - NonterminalKind::PatParam { inferred: true } | NonterminalKind::PatWithOr => sym::pat, - NonterminalKind::Expr | NonterminalKind::Expr2021 { inferred: true } => sym::expr, - NonterminalKind::Expr2021 { inferred: false } => sym::expr_2021, + NonterminalKind::Pat(PatParam { inferred: true } | PatWithOr) => sym::pat, + NonterminalKind::Pat(PatParam { inferred: false }) => sym::pat_param, + NonterminalKind::Expr(Expr2021 { inferred: true } | Expr) => sym::expr, + NonterminalKind::Expr(Expr2021 { inferred: false }) => sym::expr_2021, NonterminalKind::Ty => sym::ty, NonterminalKind::Ident => sym::ident, NonterminalKind::Lifetime => sym::lifetime, diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 0045baf7c569b..e43ba7c3a5a8f 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -10,7 +10,9 @@ use crate::mbe::transcribe::transcribe; use ast::token::IdentIsRaw; use rustc_ast as ast; -use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind, TokenKind::*}; +use rustc_ast::token::{ + self, Delimiter, NonterminalKind, NtPatKind::*, Token, TokenKind, TokenKind::*, +}; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; @@ -1145,7 +1147,7 @@ fn check_matcher_core<'tt>( // Macros defined in the current crate have a real node id, // whereas macros from an external crate have a dummy id. if def.id != DUMMY_NODE_ID - && matches!(kind, NonterminalKind::PatParam { inferred: true }) + && matches!(kind, NonterminalKind::Pat(PatParam { inferred: true })) && matches!( next_token, TokenTree::Token(token) if token.kind == BinOp(token::BinOpToken::Or) @@ -1155,7 +1157,7 @@ fn check_matcher_core<'tt>( let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl( span, name, - Some(NonterminalKind::PatParam { inferred: false }), + Some(NonterminalKind::Pat(PatParam { inferred: false })), )); sess.psess.buffer_lint( RUST_2021_INCOMPATIBLE_OR_PATTERNS, @@ -1188,14 +1190,14 @@ fn check_matcher_core<'tt>( ); err.span_label(sp, format!("not allowed after `{kind}` fragments")); - if kind == NonterminalKind::PatWithOr + if kind == NonterminalKind::Pat(PatWithOr) && sess.psess.edition.at_least_rust_2021() && next_token.is_token(&BinOp(token::BinOpToken::Or)) { let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl( span, name, - Some(NonterminalKind::PatParam { inferred: false }), + Some(NonterminalKind::Pat(PatParam { inferred: false })), )); err.span_suggestion( span, @@ -1295,9 +1297,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { // maintain IsInFollow::Yes } - NonterminalKind::Stmt - | NonterminalKind::Expr - | NonterminalKind::Expr2021 { inferred: _ } => { + NonterminalKind::Stmt | NonterminalKind::Expr(_) => { const TOKENS: &[&str] = &["`=>`", "`,`", "`;`"]; match tok { TokenTree::Token(token) => match token.kind { @@ -1307,7 +1307,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { _ => IsInFollow::No(TOKENS), } } - NonterminalKind::PatParam { .. } => { + NonterminalKind::Pat(PatParam { .. }) => { const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"]; match tok { TokenTree::Token(token) => match token.kind { @@ -1320,7 +1320,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { _ => IsInFollow::No(TOKENS), } } - NonterminalKind::PatWithOr => { + NonterminalKind::Pat(PatWithOr) => { const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"]; match tok { TokenTree::Token(token) => match token.kind { diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index e60e4d70fbb73..9c480f17b4215 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -2,7 +2,7 @@ use crate::errors; use crate::mbe::macro_parser::count_metavar_decls; use crate::mbe::{Delimited, KleeneOp, KleeneToken, MetaVarExpr, SequenceRepetition, TokenTree}; -use rustc_ast::token::{self, Delimiter, IdentIsRaw, NonterminalKind, Token}; +use rustc_ast::token::{self, Delimiter, IdentIsRaw, NonterminalKind, NtExprKind::*, Token}; use rustc_ast::{tokenstream, NodeId}; use rustc_ast_pretty::pprust; use rustc_feature::Features; @@ -109,7 +109,7 @@ pub(super) fn parse( }); NonterminalKind::Ident }); - if kind == (NonterminalKind::Expr2021 { inferred: false }) + if kind == NonterminalKind::Expr(Expr2021 { inferred: false }) && !features.expr_fragment_specifier_2024 { rustc_session::parse::feature_err( diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 59f6eff07b320..4a78b427832c5 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -1,5 +1,7 @@ use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, Nonterminal::*, NonterminalKind, Token}; +use rustc_ast::token::{ + self, Delimiter, Nonterminal::*, NonterminalKind, NtExprKind::*, NtPatKind::*, Token, +}; use rustc_ast::HasTokens; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; @@ -36,14 +38,14 @@ impl<'a> Parser<'a> { } match kind { - NonterminalKind::Expr2021 { inferred: _ } => { + NonterminalKind::Expr(Expr2021 { .. }) => { token.can_begin_expr() // This exception is here for backwards compatibility. && !token.is_keyword(kw::Let) // This exception is here for backwards compatibility. && !token.is_keyword(kw::Const) } - NonterminalKind::Expr => { + NonterminalKind::Expr(Expr) => { token.can_begin_expr() // This exception is here for backwards compatibility. && !token.is_keyword(kw::Let) @@ -74,7 +76,7 @@ impl<'a> Parser<'a> { token::Interpolated(nt) => may_be_ident(nt), _ => false, }, - NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => match &token.kind { + NonterminalKind::Pat(pat_kind) => match &token.kind { // box, ref, mut, and other identifiers (can stricten) token::Ident(..) | token::NtIdent(..) | token::OpenDelim(Delimiter::Parenthesis) | // tuple pattern @@ -89,7 +91,7 @@ impl<'a> Parser<'a> { token::Lt | // path (UFCS constant) token::BinOp(token::Shl) => true, // path (double UFCS) // leading vert `|` or-pattern - token::BinOp(token::Or) => matches!(kind, NonterminalKind::PatWithOr), + token::BinOp(token::Or) => matches!(pat_kind, PatWithOr), token::Interpolated(nt) => may_be_ident(nt), _ => false, }, @@ -135,31 +137,25 @@ impl<'a> Parser<'a> { .create_err(UnexpectedNonterminal::Statement(self.token.span))); } }, - NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => { - NtPat(self.collect_tokens_no_attrs(|this| match kind { - NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None, None), - NonterminalKind::PatWithOr => this.parse_pat_allow_top_alt( + NonterminalKind::Pat(pat_kind) => { + NtPat(self.collect_tokens_no_attrs(|this| match pat_kind { + PatParam { .. } => this.parse_pat_no_top_alt(None, None), + PatWithOr => this.parse_pat_allow_top_alt( None, RecoverComma::No, RecoverColon::No, CommaRecoveryMode::EitherTupleOrPipe, ), - _ => unreachable!(), })?) } - - NonterminalKind::Expr | NonterminalKind::Expr2021 { inferred: _ } => { - NtExpr(self.parse_expr_force_collect()?) - } + NonterminalKind::Expr(_) => NtExpr(self.parse_expr_force_collect()?), NonterminalKind::Literal => { // The `:literal` matcher does not support attributes NtLiteral(self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?) } - NonterminalKind::Ty => { NtTy(self.collect_tokens_no_attrs(|this| this.parse_ty_no_question_mark_recover())?) } - // this could be handled like a token, since it is one NonterminalKind::Ident => { return if let Some((ident, is_raw)) = get_macro_ident(&self.token) { diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs index 89169e10715b2..60c827fd03bbb 100644 --- a/src/tools/rustfmt/src/parse/macros/mod.rs +++ b/src/tools/rustfmt/src/parse/macros/mod.rs @@ -1,4 +1,4 @@ -use rustc_ast::token::{Delimiter, NonterminalKind, TokenKind}; +use rustc_ast::token::{Delimiter, NonterminalKind, NtExprKind::*, NtPatKind::*, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{ast, ptr}; use rustc_parse::parser::{ForceCollect, Parser, Recovery}; @@ -48,7 +48,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { parse_macro_arg!( Expr, - NonterminalKind::Expr, + NonterminalKind::Expr(Expr), |parser: &mut Parser<'b>| parser.parse_expr(), |x: ptr::P| Some(x) ); @@ -60,7 +60,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { ); parse_macro_arg!( Pat, - NonterminalKind::PatParam { inferred: false }, + NonterminalKind::Pat(PatParam { inferred: false }), |parser: &mut Parser<'b>| parser.parse_pat_no_top_alt(None, None), |x: ptr::P| Some(x) ); From b8a003035887586eb180b2618cc9891f8f913678 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Sun, 23 Jun 2024 13:42:52 +0000 Subject: [PATCH 13/13] Add need-symlink directive to compiletest --- src/tools/compiletest/src/header.rs | 1 + src/tools/compiletest/src/header/needs.rs | 26 +++++++++++++++++++++ tests/run-make/symlinked-extern/rmake.rs | 1 + tests/run-make/symlinked-libraries/rmake.rs | 1 + tests/run-make/symlinked-rlib/rmake.rs | 1 + 5 files changed, 30 insertions(+) diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 5469b9f1a0a2c..0a618ef990eea 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -875,6 +875,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "needs-sanitizer-shadow-call-stack", "needs-sanitizer-support", "needs-sanitizer-thread", + "needs-symlink", "needs-threads", "needs-unwind", "needs-wasmtime", diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs index b96832db67bed..5b2665f7d0ba7 100644 --- a/src/tools/compiletest/src/header/needs.rs +++ b/src/tools/compiletest/src/header/needs.rs @@ -144,6 +144,11 @@ pub(super) fn handle_needs( condition: config.runner.as_ref().is_some_and(|r| r.contains("wasmtime")), ignore_reason: "ignored when wasmtime runner is not available", }, + Need { + name: "needs-symlink", + condition: cache.symlinks, + ignore_reason: "ignored if symlinks are unavailable", + }, ]; let (name, comment) = match ln.split_once([':', ' ']) { @@ -209,6 +214,7 @@ pub(super) struct CachedNeedsConditions { xray: bool, rust_lld: bool, dlltool: bool, + symlinks: bool, } impl CachedNeedsConditions { @@ -253,6 +259,7 @@ impl CachedNeedsConditions { .exists(), dlltool: find_dlltool(&config), + symlinks: has_symlinks(), } } } @@ -279,3 +286,22 @@ fn find_dlltool(config: &Config) -> bool { }; dlltool_found } + +#[cfg(windows)] +fn has_symlinks() -> bool { + if std::env::var_os("CI").is_some() { + return true; + } + let link = std::env::temp_dir().join("RUST_COMPILETEST_SYMLINK_CHECK"); + if std::os::windows::fs::symlink_file("DOES NOT EXIST", &link).is_ok() { + std::fs::remove_file(&link).unwrap(); + true + } else { + false + } +} + +#[cfg(not(windows))] +fn has_symlinks() -> bool { + true +} diff --git a/tests/run-make/symlinked-extern/rmake.rs b/tests/run-make/symlinked-extern/rmake.rs index 98f69aefbe622..9ed5b76edcbd6 100644 --- a/tests/run-make/symlinked-extern/rmake.rs +++ b/tests/run-make/symlinked-extern/rmake.rs @@ -9,6 +9,7 @@ // can result in successful compilation. //@ ignore-cross-compile +//@ needs-symlink use run_make_support::{create_symlink, cwd, fs_wrapper, rustc}; diff --git a/tests/run-make/symlinked-libraries/rmake.rs b/tests/run-make/symlinked-libraries/rmake.rs index 3f02f19ccd5b0..1d1dce5b5cfe8 100644 --- a/tests/run-make/symlinked-libraries/rmake.rs +++ b/tests/run-make/symlinked-libraries/rmake.rs @@ -6,6 +6,7 @@ // See https://github.com/rust-lang/rust/issues/12459 //@ ignore-cross-compile +//@ needs-symlink use run_make_support::{create_symlink, dynamic_lib_name, fs_wrapper, rustc}; diff --git a/tests/run-make/symlinked-rlib/rmake.rs b/tests/run-make/symlinked-rlib/rmake.rs index 3759ca25928a1..65ebb19142823 100644 --- a/tests/run-make/symlinked-rlib/rmake.rs +++ b/tests/run-make/symlinked-rlib/rmake.rs @@ -6,6 +6,7 @@ // See https://github.com/rust-lang/rust/pull/32828 //@ ignore-cross-compile +//@ needs-symlink use run_make_support::{create_symlink, cwd, rustc};