From c293af9b57a3eae2969d49f629b67bb3d88c1bf3 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Wed, 19 Feb 2025 21:06:48 -0700 Subject: [PATCH 01/15] add `IntoBounds::intersect` and `RangeBounds::is_empty` --- library/core/src/ops/range.rs | 140 +++++++++++++++++- .../method-suggestion-no-duplication.stderr | 3 +- 2 files changed, 137 insertions(+), 6 deletions(-) diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 5580faefacc0d..e0c442e529215 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -771,13 +771,11 @@ pub trait RangeBounds { /// # Examples /// /// ``` - /// # fn main() { /// use std::ops::Bound::*; /// use std::ops::RangeBounds; /// /// assert_eq!((..10).start_bound(), Unbounded); /// assert_eq!((3..10).start_bound(), Included(&3)); - /// # } /// ``` #[stable(feature = "collections_range", since = "1.28.0")] fn start_bound(&self) -> Bound<&T>; @@ -789,13 +787,11 @@ pub trait RangeBounds { /// # Examples /// /// ``` - /// # fn main() { /// use std::ops::Bound::*; /// use std::ops::RangeBounds; /// /// assert_eq!((3..).end_bound(), Unbounded); /// assert_eq!((3..10).end_bound(), Excluded(&10)); - /// # } /// ``` #[stable(feature = "collections_range", since = "1.28.0")] fn end_bound(&self) -> Bound<&T>; @@ -829,6 +825,71 @@ pub trait RangeBounds { Unbounded => true, }) } + + /// Returns `true` if the range contains no items. + /// One-sided ranges (`RangeFrom`, etc) always return `true`. + /// + /// # Examples + /// + /// ``` + /// #![feature(range_bounds_is_empty)] + /// use std::ops::RangeBounds; + /// + /// assert!(!(3..).is_empty()); + /// assert!(!(..2).is_empty()); + /// assert!(!RangeBounds::is_empty(&(3..5))); + /// assert!( RangeBounds::is_empty(&(3..3))); + /// assert!( RangeBounds::is_empty(&(3..2))); + /// ``` + /// + /// The range is empty if either side is incomparable: + /// + /// ``` + /// #![feature(range_bounds_is_empty)] + /// use std::ops::RangeBounds; + /// + /// assert!(!RangeBounds::is_empty(&(3.0..5.0))); + /// assert!( RangeBounds::is_empty(&(3.0..f32::NAN))); + /// assert!( RangeBounds::is_empty(&(f32::NAN..5.0))); + /// ``` + /// + /// But never empty is either side is unbounded: + /// + /// ``` + /// #![feature(range_bounds_is_empty)] + /// use std::ops::RangeBounds; + /// + /// assert!(!(..0).is_empty()); + /// assert!(!(i32::MAX..).is_empty()); + /// assert!(!RangeBounds::::is_empty(&(..))); + /// ``` + /// + /// `(Excluded(a), Excluded(b))` is only empty if `a >= b`: + /// + /// ``` + /// #![feature(range_bounds_is_empty)] + /// use std::ops::Bound::*; + /// use std::ops::RangeBounds; + /// + /// assert!(!(Excluded(1), Excluded(3)).is_empty()); + /// assert!(!(Excluded(1), Excluded(2)).is_empty()); + /// assert!( (Excluded(1), Excluded(1)).is_empty()); + /// assert!( (Excluded(2), Excluded(1)).is_empty()); + /// assert!( (Excluded(3), Excluded(1)).is_empty()); + /// ``` + #[unstable(feature = "range_bounds_is_empty", issue = "137300")] + fn is_empty(&self) -> bool + where + T: PartialOrd, + { + !match (self.start_bound(), self.end_bound()) { + (Unbounded, _) | (_, Unbounded) => true, + (Included(start), Excluded(end)) + | (Excluded(start), Included(end)) + | (Excluded(start), Excluded(end)) => start < end, + (Included(start), Included(end)) => start <= end, + } + } } /// Used to convert a range into start and end bounds, consuming the @@ -845,7 +906,6 @@ pub trait IntoBounds: RangeBounds { /// /// ``` /// #![feature(range_into_bounds)] - /// /// use std::ops::Bound::*; /// use std::ops::IntoBounds; /// @@ -853,6 +913,76 @@ pub trait IntoBounds: RangeBounds { /// assert_eq!((..=7).into_bounds(), (Unbounded, Included(7))); /// ``` fn into_bounds(self) -> (Bound, Bound); + + /// Compute the intersection of `self` and `other`. + /// + /// # Examples + /// + /// ``` + /// #![feature(range_into_bounds)] + /// use std::ops::Bound::*; + /// use std::ops::IntoBounds; + /// + /// assert_eq!((3..).intersect(..5), (Included(3), Excluded(5))); + /// assert_eq!((-12..387).intersect(0..256), (Included(0), Excluded(256))); + /// assert_eq!((1..5).intersect(..), (Included(1), Excluded(5))); + /// assert_eq!((1..=9).intersect(0..10), (Included(1), Included(9))); + /// assert_eq!((7..=13).intersect(8..13), (Included(8), Excluded(13))); + /// ``` + /// + /// Combine with `is_empty` to determine if two ranges overlap. + /// + /// ``` + /// #![feature(range_into_bounds)] + /// #![feature(range_bounds_is_empty)] + /// use std::ops::{RangeBounds, IntoBounds}; + /// + /// assert!(!(3..).intersect(..5).is_empty()); + /// assert!(!(-12..387).intersect(0..256).is_empty()); + /// assert!((1..5).intersect(6..).is_empty()); + /// ``` + fn intersect(self, other: R) -> (Bound, Bound) + where + Self: Sized, + T: Ord, + R: Sized + IntoBounds, + { + let (self_start, self_end) = IntoBounds::into_bounds(self); + let (other_start, other_end) = IntoBounds::into_bounds(other); + + let start = match (self_start, other_start) { + (Included(a), Included(b)) => Included(Ord::max(a, b)), + (Excluded(a), Excluded(b)) => Excluded(Ord::max(a, b)), + (Unbounded, Unbounded) => Unbounded, + + (x, Unbounded) | (Unbounded, x) => x, + + (Included(i), Excluded(e)) | (Excluded(e), Included(i)) => { + if i > e { + Included(i) + } else { + Excluded(e) + } + } + }; + let end = match (self_end, other_end) { + (Included(a), Included(b)) => Included(Ord::min(a, b)), + (Excluded(a), Excluded(b)) => Excluded(Ord::min(a, b)), + (Unbounded, Unbounded) => Unbounded, + + (x, Unbounded) | (Unbounded, x) => x, + + (Included(i), Excluded(e)) | (Excluded(e), Included(i)) => { + if i < e { + Included(i) + } else { + Excluded(e) + } + } + }; + + (start, end) + } } use self::Bound::{Excluded, Included, Unbounded}; diff --git a/tests/ui/impl-trait/method-suggestion-no-duplication.stderr b/tests/ui/impl-trait/method-suggestion-no-duplication.stderr index c401269da83cc..6bc57f89467d8 100644 --- a/tests/ui/impl-trait/method-suggestion-no-duplication.stderr +++ b/tests/ui/impl-trait/method-suggestion-no-duplication.stderr @@ -8,8 +8,9 @@ LL | foo(|s| s.is_empty()); | ^^^^^^^^ method not found in `Foo` | = help: items from traits can only be used if the trait is implemented and in scope - = note: the following trait defines an item `is_empty`, perhaps you need to implement it: + = note: the following traits define an item `is_empty`, perhaps you need to implement one of them: candidate #1: `ExactSizeIterator` + candidate #2: `RangeBounds` error: aborting due to 1 previous error From 5d6a6e70e34e9b39e1bccb1c48e7b7c5264c838a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Tue, 4 Feb 2025 21:54:15 +0800 Subject: [PATCH 02/15] compiletest: disambiguate between root build dir vs test suite specific build dir - Introduce and use `--build-{root,test-suite-root}` over `--build-base`. - A few minor cleanups. --- src/tools/compiletest/src/common.rs | 20 +++++++++++------- src/tools/compiletest/src/header.rs | 10 +++++---- src/tools/compiletest/src/header/tests.rs | 3 ++- src/tools/compiletest/src/lib.rs | 21 ++++++++++++++----- src/tools/compiletest/src/runtest.rs | 16 +++++++------- src/tools/compiletest/src/runtest/run_make.rs | 17 ++++++--------- src/tools/compiletest/src/runtest/ui.rs | 2 +- 7 files changed, 51 insertions(+), 38 deletions(-) diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 98ab7adf5a726..b302c6a49f58e 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -220,8 +220,10 @@ pub struct Config { /// The directory containing the test suite sources. Must be a subdirectory of `src_root`. pub src_test_suite_root: PathBuf, - /// The directory where programs should be built - pub build_base: PathBuf, + /// Root build directory (e.g. `build/`). + pub build_root: PathBuf, + /// Test suite specific build directory (e.g. `build/host/test/ui/`). + pub build_test_suite_root: PathBuf, /// The directory containing the compiler sysroot pub sysroot_base: PathBuf, @@ -347,7 +349,7 @@ pub struct Config { /// If true, this will generate a coverage file with UI test files that run `MachineApplicable` /// diagnostics but are missing `run-rustfix` annotations. The generated coverage file is - /// created in `//rustfix_missing_coverage.txt` + /// created in `/rustfix_missing_coverage.txt` pub rustfix_coverage: bool, /// whether to run `tidy` (html-tidy) when a rustdoc test fails @@ -812,12 +814,16 @@ pub const UI_STDERR_16: &str = "16bit.stderr"; pub const UI_COVERAGE: &str = "coverage"; pub const UI_COVERAGE_MAP: &str = "cov-map"; -/// Absolute path to the directory where all output for all tests in the given -/// `relative_dir` group should reside. Example: -/// /path/to/build/host-tuple/test/ui/relative/ +/// Absolute path to the directory where all output for all tests in the given `relative_dir` group +/// should reside. Example: +/// +/// ```text +/// /path/to/build/host-tuple/test/ui/relative/ +/// ``` +/// /// This is created early when tests are collected to avoid race conditions. pub fn output_relative_path(config: &Config, relative_dir: &Path) -> PathBuf { - config.build_base.join(relative_dir) + config.build_test_suite_root.join(relative_dir) } /// Generates a unique name for the test, such as `testname.revision.mode`. diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 3bdf37a1f294a..53ee901b8bc48 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -1070,10 +1070,11 @@ impl Config { } } +// FIXME(jieyouxu): fix some of these variable names to more accurately reflect what they do. fn expand_variables(mut value: String, config: &Config) -> String { const CWD: &str = "{{cwd}}"; const SRC_BASE: &str = "{{src-base}}"; - const BUILD_BASE: &str = "{{build-base}}"; + const TEST_SUITE_BUILD_BASE: &str = "{{build-base}}"; const RUST_SRC_BASE: &str = "{{rust-src-base}}"; const SYSROOT_BASE: &str = "{{sysroot-base}}"; const TARGET_LINKER: &str = "{{target-linker}}"; @@ -1088,12 +1089,13 @@ fn expand_variables(mut value: String, config: &Config) -> String { value = value.replace(SRC_BASE, &config.src_test_suite_root.to_str().unwrap()); } - if value.contains(BUILD_BASE) { - value = value.replace(BUILD_BASE, &config.build_base.to_string_lossy()); + if value.contains(TEST_SUITE_BUILD_BASE) { + value = + value.replace(TEST_SUITE_BUILD_BASE, &config.build_test_suite_root.to_str().unwrap()); } if value.contains(SYSROOT_BASE) { - value = value.replace(SYSROOT_BASE, &config.sysroot_base.to_string_lossy()); + value = value.replace(SYSROOT_BASE, &config.sysroot_base.to_str().unwrap()); } if value.contains(TARGET_LINKER) { diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index c9536b7a19b79..e47ea001e7e6a 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -155,7 +155,8 @@ impl ConfigBuilder { "--jsondocck-path=", "--src-root=", "--src-test-suite-root=", - "--build-base=", + "--build-root=", + "--build-test-suite-root=", "--sysroot-base=", "--cc=c", "--cxx=c++", diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 979821701385e..9dff7047bc4ab 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -63,7 +63,8 @@ pub fn parse_config(args: Vec) -> Config { .optopt("", "llvm-filecheck", "path to LLVM's FileCheck binary", "DIR") .reqopt("", "src-root", "directory containing sources", "PATH") .reqopt("", "src-test-suite-root", "directory containing test suite sources", "PATH") - .reqopt("", "build-base", "directory to deposit test outputs", "PATH") + .reqopt("", "build-root", "path to root build directory", "PATH") + .reqopt("", "build-test-suite-root", "path to test suite specific build directory", "PATH") .reqopt("", "sysroot-base", "directory containing the compiler sysroot", "PATH") .reqopt("", "stage", "stage number under test", "N") .reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET") @@ -157,7 +158,7 @@ pub fn parse_config(args: Vec) -> Config { "", "rustfix-coverage", "enable this to generate a Rustfix coverage file, which is saved in \ - `.//rustfix_missing_coverage.txt`", + `.//rustfix_missing_coverage.txt`", ) .optflag("", "force-rerun", "rerun tests even if the inputs are unchanged") .optflag("", "only-modified", "only run tests that result been modified") @@ -309,6 +310,10 @@ pub fn parse_config(args: Vec) -> Config { src_test_suite_root.display() ); + let build_root = opt_path(matches, "build-root"); + let build_test_suite_root = opt_path(matches, "build-test-suite-root"); + assert!(build_test_suite_root.starts_with(&build_root)); + Config { bless: matches.opt_present("bless"), compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")), @@ -327,7 +332,9 @@ pub fn parse_config(args: Vec) -> Config { src_root, src_test_suite_root, - build_base: opt_path(matches, "build-base"), + build_root, + build_test_suite_root, + sysroot_base: opt_path(matches, "sysroot-base"), stage, @@ -438,7 +445,11 @@ pub fn log_config(config: &Config) { logv(c, format!("src_root: {}", config.src_root.display())); logv(c, format!("src_test_suite_root: {}", config.src_test_suite_root.display())); - logv(c, format!("build_base: {:?}", config.build_base.display())); + logv(c, format!("build_root: {}", config.build_root.display())); + logv(c, format!("build_test_suite_root: {}", config.build_test_suite_root.display())); + + logv(c, format!("sysroot_base: {}", config.sysroot_base.display())); + logv(c, format!("stage: {}", config.stage)); logv(c, format!("stage_id: {}", config.stage_id)); logv(c, format!("mode: {}", config.mode)); @@ -488,7 +499,7 @@ pub fn run_tests(config: Arc) { // we first make sure that the coverage file does not exist. // It will be created later on. if config.rustfix_coverage { - let mut coverage_file_path = config.build_base.clone(); + let mut coverage_file_path = config.build_test_suite_root.clone(); coverage_file_path.push("rustfix_missing_coverage.txt"); if coverage_file_path.exists() { if let Err(e) = fs::remove_file(&coverage_file_path) { diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 536e19bc4933e..f36d43b211fff 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -535,7 +535,9 @@ impl<'test> TestCx<'test> { .arg(&out_dir) .arg(&format!("--target={}", target)) .arg("-L") - .arg(&self.config.build_base) + // FIXME(jieyouxu): this search path seems questionable. Is this intended for + // `rust_test_helpers` in ui tests? + .arg(&self.config.build_test_suite_root) .arg("-L") .arg(aux_dir) .arg("-A") @@ -1366,7 +1368,7 @@ impl<'test> TestCx<'test> { // Note: avoid adding a subdirectory of an already filtered directory here, otherwise the // same slice of text will be double counted and the truncation might not happen. add_path(&self.config.src_test_suite_root); - add_path(&self.config.build_base); + add_path(&self.config.build_test_suite_root); read2_abbreviated(child, &filter_paths_from_len).expect("failed to read output") } @@ -1421,7 +1423,7 @@ impl<'test> TestCx<'test> { } fn get_mir_dump_dir(&self) -> PathBuf { - let mut mir_dump_dir = PathBuf::from(self.config.build_base.as_path()); + let mut mir_dump_dir = self.config.build_test_suite_root.clone(); debug!("input_file: {:?}", self.testpaths.file); mir_dump_dir.push(&self.testpaths.relative_dir); mir_dump_dir.push(self.testpaths.file.file_stem().unwrap()); @@ -2410,14 +2412,10 @@ impl<'test> TestCx<'test> { let rust_src_dir = rust_src_dir.read_link().unwrap_or(rust_src_dir.to_path_buf()); normalize_path(&rust_src_dir.join("library"), "$SRC_DIR_REAL"); - // Paths into the build directory - let test_build_dir = &self.config.build_base; - let parent_build_dir = test_build_dir.parent().unwrap().parent().unwrap().parent().unwrap(); - // eg. /home/user/rust/build/x86_64-unknown-linux-gnu/test/ui - normalize_path(test_build_dir, "$TEST_BUILD_DIR"); + normalize_path(&self.config.build_test_suite_root, "$TEST_BUILD_DIR"); // eg. /home/user/rust/build - normalize_path(parent_build_dir, "$BUILD_DIR"); + normalize_path(&self.config.build_root, "$BUILD_DIR"); if json { // escaped newlines in json strings should be readable diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index 74e6af36ea1dd..9bb3993223e21 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -179,12 +179,7 @@ impl TestCx<'_> { // library. // 2. We need to run the recipe binary. - // `self.config.build_base` is actually the build base folder + "test" + test suite name, it - // looks like `build//test/run-make`. But we want `build//`. Note - // that the `build` directory does not need to be called `build`, nor does it need to be - // under `src_root`, so we must compute it based off of `self.config.build_base`. - let build_root = - self.config.build_base.parent().and_then(Path::parent).unwrap().to_path_buf(); + let host_build_root = self.config.build_root.join(&self.config.host); // We construct the following directory tree for each rmake.rs test: // ``` @@ -242,10 +237,10 @@ impl TestCx<'_> { let stage_number = self.config.stage; - let stage_tools_bin = build_root.join(format!("stage{stage_number}-tools-bin")); + let stage_tools_bin = host_build_root.join(format!("stage{stage_number}-tools-bin")); let support_lib_path = stage_tools_bin.join("librun_make_support.rlib"); - let stage_tools = build_root.join(format!("stage{stage_number}-tools")); + let stage_tools = host_build_root.join(format!("stage{stage_number}-tools")); let support_lib_deps = stage_tools.join(&self.config.host).join("release").join("deps"); let support_lib_deps_deps = stage_tools.join("release").join("deps"); @@ -311,7 +306,7 @@ impl TestCx<'_> { // to work correctly. // // See for more background. - let stage0_sysroot = build_root.join("stage0-sysroot"); + let stage0_sysroot = host_build_root.join("stage0-sysroot"); if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() { rustc.arg("--sysroot").arg(&stage0_sysroot); } @@ -326,7 +321,7 @@ impl TestCx<'_> { // provided through env vars. // Compute stage-specific standard library paths. - let stage_std_path = build_root.join(format!("stage{stage_number}")).join("lib"); + let stage_std_path = host_build_root.join(format!("stage{stage_number}")).join("lib"); // Compute dynamic library search paths for recipes. let recipe_dylib_search_paths = { @@ -372,7 +367,7 @@ impl TestCx<'_> { // Provide path to sources root. .env("SOURCE_ROOT", &self.config.src_root) // Path to the host build directory. - .env("BUILD_ROOT", &build_root) + .env("BUILD_ROOT", &host_build_root) // Provide path to stage-corresponding rustc. .env("RUSTC", &self.config.rustc_path) // Provide the directory to libraries that are needed to run the *compiler*. This is not diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs index 0c6d46188e6f4..3329e10745f8d 100644 --- a/src/tools/compiletest/src/runtest/ui.rs +++ b/src/tools/compiletest/src/runtest/ui.rs @@ -66,7 +66,7 @@ impl TestCx<'_> { && !self.props.run_rustfix && !self.props.rustfix_only_machine_applicable { - let mut coverage_file_path = self.config.build_base.clone(); + let mut coverage_file_path = self.config.build_test_suite_root.clone(); coverage_file_path.push("rustfix_missing_coverage.txt"); debug!("coverage_file_path: {}", coverage_file_path.display()); From 5238337d522351eaa71659c3865b6bd7cf9341c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Tue, 4 Feb 2025 21:28:32 +0800 Subject: [PATCH 03/15] bootstrap: remove `--build-base` and use `--build-root` and `--build-test-suite-root` instead --- src/bootstrap/src/core/build_steps/test.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 604f9e73b4581..cdb1ae70ab613 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1774,7 +1774,11 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the cmd.arg("--src-root").arg(&builder.src); cmd.arg("--src-test-suite-root").arg(builder.src.join("tests").join(suite)); - cmd.arg("--build-base").arg(testdir(builder, compiler.host).join(suite)); + // N.B. it's important to distinguish between the *root* build directory, the *host* build + // directory immediately under the root build directory, and the test-suite-specific build + // directory. + cmd.arg("--build-root").arg(&builder.out); + cmd.arg("--build-test-suite-root").arg(testdir(builder, compiler.host).join(suite)); // When top stage is 0, that means that we're testing an externally provided compiler. // In that case we need to use its specific sysroot for tests to pass. From 7d2e4e4b6966ae4646e4f87800dafe6cc1b839c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Tue, 4 Feb 2025 21:30:36 +0800 Subject: [PATCH 04/15] bootstrap: remove redundant `to_path_buf()` --- src/bootstrap/src/core/build_steps/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index cdb1ae70ab613..2053e9c97d740 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1785,7 +1785,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the let sysroot = if builder.top_stage == 0 { builder.initial_sysroot.clone() } else { - builder.sysroot(compiler).to_path_buf() + builder.sysroot(compiler) }; cmd.arg("--sysroot-base").arg(sysroot); From 7041eedb1a00640805d9e4dd3cd1efd142655652 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 12 Feb 2025 10:37:49 +0000 Subject: [PATCH 05/15] Prefer a two value enum over bool --- compiler/rustc_hir/src/hir.rs | 2 +- .../rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 5 ----- compiler/rustc_lint/src/types.rs | 11 ++++------- compiler/rustc_middle/src/ty/pattern.rs | 8 +++----- compiler/rustc_middle/src/ty/structural_impls.rs | 1 + compiler/rustc_smir/src/rustc_internal/internal.rs | 3 ++- compiler/rustc_smir/src/rustc_smir/convert/ty.rs | 2 +- compiler/rustc_symbol_mangling/src/v0.rs | 5 ++++- compiler/rustc_ty_utils/src/layout.rs | 5 +++-- 9 files changed, 19 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index f0eaec55dbdd3..63b1ae5c88651 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1600,7 +1600,7 @@ pub struct PatField<'hir> { pub span: Span, } -#[derive(Copy, Clone, PartialEq, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Debug, HashStable_Generic, Hash, Eq, Encodable, Decodable)] pub enum RangeEnd { Included, Excluded, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 750770178eef9..6f87707f479de 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2468,11 +2468,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let start = start.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)); let end = end.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)); - let include_end = match include_end { - hir::RangeEnd::Included => true, - hir::RangeEnd::Excluded => false, - }; - let pat = tcx.mk_pat(ty::PatternKind::Range { start, end, include_end }); Ty::new_pat(tcx, ty, pat) } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index cb83d405cc3ed..0740ee7e82348 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -5,7 +5,7 @@ use rustc_abi::{BackendRepr, ExternAbi, TagEncoding, VariantIdx, Variants, Wrapp use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; use rustc_hir::intravisit::VisitorExt; -use rustc_hir::{AmbigArg, Expr, ExprKind, HirId, LangItem}; +use rustc_hir::{AmbigArg, Expr, ExprKind, HirId, LangItem, RangeEnd}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton}; use rustc_middle::ty::{ @@ -893,12 +893,9 @@ fn ty_is_known_nonnull<'tcx>( let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?; - if include_end { - // This also works for negative numbers, as we just need - // to ensure we aren't wrapping over zero. - start > 0 && end >= start - } else { - start > 0 && end > start + match include_end { + RangeEnd::Included => start > 0 && end >= start, + RangeEnd::Excluded => start > 0 && end > start, } } _ => false, diff --git a/compiler/rustc_middle/src/ty/pattern.rs b/compiler/rustc_middle/src/ty/pattern.rs index e604aedd05e32..48ae5b1e657de 100644 --- a/compiler/rustc_middle/src/ty/pattern.rs +++ b/compiler/rustc_middle/src/ty/pattern.rs @@ -1,6 +1,7 @@ use std::fmt; use rustc_data_structures::intern::Interned; +use rustc_hir::RangeEnd; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use crate::ty; @@ -30,10 +31,7 @@ impl<'tcx> fmt::Debug for PatternKind<'tcx> { if let Some(start) = start { write!(f, "{start}")?; } - write!(f, "..")?; - if include_end { - write!(f, "=")?; - } + write!(f, "{include_end}")?; if let Some(end) = end { write!(f, "{end}")?; } @@ -46,5 +44,5 @@ impl<'tcx> fmt::Debug for PatternKind<'tcx> { #[derive(Clone, PartialEq, Eq, Hash)] #[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)] pub enum PatternKind<'tcx> { - Range { start: Option>, end: Option>, include_end: bool }, + Range { start: Option>, end: Option>, include_end: RangeEnd }, } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index db9e9fbc643b2..6c62c04f42e4c 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -284,6 +284,7 @@ TrivialTypeTraversalImpls! { rustc_hir::def_id::LocalDefId, rustc_hir::HirId, rustc_hir::MatchSource, + rustc_hir::RangeEnd, rustc_span::Ident, rustc_span::Span, rustc_span::Symbol, diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index 50cf605ba2a82..093ce5f56488b 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -5,6 +5,7 @@ // Prefer importing stable_mir over internal rustc constructs to make this file more readable. +use rustc_hir::RangeEnd; use rustc_middle::ty::{self as rustc_ty, Const as InternalConst, Ty as InternalTy, TyCtxt}; use rustc_span::Symbol; use stable_mir::abi::Layout; @@ -91,7 +92,7 @@ impl RustcInternal for Pattern { Pattern::Range { start, end, include_end } => rustc_ty::PatternKind::Range { start: start.as_ref().map(|c| c.internal(tables, tcx)), end: end.as_ref().map(|c| c.internal(tables, tcx)), - include_end: *include_end, + include_end: if *include_end { RangeEnd::Included } else { RangeEnd::Excluded }, }, }) } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index a0faf20c79a67..9c06d08d61b5a 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -408,7 +408,7 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> { ty::PatternKind::Range { start, end, include_end } => stable_mir::ty::Pattern::Range { start: start.stable(tables), end: end.stable(tables), - include_end, + include_end: matches!(include_end, rustc_hir::RangeEnd::Included), }, } } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 4fafd1ac3509a..66b6af6bf51d4 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -417,7 +417,10 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { let consts = [ start.unwrap_or(self.tcx.consts.unit), end.unwrap_or(self.tcx.consts.unit), - ty::Const::from_bool(self.tcx, include_end), + ty::Const::from_bool( + self.tcx, + matches!(include_end, rustc_hir::RangeEnd::Included), + ), ]; // HACK: Represent as tuple until we have something better. // HACK: constants are used in arrays, even if the types don't match. diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 2ab6b8e17c4cc..0e5a299956154 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -220,8 +220,9 @@ fn layout_of_uncached<'tcx>( let mut end = extract_const_value(cx, ty, end)? .try_to_bits(tcx, cx.typing_env) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; - if !include_end { - end = end.wrapping_sub(1); + match include_end { + rustc_hir::RangeEnd::Included => {} + rustc_hir::RangeEnd::Excluded => end = end.wrapping_sub(1), } scalar.valid_range_mut().end = end; } From 0ea02cdcbc0780fbba4d93fdbf9b36b339ea4518 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 5 Feb 2025 15:22:10 +0000 Subject: [PATCH 06/15] Avoid having to handle an `Option` in the type system --- .../src/hir_ty_lowering/mod.rs | 89 ++++++++++++++++--- .../src/variance/constraints.rs | 8 +- compiler/rustc_lint/src/types.rs | 24 ++--- compiler/rustc_middle/src/ty/flags.rs | 8 +- compiler/rustc_middle/src/ty/pattern.rs | 11 +-- compiler/rustc_middle/src/ty/relate.rs | 23 +++-- compiler/rustc_middle/src/ty/walk.rs | 4 +- .../rustc_smir/src/rustc_internal/internal.rs | 4 +- .../rustc_smir/src/rustc_smir/convert/ty.rs | 5 +- compiler/rustc_symbol_mangling/src/v0.rs | 4 +- .../rustc_trait_selection/src/traits/wf.rs | 8 +- compiler/rustc_ty_utils/src/layout.rs | 25 +++--- .../pattern_types.main.PreCodegen.after.mir | 4 +- tests/mir-opt/pattern_types.rs | 4 +- tests/ui/lint/clashing-extern-fn.stderr | 4 +- tests/ui/type/pattern_types/nested.stderr | 18 ++-- .../type/pattern_types/range_patterns.stderr | 4 +- .../range_patterns_trait_impls2.stderr | 2 +- .../range_patterns_unusable.stderr | 2 +- .../range_patterns_unusable_math.rs | 2 +- .../range_patterns_unusable_math.stderr | 4 +- 21 files changed, 147 insertions(+), 110 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 6f87707f479de..efafac19defe0 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -21,8 +21,9 @@ pub mod generics; mod lint; use std::assert_matches::assert_matches; -use std::slice; +use std::{char, slice}; +use rustc_abi::Size; use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; @@ -31,7 +32,7 @@ use rustc_errors::{ }; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId}; +use rustc_hir::{self as hir, AnonConst, ConstArg, GenericArg, GenericArgs, HirId}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability::AllowUnstable; @@ -2453,20 +2454,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let ty = self.lower_ty(ty); let pat_ty = match pat.kind { hir::TyPatKind::Range(start, end, include_end) => { - let ty = match ty.kind() { - ty::Int(_) | ty::Uint(_) | ty::Char => ty, - _ => Ty::new_error( - tcx, - self.dcx().emit_err(InvalidBaseType { + let (ty, start, end) = match ty.kind() { + ty::Int(_) | ty::Uint(_) | ty::Char => { + let (start, end) = self.lower_ty_pat_range(ty, start, end); + (ty, start, end) + } + _ => { + let guar = self.dcx().emit_err(InvalidBaseType { ty, pat: "range", ty_span, pat_span: pat.span, - }), - ), + }); + let errc = ty::Const::new_error(tcx, guar); + (Ty::new_error(tcx, guar), errc, errc) + } }; - let start = start.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)); - let end = end.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)); let pat = tcx.mk_pat(ty::PatternKind::Range { start, end, include_end }); Ty::new_pat(tcx, ty, pat) @@ -2483,6 +2486,70 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { result_ty } + fn lower_ty_pat_range( + &self, + base: Ty<'tcx>, + start: Option<&ConstArg<'tcx>>, + end: Option<&ConstArg<'tcx>>, + ) -> (ty::Const<'tcx>, ty::Const<'tcx>) { + let tcx = self.tcx(); + let size = match base.kind() { + ty::Int(i) => { + i.bit_width().map_or(tcx.data_layout.pointer_size, |bits| Size::from_bits(bits)) + } + ty::Uint(ui) => { + ui.bit_width().map_or(tcx.data_layout.pointer_size, |bits| Size::from_bits(bits)) + } + ty::Char => Size::from_bytes(4), + _ => unreachable!(), + }; + let start = + start.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)).unwrap_or_else(|| { + match base.kind() { + ty::Char | ty::Uint(_) => ty::Const::new_value( + tcx, + ty::ValTree::from_scalar_int(ty::ScalarInt::null(size)), + base, + ), + ty::Int(_) => ty::Const::new_value( + tcx, + ty::ValTree::from_scalar_int( + ty::ScalarInt::truncate_from_int(size.signed_int_min(), size).0, + ), + base, + ), + _ => unreachable!(), + } + }); + let end = end.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)).unwrap_or_else( + || match base.kind() { + ty::Char => ty::Const::new_value( + tcx, + ty::ValTree::from_scalar_int( + ty::ScalarInt::truncate_from_uint(char::MAX, size).0, + ), + base, + ), + ty::Uint(_) => ty::Const::new_value( + tcx, + ty::ValTree::from_scalar_int( + ty::ScalarInt::truncate_from_uint(size.unsigned_int_max(), size).0, + ), + base, + ), + ty::Int(_) => ty::Const::new_value( + tcx, + ty::ValTree::from_scalar_int( + ty::ScalarInt::truncate_from_int(size.signed_int_max(), size).0, + ), + base, + ), + _ => unreachable!(), + }, + ); + (start, end) + } + /// Lower an opaque type (i.e., an existential impl-Trait type) from the HIR. #[instrument(level = "debug", skip(self), ret)] fn lower_opaque_ty(&self, def_id: LocalDefId, in_trait: bool) -> Ty<'tcx> { diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index e954d2b9ea48e..4b336769cfb80 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -253,12 +253,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { ty::Pat(typ, pat) => { match *pat { ty::PatternKind::Range { start, end, include_end: _ } => { - if let Some(start) = start { - self.add_constraints_from_const(current, start, variance); - } - if let Some(end) = end { - self.add_constraints_from_const(current, end, variance); - } + self.add_constraints_from_const(current, start, variance); + self.add_constraints_from_const(current, end, variance); } } self.add_constraints_from_ty(current, typ, variance); diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 0740ee7e82348..73fe8f1057885 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -883,22 +883,14 @@ fn ty_is_known_nonnull<'tcx>( try { match **pat { ty::PatternKind::Range { start, end, include_end } => { - match (start, end) { - (Some(start), None) => { - start.try_to_value()?.try_to_bits(tcx, typing_env)? > 0 - } - (Some(start), Some(end)) => { - let start = - start.try_to_value()?.try_to_bits(tcx, typing_env)?; - let end = - end.try_to_value()?.try_to_bits(tcx, typing_env)?; - - match include_end { - RangeEnd::Included => start > 0 && end >= start, - RangeEnd::Excluded => start > 0 && end > start, - } - } - _ => false, + let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?; + let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?; + + match include_end { + // This also works for negative numbers, as we just need + // to ensure we aren't wrapping over zero. + RangeEnd::Included => start > 0 && end >= start, + RangeEnd::Excluded => start > 0 && end > start, } } } diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index ec0498b168c01..c08d78e25b8d1 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -221,12 +221,8 @@ impl FlagComputation { self.add_ty(ty); match *pat { ty::PatternKind::Range { start, end, include_end: _ } => { - if let Some(start) = start { - self.add_const(start) - } - if let Some(end) = end { - self.add_const(end) - } + self.add_const(start); + self.add_const(end); } } } diff --git a/compiler/rustc_middle/src/ty/pattern.rs b/compiler/rustc_middle/src/ty/pattern.rs index 48ae5b1e657de..a1dfd2e8d34f0 100644 --- a/compiler/rustc_middle/src/ty/pattern.rs +++ b/compiler/rustc_middle/src/ty/pattern.rs @@ -28,14 +28,7 @@ impl<'tcx> fmt::Debug for PatternKind<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { PatternKind::Range { start, end, include_end } => { - if let Some(start) = start { - write!(f, "{start}")?; - } - write!(f, "{include_end}")?; - if let Some(end) = end { - write!(f, "{end}")?; - } - Ok(()) + write!(f, "{start}{include_end}{end}") } } } @@ -44,5 +37,5 @@ impl<'tcx> fmt::Debug for PatternKind<'tcx> { #[derive(Clone, PartialEq, Eq, Hash)] #[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)] pub enum PatternKind<'tcx> { - Range { start: Option>, end: Option>, include_end: RangeEnd }, + Range { start: ty::Const<'tcx>, end: ty::Const<'tcx>, include_end: RangeEnd }, } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 839c1c346a47b..6d5cc42065f22 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -54,19 +54,18 @@ impl<'tcx> Relate> for ty::Pattern<'tcx> { &ty::PatternKind::Range { start: start_a, end: end_a, include_end: inc_a }, &ty::PatternKind::Range { start: start_b, end: end_b, include_end: inc_b }, ) => { - // FIXME(pattern_types): make equal patterns equal (`0..=` is the same as `..=`). - let mut relate_opt_const = |a, b| match (a, b) { - (None, None) => Ok(None), - (Some(a), Some(b)) => relation.relate(a, b).map(Some), - // FIXME(pattern_types): report a better error - _ => Err(TypeError::Mismatch), - }; - let start = relate_opt_const(start_a, start_b)?; - let end = relate_opt_const(end_a, end_b)?; - if inc_a != inc_b { - todo!() + let start = relation.relate(start_a, start_b)?; + // FIXME(pattern_types): make equal patterns equal (`0..5` is the same as `0..=6`). + let end = relation.relate(end_a, end_b)?; + if inc_a == inc_b { + Ok(relation.cx().mk_pat(ty::PatternKind::Range { + start, + end, + include_end: inc_a, + })) + } else { + Err(TypeError::Mismatch) } - Ok(relation.cx().mk_pat(ty::PatternKind::Range { start, end, include_end: inc_a })) } } } diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index 3e8a3d1a28923..b31b7cac41ef2 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -138,8 +138,8 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) ty::Pat(ty, pat) => { match *pat { ty::PatternKind::Range { start, end, include_end: _ } => { - stack.extend(end.map(Into::into)); - stack.extend(start.map(Into::into)); + stack.push(end.into()); + stack.push(start.into()); } } stack.push(ty.into()); diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index 093ce5f56488b..a8c4cd660fd98 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -90,8 +90,8 @@ impl RustcInternal for Pattern { fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { tcx.mk_pat(match self { Pattern::Range { start, end, include_end } => rustc_ty::PatternKind::Range { - start: start.as_ref().map(|c| c.internal(tables, tcx)), - end: end.as_ref().map(|c| c.internal(tables, tcx)), + start: start.as_ref().unwrap().internal(tables, tcx), + end: end.as_ref().unwrap().internal(tables, tcx), include_end: if *include_end { RangeEnd::Included } else { RangeEnd::Excluded }, }, }) diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index 9c06d08d61b5a..f6a6a3c1737fb 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -406,8 +406,9 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> { fn stable(&self, tables: &mut Tables<'_>) -> Self::T { match **self { ty::PatternKind::Range { start, end, include_end } => stable_mir::ty::Pattern::Range { - start: start.stable(tables), - end: end.stable(tables), + // FIXME(SMIR): update data structures to not have an Option here anymore + start: Some(start.stable(tables)), + end: Some(end.stable(tables)), include_end: matches!(include_end, rustc_hir::RangeEnd::Included), }, } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 66b6af6bf51d4..754c74c4f009f 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -415,8 +415,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { ty::Pat(ty, pat) => match *pat { ty::PatternKind::Range { start, end, include_end } => { let consts = [ - start.unwrap_or(self.tcx.consts.unit), - end.unwrap_or(self.tcx.consts.unit), + start, + end, ty::Const::from_bool( self.tcx, matches!(include_end, rustc_hir::RangeEnd::Included), diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 18906a6a8ce0f..0fece96a3eb41 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -738,12 +738,8 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { } } }; - if let Some(start) = start { - check(start) - } - if let Some(end) = end { - check(end) - } + check(start); + check(end); } } } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 0e5a299956154..781b86ae92362 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -211,21 +211,18 @@ fn layout_of_uncached<'tcx>( if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) = &mut layout.backend_repr { - if let Some(start) = start { - scalar.valid_range_mut().start = extract_const_value(cx, ty, start)? - .try_to_bits(tcx, cx.typing_env) - .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; - } - if let Some(end) = end { - let mut end = extract_const_value(cx, ty, end)? - .try_to_bits(tcx, cx.typing_env) - .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; - match include_end { - rustc_hir::RangeEnd::Included => {} - rustc_hir::RangeEnd::Excluded => end = end.wrapping_sub(1), - } - scalar.valid_range_mut().end = end; + scalar.valid_range_mut().start = extract_const_value(cx, ty, start)? + .try_to_bits(tcx, cx.typing_env) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; + + let mut end = extract_const_value(cx, ty, end)? + .try_to_bits(tcx, cx.typing_env) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; + match include_end { + rustc_hir::RangeEnd::Included => {} + rustc_hir::RangeEnd::Excluded => end = end.wrapping_sub(1), } + scalar.valid_range_mut().end = end; let niche = Niche { offset: Size::ZERO, diff --git a/tests/mir-opt/pattern_types.main.PreCodegen.after.mir b/tests/mir-opt/pattern_types.main.PreCodegen.after.mir index 5ff90de961506..8214ff61d75e9 100644 --- a/tests/mir-opt/pattern_types.main.PreCodegen.after.mir +++ b/tests/mir-opt/pattern_types.main.PreCodegen.after.mir @@ -3,9 +3,9 @@ fn main() -> () { let mut _0: (); scope 1 { - debug x => const 2_u32 is 1..=; + debug x => const 2_u32 is 1..=u32::MAX; scope 2 { - debug y => const {transmute(0x00000000): (u32) is 1..=}; + debug y => const {transmute(0x00000000): (u32) is 1..=u32::MAX}; } } diff --git a/tests/mir-opt/pattern_types.rs b/tests/mir-opt/pattern_types.rs index 0369ccf9a9d56..6d53919934528 100644 --- a/tests/mir-opt/pattern_types.rs +++ b/tests/mir-opt/pattern_types.rs @@ -5,8 +5,8 @@ use std::pat::pattern_type; // EMIT_MIR pattern_types.main.PreCodegen.after.mir fn main() { - // CHECK: debug x => const 2_u32 is 1..= + // CHECK: debug x => const 2_u32 is 1..=u32::MAX let x: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(2) }; - // CHECK: debug y => const {transmute(0x00000000): (u32) is 1..=} + // CHECK: debug y => const {transmute(0x00000000): (u32) is 1..=u32::MAX} let y: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) }; } diff --git a/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr index 118b18b224c05..85ca701a3b64d 100644 --- a/tests/ui/lint/clashing-extern-fn.stderr +++ b/tests/ui/lint/clashing-extern-fn.stderr @@ -17,7 +17,7 @@ LL | fn hidden_niche_unsafe_cell() -> Option`, which is not FFI-safe +warning: `extern` block uses type `Option<(usize) is 0..=usize::MAX>`, which is not FFI-safe --> $DIR/clashing-extern-fn.rs:502:54 | LL | fn pt_non_zero_usize_opt_full_range() -> Option; @@ -276,7 +276,7 @@ LL | fn pt_non_null_ptr() -> pattern_type!(usize is 1..); LL | fn pt_non_null_ptr() -> *const (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration | - = note: expected `unsafe extern "C" fn() -> (usize) is 1..=` + = note: expected `unsafe extern "C" fn() -> (usize) is 1..=usize::MAX` found `unsafe extern "C" fn() -> *const ()` warning: 24 warnings emitted diff --git a/tests/ui/type/pattern_types/nested.stderr b/tests/ui/type/pattern_types/nested.stderr index b753b0a9c9baa..a606fd013c5ea 100644 --- a/tests/ui/type/pattern_types/nested.stderr +++ b/tests/ui/type/pattern_types/nested.stderr @@ -1,4 +1,4 @@ -error: `(u32) is 1..=` is not a valid base type for range patterns +error: `(u32) is 1..=u32::MAX` is not a valid base type for range patterns --> $DIR/nested.rs:10:34 | LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); @@ -10,7 +10,7 @@ note: range patterns only support integers LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); | ^^^ -error: `(i32) is 1..=` is not a valid base type for range patterns +error: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns --> $DIR/nested.rs:15:35 | LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); @@ -22,7 +22,7 @@ note: range patterns only support integers LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); | ^^^^^ -error: `(i32) is 1..=` is not a valid base type for range patterns +error: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns --> $DIR/nested.rs:19:35 | LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); @@ -62,27 +62,27 @@ error[E0308]: mismatched types --> $DIR/nested.rs:10:63 | LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); - | ^ expected `(u32) is 1..=`, found integer + | ^ expected `(u32) is 1..=u32::MAX`, found integer | - = note: expected pattern type `(u32) is 1..=` + = note: expected pattern type `(u32) is 1..=u32::MAX` found type `{integer}` error[E0308]: mismatched types --> $DIR/nested.rs:15:67 | LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); - | ^^ expected `(i32) is 1..=`, found integer + | ^^ expected `(i32) is 1..=i32::MAX`, found integer | - = note: expected pattern type `(i32) is 1..=` + = note: expected pattern type `(i32) is 1..=i32::MAX` found type `{integer}` error[E0308]: mismatched types --> $DIR/nested.rs:19:66 | LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); - | ^ expected `(i32) is 1..=`, found integer + | ^ expected `(i32) is 1..=i32::MAX`, found integer | - = note: expected pattern type `(i32) is 1..=` + = note: expected pattern type `(i32) is 1..=i32::MAX` found type `{integer}` error[E0308]: mismatched types diff --git a/tests/ui/type/pattern_types/range_patterns.stderr b/tests/ui/type/pattern_types/range_patterns.stderr index 690592ba0b8da..43ae7e870f002 100644 --- a/tests/ui/type/pattern_types/range_patterns.stderr +++ b/tests/ui/type/pattern_types/range_patterns.stderr @@ -44,7 +44,7 @@ error: layout_of(NonZero) = Layout { LL | type X = std::num::NonZeroU32; | ^^^^^^ -error: layout_of((u32) is 1..=) = Layout { +error: layout_of((u32) is 1..=u32::MAX) = Layout { size: Size(4 bytes), align: AbiAndPrefAlign { abi: Align(4 bytes), @@ -83,7 +83,7 @@ error: layout_of((u32) is 1..=) = Layout { LL | type Y = pattern_type!(u32 is 1..); | ^^^^^^ -error: layout_of(Option<(u32) is 1..=>) = Layout { +error: layout_of(Option<(u32) is 1..=u32::MAX>) = Layout { size: Size(4 bytes), align: AbiAndPrefAlign { abi: Align(4 bytes), diff --git a/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr b/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr index d5c539b6c52e6..565d8f18cdbb6 100644 --- a/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr +++ b/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr @@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl Eq for Y {} | ^^^^^^^^^^^^- | | - | `(u32) is 1..=` is not defined in the current crate + | `(u32) is 1..=u32::MAX` is not defined in the current crate | = note: impl doesn't have any local type before any uncovered type parameters = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules diff --git a/tests/ui/type/pattern_types/range_patterns_unusable.stderr b/tests/ui/type/pattern_types/range_patterns_unusable.stderr index 8377d417452bb..a9558852eb29e 100644 --- a/tests/ui/type/pattern_types/range_patterns_unusable.stderr +++ b/tests/ui/type/pattern_types/range_patterns_unusable.stderr @@ -4,7 +4,7 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- LL | let _: Option = unsafe { std::mem::transmute(z) }; | ^^^^^^^^^^^^^^^^^^^ | - = note: source type: `Option<(u32) is 1..=>` (32 bits) + = note: source type: `Option<(u32) is 1..=u32::MAX>` (32 bits) = note: target type: `Option` (64 bits) error: aborting due to 1 previous error diff --git a/tests/ui/type/pattern_types/range_patterns_unusable_math.rs b/tests/ui/type/pattern_types/range_patterns_unusable_math.rs index ece4009e1e7d8..54ad6096c577d 100644 --- a/tests/ui/type/pattern_types/range_patterns_unusable_math.rs +++ b/tests/ui/type/pattern_types/range_patterns_unusable_math.rs @@ -11,5 +11,5 @@ type Z = Option; fn main() { let x: Y = unsafe { std::mem::transmute(42_u32) }; - let x = x + 1_u32; //~ ERROR cannot add `u32` to `(u32) is 1..=` + let x = x + 1_u32; //~ ERROR cannot add `u32` to `(u32) is 1..=u32::MAX` } diff --git a/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr b/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr index 373615e3714e5..68ddd63cd5306 100644 --- a/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr +++ b/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr @@ -1,10 +1,10 @@ -error[E0369]: cannot add `u32` to `(u32) is 1..=` +error[E0369]: cannot add `u32` to `(u32) is 1..=u32::MAX` --> $DIR/range_patterns_unusable_math.rs:14:15 | LL | let x = x + 1_u32; | - ^ ----- u32 | | - | (u32) is 1..= + | (u32) is 1..=u32::MAX error: aborting due to 1 previous error From ff7c93ff8ea36ed5f99597ce257374c7cc5dd3e0 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 5 Feb 2025 15:22:10 +0000 Subject: [PATCH 07/15] Remove the `Option` part of range ends in the HIR --- compiler/rustc_ast_lowering/src/lib.rs | 4 +- compiler/rustc_ast_lowering/src/pat.rs | 122 +++++++++- compiler/rustc_hir/src/hir.rs | 2 +- compiler/rustc_hir/src/intravisit.rs | 6 +- compiler/rustc_hir/src/lang_items.rs | 3 + compiler/rustc_hir_analysis/messages.ftl | 3 - compiler/rustc_hir_analysis/src/errors.rs | 2 - .../src/errors/pattern_types.rs | 14 -- .../src/hir_ty_lowering/mod.rs | 90 +------- .../src/variance/constraints.rs | 2 +- compiler/rustc_hir_pretty/src/lib.rs | 15 +- compiler/rustc_lint/src/types.rs | 13 +- compiler/rustc_middle/src/ty/flags.rs | 2 +- compiler/rustc_middle/src/ty/pattern.rs | 7 +- compiler/rustc_middle/src/ty/relate.rs | 15 +- compiler/rustc_middle/src/ty/walk.rs | 2 +- .../rustc_smir/src/rustc_internal/internal.rs | 4 +- .../rustc_smir/src/rustc_smir/convert/ty.rs | 4 +- compiler/rustc_span/src/hygiene.rs | 3 + compiler/rustc_span/src/symbol.rs | 4 + compiler/rustc_symbol_mangling/src/v0.rs | 11 +- .../rustc_trait_selection/src/traits/wf.rs | 2 +- compiler/rustc_ty_utils/src/layout.rs | 9 +- library/core/src/pat.rs | 57 +++++ .../clippy/clippy_utils/src/hir_utils.rs | 11 +- tests/ui/lint/clashing-extern-fn.stderr | 2 +- tests/ui/type/pattern_types/nested.rs | 2 + tests/ui/type/pattern_types/nested.stderr | 214 ++++++++++++------ .../range_patterns_trait_impls2.stderr | 2 +- tests/ui/type/pattern_types/reverse_range.rs | 13 +- .../type/pattern_types/reverse_range.stderr | 16 +- tests/ui/type/pattern_types/validity.rs | 2 +- 32 files changed, 392 insertions(+), 266 deletions(-) delete mode 100644 compiler/rustc_hir_analysis/src/errors/pattern_types.rs diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 1c69937eed07a..135d56c89d48a 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -136,6 +136,7 @@ struct LoweringContext<'a, 'hir> { allow_try_trait: Arc<[Symbol]>, allow_gen_future: Arc<[Symbol]>, + allow_pattern_type: Arc<[Symbol]>, allow_async_iterator: Arc<[Symbol]>, allow_for_await: Arc<[Symbol]>, allow_async_fn_traits: Arc<[Symbol]>, @@ -176,6 +177,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { impl_trait_defs: Vec::new(), impl_trait_bounds: Vec::new(), allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(), + allow_pattern_type: [sym::pattern_types, sym::pattern_type_range_trait].into(), allow_gen_future: if tcx.features().async_fn_track_caller() { [sym::gen_future, sym::closure_track_caller].into() } else { @@ -1357,7 +1359,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } TyKind::Pat(ty, pat) => { - hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat)) + hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat, ty.span)) } TyKind::MacCall(_) => { span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now") diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 2dcfe7c745da5..728981dea5f83 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -3,11 +3,11 @@ use std::sync::Arc; use rustc_ast::ptr::P; use rustc_ast::*; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_hir as hir; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{self as hir, LangItem}; use rustc_middle::span_bug; use rustc_span::source_map::{Spanned, respan}; -use rustc_span::{Ident, Span}; +use rustc_span::{DesugaringKind, Ident, Span, kw}; use super::errors::{ ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding, @@ -430,22 +430,124 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.arena.alloc(hir::PatExpr { hir_id: self.lower_node_id(expr.id), span, kind }) } - pub(crate) fn lower_ty_pat(&mut self, pattern: &TyPat) -> &'hir hir::TyPat<'hir> { - self.arena.alloc(self.lower_ty_pat_mut(pattern)) + pub(crate) fn lower_ty_pat( + &mut self, + pattern: &TyPat, + base_type: Span, + ) -> &'hir hir::TyPat<'hir> { + self.arena.alloc(self.lower_ty_pat_mut(pattern, base_type)) } - fn lower_ty_pat_mut(&mut self, pattern: &TyPat) -> hir::TyPat<'hir> { + fn lower_ty_pat_mut(&mut self, pattern: &TyPat, base_type: Span) -> hir::TyPat<'hir> { // loop here to avoid recursion let pat_hir_id = self.lower_node_id(pattern.id); let node = match &pattern.kind { - TyPatKind::Range(e1, e2, Spanned { node: end, .. }) => hir::TyPatKind::Range( - e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)), - e2.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)), - self.lower_range_end(end, e2.is_some()), + TyPatKind::Range(e1, e2, Spanned { node: end, span }) => hir::TyPatKind::Range( + e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)).unwrap_or_else(|| { + self.lower_ty_pat_range_end( + hir::LangItem::RangeMin, + span.shrink_to_lo(), + base_type, + ) + }), + e2.as_deref() + .map(|e| match end { + RangeEnd::Included(..) => self.lower_anon_const_to_const_arg(e), + RangeEnd::Excluded => self.lower_excluded_range_end(e), + }) + .unwrap_or_else(|| { + self.lower_ty_pat_range_end( + hir::LangItem::RangeMax, + span.shrink_to_hi(), + base_type, + ) + }), ), TyPatKind::Err(guar) => hir::TyPatKind::Err(*guar), }; hir::TyPat { hir_id: pat_hir_id, kind: node, span: self.lower_span(pattern.span) } } + + /// Lowers the range end of an exclusive range (`2..5`) to an inclusive range 2..=(5 - 1). + /// This way the type system doesn't have to handle the distinction between inclusive/exclusive ranges. + fn lower_excluded_range_end(&mut self, e: &AnonConst) -> &'hir hir::ConstArg<'hir> { + let span = self.lower_span(e.value.span); + let unstable_span = self.mark_span_with_reason( + DesugaringKind::PatTyRange, + span, + Some(Arc::clone(&self.allow_pattern_type)), + ); + let anon_const = self.with_new_scopes(span, |this| { + let def_id = this.local_def_id(e.id); + let hir_id = this.lower_node_id(e.id); + let body = this.lower_body(|this| { + // Need to use a custom function as we can't just subtract `1` from a `char`. + let kind = hir::ExprKind::Path(this.make_lang_item_qpath( + hir::LangItem::RangeSub, + unstable_span, + None, + )); + let fn_def = this.arena.alloc(hir::Expr { hir_id: this.next_id(), kind, span }); + let args = this.arena.alloc([this.lower_expr_mut(&e.value)]); + ( + &[], + hir::Expr { + hir_id: this.next_id(), + kind: hir::ExprKind::Call(fn_def, args), + span, + }, + ) + }); + hir::AnonConst { def_id, hir_id, body, span } + }); + self.arena.alloc(hir::ConstArg { + hir_id: self.next_id(), + kind: hir::ConstArgKind::Anon(self.arena.alloc(anon_const)), + }) + } + + /// When a range has no end specified (`1..` or `1..=`) or no start specified (`..5` or `..=5`), + /// we instead use a constant of the MAX/MIN of the type. + /// This way the type system does not have to handle the lack of a start/end. + fn lower_ty_pat_range_end( + &mut self, + lang_item: LangItem, + span: Span, + base_type: Span, + ) -> &'hir hir::ConstArg<'hir> { + let parent_def_id = self.current_hir_id_owner.def_id; + let node_id = self.next_node_id(); + + // Add a definition for the in-band const def. + // We're generating a range end that didn't exist in the AST, + // so the def collector didn't create the def ahead of time. That's why we have to do + // it here. + let def_id = self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, span); + let hir_id = self.lower_node_id(node_id); + + let unstable_span = self.mark_span_with_reason( + DesugaringKind::PatTyRange, + self.lower_span(span), + Some(Arc::clone(&self.allow_pattern_type)), + ); + let span = self.lower_span(base_type); + + let path_expr = hir::Expr { + hir_id: self.next_id(), + kind: hir::ExprKind::Path(self.make_lang_item_qpath(lang_item, unstable_span, None)), + span, + }; + + let ct = self.with_new_scopes(span, |this| { + self.arena.alloc(hir::AnonConst { + def_id, + hir_id, + body: this.lower_body(|_this| (&[], path_expr)), + span, + }) + }); + let hir_id = self.next_id(); + self.arena.alloc(hir::ConstArg { kind: hir::ConstArgKind::Anon(ct), hir_id }) + } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 63b1ae5c88651..ba96fd87973a1 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1668,7 +1668,7 @@ pub enum PatExprKind<'hir> { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum TyPatKind<'hir> { /// A range pattern (e.g., `1..=2` or `1..2`). - Range(Option<&'hir ConstArg<'hir>>, Option<&'hir ConstArg<'hir>>, RangeEnd), + Range(&'hir ConstArg<'hir>, &'hir ConstArg<'hir>), /// A placeholder for a pattern that wasn't well formed in some way. Err(ErrorGuaranteed), diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index d5fa7ec366b28..db08538b90a83 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -708,9 +708,9 @@ pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) -> V::Res pub fn walk_ty_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v TyPat<'v>) -> V::Result { try_visit!(visitor.visit_id(pattern.hir_id)); match pattern.kind { - TyPatKind::Range(lower_bound, upper_bound, _) => { - visit_opt!(visitor, visit_const_arg_unambig, lower_bound); - visit_opt!(visitor, visit_const_arg_unambig, upper_bound); + TyPatKind::Range(lower_bound, upper_bound) => { + try_visit!(visitor.visit_const_arg_unambig(lower_bound)); + try_visit!(visitor.visit_const_arg_unambig(upper_bound)); } TyPatKind::Err(_) => (), } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index f5626937ec456..c3b14a4e06c79 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -418,6 +418,9 @@ language_item_table! { Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None; RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None; RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None; + RangeMax, sym::RangeMax, range_max, Target::AssocConst, GenericRequirement::Exact(0); + RangeMin, sym::RangeMin, range_min, Target::AssocConst, GenericRequirement::Exact(0); + RangeSub, sym::RangeSub, range_sub, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::Exact(0); // `new_range` types that are `Copy + IntoIterator` RangeFromCopy, sym::RangeFromCopy, range_from_copy_struct, Target::Struct, GenericRequirement::None; diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 3f75cce009225..d16aba197fe59 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -244,9 +244,6 @@ hir_analysis_inherent_ty_outside_relevant = cannot define inherent `impl` for a .help = consider moving this inherent impl into the crate defining the type if possible .span_help = alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items -hir_analysis_invalid_base_type = `{$ty}` is not a valid base type for range patterns - .note = range patterns only support integers - hir_analysis_invalid_generic_receiver_ty = invalid generic `self` parameter type: `{$receiver_ty}` .note = type of `self` must not be a method generic parameter type diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 852533ff5c954..f3e1e89812ca7 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -11,8 +11,6 @@ use rustc_middle::ty::Ty; use rustc_span::{Ident, Span, Symbol}; use crate::fluent_generated as fluent; -mod pattern_types; -pub(crate) use pattern_types::*; pub(crate) mod wrong_number_of_generic_args; mod precise_captures; diff --git a/compiler/rustc_hir_analysis/src/errors/pattern_types.rs b/compiler/rustc_hir_analysis/src/errors/pattern_types.rs deleted file mode 100644 index ec7b3aaa1c14c..0000000000000 --- a/compiler/rustc_hir_analysis/src/errors/pattern_types.rs +++ /dev/null @@ -1,14 +0,0 @@ -use rustc_macros::Diagnostic; -use rustc_middle::ty::Ty; -use rustc_span::Span; - -#[derive(Diagnostic)] -#[diag(hir_analysis_invalid_base_type)] -pub(crate) struct InvalidBaseType<'tcx> { - pub ty: Ty<'tcx>, - #[primary_span] - pub ty_span: Span, - pub pat: &'static str, - #[note] - pub pat_span: Span, -} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index efafac19defe0..5daf7f18cfd96 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -21,9 +21,8 @@ pub mod generics; mod lint; use std::assert_matches::assert_matches; -use std::{char, slice}; +use std::slice; -use rustc_abi::Size; use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; @@ -32,7 +31,7 @@ use rustc_errors::{ }; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, AnonConst, ConstArg, GenericArg, GenericArgs, HirId}; +use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability::AllowUnstable; @@ -54,9 +53,7 @@ use rustc_type_ir::Upcast; use tracing::{debug, instrument}; use crate::check::check_abi_fn_ptr; -use crate::errors::{ - AmbiguousLifetimeBound, BadReturnTypeNotation, InvalidBaseType, NoVariantNamed, -}; +use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, NoVariantNamed}; use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; @@ -2453,25 +2450,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let ty_span = ty.span; let ty = self.lower_ty(ty); let pat_ty = match pat.kind { - hir::TyPatKind::Range(start, end, include_end) => { + hir::TyPatKind::Range(start, end) => { let (ty, start, end) = match ty.kind() { + // Keep this list of types in sync with the list of types that + // the `RangePattern` trait is implemented for. ty::Int(_) | ty::Uint(_) | ty::Char => { - let (start, end) = self.lower_ty_pat_range(ty, start, end); + let start = self.lower_const_arg(start, FeedConstTy::No); + let end = self.lower_const_arg(end, FeedConstTy::No); (ty, start, end) } _ => { - let guar = self.dcx().emit_err(InvalidBaseType { - ty, - pat: "range", + let guar = self.dcx().span_delayed_bug( ty_span, - pat_span: pat.span, - }); + "invalid base type for range pattern", + ); let errc = ty::Const::new_error(tcx, guar); (Ty::new_error(tcx, guar), errc, errc) } }; - let pat = tcx.mk_pat(ty::PatternKind::Range { start, end, include_end }); + let pat = tcx.mk_pat(ty::PatternKind::Range { start, end }); Ty::new_pat(tcx, ty, pat) } hir::TyPatKind::Err(e) => Ty::new_error(tcx, e), @@ -2486,70 +2484,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { result_ty } - fn lower_ty_pat_range( - &self, - base: Ty<'tcx>, - start: Option<&ConstArg<'tcx>>, - end: Option<&ConstArg<'tcx>>, - ) -> (ty::Const<'tcx>, ty::Const<'tcx>) { - let tcx = self.tcx(); - let size = match base.kind() { - ty::Int(i) => { - i.bit_width().map_or(tcx.data_layout.pointer_size, |bits| Size::from_bits(bits)) - } - ty::Uint(ui) => { - ui.bit_width().map_or(tcx.data_layout.pointer_size, |bits| Size::from_bits(bits)) - } - ty::Char => Size::from_bytes(4), - _ => unreachable!(), - }; - let start = - start.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)).unwrap_or_else(|| { - match base.kind() { - ty::Char | ty::Uint(_) => ty::Const::new_value( - tcx, - ty::ValTree::from_scalar_int(ty::ScalarInt::null(size)), - base, - ), - ty::Int(_) => ty::Const::new_value( - tcx, - ty::ValTree::from_scalar_int( - ty::ScalarInt::truncate_from_int(size.signed_int_min(), size).0, - ), - base, - ), - _ => unreachable!(), - } - }); - let end = end.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)).unwrap_or_else( - || match base.kind() { - ty::Char => ty::Const::new_value( - tcx, - ty::ValTree::from_scalar_int( - ty::ScalarInt::truncate_from_uint(char::MAX, size).0, - ), - base, - ), - ty::Uint(_) => ty::Const::new_value( - tcx, - ty::ValTree::from_scalar_int( - ty::ScalarInt::truncate_from_uint(size.unsigned_int_max(), size).0, - ), - base, - ), - ty::Int(_) => ty::Const::new_value( - tcx, - ty::ValTree::from_scalar_int( - ty::ScalarInt::truncate_from_int(size.signed_int_max(), size).0, - ), - base, - ), - _ => unreachable!(), - }, - ); - (start, end) - } - /// Lower an opaque type (i.e., an existential impl-Trait type) from the HIR. #[instrument(level = "debug", skip(self), ret)] fn lower_opaque_ty(&self, def_id: LocalDefId, in_trait: bool) -> Ty<'tcx> { diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 4b336769cfb80..8475903c68fde 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -252,7 +252,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { ty::Pat(typ, pat) => { match *pat { - ty::PatternKind::Range { start, end, include_end: _ } => { + ty::PatternKind::Range { start, end } => { self.add_constraints_from_const(current, start, variance); self.add_constraints_from_const(current, end, variance); } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 5c7426d76b31c..bc8e4ffb69820 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1869,17 +1869,10 @@ impl<'a> State<'a> { // Pat isn't normalized, but the beauty of it // is that it doesn't matter match pat.kind { - TyPatKind::Range(begin, end, end_kind) => { - if let Some(expr) = begin { - self.print_const_arg(expr); - } - match end_kind { - RangeEnd::Included => self.word("..."), - RangeEnd::Excluded => self.word(".."), - } - if let Some(expr) = end { - self.print_const_arg(expr); - } + TyPatKind::Range(begin, end) => { + self.print_const_arg(begin); + self.word("..="); + self.print_const_arg(end); } TyPatKind::Err(_) => { self.popen(); diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 73fe8f1057885..14710e5545202 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -5,7 +5,7 @@ use rustc_abi::{BackendRepr, ExternAbi, TagEncoding, VariantIdx, Variants, Wrapp use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; use rustc_hir::intravisit::VisitorExt; -use rustc_hir::{AmbigArg, Expr, ExprKind, HirId, LangItem, RangeEnd}; +use rustc_hir::{AmbigArg, Expr, ExprKind, HirId, LangItem}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton}; use rustc_middle::ty::{ @@ -882,16 +882,13 @@ fn ty_is_known_nonnull<'tcx>( || Option::unwrap_or_default( try { match **pat { - ty::PatternKind::Range { start, end, include_end } => { + ty::PatternKind::Range { start, end } => { let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?; let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?; - match include_end { - // This also works for negative numbers, as we just need - // to ensure we aren't wrapping over zero. - RangeEnd::Included => start > 0 && end >= start, - RangeEnd::Excluded => start > 0 && end > start, - } + // This also works for negative numbers, as we just need + // to ensure we aren't wrapping over zero. + start > 0 && end >= start } } }, diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index c08d78e25b8d1..0b8f0e8cd41d9 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -220,7 +220,7 @@ impl FlagComputation { &ty::Pat(ty, pat) => { self.add_ty(ty); match *pat { - ty::PatternKind::Range { start, end, include_end: _ } => { + ty::PatternKind::Range { start, end } => { self.add_const(start); self.add_const(end); } diff --git a/compiler/rustc_middle/src/ty/pattern.rs b/compiler/rustc_middle/src/ty/pattern.rs index a1dfd2e8d34f0..89dadb36ad649 100644 --- a/compiler/rustc_middle/src/ty/pattern.rs +++ b/compiler/rustc_middle/src/ty/pattern.rs @@ -1,7 +1,6 @@ use std::fmt; use rustc_data_structures::intern::Interned; -use rustc_hir::RangeEnd; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use crate::ty; @@ -27,8 +26,8 @@ impl<'tcx> fmt::Debug for Pattern<'tcx> { impl<'tcx> fmt::Debug for PatternKind<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - PatternKind::Range { start, end, include_end } => { - write!(f, "{start}{include_end}{end}") + PatternKind::Range { start, end } => { + write!(f, "{start}..={end}") } } } @@ -37,5 +36,5 @@ impl<'tcx> fmt::Debug for PatternKind<'tcx> { #[derive(Clone, PartialEq, Eq, Hash)] #[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)] pub enum PatternKind<'tcx> { - Range { start: ty::Const<'tcx>, end: ty::Const<'tcx>, include_end: RangeEnd }, + Range { start: ty::Const<'tcx>, end: ty::Const<'tcx> }, } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 6d5cc42065f22..b1dfcb80bde57 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -51,21 +51,12 @@ impl<'tcx> Relate> for ty::Pattern<'tcx> { ) -> RelateResult<'tcx, Self> { match (&*a, &*b) { ( - &ty::PatternKind::Range { start: start_a, end: end_a, include_end: inc_a }, - &ty::PatternKind::Range { start: start_b, end: end_b, include_end: inc_b }, + &ty::PatternKind::Range { start: start_a, end: end_a }, + &ty::PatternKind::Range { start: start_b, end: end_b }, ) => { let start = relation.relate(start_a, start_b)?; - // FIXME(pattern_types): make equal patterns equal (`0..5` is the same as `0..=6`). let end = relation.relate(end_a, end_b)?; - if inc_a == inc_b { - Ok(relation.cx().mk_pat(ty::PatternKind::Range { - start, - end, - include_end: inc_a, - })) - } else { - Err(TypeError::Mismatch) - } + Ok(relation.cx().mk_pat(ty::PatternKind::Range { start, end })) } } } diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index b31b7cac41ef2..a23316ae6fc88 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -137,7 +137,7 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) ty::Pat(ty, pat) => { match *pat { - ty::PatternKind::Range { start, end, include_end: _ } => { + ty::PatternKind::Range { start, end } => { stack.push(end.into()); stack.push(start.into()); } diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index a8c4cd660fd98..bb2b2dea2f37f 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -5,7 +5,6 @@ // Prefer importing stable_mir over internal rustc constructs to make this file more readable. -use rustc_hir::RangeEnd; use rustc_middle::ty::{self as rustc_ty, Const as InternalConst, Ty as InternalTy, TyCtxt}; use rustc_span::Symbol; use stable_mir::abi::Layout; @@ -89,10 +88,9 @@ impl RustcInternal for Pattern { type T<'tcx> = rustc_ty::Pattern<'tcx>; fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { tcx.mk_pat(match self { - Pattern::Range { start, end, include_end } => rustc_ty::PatternKind::Range { + Pattern::Range { start, end, include_end: _ } => rustc_ty::PatternKind::Range { start: start.as_ref().unwrap().internal(tables, tcx), end: end.as_ref().unwrap().internal(tables, tcx), - include_end: if *include_end { RangeEnd::Included } else { RangeEnd::Excluded }, }, }) } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index f6a6a3c1737fb..aa0eac628dd0f 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -405,11 +405,11 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> { fn stable(&self, tables: &mut Tables<'_>) -> Self::T { match **self { - ty::PatternKind::Range { start, end, include_end } => stable_mir::ty::Pattern::Range { + ty::PatternKind::Range { start, end } => stable_mir::ty::Pattern::Range { // FIXME(SMIR): update data structures to not have an Option here anymore start: Some(start.stable(tables)), end: Some(end.stable(tables)), - include_end: matches!(include_end, rustc_hir::RangeEnd::Included), + include_end: true, }, } } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 84e89ff4b7dab..64982b858c644 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1173,6 +1173,8 @@ pub enum DesugaringKind { BoundModifier, /// Calls to contract checks (`#[requires]` to precond, `#[ensures]` to postcond) Contract, + /// A pattern type range start/end + PatTyRange, } impl DesugaringKind { @@ -1190,6 +1192,7 @@ impl DesugaringKind { DesugaringKind::WhileLoop => "`while` loop", DesugaringKind::BoundModifier => "trait bound modifier", DesugaringKind::Contract => "contract check", + DesugaringKind::PatTyRange => "pattern type", } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 501c9039f2de9..43ebd41571772 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -308,6 +308,9 @@ symbols! { RangeFull, RangeInclusive, RangeInclusiveCopy, + RangeMax, + RangeMin, + RangeSub, RangeTo, RangeToInclusive, Rc, @@ -1521,6 +1524,7 @@ symbols! { pattern_complexity_limit, pattern_parentheses, pattern_type, + pattern_type_range_trait, pattern_types, permissions_from_mode, phantom_data, diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 754c74c4f009f..7756a4f1c3a15 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -413,15 +413,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { } ty::Pat(ty, pat) => match *pat { - ty::PatternKind::Range { start, end, include_end } => { - let consts = [ - start, - end, - ty::Const::from_bool( - self.tcx, - matches!(include_end, rustc_hir::RangeEnd::Included), - ), - ]; + ty::PatternKind::Range { start, end } => { + let consts = [start, end]; // HACK: Represent as tuple until we have something better. // HACK: constants are used in arrays, even if the types don't match. self.push("T"); diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 0fece96a3eb41..54b6c22b2d821 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -708,7 +708,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { ty::Pat(subty, pat) => { self.require_sized(subty, ObligationCauseCode::Misc); match *pat { - ty::PatternKind::Range { start, end, include_end: _ } => { + ty::PatternKind::Range { start, end } => { let mut check = |c| { let cause = self.cause(ObligationCauseCode::Misc); self.out.push(traits::Obligation::with_depth( diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 781b86ae92362..55af8ceeab920 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -207,7 +207,7 @@ fn layout_of_uncached<'tcx>( let layout = cx.layout_of(ty)?.layout; let mut layout = LayoutData::clone(&layout.0); match *pat { - ty::PatternKind::Range { start, end, include_end } => { + ty::PatternKind::Range { start, end } => { if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) = &mut layout.backend_repr { @@ -215,14 +215,9 @@ fn layout_of_uncached<'tcx>( .try_to_bits(tcx, cx.typing_env) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; - let mut end = extract_const_value(cx, ty, end)? + scalar.valid_range_mut().end = extract_const_value(cx, ty, end)? .try_to_bits(tcx, cx.typing_env) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; - match include_end { - rustc_hir::RangeEnd::Included => {} - rustc_hir::RangeEnd::Excluded => end = end.wrapping_sub(1), - } - scalar.valid_range_mut().end = end; let niche = Niche { offset: Size::ZERO, diff --git a/library/core/src/pat.rs b/library/core/src/pat.rs index 752e79c2dacee..5122d55f5163a 100644 --- a/library/core/src/pat.rs +++ b/library/core/src/pat.rs @@ -12,3 +12,60 @@ macro_rules! pattern_type { /* compiler built-in */ }; } + +/// A trait implemented for integer types and `char`. +/// Useful in the future for generic pattern types, but +/// used right now to simplify ast lowering of pattern type ranges. +#[unstable(feature = "pattern_type_range_trait", issue = "123646")] +#[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")] +#[const_trait] +#[diagnostic::on_unimplemented( + message = "`{Self}` is not a valid base type for range patterns", + label = "only integer types and `char` are supported" +)] +pub trait RangePattern { + /// Trait version of the inherent `MIN` assoc const. + #[cfg_attr(not(bootstrap), lang = "RangeMin")] + const MIN: Self; + + /// Trait version of the inherent `MIN` assoc const. + #[cfg_attr(not(bootstrap), lang = "RangeMax")] + const MAX: Self; + + /// A compile-time helper to subtract 1 for exclusive ranges. + #[cfg_attr(not(bootstrap), lang = "RangeSub")] + #[track_caller] + fn sub_one(self) -> Self; +} + +macro_rules! impl_range_pat { + ($($ty:ty,)*) => { + $( + impl const RangePattern for $ty { + const MIN: $ty = <$ty>::MIN; + const MAX: $ty = <$ty>::MAX; + fn sub_one(self) -> Self { + self - 1 + } + } + )* + } +} + +impl_range_pat! { + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize, +} + +impl const RangePattern for char { + const MIN: Self = char::MIN; + + const MAX: Self = char::MAX; + + fn sub_one(self) -> Self { + match char::from_u32(self as u32 - 1) { + None => panic!("exclusive range to start of valid chars"), + Some(val) => val, + } + } +} diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 0ac675345ae0c..cde201196c57a 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1108,14 +1108,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_ty_pat(&mut self, pat: &TyPat<'_>) { std::mem::discriminant(&pat.kind).hash(&mut self.s); match pat.kind { - TyPatKind::Range(s, e, i) => { - if let Some(s) = s { - self.hash_const_arg(s); - } - if let Some(e) = e { - self.hash_const_arg(e); - } - std::mem::discriminant(&i).hash(&mut self.s); + TyPatKind::Range(s, e) => { + self.hash_const_arg(s); + self.hash_const_arg(e); }, TyPatKind::Err(_) => {}, } diff --git a/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr index 85ca701a3b64d..4e9f6ea3f78c7 100644 --- a/tests/ui/lint/clashing-extern-fn.stderr +++ b/tests/ui/lint/clashing-extern-fn.stderr @@ -276,7 +276,7 @@ LL | fn pt_non_null_ptr() -> pattern_type!(usize is 1..); LL | fn pt_non_null_ptr() -> *const (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration | - = note: expected `unsafe extern "C" fn() -> (usize) is 1..=usize::MAX` + = note: expected `unsafe extern "C" fn() -> (usize) is 1..=` found `unsafe extern "C" fn() -> *const ()` warning: 24 warnings emitted diff --git a/tests/ui/type/pattern_types/nested.rs b/tests/ui/type/pattern_types/nested.rs index 9ca9c7923dedd..0d8cd22190e55 100644 --- a/tests/ui/type/pattern_types/nested.rs +++ b/tests/ui/type/pattern_types/nested.rs @@ -18,10 +18,12 @@ const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); //~^ ERROR: not a valid base type for range patterns +//~| ERROR: not a valid base type for range patterns //~| ERROR: mismatched types const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); //~^ ERROR: not a valid base type for range patterns +//~| ERROR: not a valid base type for range patterns //~| ERROR: mismatched types const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!(); diff --git a/tests/ui/type/pattern_types/nested.stderr b/tests/ui/type/pattern_types/nested.stderr index a606fd013c5ea..04aacabc136e7 100644 --- a/tests/ui/type/pattern_types/nested.stderr +++ b/tests/ui/type/pattern_types/nested.stderr @@ -1,96 +1,184 @@ -error: `(u32) is 1..=u32::MAX` is not a valid base type for range patterns - --> $DIR/nested.rs:10:34 +error[E0308]: mismatched types + --> $DIR/nested.rs:10:63 | LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ expected `(u32) is 1..=u32::MAX`, found integer | -note: range patterns only support integers - --> $DIR/nested.rs:10:63 + = note: expected pattern type `(u32) is 1..=u32::MAX` + found type `{integer}` + +error[E0277]: `(u32) is 1..=u32::MAX` is not a valid base type for range patterns + --> $DIR/nested.rs:10:34 | LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); - | ^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported + | + = help: the trait `core::pat::RangePattern` is not implemented for `(u32) is 1..=u32::MAX` + = help: the following other types implement trait `core::pat::RangePattern`: + char + i128 + i16 + i32 + i64 + i8 + isize + u128 + and 5 others -error: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns +error[E0277]: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns --> $DIR/nested.rs:15:35 | LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: range patterns only support integers - --> $DIR/nested.rs:15:64 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported + | + = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..=i32::MAX` + = help: the following other types implement trait `core::pat::RangePattern`: + char + i128 + i16 + i32 + i64 + i8 + isize + u128 + and 5 others + +error[E0308]: mismatched types + --> $DIR/nested.rs:15:67 | LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); - | ^^^^^ + | ^^ expected `(i32) is 1..=i32::MAX`, found integer + | + = note: expected pattern type `(i32) is 1..=i32::MAX` + found type `{integer}` -error: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns +error[E0277]: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns --> $DIR/nested.rs:19:35 | LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: range patterns only support integers - --> $DIR/nested.rs:19:64 - | -LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); - | ^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported + | + = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..=i32::MAX` + = help: the following other types implement trait `core::pat::RangePattern`: + char + i128 + i16 + i32 + i64 + i8 + isize + u128 + and 5 others -error: `()` is not a valid base type for range patterns - --> $DIR/nested.rs:23:35 +error[E0308]: mismatched types + --> $DIR/nested.rs:19:66 | -LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); - | ^^ +LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); + | ^ + | | + | expected `(i32) is 1..=i32::MAX`, found integer + | arguments to this function are incorrect | -note: range patterns only support integers - --> $DIR/nested.rs:23:41 + = note: expected pattern type `(i32) is 1..=i32::MAX` + found type `{integer}` +help: the return type of this call is `{integer}` due to the type of the argument passed + --> $DIR/nested.rs:19:66 | -LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); - | ^^^ +LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); + | ^ this argument influences the return type of `RangeSub` +note: method defined here + --> $SRC_DIR/core/src/pat.rs:LL:COL -error: `f32` is not a valid base type for range patterns - --> $DIR/nested.rs:27:35 - | -LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!(); - | ^^^ - | -note: range patterns only support integers - --> $DIR/nested.rs:27:42 +error[E0277]: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns + --> $DIR/nested.rs:19:66 | -LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!(); - | ^^^^^^^^^^ +LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); + | ^ only integer types and `char` are supported + | + = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..=i32::MAX` + = help: the following other types implement trait `core::pat::RangePattern`: + char + i128 + i16 + i32 + i64 + i8 + isize + u128 + and 5 others -error[E0308]: mismatched types - --> $DIR/nested.rs:10:63 +error[E0277]: `()` is not a valid base type for range patterns + --> $DIR/nested.rs:24:35 | -LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); - | ^ expected `(u32) is 1..=u32::MAX`, found integer - | - = note: expected pattern type `(u32) is 1..=u32::MAX` - found type `{integer}` +LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); + | ^^ only integer types and `char` are supported + | + = help: the trait `core::pat::RangePattern` is not implemented for `()` + = help: the following other types implement trait `core::pat::RangePattern`: + char + i128 + i16 + i32 + i64 + i8 + isize + u128 + and 5 others error[E0308]: mismatched types - --> $DIR/nested.rs:15:67 - | -LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); - | ^^ expected `(i32) is 1..=i32::MAX`, found integer + --> $DIR/nested.rs:24:43 | - = note: expected pattern type `(i32) is 1..=i32::MAX` - found type `{integer}` - -error[E0308]: mismatched types - --> $DIR/nested.rs:19:66 +LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); + | ^ + | | + | expected `()`, found integer + | arguments to this function are incorrect | -LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); - | ^ expected `(i32) is 1..=i32::MAX`, found integer +help: the return type of this call is `{integer}` due to the type of the argument passed + --> $DIR/nested.rs:24:43 | - = note: expected pattern type `(i32) is 1..=i32::MAX` - found type `{integer}` +LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); + | ^ this argument influences the return type of `RangeSub` +note: method defined here + --> $SRC_DIR/core/src/pat.rs:LL:COL -error[E0308]: mismatched types - --> $DIR/nested.rs:23:43 +error[E0277]: `()` is not a valid base type for range patterns + --> $DIR/nested.rs:24:43 | LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); - | ^ expected `()`, found integer + | ^ only integer types and `char` are supported + | + = help: the trait `core::pat::RangePattern` is not implemented for `()` + = help: the following other types implement trait `core::pat::RangePattern`: + char + i128 + i16 + i32 + i64 + i8 + isize + u128 + and 5 others + +error[E0277]: `f32` is not a valid base type for range patterns + --> $DIR/nested.rs:29:49 + | +LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!(); + | ^^^ only integer types and `char` are supported + | + = help: the trait `core::pat::RangePattern` is not implemented for `f32` + = help: the following other types implement trait `core::pat::RangePattern`: + i128 + i16 + i32 + i64 + i8 + isize + u128 + u16 + and 4 others -error: aborting due to 9 previous errors +error: aborting due to 11 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr b/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr index 565d8f18cdbb6..d5c539b6c52e6 100644 --- a/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr +++ b/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr @@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl Eq for Y {} | ^^^^^^^^^^^^- | | - | `(u32) is 1..=u32::MAX` is not defined in the current crate + | `(u32) is 1..=` is not defined in the current crate | = note: impl doesn't have any local type before any uncovered type parameters = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules diff --git a/tests/ui/type/pattern_types/reverse_range.rs b/tests/ui/type/pattern_types/reverse_range.rs index 6a245615f1ac0..4471624d66440 100644 --- a/tests/ui/type/pattern_types/reverse_range.rs +++ b/tests/ui/type/pattern_types/reverse_range.rs @@ -1,14 +1,11 @@ //! Check that the range start must be smaller than the range end -//@ known-bug: unknown -//@ failure-status: 101 -//@ normalize-stderr: "note: .*\n\n" -> "" -//@ normalize-stderr: "thread 'rustc' panicked.*\n" -> "" -//@ normalize-stderr: "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " -//@ rustc-env:RUST_BACKTRACE=0 - -#![feature(pattern_types)] +#![feature(pattern_types, const_trait_impl, pattern_type_range_trait)] #![feature(pattern_type_macro)] use std::pat::pattern_type; const NONE: pattern_type!(u8 is 1..0) = unsafe { std::mem::transmute(3_u8) }; +//~^ NOTE: attempt to compute `0_u8 - 1_u8`, which would overflow +//~| ERROR: evaluation of constant value failed + +fn main() {} diff --git a/tests/ui/type/pattern_types/reverse_range.stderr b/tests/ui/type/pattern_types/reverse_range.stderr index b714ca7d9ab96..8bdd40297870a 100644 --- a/tests/ui/type/pattern_types/reverse_range.stderr +++ b/tests/ui/type/pattern_types/reverse_range.stderr @@ -1,17 +1,9 @@ -error[E0601]: `main` function not found in crate `reverse_range` - --> $DIR/reverse_range.rs:14:78 +error[E0080]: evaluation of constant value failed + --> $DIR/reverse_range.rs:7:36 | LL | const NONE: pattern_type!(u8 is 1..0) = unsafe { std::mem::transmute(3_u8) }; - | ^ consider adding a `main` function to `$DIR/reverse_range.rs` + | ^ attempt to compute `0_u8 - 1_u8`, which would overflow - -assertion failed: end <= max_value -error: the compiler unexpectedly panicked. this is a bug. - -query stack during panic: -#0 [eval_to_allocation_raw] const-evaluating + checking `NONE` -#1 [eval_to_const_value_raw] simplifying constant for the type system `NONE` -... and 1 other queries... use `env RUST_BACKTRACE=1` to see the full query stack error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0601`. +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/type/pattern_types/validity.rs b/tests/ui/type/pattern_types/validity.rs index 77a4e72f6755a..5a6a688e1b305 100644 --- a/tests/ui/type/pattern_types/validity.rs +++ b/tests/ui/type/pattern_types/validity.rs @@ -1,6 +1,6 @@ //! Check that pattern types have their validity checked -#![feature(pattern_types)] +#![feature(pattern_types, const_trait_impl, pattern_type_range_trait)] #![feature(pattern_type_macro)] use std::pat::pattern_type; From 73b7e5f9cb5a68a6cfa6e899b839e59a1df5a369 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 5 Feb 2025 16:21:54 +0000 Subject: [PATCH 08/15] Hide the end of ranges in pretty printing if it's also the maximum of the type --- compiler/rustc_middle/src/ty/pattern.rs | 24 +++++++++++++++- tests/codegen/pattern_type_symbols.rs | 4 +-- .../pattern_types.main.PreCodegen.after.mir | 4 +-- tests/mir-opt/pattern_types.rs | 4 +-- tests/ui/lint/clashing-extern-fn.stderr | 2 +- tests/ui/type/pattern_types/nested.stderr | 28 +++++++++---------- .../type/pattern_types/range_patterns.stderr | 4 +-- .../range_patterns_unusable.stderr | 2 +- .../range_patterns_unusable_math.rs | 2 +- .../range_patterns_unusable_math.stderr | 4 +-- 10 files changed, 50 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_middle/src/ty/pattern.rs b/compiler/rustc_middle/src/ty/pattern.rs index 89dadb36ad649..4cad1ab209916 100644 --- a/compiler/rustc_middle/src/ty/pattern.rs +++ b/compiler/rustc_middle/src/ty/pattern.rs @@ -27,7 +27,29 @@ impl<'tcx> fmt::Debug for PatternKind<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { PatternKind::Range { start, end } => { - write!(f, "{start}..={end}") + write!(f, "{start}")?; + + if let Some(c) = end.try_to_value() { + let end = c.valtree.unwrap_leaf(); + let size = end.size(); + let max = match c.ty.kind() { + ty::Int(_) => { + Some(ty::ScalarInt::truncate_from_int(size.signed_int_max(), size)) + } + ty::Uint(_) => { + Some(ty::ScalarInt::truncate_from_uint(size.unsigned_int_max(), size)) + } + ty::Char => Some(ty::ScalarInt::truncate_from_uint(char::MAX, size)), + _ => None, + }; + if let Some((max, _)) = max + && end == max + { + return write!(f, ".."); + } + } + + write!(f, "..={end}") } } } diff --git a/tests/codegen/pattern_type_symbols.rs b/tests/codegen/pattern_type_symbols.rs index b504a3508f94c..e86a9ef27de1b 100644 --- a/tests/codegen/pattern_type_symbols.rs +++ b/tests/codegen/pattern_type_symbols.rs @@ -16,7 +16,7 @@ pub fn bar() { // CHECK: call pattern_type_symbols::foo:: // CHECK: call void @_RINvC[[CRATE_IDENT:[a-zA-Z0-9]{12}]]_20pattern_type_symbols3foomEB2_ foo::(); - // CHECK: call pattern_type_symbols::foo::<(u32, [(); 0], [(); 999999999], [(); true])> - // CHECK: call void @_RINvC[[CRATE_IDENT]]_20pattern_type_symbols3fooTmAum0_Aum3b9ac9ff_Aub1_EEB2_ + // CHECK: call pattern_type_symbols::foo::<(u32, [(); 0], [(); 999999999])> + // CHECK: call void @_RINvC[[CRATE_IDENT]]_20pattern_type_symbols3fooTmAum0_Aum3b9ac9ff_EEB2_ foo::(); } diff --git a/tests/mir-opt/pattern_types.main.PreCodegen.after.mir b/tests/mir-opt/pattern_types.main.PreCodegen.after.mir index 8214ff61d75e9..cc01f51973c84 100644 --- a/tests/mir-opt/pattern_types.main.PreCodegen.after.mir +++ b/tests/mir-opt/pattern_types.main.PreCodegen.after.mir @@ -3,9 +3,9 @@ fn main() -> () { let mut _0: (); scope 1 { - debug x => const 2_u32 is 1..=u32::MAX; + debug x => const 2_u32 is 1..; scope 2 { - debug y => const {transmute(0x00000000): (u32) is 1..=u32::MAX}; + debug y => const {transmute(0x00000000): (u32) is 1..}; } } diff --git a/tests/mir-opt/pattern_types.rs b/tests/mir-opt/pattern_types.rs index 6d53919934528..d5847b95f7328 100644 --- a/tests/mir-opt/pattern_types.rs +++ b/tests/mir-opt/pattern_types.rs @@ -5,8 +5,8 @@ use std::pat::pattern_type; // EMIT_MIR pattern_types.main.PreCodegen.after.mir fn main() { - // CHECK: debug x => const 2_u32 is 1..=u32::MAX + // CHECK: debug x => const 2_u32 is 1.. let x: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(2) }; - // CHECK: debug y => const {transmute(0x00000000): (u32) is 1..=u32::MAX} + // CHECK: debug y => const {transmute(0x00000000): (u32) is 1..} let y: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) }; } diff --git a/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr index 4e9f6ea3f78c7..0c27547a6ed8f 100644 --- a/tests/ui/lint/clashing-extern-fn.stderr +++ b/tests/ui/lint/clashing-extern-fn.stderr @@ -17,7 +17,7 @@ LL | fn hidden_niche_unsafe_cell() -> Option`, which is not FFI-safe +warning: `extern` block uses type `Option<(usize) is 0..>`, which is not FFI-safe --> $DIR/clashing-extern-fn.rs:502:54 | LL | fn pt_non_zero_usize_opt_full_range() -> Option; diff --git a/tests/ui/type/pattern_types/nested.stderr b/tests/ui/type/pattern_types/nested.stderr index 04aacabc136e7..f79d12bc3f376 100644 --- a/tests/ui/type/pattern_types/nested.stderr +++ b/tests/ui/type/pattern_types/nested.stderr @@ -2,18 +2,18 @@ error[E0308]: mismatched types --> $DIR/nested.rs:10:63 | LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); - | ^ expected `(u32) is 1..=u32::MAX`, found integer + | ^ expected `(u32) is 1..`, found integer | - = note: expected pattern type `(u32) is 1..=u32::MAX` + = note: expected pattern type `(u32) is 1..` found type `{integer}` -error[E0277]: `(u32) is 1..=u32::MAX` is not a valid base type for range patterns +error[E0277]: `(u32) is 1..` is not a valid base type for range patterns --> $DIR/nested.rs:10:34 | LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported | - = help: the trait `core::pat::RangePattern` is not implemented for `(u32) is 1..=u32::MAX` + = help: the trait `core::pat::RangePattern` is not implemented for `(u32) is 1..` = help: the following other types implement trait `core::pat::RangePattern`: char i128 @@ -25,13 +25,13 @@ LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!( u128 and 5 others -error[E0277]: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns +error[E0277]: `(i32) is 1..` is not a valid base type for range patterns --> $DIR/nested.rs:15:35 | LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported | - = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..=i32::MAX` + = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..` = help: the following other types implement trait `core::pat::RangePattern`: char i128 @@ -47,18 +47,18 @@ error[E0308]: mismatched types --> $DIR/nested.rs:15:67 | LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); - | ^^ expected `(i32) is 1..=i32::MAX`, found integer + | ^^ expected `(i32) is 1..`, found integer | - = note: expected pattern type `(i32) is 1..=i32::MAX` + = note: expected pattern type `(i32) is 1..` found type `{integer}` -error[E0277]: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns +error[E0277]: `(i32) is 1..` is not a valid base type for range patterns --> $DIR/nested.rs:19:35 | LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported | - = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..=i32::MAX` + = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..` = help: the following other types implement trait `core::pat::RangePattern`: char i128 @@ -76,10 +76,10 @@ error[E0308]: mismatched types LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); | ^ | | - | expected `(i32) is 1..=i32::MAX`, found integer + | expected `(i32) is 1..`, found integer | arguments to this function are incorrect | - = note: expected pattern type `(i32) is 1..=i32::MAX` + = note: expected pattern type `(i32) is 1..` found type `{integer}` help: the return type of this call is `{integer}` due to the type of the argument passed --> $DIR/nested.rs:19:66 @@ -89,13 +89,13 @@ LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo! note: method defined here --> $SRC_DIR/core/src/pat.rs:LL:COL -error[E0277]: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns +error[E0277]: `(i32) is 1..` is not a valid base type for range patterns --> $DIR/nested.rs:19:66 | LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); | ^ only integer types and `char` are supported | - = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..=i32::MAX` + = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..` = help: the following other types implement trait `core::pat::RangePattern`: char i128 diff --git a/tests/ui/type/pattern_types/range_patterns.stderr b/tests/ui/type/pattern_types/range_patterns.stderr index 43ae7e870f002..512181c22d535 100644 --- a/tests/ui/type/pattern_types/range_patterns.stderr +++ b/tests/ui/type/pattern_types/range_patterns.stderr @@ -44,7 +44,7 @@ error: layout_of(NonZero) = Layout { LL | type X = std::num::NonZeroU32; | ^^^^^^ -error: layout_of((u32) is 1..=u32::MAX) = Layout { +error: layout_of((u32) is 1..) = Layout { size: Size(4 bytes), align: AbiAndPrefAlign { abi: Align(4 bytes), @@ -83,7 +83,7 @@ error: layout_of((u32) is 1..=u32::MAX) = Layout { LL | type Y = pattern_type!(u32 is 1..); | ^^^^^^ -error: layout_of(Option<(u32) is 1..=u32::MAX>) = Layout { +error: layout_of(Option<(u32) is 1..>) = Layout { size: Size(4 bytes), align: AbiAndPrefAlign { abi: Align(4 bytes), diff --git a/tests/ui/type/pattern_types/range_patterns_unusable.stderr b/tests/ui/type/pattern_types/range_patterns_unusable.stderr index a9558852eb29e..7daa41d708179 100644 --- a/tests/ui/type/pattern_types/range_patterns_unusable.stderr +++ b/tests/ui/type/pattern_types/range_patterns_unusable.stderr @@ -4,7 +4,7 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- LL | let _: Option = unsafe { std::mem::transmute(z) }; | ^^^^^^^^^^^^^^^^^^^ | - = note: source type: `Option<(u32) is 1..=u32::MAX>` (32 bits) + = note: source type: `Option<(u32) is 1..>` (32 bits) = note: target type: `Option` (64 bits) error: aborting due to 1 previous error diff --git a/tests/ui/type/pattern_types/range_patterns_unusable_math.rs b/tests/ui/type/pattern_types/range_patterns_unusable_math.rs index 54ad6096c577d..6125063699be9 100644 --- a/tests/ui/type/pattern_types/range_patterns_unusable_math.rs +++ b/tests/ui/type/pattern_types/range_patterns_unusable_math.rs @@ -11,5 +11,5 @@ type Z = Option; fn main() { let x: Y = unsafe { std::mem::transmute(42_u32) }; - let x = x + 1_u32; //~ ERROR cannot add `u32` to `(u32) is 1..=u32::MAX` + let x = x + 1_u32; //~ ERROR cannot add `u32` to `(u32) is 1..` } diff --git a/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr b/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr index 68ddd63cd5306..a64f1db31766f 100644 --- a/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr +++ b/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr @@ -1,10 +1,10 @@ -error[E0369]: cannot add `u32` to `(u32) is 1..=u32::MAX` +error[E0369]: cannot add `u32` to `(u32) is 1..` --> $DIR/range_patterns_unusable_math.rs:14:15 | LL | let x = x + 1_u32; | - ^ ----- u32 | | - | (u32) is 1..=u32::MAX + | (u32) is 1.. error: aborting due to 1 previous error From 3477297c374f2b12a5846b8b522b1e69ea08cc5e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 24 Jan 2025 15:40:41 +0000 Subject: [PATCH 09/15] Fix UB in ThinVec::flat_map_in_place thin_vec.as_ptr() goes through the Deref impl of ThinVec, which will not allow access to any memory as we did call set_len(0) first. --- .../src/flat_map_in_place.rs | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_data_structures/src/flat_map_in_place.rs b/compiler/rustc_data_structures/src/flat_map_in_place.rs index e66b00b755760..6d718059f9c81 100644 --- a/compiler/rustc_data_structures/src/flat_map_in_place.rs +++ b/compiler/rustc_data_structures/src/flat_map_in_place.rs @@ -1,4 +1,4 @@ -use std::ptr; +use std::{mem, ptr}; use smallvec::{Array, SmallVec}; use thin_vec::ThinVec; @@ -13,39 +13,44 @@ pub trait FlatMapInPlace: Sized { // The implementation of this method is syntactically identical for all the // different vector types. macro_rules! flat_map_in_place { - () => { + ($vec:ident $( where T: $bound:path)?) => { fn flat_map_in_place(&mut self, mut f: F) where F: FnMut(T) -> I, I: IntoIterator, { + struct LeakGuard<'a, T $(: $bound)?>(&'a mut $vec); + + impl<'a, T $(: $bound)?> Drop for LeakGuard<'a, T> { + fn drop(&mut self) { + unsafe { + self.0.set_len(0); // make sure we just leak elements in case of panic + } + } + } + + let this = LeakGuard(self); + let mut read_i = 0; let mut write_i = 0; unsafe { - let mut old_len = self.len(); - self.set_len(0); // make sure we just leak elements in case of panic - - while read_i < old_len { + while read_i < this.0.len() { // move the read_i'th item out of the vector and map it // to an iterator - let e = ptr::read(self.as_ptr().add(read_i)); + let e = ptr::read(this.0.as_ptr().add(read_i)); let iter = f(e).into_iter(); read_i += 1; for e in iter { if write_i < read_i { - ptr::write(self.as_mut_ptr().add(write_i), e); + ptr::write(this.0.as_mut_ptr().add(write_i), e); write_i += 1; } else { // If this is reached we ran out of space // in the middle of the vector. // However, the vector is in a valid state here, // so we just do a somewhat inefficient insert. - self.set_len(old_len); - self.insert(write_i, e); - - old_len = self.len(); - self.set_len(0); + this.0.insert(write_i, e); read_i += 1; write_i += 1; @@ -54,20 +59,23 @@ macro_rules! flat_map_in_place { } // write_i tracks the number of actually written new items. - self.set_len(write_i); + this.0.set_len(write_i); + + // The ThinVec is in a sane state again. Prevent the LeakGuard from leaking the data. + mem::forget(this); } } }; } impl FlatMapInPlace for Vec { - flat_map_in_place!(); + flat_map_in_place!(Vec); } impl> FlatMapInPlace for SmallVec { - flat_map_in_place!(); + flat_map_in_place!(SmallVec where T: Array); } impl FlatMapInPlace for ThinVec { - flat_map_in_place!(); + flat_map_in_place!(ThinVec); } From 864cca80b02e9daa2c41440f1bf9787083c9f149 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 23 Feb 2025 03:34:33 +0000 Subject: [PATCH 10/15] Print out destructor --- Cargo.lock | 1 + compiler/rustc_lint/Cargo.toml | 1 + compiler/rustc_lint/messages.ftl | 5 + compiler/rustc_lint/src/if_let_rescope.rs | 77 ++++++-- compiler/rustc_middle/src/ty/mod.rs | 1 + .../src/ty/significant_drop_order.rs | 172 +++++++++++++++++ .../src/lint_tail_expr_drop_order.rs | 174 +----------------- .../drop/drop-order-comparisons.e2021.stderr | 90 +++++++++ ...nt-if-let-rescope-gated.edition2021.stderr | 9 + .../lint-if-let-rescope-with-macro.stderr | 9 + tests/ui/drop/lint-if-let-rescope.stderr | 72 ++++++++ 11 files changed, 425 insertions(+), 186 deletions(-) create mode 100644 compiler/rustc_middle/src/ty/significant_drop_order.rs diff --git a/Cargo.lock b/Cargo.lock index fb4cf235c6f49..2227f5f3e9934 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3916,6 +3916,7 @@ dependencies = [ "rustc_target", "rustc_trait_selection", "rustc_type_ir", + "smallvec", "tracing", "unicode-security", ] diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 24e7b40c8a2d3..d6014f5006ad0 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -24,6 +24,7 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_type_ir = { path = "../rustc_type_ir" } +smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" unicode-security = "0.1.0" # tidy-alphabetical-end diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 679367634276d..d51865810b9a8 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -333,6 +333,11 @@ lint_identifier_uncommon_codepoints = identifier contains {$codepoints_len -> *[other] {" "}{$identifier_type} } Unicode general security profile +lint_if_let_dtor = {$dtor_kind -> + [dyn] value may invoke a custom destructor because it contains a trait object + *[concrete] value invokes this custom destructor + } + lint_if_let_rescope = `if let` assigns a shorter lifetime since Edition 2024 .label = this value has a significant drop implementation which may observe a major change in drop order and requires your discretion .help = the value is now dropped here in Edition 2024 diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 23f037f3692ab..39ea8d8e3246c 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -7,13 +7,17 @@ use rustc_errors::{ Applicability, Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic, SuggestionStyle, }; use rustc_hir::{self as hir, HirIdSet}; -use rustc_macros::LintDiagnostic; -use rustc_middle::ty::TyCtxt; +use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::significant_drop_order::{ + extract_component_with_significant_dtor, ty_dtor_span, +}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint::{FutureIncompatibilityReason, LintId}; use rustc_session::{declare_lint, impl_lint_pass}; -use rustc_span::Span; use rustc_span::edition::Edition; +use rustc_span::{DUMMY_SP, Span}; +use smallvec::SmallVec; use crate::{LateContext, LateLintPass}; @@ -130,6 +134,7 @@ impl IfLetRescope { hir::ExprKind::If(_cond, _conseq, Some(alt)) => alt.span.shrink_to_hi(), _ => return, }; + let mut seen_dyn = false; let mut add_bracket_to_match_head = match_head_needs_bracket(tcx, expr); let mut significant_droppers = vec![]; let mut lifetime_ends = vec![]; @@ -137,6 +142,7 @@ impl IfLetRescope { let mut alt_heads = vec![]; let mut match_heads = vec![]; let mut consequent_heads = vec![]; + let mut destructors = vec![]; let mut first_if_to_lint = None; let mut first_if_to_rewrite = false; let mut empty_alt = false; @@ -160,11 +166,25 @@ impl IfLetRescope { let before_conseq = conseq.span.shrink_to_lo(); let lifetime_end = source_map.end_point(conseq.span); - if let ControlFlow::Break(significant_dropper) = + if let ControlFlow::Break((drop_span, drop_tys)) = (FindSignificantDropper { cx }).check_if_let_scrutinee(init) { + destructors.extend(drop_tys.into_iter().filter_map(|ty| { + if let Some(span) = ty_dtor_span(tcx, ty) { + Some(DestructorLabel { span, dtor_kind: "concrete" }) + } else if matches!(ty.kind(), ty::Dynamic(..)) { + if seen_dyn { + None + } else { + seen_dyn = true; + Some(DestructorLabel { span: DUMMY_SP, dtor_kind: "dyn" }) + } + } else { + None + } + })); first_if_to_lint = first_if_to_lint.or_else(|| Some((span, expr.hir_id))); - significant_droppers.push(significant_dropper); + significant_droppers.push(drop_span); lifetime_ends.push(lifetime_end); if ty_ascription.is_some() || !expr.span.can_be_used_for_suggestions() @@ -227,6 +247,7 @@ impl IfLetRescope { hir_id, span, IfLetRescopeLint { + destructors, significant_droppers, lifetime_ends, rewrite: first_if_to_rewrite.then_some(IfLetRescopeRewrite { @@ -288,6 +309,8 @@ impl<'tcx> LateLintPass<'tcx> for IfLetRescope { #[derive(LintDiagnostic)] #[diag(lint_if_let_rescope)] struct IfLetRescopeLint { + #[subdiagnostic] + destructors: Vec, #[label] significant_droppers: Vec, #[help] @@ -347,6 +370,14 @@ impl Subdiagnostic for IfLetRescopeRewrite { } } +#[derive(Subdiagnostic)] +#[note(lint_if_let_dtor)] +struct DestructorLabel { + #[primary_span] + span: Span, + dtor_kind: &'static str, +} + struct AltHead(Span); struct ConsequentRewrite { @@ -374,7 +405,10 @@ impl<'tcx> FindSignificantDropper<'_, 'tcx> { /// of the scrutinee itself, and also recurses into the expression to find any ref /// exprs (or autoref) which would promote temporaries that would be scoped to the /// end of this `if`. - fn check_if_let_scrutinee(&mut self, init: &'tcx hir::Expr<'tcx>) -> ControlFlow { + fn check_if_let_scrutinee( + &mut self, + init: &'tcx hir::Expr<'tcx>, + ) -> ControlFlow<(Span, SmallVec<[Ty<'tcx>; 4]>)> { self.check_promoted_temp_with_drop(init)?; self.visit_expr(init) } @@ -385,28 +419,35 @@ impl<'tcx> FindSignificantDropper<'_, 'tcx> { /// An expression is a promoted temporary if it has an addr taken (i.e. `&expr` or autoref) /// or is the scrutinee of the `if let`, *and* the expression is not a place /// expr, and it has a significant drop. - fn check_promoted_temp_with_drop(&self, expr: &'tcx hir::Expr<'tcx>) -> ControlFlow { - if !expr.is_place_expr(|base| { + fn check_promoted_temp_with_drop( + &self, + expr: &'tcx hir::Expr<'tcx>, + ) -> ControlFlow<(Span, SmallVec<[Ty<'tcx>; 4]>)> { + if expr.is_place_expr(|base| { self.cx .typeck_results() .adjustments() .get(base.hir_id) .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) - }) && self - .cx - .typeck_results() - .expr_ty(expr) - .has_significant_drop(self.cx.tcx, self.cx.typing_env()) - { - ControlFlow::Break(expr.span) - } else { - ControlFlow::Continue(()) + }) { + return ControlFlow::Continue(()); } + + let drop_tys = extract_component_with_significant_dtor( + self.cx.tcx, + self.cx.typing_env(), + self.cx.typeck_results().expr_ty(expr), + ); + if drop_tys.is_empty() { + return ControlFlow::Continue(()); + } + + ControlFlow::Break((expr.span, drop_tys)) } } impl<'tcx> Visitor<'tcx> for FindSignificantDropper<'_, 'tcx> { - type Result = ControlFlow; + type Result = ControlFlow<(Span, SmallVec<[Ty<'tcx>; 4]>)>; fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) -> Self::Result { // Blocks introduce temporary terminating scope for all of its diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index eb70a35d3708c..d926d6cc02a5f 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -122,6 +122,7 @@ pub mod normalize_erasing_regions; pub mod pattern; pub mod print; pub mod relate; +pub mod significant_drop_order; pub mod trait_def; pub mod util; pub mod visit; diff --git a/compiler/rustc_middle/src/ty/significant_drop_order.rs b/compiler/rustc_middle/src/ty/significant_drop_order.rs new file mode 100644 index 0000000000000..7f0d82d89fede --- /dev/null +++ b/compiler/rustc_middle/src/ty/significant_drop_order.rs @@ -0,0 +1,172 @@ +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::unord::UnordSet; +use rustc_hir::def_id::DefId; +use rustc_span::Span; +use smallvec::{SmallVec, smallvec}; +use tracing::{debug, instrument}; + +use crate::ty::{self, Ty, TyCtxt}; + +/// An additional filter to exclude well-known types from the ecosystem +/// because their drops are trivial. +/// This returns additional types to check if the drops are delegated to those. +/// A typical example is `hashbrown::HashMap`, whose drop is delegated to `K` and `V`. +fn true_significant_drop_ty<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Option; 2]>> { + if let ty::Adt(def, args) = ty.kind() { + let mut did = def.did(); + let mut name_rev = vec![]; + loop { + let key = tcx.def_key(did); + + match key.disambiguated_data.data { + rustc_hir::definitions::DefPathData::CrateRoot => { + name_rev.push(tcx.crate_name(did.krate)) + } + rustc_hir::definitions::DefPathData::TypeNs(symbol) => name_rev.push(symbol), + _ => return None, + } + if let Some(parent) = key.parent { + did = DefId { krate: did.krate, index: parent }; + } else { + break; + } + } + let name_str: Vec<_> = name_rev.iter().rev().map(|x| x.as_str()).collect(); + debug!(?name_str); + match name_str[..] { + // These are the types from Rust core ecosystem + ["syn" | "proc_macro2", ..] + | ["core" | "std", "task", "LocalWaker" | "Waker"] + | ["core" | "std", "task", "wake", "LocalWaker" | "Waker"] => Some(smallvec![]), + // These are important types from Rust ecosystem + ["tracing", "instrument", "Instrumented"] | ["bytes", "Bytes"] => Some(smallvec![]), + ["hashbrown", "raw", "RawTable" | "RawIntoIter"] => { + if let [ty, ..] = &***args + && let Some(ty) = ty.as_type() + { + Some(smallvec![ty]) + } else { + None + } + } + ["hashbrown", "raw", "RawDrain"] => { + if let [_, ty, ..] = &***args + && let Some(ty) = ty.as_type() + { + Some(smallvec![ty]) + } else { + None + } + } + _ => None, + } + } else { + None + } +} + +/// Returns the list of types with a "potentially sigificant" that may be dropped +/// by dropping a value of type `ty`. +#[instrument(level = "trace", skip(tcx, typing_env))] +pub fn extract_component_raw<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, + ty_seen: &mut UnordSet>, +) -> SmallVec<[Ty<'tcx>; 4]> { + // Droppiness does not depend on regions, so let us erase them. + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + + let tys = tcx.list_significant_drop_tys(typing_env.as_query_input(ty)); + debug!(?ty, "components"); + let mut out_tys = smallvec![]; + for ty in tys { + if let Some(tys) = true_significant_drop_ty(tcx, ty) { + // Some types can be further opened up because the drop is simply delegated + for ty in tys { + if ty_seen.insert(ty) { + out_tys.extend(extract_component_raw(tcx, typing_env, ty, ty_seen)); + } + } + } else { + if ty_seen.insert(ty) { + out_tys.push(ty); + } + } + } + out_tys +} + +#[instrument(level = "trace", skip(tcx, typing_env))] +pub fn extract_component_with_significant_dtor<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, +) -> SmallVec<[Ty<'tcx>; 4]> { + let mut tys = extract_component_raw(tcx, typing_env, ty, &mut Default::default()); + let mut deduplicate = FxHashSet::default(); + tys.retain(|oty| deduplicate.insert(*oty)); + tys.into_iter().collect() +} + +/// Extract the span of the custom destructor of a type +/// especially the span of the `impl Drop` header or its entire block +/// when we are working with current local crate. +#[instrument(level = "trace", skip(tcx))] +pub fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { + match ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Error(_) + | ty::Str + | ty::Never + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnPtr(_, _) + | ty::Tuple(_) + | ty::Dynamic(_, _, _) + | ty::Alias(_, _) + | ty::Bound(_, _) + | ty::Pat(_, _) + | ty::Placeholder(_) + | ty::Infer(_) + | ty::Slice(_) + | ty::Array(_, _) + | ty::UnsafeBinder(_) => None, + + ty::Adt(adt_def, _) => { + let did = adt_def.did(); + let try_local_did_span = |did: DefId| { + if let Some(local) = did.as_local() { + tcx.source_span(local) + } else { + tcx.def_span(did) + } + }; + let dtor = if let Some(dtor) = tcx.adt_destructor(did) { + dtor.did + } else if let Some(dtor) = tcx.adt_async_destructor(did) { + dtor.future + } else { + return Some(try_local_did_span(did)); + }; + let def_key = tcx.def_key(dtor); + let Some(parent_index) = def_key.parent else { return Some(try_local_did_span(dtor)) }; + let parent_did = DefId { index: parent_index, krate: dtor.krate }; + Some(try_local_did_span(parent_did)) + } + ty::Coroutine(did, _) + | ty::CoroutineWitness(did, _) + | ty::CoroutineClosure(did, _) + | ty::Closure(did, _) + | ty::FnDef(did, _) + | ty::Foreign(did) => Some(tcx.def_span(did)), + ty::Param(_) => None, + } +} diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs index e3260e45bc524..7d77fffa83fd1 100644 --- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs +++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs @@ -6,7 +6,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::Subdiagnostic; use rustc_hir::CRATE_HIR_ID; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::LocalDefId; use rustc_index::bit_set::MixedBitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; @@ -15,7 +15,10 @@ use rustc_middle::mir::{ self, BasicBlock, Body, ClearCrossCrate, Local, Location, Place, StatementKind, TerminatorKind, dump_mir, }; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::significant_drop_order::{ + extract_component_with_significant_dtor, ty_dtor_span, +}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex}; use rustc_mir_dataflow::{Analysis, MaybeReachable, ResultsCursor}; @@ -23,8 +26,7 @@ use rustc_session::lint::builtin::TAIL_EXPR_DROP_ORDER; use rustc_session::lint::{self}; use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_type_ir::data_structures::IndexMap; -use smallvec::{SmallVec, smallvec}; -use tracing::{debug, instrument}; +use tracing::debug; fn place_has_common_prefix<'tcx>(left: &Place<'tcx>, right: &Place<'tcx>) -> bool { left.local == right.local @@ -155,170 +157,6 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> { } } -/// An additional filter to exclude well-known types from the ecosystem -/// because their drops are trivial. -/// This returns additional types to check if the drops are delegated to those. -/// A typical example is `hashbrown::HashMap`, whose drop is delegated to `K` and `V`. -fn true_significant_drop_ty<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, -) -> Option; 2]>> { - if let ty::Adt(def, args) = ty.kind() { - let mut did = def.did(); - let mut name_rev = vec![]; - loop { - let key = tcx.def_key(did); - - match key.disambiguated_data.data { - rustc_hir::definitions::DefPathData::CrateRoot => { - name_rev.push(tcx.crate_name(did.krate)) - } - rustc_hir::definitions::DefPathData::TypeNs(symbol) => name_rev.push(symbol), - _ => return None, - } - if let Some(parent) = key.parent { - did = DefId { krate: did.krate, index: parent }; - } else { - break; - } - } - let name_str: Vec<_> = name_rev.iter().rev().map(|x| x.as_str()).collect(); - debug!(?name_str); - match name_str[..] { - // These are the types from Rust core ecosystem - ["syn" | "proc_macro2", ..] - | ["core" | "std", "task", "LocalWaker" | "Waker"] - | ["core" | "std", "task", "wake", "LocalWaker" | "Waker"] => Some(smallvec![]), - // These are important types from Rust ecosystem - ["tracing", "instrument", "Instrumented"] | ["bytes", "Bytes"] => Some(smallvec![]), - ["hashbrown", "raw", "RawTable" | "RawIntoIter"] => { - if let [ty, ..] = &***args - && let Some(ty) = ty.as_type() - { - Some(smallvec![ty]) - } else { - None - } - } - ["hashbrown", "raw", "RawDrain"] => { - if let [_, ty, ..] = &***args - && let Some(ty) = ty.as_type() - { - Some(smallvec![ty]) - } else { - None - } - } - _ => None, - } - } else { - None - } -} - -/// Returns the list of types with a "potentially sigificant" that may be dropped -/// by dropping a value of type `ty`. -#[instrument(level = "debug", skip(tcx, typing_env))] -fn extract_component_raw<'tcx>( - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ty: Ty<'tcx>, - ty_seen: &mut UnordSet>, -) -> SmallVec<[Ty<'tcx>; 4]> { - // Droppiness does not depend on regions, so let us erase them. - let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); - - let tys = tcx.list_significant_drop_tys(typing_env.as_query_input(ty)); - debug!(?ty, "components"); - let mut out_tys = smallvec![]; - for ty in tys { - if let Some(tys) = true_significant_drop_ty(tcx, ty) { - // Some types can be further opened up because the drop is simply delegated - for ty in tys { - if ty_seen.insert(ty) { - out_tys.extend(extract_component_raw(tcx, typing_env, ty, ty_seen)); - } - } - } else { - if ty_seen.insert(ty) { - out_tys.push(ty); - } - } - } - out_tys -} - -#[instrument(level = "debug", skip(tcx, typing_env))] -fn extract_component_with_significant_dtor<'tcx>( - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ty: Ty<'tcx>, -) -> SmallVec<[Ty<'tcx>; 4]> { - let mut tys = extract_component_raw(tcx, typing_env, ty, &mut Default::default()); - let mut deduplicate = FxHashSet::default(); - tys.retain(|oty| deduplicate.insert(*oty)); - tys.into_iter().collect() -} - -/// Extract the span of the custom destructor of a type -/// especially the span of the `impl Drop` header or its entire block -/// when we are working with current local crate. -#[instrument(level = "debug", skip(tcx))] -fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { - match ty.kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Error(_) - | ty::Str - | ty::Never - | ty::RawPtr(_, _) - | ty::Ref(_, _, _) - | ty::FnPtr(_, _) - | ty::Tuple(_) - | ty::Dynamic(_, _, _) - | ty::Alias(_, _) - | ty::Bound(_, _) - | ty::Pat(_, _) - | ty::Placeholder(_) - | ty::Infer(_) - | ty::Slice(_) - | ty::Array(_, _) - | ty::UnsafeBinder(_) => None, - - ty::Adt(adt_def, _) => { - let did = adt_def.did(); - let try_local_did_span = |did: DefId| { - if let Some(local) = did.as_local() { - tcx.source_span(local) - } else { - tcx.def_span(did) - } - }; - let dtor = if let Some(dtor) = tcx.adt_destructor(did) { - dtor.did - } else if let Some(dtor) = tcx.adt_async_destructor(did) { - dtor.future - } else { - return Some(try_local_did_span(did)); - }; - let def_key = tcx.def_key(dtor); - let Some(parent_index) = def_key.parent else { return Some(try_local_did_span(dtor)) }; - let parent_did = DefId { index: parent_index, krate: dtor.krate }; - Some(try_local_did_span(parent_did)) - } - ty::Coroutine(did, _) - | ty::CoroutineWitness(did, _) - | ty::CoroutineClosure(did, _) - | ty::Closure(did, _) - | ty::FnDef(did, _) - | ty::Foreign(did) => Some(tcx.def_span(did)), - ty::Param(_) => None, - } -} - /// Check if a moved place at `idx` is a part of a BID. /// The use of this check is that we will consider drops on these /// as a drop of the overall BID and, thus, we can exclude it from the diagnosis. diff --git a/tests/ui/drop/drop-order-comparisons.e2021.stderr b/tests/ui/drop/drop-order-comparisons.e2021.stderr index 158d18f68826b..601b0a38412f0 100644 --- a/tests/ui/drop/drop-order-comparisons.e2021.stderr +++ b/tests/ui/drop/drop-order-comparisons.e2021.stderr @@ -242,6 +242,15 @@ LL | _ = (if let Ok(_) = e.ok(4).as_ref() { | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/drop-order-comparisons.rs:127:5 | @@ -267,6 +276,15 @@ LL | _ = (if let Ok(_) = e.err(4).as_ref() {} else { | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/drop-order-comparisons.rs:145:44 | @@ -291,6 +309,15 @@ LL | if let Ok(_) = e.err(4).as_ref() {} else { | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/drop-order-comparisons.rs:247:43 | @@ -315,6 +342,15 @@ LL | if let true = e.err(9).is_ok() {} else { | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/drop-order-comparisons.rs:352:41 | @@ -339,6 +375,15 @@ LL | if let Ok(_v) = e.err(8) {} else { | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/drop-order-comparisons.rs:355:35 | @@ -363,6 +408,15 @@ LL | if let Ok(_) = e.err(7) {} else { | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/drop-order-comparisons.rs:358:34 | @@ -387,6 +441,15 @@ LL | if let Ok(_) = e.err(6).as_ref() {} else { | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/drop-order-comparisons.rs:361:43 | @@ -411,6 +474,15 @@ LL | if let Ok(_v) = e.err(5) {} else { | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/drop-order-comparisons.rs:365:35 | @@ -435,6 +507,15 @@ LL | if let Ok(_) = e.err(4) {} else { | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/drop-order-comparisons.rs:368:34 | @@ -459,6 +540,15 @@ LL | if let Ok(_) = e.err(4).as_ref() {} else { | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/drop-order-comparisons.rs:404:43 | diff --git a/tests/ui/drop/lint-if-let-rescope-gated.edition2021.stderr b/tests/ui/drop/lint-if-let-rescope-gated.edition2021.stderr index 546a5fe0fd045..070ba1c6a4cfa 100644 --- a/tests/ui/drop/lint-if-let-rescope-gated.edition2021.stderr +++ b/tests/ui/drop/lint-if-let-rescope-gated.edition2021.stderr @@ -8,6 +8,15 @@ LL | if let Some(_value) = Droppy.get() { | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/lint-if-let-rescope-gated.rs:14:1 + | +LL | / impl Drop for Droppy { +LL | | fn drop(&mut self) { +LL | | println!("dropped"); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope-gated.rs:30:5 | diff --git a/tests/ui/drop/lint-if-let-rescope-with-macro.stderr b/tests/ui/drop/lint-if-let-rescope-with-macro.stderr index 029d5c7492936..f1ca0ba57de4f 100644 --- a/tests/ui/drop/lint-if-let-rescope-with-macro.stderr +++ b/tests/ui/drop/lint-if-let-rescope-with-macro.stderr @@ -15,6 +15,15 @@ LL | | }; | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/lint-if-let-rescope-with-macro.rs:22:1 + | +LL | / impl Drop for Droppy { +LL | | fn drop(&mut self) { +LL | | println!("dropped"); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope-with-macro.rs:12:38 | diff --git a/tests/ui/drop/lint-if-let-rescope.stderr b/tests/ui/drop/lint-if-let-rescope.stderr index b17239976cc0d..e95ec8fcea7fe 100644 --- a/tests/ui/drop/lint-if-let-rescope.stderr +++ b/tests/ui/drop/lint-if-let-rescope.stderr @@ -8,6 +8,15 @@ LL | if let Some(_value) = droppy().get() { | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/lint-if-let-rescope.rs:11:1 + | +LL | / impl Drop for Droppy { +LL | | fn drop(&mut self) { +LL | | println!("dropped"); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope.rs:32:5 | @@ -43,6 +52,24 @@ LL | } else if let Some(_value) = droppy().get() { | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/lint-if-let-rescope.rs:11:1 + | +LL | / impl Drop for Droppy { +LL | | fn drop(&mut self) { +LL | | println!("dropped"); +LL | | } +LL | | } + | |_^ +note: value invokes this custom destructor + --> $DIR/lint-if-let-rescope.rs:11:1 + | +LL | / impl Drop for Droppy { +LL | | fn drop(&mut self) { +LL | | println!("dropped"); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope.rs:42:5 | @@ -75,6 +102,15 @@ LL | } else if let Some(_value) = droppy().get() { | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/lint-if-let-rescope.rs:11:1 + | +LL | / impl Drop for Droppy { +LL | | fn drop(&mut self) { +LL | | println!("dropped"); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope.rs:54:5 | @@ -101,6 +137,15 @@ LL | if let Some(1) = { if let Some(_value) = Droppy.get() { Some(1) } else | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/lint-if-let-rescope.rs:11:1 + | +LL | / impl Drop for Droppy { +LL | | fn drop(&mut self) { +LL | | println!("dropped"); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope.rs:58:69 | @@ -122,6 +167,15 @@ LL | if (if let Some(_value) = droppy().get() { true } else { false }) { | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/lint-if-let-rescope.rs:11:1 + | +LL | / impl Drop for Droppy { +LL | | fn drop(&mut self) { +LL | | println!("dropped"); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope.rs:72:53 | @@ -143,6 +197,15 @@ LL | } else if (((if let Some(_value) = droppy().get() { true } else { false | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/lint-if-let-rescope.rs:11:1 + | +LL | / impl Drop for Droppy { +LL | | fn drop(&mut self) { +LL | | println!("dropped"); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope.rs:78:62 | @@ -164,6 +227,15 @@ LL | while (if let Some(_value) = droppy().get() { false } else { true }) { | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: value invokes this custom destructor + --> $DIR/lint-if-let-rescope.rs:11:1 + | +LL | / impl Drop for Droppy { +LL | | fn drop(&mut self) { +LL | | println!("dropped"); +LL | | } +LL | | } + | |_^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope.rs:90:57 | From a8364f3b2ad63c19037bdf060e49ad9ca07e16b6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 19 Dec 2024 16:24:06 +1100 Subject: [PATCH 11/15] In `AssocOp::AssignOp`, use `BinOpKind` instead of `BinOpToken` `AssocOp::AssignOp` contains a `BinOpToken`. `ExprKind::AssignOp` contains a `BinOpKind`. Given that `AssocOp` is basically a cut-down version of `ExprKind`, it makes sense to make `AssocOp` more like `ExprKind`. Especially given that `AssocOp` and `BinOpKind` use semantic operation names (e.g. `Mul`, `Div`), but `BinOpToken` uses syntactic names (e.g. `Star`, `Slash`). This results in more concise code, and removes the need for various conversions. (Note that the removed functions `hirbinop2assignop` and `astbinop2assignop` are semantically identical, because `hir::BinOp` is just a synonum for `ast::BinOp`!) The only downside to this is that it allows the possibility of some nonsensical combinations, such as `AssocOp::AssignOp(BinOpKind::Lt)`. But `ExprKind::AssignOp` already has that problem. The problem can be fixed for both types in the future with some effort, by introducing an `AssignOpKind` type. --- compiler/rustc_ast/src/util/parser.rs | 25 ++++++---- compiler/rustc_parse/src/parser/expr.rs | 16 +------ src/tools/clippy/clippy_utils/src/lib.rs | 1 - src/tools/clippy/clippy_utils/src/sugg.rs | 58 ++--------------------- 4 files changed, 23 insertions(+), 77 deletions(-) diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index 8f2b7a23c01c8..69df9ff93b4f6 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -46,8 +46,8 @@ pub enum AssocOp { GreaterEqual, /// `=` Assign, - /// `?=` where ? is one of the BinOpToken - AssignOp(BinOpToken), + /// `?=` where ? is one of the assignable BinOps + AssignOp(BinOpKind), /// `as` As, /// `..` range @@ -71,18 +71,27 @@ impl AssocOp { pub fn from_token(t: &Token) -> Option { use AssocOp::*; match t.kind { - token::BinOpEq(k) => Some(AssignOp(k)), token::Eq => Some(Assign), + token::BinOpEq(BinOpToken::Plus) => Some(AssignOp(BinOpKind::Add)), + token::BinOpEq(BinOpToken::Minus) => Some(AssignOp(BinOpKind::Sub)), + token::BinOpEq(BinOpToken::Star) => Some(AssignOp(BinOpKind::Mul)), + token::BinOpEq(BinOpToken::Slash) => Some(AssignOp(BinOpKind::Div)), + token::BinOpEq(BinOpToken::Percent) => Some(AssignOp(BinOpKind::Rem)), + token::BinOpEq(BinOpToken::Caret) => Some(AssignOp(BinOpKind::BitXor)), + token::BinOpEq(BinOpToken::And) => Some(AssignOp(BinOpKind::BitAnd)), + token::BinOpEq(BinOpToken::Or) => Some(AssignOp(BinOpKind::BitOr)), + token::BinOpEq(BinOpToken::Shl) => Some(AssignOp(BinOpKind::Shl)), + token::BinOpEq(BinOpToken::Shr) => Some(AssignOp(BinOpKind::Shr)), + token::BinOp(BinOpToken::Plus) => Some(Add), + token::BinOp(BinOpToken::Minus) => Some(Subtract), token::BinOp(BinOpToken::Star) => Some(Multiply), token::BinOp(BinOpToken::Slash) => Some(Divide), token::BinOp(BinOpToken::Percent) => Some(Modulus), - token::BinOp(BinOpToken::Plus) => Some(Add), - token::BinOp(BinOpToken::Minus) => Some(Subtract), - token::BinOp(BinOpToken::Shl) => Some(ShiftLeft), - token::BinOp(BinOpToken::Shr) => Some(ShiftRight), - token::BinOp(BinOpToken::And) => Some(BitAnd), token::BinOp(BinOpToken::Caret) => Some(BitXor), + token::BinOp(BinOpToken::And) => Some(BitAnd), token::BinOp(BinOpToken::Or) => Some(BitOr), + token::BinOp(BinOpToken::Shl) => Some(ShiftLeft), + token::BinOp(BinOpToken::Shr) => Some(ShiftRight), token::Lt => Some(Less), token::Le => Some(LessEqual), token::Ge => Some(GreaterEqual), diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index b2e58c9428064..935e13cedef82 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -313,19 +313,7 @@ impl<'a> Parser<'a> { self.mk_expr(span, binary) } AssocOp::Assign => self.mk_expr(span, ExprKind::Assign(lhs, rhs, cur_op_span)), - AssocOp::AssignOp(k) => { - let aop = match k { - token::Plus => BinOpKind::Add, - token::Minus => BinOpKind::Sub, - token::Star => BinOpKind::Mul, - token::Slash => BinOpKind::Div, - token::Percent => BinOpKind::Rem, - token::Caret => BinOpKind::BitXor, - token::And => BinOpKind::BitAnd, - token::Or => BinOpKind::BitOr, - token::Shl => BinOpKind::Shl, - token::Shr => BinOpKind::Shr, - }; + AssocOp::AssignOp(aop) => { let aopexpr = self.mk_assign_op(source_map::respan(cur_op_span, aop), lhs, rhs); self.mk_expr(span, aopexpr) } @@ -395,7 +383,7 @@ impl<'a> Parser<'a> { AssocOp::ShiftRight | AssocOp::Greater | AssocOp::GreaterEqual - | AssocOp::AssignOp(token::BinOpToken::Shr), + | AssocOp::AssignOp(BinOpKind::Shr), ), _, ) if self.restrictions.contains(Restrictions::CONST_EXPR) => { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 7fc25e3617d07..f7266ef4d4ebb 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -31,7 +31,6 @@ // (Currently there is no way to opt into sysroot crates without `extern crate`.) extern crate rustc_abi; extern crate rustc_ast; -extern crate rustc_ast_pretty; extern crate rustc_attr_parsing; extern crate rustc_const_eval; extern crate rustc_data_structures; diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index d5e0e2e3436e2..ff5bcba5adb8f 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -5,8 +5,7 @@ use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_wi use crate::ty::expr_sig; use crate::{get_parent_expr_for_hir, higher}; use rustc_ast::util::parser::AssocOp; -use rustc_ast::{ast, token}; -use rustc_ast_pretty::pprust::token_kind_to_string; +use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind}; @@ -158,7 +157,7 @@ impl<'a> Sugg<'a> { Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span)) }, ExprKind::AssignOp(op, lhs, rhs) => { - Sugg::BinOp(hirbinop2assignop(op), get_snippet(lhs.span), get_snippet(rhs.span)) + Sugg::BinOp(AssocOp::AssignOp(op.node), get_snippet(lhs.span), get_snippet(rhs.span)) }, ExprKind::Binary(op, lhs, rhs) => Sugg::BinOp( AssocOp::from_ast_binop(op.node), @@ -245,7 +244,7 @@ impl<'a> Sugg<'a> { snippet(rhs.span), ), ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp( - astbinop2assignop(op), + AssocOp::AssignOp(op.node), snippet(lhs.span), snippet(rhs.span), ), @@ -389,7 +388,7 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String { }, AssocOp::Assign => format!("{lhs} = {rhs}"), AssocOp::AssignOp(op) => { - format!("{lhs} {}= {rhs}", token_kind_to_string(&token::BinOp(op))) + format!("{lhs} {}= {rhs}", op.as_str()) }, AssocOp::As => format!("{lhs} as {rhs}"), AssocOp::DotDot => format!("{lhs}..{rhs}"), @@ -619,55 +618,6 @@ fn associativity(op: AssocOp) -> Associativity { } } -/// Converts a `hir::BinOp` to the corresponding assigning binary operator. -fn hirbinop2assignop(op: hir::BinOp) -> AssocOp { - use rustc_ast::token::BinOpToken::{And, Caret, Minus, Or, Percent, Plus, Shl, Shr, Slash, Star}; - - AssocOp::AssignOp(match op.node { - hir::BinOpKind::Add => Plus, - hir::BinOpKind::BitAnd => And, - hir::BinOpKind::BitOr => Or, - hir::BinOpKind::BitXor => Caret, - hir::BinOpKind::Div => Slash, - hir::BinOpKind::Mul => Star, - hir::BinOpKind::Rem => Percent, - hir::BinOpKind::Shl => Shl, - hir::BinOpKind::Shr => Shr, - hir::BinOpKind::Sub => Minus, - - hir::BinOpKind::And - | hir::BinOpKind::Eq - | hir::BinOpKind::Ge - | hir::BinOpKind::Gt - | hir::BinOpKind::Le - | hir::BinOpKind::Lt - | hir::BinOpKind::Ne - | hir::BinOpKind::Or => panic!("This operator does not exist"), - }) -} - -/// Converts an `ast::BinOp` to the corresponding assigning binary operator. -fn astbinop2assignop(op: ast::BinOp) -> AssocOp { - use rustc_ast::ast::BinOpKind::{ - Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub, - }; - use rustc_ast::token::BinOpToken; - - AssocOp::AssignOp(match op.node { - Add => BinOpToken::Plus, - BitAnd => BinOpToken::And, - BitOr => BinOpToken::Or, - BitXor => BinOpToken::Caret, - Div => BinOpToken::Slash, - Mul => BinOpToken::Star, - Rem => BinOpToken::Percent, - Shl => BinOpToken::Shl, - Shr => BinOpToken::Shr, - Sub => BinOpToken::Minus, - And | Eq | Ge | Gt | Le | Lt | Ne | Or => panic!("This operator does not exist"), - }) -} - /// Returns the indentation before `span` if there are nothing but `[ \t]` /// before it on its line. fn indentation(cx: &T, span: Span) -> Option { From ceafbad81fcf71423d4dc7a90fe962bc5895b108 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 19 Dec 2024 18:24:07 +1100 Subject: [PATCH 12/15] Introduce `AssocOp::Binary`. It mirrors `ExprKind::Binary`, and contains a `BinOpKind`. This makes `AssocOp` more like `ExprKind`. Note that the variants removed from `AssocOp` are all named differently to `BinOpToken`, e.g. `Multiply` instead of `Mul`, so that's an inconsistency removed. The commit adds `precedence` and `fixity` methods to `BinOpKind`, and calls them from the corresponding methods in `AssocOp`. This avoids the need to create an `AssocOp` from a `BinOpKind` in a bunch of places, and `AssocOp::from_ast_binop` is removed. `AssocOp::to_ast_binop` is also no longer needed. Overall things are shorter and nicer. --- compiler/rustc_ast/src/ast.rs | 35 +++- compiler/rustc_ast/src/util/parser.rs | 182 ++++-------------- .../rustc_ast_pretty/src/pprust/state/expr.rs | 7 +- compiler/rustc_hir/src/hir.rs | 4 +- compiler/rustc_hir_pretty/src/lib.rs | 7 +- compiler/rustc_middle/src/ty/print/pretty.rs | 5 +- .../rustc_parse/src/parser/diagnostics.rs | 36 ++-- compiler/rustc_parse/src/parser/expr.rs | 61 ++---- .../operators/float_equality_without_abs.rs | 2 +- src/tools/clippy/clippy_utils/src/sugg.rs | 69 +++---- 10 files changed, 152 insertions(+), 256 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 29c1d34a125a4..65dd0a34aecad 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -39,7 +39,7 @@ pub use crate::format::*; use crate::ptr::P; use crate::token::{self, CommentKind, Delimiter}; use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream}; -use crate::util::parser::{AssocOp, ExprPrecedence}; +use crate::util::parser::{ExprPrecedence, Fixity}; /// A "Label" is an identifier of some point in sources, /// e.g. in the following code: @@ -937,8 +937,37 @@ impl BinOpKind { matches!(self, BinOpKind::And | BinOpKind::Or) } + pub fn precedence(&self) -> ExprPrecedence { + use BinOpKind::*; + match *self { + Mul | Div | Rem => ExprPrecedence::Product, + Add | Sub => ExprPrecedence::Sum, + Shl | Shr => ExprPrecedence::Shift, + BitAnd => ExprPrecedence::BitAnd, + BitXor => ExprPrecedence::BitXor, + BitOr => ExprPrecedence::BitOr, + Lt | Gt | Le | Ge | Eq | Ne => ExprPrecedence::Compare, + And => ExprPrecedence::LAnd, + Or => ExprPrecedence::LOr, + } + } + + pub fn fixity(&self) -> Fixity { + use BinOpKind::*; + match self { + Eq | Ne | Lt | Le | Gt | Ge => Fixity::None, + Add | Sub | Mul | Div | Rem | And | Or | BitXor | BitAnd | BitOr | Shl | Shr => { + Fixity::Left + } + } + } + pub fn is_comparison(self) -> bool { - crate::util::parser::AssocOp::from_ast_binop(self).is_comparison() + use BinOpKind::*; + match self { + Eq | Ne | Lt | Le | Gt | Ge => true, + Add | Sub | Mul | Div | Rem | And | Or | BitXor | BitAnd | BitOr | Shl | Shr => false, + } } /// Returns `true` if the binary operator takes its arguments by value. @@ -1332,7 +1361,7 @@ impl Expr { ExprKind::Range(..) => ExprPrecedence::Range, // Binop-like expr kinds, handled by `AssocOp`. - ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(), + ExprKind::Binary(op, ..) => op.node.precedence(), ExprKind::Cast(..) => ExprPrecedence::Cast, ExprKind::Assign(..) | diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index 69df9ff93b4f6..e7c26aa30928a 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -3,51 +3,15 @@ use rustc_span::kw; use crate::ast::{self, BinOpKind}; use crate::token::{self, BinOpToken, Token}; -/// Associative operator with precedence. -/// -/// This is the enum which specifies operator precedence and fixity to the parser. +/// Associative operator. #[derive(Copy, Clone, PartialEq, Debug)] pub enum AssocOp { - /// `+` - Add, - /// `-` - Subtract, - /// `*` - Multiply, - /// `/` - Divide, - /// `%` - Modulus, - /// `&&` - LAnd, - /// `||` - LOr, - /// `^` - BitXor, - /// `&` - BitAnd, - /// `|` - BitOr, - /// `<<` - ShiftLeft, - /// `>>` - ShiftRight, - /// `==` - Equal, - /// `<` - Less, - /// `<=` - LessEqual, - /// `!=` - NotEqual, - /// `>` - Greater, - /// `>=` - GreaterEqual, - /// `=` - Assign, + /// A binary op. + Binary(BinOpKind), /// `?=` where ? is one of the assignable BinOps AssignOp(BinOpKind), + /// `=` + Assign, /// `as` As, /// `..` range @@ -67,11 +31,21 @@ pub enum Fixity { } impl AssocOp { - /// Creates a new AssocOP from a token + /// Creates a new AssocOp from a token. pub fn from_token(t: &Token) -> Option { use AssocOp::*; match t.kind { token::Eq => Some(Assign), + token::BinOp(BinOpToken::Plus) => Some(Binary(BinOpKind::Add)), + token::BinOp(BinOpToken::Minus) => Some(Binary(BinOpKind::Sub)), + token::BinOp(BinOpToken::Star) => Some(Binary(BinOpKind::Mul)), + token::BinOp(BinOpToken::Slash) => Some(Binary(BinOpKind::Div)), + token::BinOp(BinOpToken::Percent) => Some(Binary(BinOpKind::Rem)), + token::BinOp(BinOpToken::Caret) => Some(Binary(BinOpKind::BitXor)), + token::BinOp(BinOpToken::And) => Some(Binary(BinOpKind::BitAnd)), + token::BinOp(BinOpToken::Or) => Some(Binary(BinOpKind::BitOr)), + token::BinOp(BinOpToken::Shl) => Some(Binary(BinOpKind::Shl)), + token::BinOp(BinOpToken::Shr) => Some(Binary(BinOpKind::Shr)), token::BinOpEq(BinOpToken::Plus) => Some(AssignOp(BinOpKind::Add)), token::BinOpEq(BinOpToken::Minus) => Some(AssignOp(BinOpKind::Sub)), token::BinOpEq(BinOpToken::Star) => Some(AssignOp(BinOpKind::Mul)), @@ -82,74 +56,31 @@ impl AssocOp { token::BinOpEq(BinOpToken::Or) => Some(AssignOp(BinOpKind::BitOr)), token::BinOpEq(BinOpToken::Shl) => Some(AssignOp(BinOpKind::Shl)), token::BinOpEq(BinOpToken::Shr) => Some(AssignOp(BinOpKind::Shr)), - token::BinOp(BinOpToken::Plus) => Some(Add), - token::BinOp(BinOpToken::Minus) => Some(Subtract), - token::BinOp(BinOpToken::Star) => Some(Multiply), - token::BinOp(BinOpToken::Slash) => Some(Divide), - token::BinOp(BinOpToken::Percent) => Some(Modulus), - token::BinOp(BinOpToken::Caret) => Some(BitXor), - token::BinOp(BinOpToken::And) => Some(BitAnd), - token::BinOp(BinOpToken::Or) => Some(BitOr), - token::BinOp(BinOpToken::Shl) => Some(ShiftLeft), - token::BinOp(BinOpToken::Shr) => Some(ShiftRight), - token::Lt => Some(Less), - token::Le => Some(LessEqual), - token::Ge => Some(GreaterEqual), - token::Gt => Some(Greater), - token::EqEq => Some(Equal), - token::Ne => Some(NotEqual), - token::AndAnd => Some(LAnd), - token::OrOr => Some(LOr), + token::Lt => Some(Binary(BinOpKind::Lt)), + token::Le => Some(Binary(BinOpKind::Le)), + token::Ge => Some(Binary(BinOpKind::Ge)), + token::Gt => Some(Binary(BinOpKind::Gt)), + token::EqEq => Some(Binary(BinOpKind::Eq)), + token::Ne => Some(Binary(BinOpKind::Ne)), + token::AndAnd => Some(Binary(BinOpKind::And)), + token::OrOr => Some(Binary(BinOpKind::Or)), token::DotDot => Some(DotDot), token::DotDotEq => Some(DotDotEq), // DotDotDot is no longer supported, but we need some way to display the error token::DotDotDot => Some(DotDotEq), // `<-` should probably be `< -` - token::LArrow => Some(Less), + token::LArrow => Some(Binary(BinOpKind::Lt)), _ if t.is_keyword(kw::As) => Some(As), _ => None, } } - /// Creates a new AssocOp from ast::BinOpKind. - pub fn from_ast_binop(op: BinOpKind) -> Self { - use AssocOp::*; - match op { - BinOpKind::Lt => Less, - BinOpKind::Gt => Greater, - BinOpKind::Le => LessEqual, - BinOpKind::Ge => GreaterEqual, - BinOpKind::Eq => Equal, - BinOpKind::Ne => NotEqual, - BinOpKind::Mul => Multiply, - BinOpKind::Div => Divide, - BinOpKind::Rem => Modulus, - BinOpKind::Add => Add, - BinOpKind::Sub => Subtract, - BinOpKind::Shl => ShiftLeft, - BinOpKind::Shr => ShiftRight, - BinOpKind::BitAnd => BitAnd, - BinOpKind::BitXor => BitXor, - BinOpKind::BitOr => BitOr, - BinOpKind::And => LAnd, - BinOpKind::Or => LOr, - } - } - /// Gets the precedence of this operator pub fn precedence(&self) -> ExprPrecedence { use AssocOp::*; match *self { As => ExprPrecedence::Cast, - Multiply | Divide | Modulus => ExprPrecedence::Product, - Add | Subtract => ExprPrecedence::Sum, - ShiftLeft | ShiftRight => ExprPrecedence::Shift, - BitAnd => ExprPrecedence::BitAnd, - BitXor => ExprPrecedence::BitXor, - BitOr => ExprPrecedence::BitOr, - Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => ExprPrecedence::Compare, - LAnd => ExprPrecedence::LAnd, - LOr => ExprPrecedence::LOr, + Binary(bin_op) => bin_op.precedence(), DotDot | DotDotEq => ExprPrecedence::Range, Assign | AssignOp(_) => ExprPrecedence::Assign, } @@ -161,22 +92,17 @@ impl AssocOp { // NOTE: it is a bug to have an operators that has same precedence but different fixities! match *self { Assign | AssignOp(_) => Fixity::Right, - As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd - | BitXor | BitOr | LAnd | LOr => Fixity::Left, - Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | DotDot | DotDotEq => { - Fixity::None - } + Binary(binop) => binop.fixity(), + As => Fixity::Left, + DotDot | DotDotEq => Fixity::None, } } pub fn is_comparison(&self) -> bool { use AssocOp::*; match *self { - Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true, - Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract - | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq => { - false - } + Binary(binop) => binop.is_comparison(), + Assign | AssignOp(_) | As | DotDot | DotDotEq => false, } } @@ -184,34 +110,7 @@ impl AssocOp { use AssocOp::*; match *self { Assign | AssignOp(_) => true, - Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply - | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor - | BitOr | LAnd | LOr | DotDot | DotDotEq => false, - } - } - - pub fn to_ast_binop(&self) -> Option { - use AssocOp::*; - match *self { - Less => Some(BinOpKind::Lt), - Greater => Some(BinOpKind::Gt), - LessEqual => Some(BinOpKind::Le), - GreaterEqual => Some(BinOpKind::Ge), - Equal => Some(BinOpKind::Eq), - NotEqual => Some(BinOpKind::Ne), - Multiply => Some(BinOpKind::Mul), - Divide => Some(BinOpKind::Div), - Modulus => Some(BinOpKind::Rem), - Add => Some(BinOpKind::Add), - Subtract => Some(BinOpKind::Sub), - ShiftLeft => Some(BinOpKind::Shl), - ShiftRight => Some(BinOpKind::Shr), - BitAnd => Some(BinOpKind::BitAnd), - BitXor => Some(BinOpKind::BitXor), - BitOr => Some(BinOpKind::BitOr), - LAnd => Some(BinOpKind::And), - LOr => Some(BinOpKind::Or), - Assign | AssignOp(_) | As | DotDot | DotDotEq => None, + As | Binary(_) | DotDot | DotDotEq => false, } } @@ -221,16 +120,19 @@ impl AssocOp { /// parentheses while having a high degree of confidence on the correctness of the suggestion. pub fn can_continue_expr_unambiguously(&self) -> bool { use AssocOp::*; + use BinOpKind::*; matches!( self, - BitXor | // `{ 42 } ^ 3` Assign | // `{ 42 } = { 42 }` - Divide | // `{ 42 } / 42` - Modulus | // `{ 42 } % 2` - ShiftRight | // `{ 42 } >> 2` - LessEqual | // `{ 42 } <= 3` - Greater | // `{ 42 } > 3` - GreaterEqual | // `{ 42 } >= 3` + Binary( + BitXor | // `{ 42 } ^ 3` + Div | // `{ 42 } / 42` + Rem | // `{ 42 } % 2` + Shr | // `{ 42 } >> 2` + Le | // `{ 42 } <= 3` + Gt | // `{ 42 } > 3` + Ge // `{ 42 } >= 3` + ) | AssignOp(_) | // `{ 42 } +=` // Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect // NotEqual | // `{ 42 } != { 42 } struct literals parser recovery. diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 4b1374ceef31e..496323a35b8d9 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -5,7 +5,7 @@ use itertools::{Itertools, Position}; use rustc_ast::ptr::P; use rustc_ast::util::classify; use rustc_ast::util::literal::escape_byte_str_symbol; -use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity}; +use rustc_ast::util::parser::{self, ExprPrecedence, Fixity}; use rustc_ast::{ self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount, FormatDebugHex, FormatSign, FormatTrait, token, @@ -279,12 +279,11 @@ impl<'a> State<'a> { rhs: &ast::Expr, fixup: FixupContext, ) { - let assoc_op = AssocOp::from_ast_binop(op.node); - let binop_prec = assoc_op.precedence(); + let binop_prec = op.node.precedence(); let left_prec = lhs.precedence(); let right_prec = rhs.precedence(); - let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() { + let (mut left_needs_paren, right_needs_paren) = match op.node.fixity() { Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec), Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec), Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec), diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index f0eaec55dbdd3..3c75bc588a138 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -4,7 +4,7 @@ use std::fmt; use rustc_abi::ExternAbi; use rustc_ast::attr::AttributeExt; use rustc_ast::token::CommentKind; -use rustc_ast::util::parser::{AssocOp, ExprPrecedence}; +use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::{ self as ast, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitIntType, LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind, @@ -2124,7 +2124,7 @@ impl Expr<'_> { | ExprKind::Become(..) => ExprPrecedence::Jump, // Binop-like expr kinds, handled by `AssocOp`. - ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(), + ExprKind::Binary(op, ..) => op.node.precedence(), ExprKind::Cast(..) => ExprPrecedence::Cast, ExprKind::Assign(..) | diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 5c7426d76b31c..b3377b487694b 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -10,7 +10,7 @@ use std::cell::Cell; use std::vec; use rustc_abi::ExternAbi; -use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity}; +use rustc_ast::util::parser::{self, ExprPrecedence, Fixity}; use rustc_ast::{AttrStyle, DUMMY_NODE_ID, DelimArgs}; use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; use rustc_ast_pretty::pp::{self, Breaks}; @@ -1296,12 +1296,11 @@ impl<'a> State<'a> { } fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) { - let assoc_op = AssocOp::from_ast_binop(op.node); - let binop_prec = assoc_op.precedence(); + let binop_prec = op.node.precedence(); let left_prec = lhs.precedence(); let right_prec = rhs.precedence(); - let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() { + let (mut left_needs_paren, right_needs_paren) = match op.node.fixity() { Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec), Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec), Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec), diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index ed0839f47e696..4a4e4a8c33d8b 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1516,10 +1516,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::ExprKind::Binop(op) => { let (_, _, c1, c2) = expr.binop_args(); - let precedence = |binop: crate::mir::BinOp| { - use rustc_ast::util::parser::AssocOp; - AssocOp::from_ast_binop(binop.to_hir_binop()).precedence() - }; + let precedence = |binop: crate::mir::BinOp| binop.to_hir_binop().precedence(); let op_precedence = precedence(op); let formatted_op = op.to_hir_binop().as_str(); let (lhs_parenthesized, rhs_parenthesized) = match (c1.kind(), c2.kind()) { diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 67abc2d539401..b35a57ae49299 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1350,13 +1350,13 @@ impl<'a> Parser<'a> { } return match (op.node, &outer_op.node) { // `x == y == z` - (BinOpKind::Eq, AssocOp::Equal) | + (BinOpKind::Eq, AssocOp::Binary(BinOpKind::Eq)) | // `x < y < z` and friends. - (BinOpKind::Lt, AssocOp::Less | AssocOp::LessEqual) | - (BinOpKind::Le, AssocOp::LessEqual | AssocOp::Less) | + (BinOpKind::Lt, AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le)) | + (BinOpKind::Le, AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le)) | // `x > y > z` and friends. - (BinOpKind::Gt, AssocOp::Greater | AssocOp::GreaterEqual) | - (BinOpKind::Ge, AssocOp::GreaterEqual | AssocOp::Greater) => { + (BinOpKind::Gt, AssocOp::Binary(BinOpKind::Gt | BinOpKind::Ge)) | + (BinOpKind::Ge, AssocOp::Binary(BinOpKind::Gt | BinOpKind::Ge)) => { let expr_to_str = |e: &Expr| { self.span_to_snippet(e.span) .unwrap_or_else(|_| pprust::expr_to_string(e)) @@ -1368,7 +1368,10 @@ impl<'a> Parser<'a> { false // Keep the current parse behavior, where the AST is `(x < y) < z`. } // `x == y < z` - (BinOpKind::Eq, AssocOp::Less | AssocOp::LessEqual | AssocOp::Greater | AssocOp::GreaterEqual) => { + ( + BinOpKind::Eq, + AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge) + ) => { // Consume `z`/outer-op-rhs. let snapshot = self.create_snapshot_for_diagnostic(); match self.parse_expr() { @@ -1389,7 +1392,10 @@ impl<'a> Parser<'a> { } } // `x > y == z` - (BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge, AssocOp::Equal) => { + ( + BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge, + AssocOp::Binary(BinOpKind::Eq) + ) => { let snapshot = self.create_snapshot_for_diagnostic(); // At this point it is always valid to enclose the lhs in parentheses, no // further checks are necessary. @@ -1457,10 +1463,10 @@ impl<'a> Parser<'a> { // Include `<` to provide this recommendation even in a case like // `Foo>>` - if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Less - || outer_op.node == AssocOp::Greater + if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Binary(BinOpKind::Lt) + || outer_op.node == AssocOp::Binary(BinOpKind::Gt) { - if outer_op.node == AssocOp::Less { + if outer_op.node == AssocOp::Binary(BinOpKind::Lt) { let snapshot = self.create_snapshot_for_diagnostic(); self.bump(); // So far we have parsed `foo Parser<'a> { ) -> PResult<'a, GenericArg> { let is_op_or_dot = AssocOp::from_token(&self.token) .and_then(|op| { - if let AssocOp::Greater - | AssocOp::Less - | AssocOp::ShiftRight - | AssocOp::GreaterEqual + if let AssocOp::Binary( + BinOpKind::Gt + | BinOpKind::Lt + | BinOpKind::Shr + | BinOpKind::Ge + ) // Don't recover from `foo::`, because this could be an attempt to // assign a value to a defaulted generic parameter. | AssocOp::Assign diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 935e13cedef82..00a5f2fa28fbc 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -188,17 +188,12 @@ impl<'a> Parser<'a> { } // Look for JS' `===` and `!==` and recover - if (op.node == AssocOp::Equal || op.node == AssocOp::NotEqual) + if let AssocOp::Binary(bop @ BinOpKind::Eq | bop @ BinOpKind::Ne) = op.node && self.token == token::Eq && self.prev_token.span.hi() == self.token.span.lo() { let sp = op.span.to(self.token.span); - let sugg = match op.node { - AssocOp::Equal => "==", - AssocOp::NotEqual => "!=", - _ => unreachable!(), - } - .into(); + let sugg = bop.as_str().into(); let invalid = format!("{sugg}="); self.dcx().emit_err(errors::InvalidComparisonOperator { span: sp, @@ -213,7 +208,7 @@ impl<'a> Parser<'a> { } // Look for PHP's `<>` and recover - if op.node == AssocOp::Less + if op.node == AssocOp::Binary(BinOpKind::Lt) && self.token == token::Gt && self.prev_token.span.hi() == self.token.span.lo() { @@ -231,7 +226,7 @@ impl<'a> Parser<'a> { } // Look for C++'s `<=>` and recover - if op.node == AssocOp::LessEqual + if op.node == AssocOp::Binary(BinOpKind::Le) && self.token == token::Gt && self.prev_token.span.hi() == self.token.span.lo() { @@ -290,25 +285,7 @@ impl<'a> Parser<'a> { let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); lhs = match op { - AssocOp::Add - | AssocOp::Subtract - | AssocOp::Multiply - | AssocOp::Divide - | AssocOp::Modulus - | AssocOp::LAnd - | AssocOp::LOr - | AssocOp::BitXor - | AssocOp::BitAnd - | AssocOp::BitOr - | AssocOp::ShiftLeft - | AssocOp::ShiftRight - | AssocOp::Equal - | AssocOp::Less - | AssocOp::LessEqual - | AssocOp::NotEqual - | AssocOp::Greater - | AssocOp::GreaterEqual => { - let ast_op = op.to_ast_binop().unwrap(); + AssocOp::Binary(ast_op) => { let binary = self.mk_binary(source_map::respan(cur_op_span, ast_op), lhs, rhs); self.mk_expr(span, binary) } @@ -335,13 +312,14 @@ impl<'a> Parser<'a> { // An exhaustive check is done in the following block, but these are checked first // because they *are* ambiguous but also reasonable looking incorrect syntax, so we // want to keep their span info to improve diagnostics in these cases in a later stage. - (true, Some(AssocOp::Multiply)) | // `{ 42 } *foo = bar;` or `{ 42 } * 3` - (true, Some(AssocOp::Subtract)) | // `{ 42 } -5` - (true, Some(AssocOp::Add)) | // `{ 42 } + 42` (unary plus) - (true, Some(AssocOp::LAnd)) | // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` - (true, Some(AssocOp::LOr)) | // `{ 42 } || 42` ("logical or" or closure) - (true, Some(AssocOp::BitOr)) // `{ 42 } | 42` or `{ 42 } |x| 42` - => { + (true, Some(AssocOp::Binary( + BinOpKind::Mul | // `{ 42 } *foo = bar;` or `{ 42 } * 3` + BinOpKind::Sub | // `{ 42 } -5` + BinOpKind::Add | // `{ 42 } + 42` (unary plus) + BinOpKind::And | // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` + BinOpKind::Or | // `{ 42 } || 42` ("logical or" or closure) + BinOpKind::BitOr // `{ 42 } | 42` or `{ 42 } |x| 42` + ))) => { // These cases are ambiguous and can't be identified in the parser alone. // // Bitwise AND is left out because guessing intent is hard. We can make @@ -380,21 +358,20 @@ impl<'a> Parser<'a> { // When parsing const expressions, stop parsing when encountering `>`. ( Some( - AssocOp::ShiftRight - | AssocOp::Greater - | AssocOp::GreaterEqual + AssocOp::Binary(BinOpKind::Shr | BinOpKind::Gt | BinOpKind::Ge) | AssocOp::AssignOp(BinOpKind::Shr), ), _, ) if self.restrictions.contains(Restrictions::CONST_EXPR) => { return None; } - // When recovering patterns as expressions, stop parsing when encountering an assignment `=`, an alternative `|`, or a range `..`. + // When recovering patterns as expressions, stop parsing when encountering an + // assignment `=`, an alternative `|`, or a range `..`. ( Some( AssocOp::Assign | AssocOp::AssignOp(_) - | AssocOp::BitOr + | AssocOp::Binary(BinOpKind::BitOr) | AssocOp::DotDot | AssocOp::DotDotEq, ), @@ -411,7 +388,7 @@ impl<'a> Parser<'a> { incorrect: "and".into(), sub: errors::InvalidLogicalOperatorSub::Conjunction(self.token.span), }); - (AssocOp::LAnd, span) + (AssocOp::Binary(BinOpKind::And), span) } (None, Some((Ident { name: sym::or, span }, IdentIsRaw::No))) if self.may_recover() => { self.dcx().emit_err(errors::InvalidLogicalOperator { @@ -419,7 +396,7 @@ impl<'a> Parser<'a> { incorrect: "or".into(), sub: errors::InvalidLogicalOperatorSub::Disjunction(self.token.span), }); - (AssocOp::LOr, span) + (AssocOp::Binary(BinOpKind::Or), span) } _ => return None, }; diff --git a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs index 34f7dbea84e49..74e0a6333db0f 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs @@ -50,7 +50,7 @@ pub(crate) fn check<'tcx>( // format the suggestion let suggestion = format!( "{}.abs()", - sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par() + sugg::make_assoc(AssocOp::Binary(BinOpKind::Sub), &sug_l, &sug_r).maybe_par() ); // spans the lint span_lint_and_then( diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index ff5bcba5adb8f..6e16778b343de 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -160,7 +160,7 @@ impl<'a> Sugg<'a> { Sugg::BinOp(AssocOp::AssignOp(op.node), get_snippet(lhs.span), get_snippet(rhs.span)) }, ExprKind::Binary(op, lhs, rhs) => Sugg::BinOp( - AssocOp::from_ast_binop(op.node), + AssocOp::Binary(op.node), get_snippet(lhs.span), get_snippet(rhs.span), ), @@ -249,7 +249,7 @@ impl<'a> Sugg<'a> { snippet(rhs.span), ), ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp( - AssocOp::from_ast_binop(op.node), + AssocOp::Binary(op.node), snippet(lhs.span), snippet(rhs.span), ), @@ -366,30 +366,9 @@ impl<'a> Sugg<'a> { /// Generates a string from the operator and both sides. fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String { match op { - AssocOp::Add - | AssocOp::Subtract - | AssocOp::Multiply - | AssocOp::Divide - | AssocOp::Modulus - | AssocOp::LAnd - | AssocOp::LOr - | AssocOp::BitXor - | AssocOp::BitAnd - | AssocOp::BitOr - | AssocOp::ShiftLeft - | AssocOp::ShiftRight - | AssocOp::Equal - | AssocOp::Less - | AssocOp::LessEqual - | AssocOp::NotEqual - | AssocOp::Greater - | AssocOp::GreaterEqual => { - format!("{lhs} {} {rhs}", op.to_ast_binop().expect("Those are AST ops").as_str()) - }, + AssocOp::Binary(op) => format!("{lhs} {} {rhs}", op.as_str()), AssocOp::Assign => format!("{lhs} = {rhs}"), - AssocOp::AssignOp(op) => { - format!("{lhs} {}= {rhs}", op.as_str()) - }, + AssocOp::AssignOp(op) => format!("{lhs} {}= {rhs}", op.as_str()), AssocOp::As => format!("{lhs} as {rhs}"), AssocOp::DotDot => format!("{lhs}..{rhs}"), AssocOp::DotDotEq => format!("{lhs}..={rhs}"), @@ -476,16 +455,17 @@ impl Neg for Sugg<'_> { impl<'a> Not for Sugg<'a> { type Output = Sugg<'a>; fn not(self) -> Sugg<'a> { - use AssocOp::{Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual}; + use AssocOp::Binary; + use ast::BinOpKind::{Eq, Gt, Ge, Lt, Le, Ne}; if let Sugg::BinOp(op, lhs, rhs) = self { let to_op = match op { - Equal => NotEqual, - NotEqual => Equal, - Less => GreaterEqual, - GreaterEqual => Less, - Greater => LessEqual, - LessEqual => Greater, + Binary(Eq) => Binary(Ne), + Binary(Ne) => Binary(Eq), + Binary(Lt) => Binary(Ge), + Binary(Ge) => Binary(Lt), + Binary(Gt) => Binary(Le), + Binary(Le) => Binary(Gt), _ => return make_unop("!", Sugg::BinOp(op, lhs, rhs)), }; Sugg::BinOp(to_op, lhs, rhs) @@ -537,7 +517,7 @@ pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> { pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> { /// Returns `true` if the operator is a shift operator `<<` or `>>`. fn is_shift(op: AssocOp) -> bool { - matches!(op, AssocOp::ShiftLeft | AssocOp::ShiftRight) + matches!(op, AssocOp::Binary(ast::BinOpKind::Shl | ast::BinOpKind::Shr)) } /// Returns `true` if the operator is an arithmetic operator @@ -545,7 +525,13 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> fn is_arith(op: AssocOp) -> bool { matches!( op, - AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus + AssocOp::Binary( + ast::BinOpKind::Add + | ast::BinOpKind::Sub + | ast::BinOpKind::Mul + | ast::BinOpKind::Div + | ast::BinOpKind::Rem + ) ) } @@ -577,9 +563,9 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> Sugg::BinOp(op, lhs.into(), rhs.into()) } -/// Convenience wrapper around `make_assoc` and `AssocOp::from_ast_binop`. +/// Convenience wrapper around `make_assoc` and `AssocOp::Binary`. pub fn make_binop(op: ast::BinOpKind, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> { - make_assoc(AssocOp::from_ast_binop(op), lhs, rhs) + make_assoc(AssocOp::Binary(op), lhs, rhs) } #[derive(PartialEq, Eq, Clone, Copy)] @@ -604,16 +590,15 @@ enum Associativity { /// associative. #[must_use] fn associativity(op: AssocOp) -> Associativity { - use rustc_ast::util::parser::AssocOp::{ - Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Divide, DotDot, DotDotEq, Equal, Greater, GreaterEqual, LAnd, - LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract, + use rustc_ast::util::parser::AssocOp::{As, Assign, AssignOp, Binary, DotDot, DotDotEq}; + use ast::BinOpKind::{ + Add, BitAnd, BitOr, BitXor, Div, Eq, Gt, Ge, And, Or, Lt, Le, Rem, Mul, Ne, Shl, Shr, Sub, }; match op { Assign | AssignOp(_) => Associativity::Right, - Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As => Associativity::Both, - Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight - | Subtract => Associativity::Left, + Binary(Add | BitAnd | BitOr | BitXor | And | Or | Mul) | As => Associativity::Both, + Binary(Div | Eq | Gt | Ge | Lt | Le | Rem | Ne | Shl | Shr | Sub) => Associativity::Left, DotDot | DotDotEq => Associativity::None, } } From fc8e87b27418704316047eb15ff40382d8d847bc Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 19 Dec 2024 21:42:46 +1100 Subject: [PATCH 13/15] Replace `AssocOp::DotDot{,Eq}` with `AssocOp::Range`. It makes `AssocOp` more similar to `ExprKind` and makes things a little simpler. And the semantic names make more sense here than the syntactic names. --- compiler/rustc_ast/src/ast.rs | 9 +++++++ compiler/rustc_ast/src/util/parser.rs | 21 +++++++--------- compiler/rustc_parse/src/parser/expr.rs | 15 +++++------- src/tools/clippy/clippy_utils/src/sugg.rs | 30 ++++++----------------- 4 files changed, 32 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 65dd0a34aecad..5c44fda226241 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1453,6 +1453,15 @@ pub enum RangeLimits { Closed, } +impl RangeLimits { + pub fn as_str(&self) -> &'static str { + match self { + RangeLimits::HalfOpen => "..", + RangeLimits::Closed => "..=", + } + } +} + /// A method call (e.g. `x.foo::(a, b, c)`). #[derive(Clone, Encodable, Decodable, Debug)] pub struct MethodCall { diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index e7c26aa30928a..c610f933074d6 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -1,6 +1,6 @@ use rustc_span::kw; -use crate::ast::{self, BinOpKind}; +use crate::ast::{self, BinOpKind, RangeLimits}; use crate::token::{self, BinOpToken, Token}; /// Associative operator. @@ -14,10 +14,8 @@ pub enum AssocOp { Assign, /// `as` As, - /// `..` range - DotDot, - /// `..=` range - DotDotEq, + /// `..` or `..=` range + Range(RangeLimits), } #[derive(PartialEq, Debug)] @@ -64,10 +62,9 @@ impl AssocOp { token::Ne => Some(Binary(BinOpKind::Ne)), token::AndAnd => Some(Binary(BinOpKind::And)), token::OrOr => Some(Binary(BinOpKind::Or)), - token::DotDot => Some(DotDot), - token::DotDotEq => Some(DotDotEq), + token::DotDot => Some(Range(RangeLimits::HalfOpen)), // DotDotDot is no longer supported, but we need some way to display the error - token::DotDotDot => Some(DotDotEq), + token::DotDotEq | token::DotDotDot => Some(Range(RangeLimits::Closed)), // `<-` should probably be `< -` token::LArrow => Some(Binary(BinOpKind::Lt)), _ if t.is_keyword(kw::As) => Some(As), @@ -81,7 +78,7 @@ impl AssocOp { match *self { As => ExprPrecedence::Cast, Binary(bin_op) => bin_op.precedence(), - DotDot | DotDotEq => ExprPrecedence::Range, + Range(_) => ExprPrecedence::Range, Assign | AssignOp(_) => ExprPrecedence::Assign, } } @@ -94,7 +91,7 @@ impl AssocOp { Assign | AssignOp(_) => Fixity::Right, Binary(binop) => binop.fixity(), As => Fixity::Left, - DotDot | DotDotEq => Fixity::None, + Range(_) => Fixity::None, } } @@ -102,7 +99,7 @@ impl AssocOp { use AssocOp::*; match *self { Binary(binop) => binop.is_comparison(), - Assign | AssignOp(_) | As | DotDot | DotDotEq => false, + Assign | AssignOp(_) | As | Range(_) => false, } } @@ -110,7 +107,7 @@ impl AssocOp { use AssocOp::*; match *self { Assign | AssignOp(_) => true, - As | Binary(_) | DotDot | DotDotEq => false, + As | Binary(_) | Range(_) => false, } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 00a5f2fa28fbc..50ce6fa1a906c 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -171,7 +171,7 @@ impl<'a> Parser<'a> { break; } // Check for deprecated `...` syntax - if self.token == token::DotDotDot && op.node == AssocOp::DotDotEq { + if self.token == token::DotDotDot && op.node == AssocOp::Range(RangeLimits::Closed) { self.err_dotdotdot_syntax(self.token.span); } @@ -267,10 +267,10 @@ impl<'a> Parser<'a> { if op == AssocOp::As { lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?; continue; - } else if op == AssocOp::DotDot || op == AssocOp::DotDotEq { + } else if let AssocOp::Range(limits) = op { // If we didn't have to handle `x..`/`x..=`, it would be pretty easy to // generalise it to the Fixity::None code. - lhs = self.parse_expr_range(prec, lhs, op, cur_op_span)?; + lhs = self.parse_expr_range(prec, lhs, limits, cur_op_span)?; break; } @@ -294,7 +294,7 @@ impl<'a> Parser<'a> { let aopexpr = self.mk_assign_op(source_map::respan(cur_op_span, aop), lhs, rhs); self.mk_expr(span, aopexpr) } - AssocOp::As | AssocOp::DotDot | AssocOp::DotDotEq => { + AssocOp::As | AssocOp::Range(_) => { self.dcx().span_bug(span, "AssocOp should have been handled by special case") } }; @@ -372,8 +372,7 @@ impl<'a> Parser<'a> { AssocOp::Assign | AssocOp::AssignOp(_) | AssocOp::Binary(BinOpKind::BitOr) - | AssocOp::DotDot - | AssocOp::DotDotEq, + | AssocOp::Range(_), ), _, ) if self.restrictions.contains(Restrictions::IS_PAT) => { @@ -414,7 +413,7 @@ impl<'a> Parser<'a> { &mut self, prec: ExprPrecedence, lhs: P, - op: AssocOp, + limits: RangeLimits, cur_op_span: Span, ) -> PResult<'a, P> { let rhs = if self.is_at_start_of_range_notation_rhs() { @@ -430,8 +429,6 @@ impl<'a> Parser<'a> { }; let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span); let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span); - let limits = - if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed }; let range = self.mk_range(Some(lhs), rhs, limits); Ok(self.mk_expr(span, range)) } diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 6e16778b343de..fe5ae54fe50b8 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -113,10 +113,7 @@ impl<'a> Sugg<'a> { /// function variants of `Sugg`, since these use different snippet functions. fn hir_from_snippet(expr: &hir::Expr<'_>, mut get_snippet: impl FnMut(Span) -> Cow<'a, str>) -> Self { if let Some(range) = higher::Range::hir(expr) { - let op = match range.limits { - ast::RangeLimits::HalfOpen => AssocOp::DotDot, - ast::RangeLimits::Closed => AssocOp::DotDotEq, - }; + let op = AssocOp::Range(range.limits); let start = range.start.map_or("".into(), |expr| get_snippet(expr.span)); let end = range.end.map_or("".into(), |expr| get_snippet(expr.span)); @@ -178,8 +175,6 @@ impl<'a> Sugg<'a> { ctxt: SyntaxContext, app: &mut Applicability, ) -> Self { - use rustc_ast::ast::RangeLimits; - let mut snippet = |span: Span| snippet_with_context(cx, span, ctxt, default, app).0; match expr.kind { @@ -228,13 +223,8 @@ impl<'a> Sugg<'a> { | ast::ExprKind::Err(_) | ast::ExprKind::Dummy | ast::ExprKind::UnsafeBinderCast(..) => Sugg::NonParen(snippet(expr.span)), - ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp( - AssocOp::DotDot, - lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)), - rhs.as_ref().map_or("".into(), |rhs| snippet(rhs.span)), - ), - ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp( - AssocOp::DotDotEq, + ast::ExprKind::Range(ref lhs, ref rhs, limits) => Sugg::BinOp( + AssocOp::Range(limits), lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)), rhs.as_ref().map_or("".into(), |rhs| snippet(rhs.span)), ), @@ -326,11 +316,8 @@ impl<'a> Sugg<'a> { /// Convenience method to create the `..` or `...` /// suggestion. - pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> { - match limit { - ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, end), - ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotEq, &self, end), - } + pub fn range(self, end: &Self, limits: ast::RangeLimits) -> Sugg<'static> { + make_assoc(AssocOp::Range(limits), &self, end) } /// Adds parentheses to any expression that might need them. Suitable to the @@ -370,8 +357,7 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String { AssocOp::Assign => format!("{lhs} = {rhs}"), AssocOp::AssignOp(op) => format!("{lhs} {}= {rhs}", op.as_str()), AssocOp::As => format!("{lhs} as {rhs}"), - AssocOp::DotDot => format!("{lhs}..{rhs}"), - AssocOp::DotDotEq => format!("{lhs}..={rhs}"), + AssocOp::Range(limits) => format!("{lhs}{}{rhs}", limits.as_str()), } } @@ -590,7 +576,7 @@ enum Associativity { /// associative. #[must_use] fn associativity(op: AssocOp) -> Associativity { - use rustc_ast::util::parser::AssocOp::{As, Assign, AssignOp, Binary, DotDot, DotDotEq}; + use rustc_ast::util::parser::AssocOp::{As, Assign, AssignOp, Binary, Range}; use ast::BinOpKind::{ Add, BitAnd, BitOr, BitXor, Div, Eq, Gt, Ge, And, Or, Lt, Le, Rem, Mul, Ne, Shl, Shr, Sub, }; @@ -599,7 +585,7 @@ fn associativity(op: AssocOp) -> Associativity { Assign | AssignOp(_) => Associativity::Right, Binary(Add | BitAnd | BitOr | BitXor | And | Or | Mul) | As => Associativity::Both, Binary(Div | Eq | Gt | Ge | Lt | Le | Rem | Ne | Shl | Shr | Sub) => Associativity::Left, - DotDot | DotDotEq => Associativity::None, + Range(_) => Associativity::None, } } From 2ac46f651789950b6bafabcd5621080c9ff649f7 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 19 Dec 2024 21:46:44 +1100 Subject: [PATCH 14/15] Rename `AssocOp::As` as `AssocOp::Cast`. To match `ExprKind::Cast`, and because a semantic name makes more sense here than a syntactic name. --- compiler/rustc_ast/src/util/parser.rs | 14 +++++++------- compiler/rustc_parse/src/parser/expr.rs | 4 ++-- src/tools/clippy/clippy_utils/src/sugg.rs | 14 +++++++------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index c610f933074d6..69454967eed62 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -13,7 +13,7 @@ pub enum AssocOp { /// `=` Assign, /// `as` - As, + Cast, /// `..` or `..=` range Range(RangeLimits), } @@ -67,7 +67,7 @@ impl AssocOp { token::DotDotEq | token::DotDotDot => Some(Range(RangeLimits::Closed)), // `<-` should probably be `< -` token::LArrow => Some(Binary(BinOpKind::Lt)), - _ if t.is_keyword(kw::As) => Some(As), + _ if t.is_keyword(kw::As) => Some(Cast), _ => None, } } @@ -76,7 +76,7 @@ impl AssocOp { pub fn precedence(&self) -> ExprPrecedence { use AssocOp::*; match *self { - As => ExprPrecedence::Cast, + Cast => ExprPrecedence::Cast, Binary(bin_op) => bin_op.precedence(), Range(_) => ExprPrecedence::Range, Assign | AssignOp(_) => ExprPrecedence::Assign, @@ -90,7 +90,7 @@ impl AssocOp { match *self { Assign | AssignOp(_) => Fixity::Right, Binary(binop) => binop.fixity(), - As => Fixity::Left, + Cast => Fixity::Left, Range(_) => Fixity::None, } } @@ -99,7 +99,7 @@ impl AssocOp { use AssocOp::*; match *self { Binary(binop) => binop.is_comparison(), - Assign | AssignOp(_) | As | Range(_) => false, + Assign | AssignOp(_) | Cast | Range(_) => false, } } @@ -107,7 +107,7 @@ impl AssocOp { use AssocOp::*; match *self { Assign | AssignOp(_) => true, - As | Binary(_) | Range(_) => false, + Cast | Binary(_) | Range(_) => false, } } @@ -133,7 +133,7 @@ impl AssocOp { AssignOp(_) | // `{ 42 } +=` // Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect // NotEqual | // `{ 42 } != { 42 } struct literals parser recovery. - As // `{ 42 } as usize` + Cast // `{ 42 } as usize` ) } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 50ce6fa1a906c..c934c1e36d528 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -264,7 +264,7 @@ impl<'a> Parser<'a> { let op = op.node; // Special cases: - if op == AssocOp::As { + if op == AssocOp::Cast { lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?; continue; } else if let AssocOp::Range(limits) = op { @@ -294,7 +294,7 @@ impl<'a> Parser<'a> { let aopexpr = self.mk_assign_op(source_map::respan(cur_op_span, aop), lhs, rhs); self.mk_expr(span, aopexpr) } - AssocOp::As | AssocOp::Range(_) => { + AssocOp::Cast | AssocOp::Range(_) => { self.dcx().span_bug(span, "AssocOp should have been handled by special case") } }; diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index fe5ae54fe50b8..4a9ab17d4a609 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -163,7 +163,7 @@ impl<'a> Sugg<'a> { ), ExprKind::Cast(lhs, ty) | //FIXME(chenyukang), remove this after type ascription is removed from AST - ExprKind::Type(lhs, ty) => Sugg::BinOp(AssocOp::As, get_snippet(lhs.span), get_snippet(ty.span)), + ExprKind::Type(lhs, ty) => Sugg::BinOp(AssocOp::Cast, get_snippet(lhs.span), get_snippet(ty.span)), } } @@ -246,7 +246,7 @@ impl<'a> Sugg<'a> { ast::ExprKind::Cast(ref lhs, ref ty) | //FIXME(chenyukang), remove this after type ascription is removed from AST ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp( - AssocOp::As, + AssocOp::Cast, snippet(lhs.span), snippet(ty.span), ), @@ -265,7 +265,7 @@ impl<'a> Sugg<'a> { /// Convenience method to create the ` as ` suggestion. pub fn as_ty(self, rhs: R) -> Sugg<'static> { - make_assoc(AssocOp::As, &self, &Sugg::NonParen(rhs.to_string().into())) + make_assoc(AssocOp::Cast, &self, &Sugg::NonParen(rhs.to_string().into())) } /// Convenience method to create the `&` suggestion. @@ -356,7 +356,7 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String { AssocOp::Binary(op) => format!("{lhs} {} {rhs}", op.as_str()), AssocOp::Assign => format!("{lhs} = {rhs}"), AssocOp::AssignOp(op) => format!("{lhs} {}= {rhs}", op.as_str()), - AssocOp::As => format!("{lhs} as {rhs}"), + AssocOp::Cast => format!("{lhs} as {rhs}"), AssocOp::Range(limits) => format!("{lhs}{}{rhs}", limits.as_str()), } } @@ -432,7 +432,7 @@ impl Neg for Sugg<'_> { type Output = Sugg<'static>; fn neg(self) -> Sugg<'static> { match &self { - Self::BinOp(AssocOp::As, ..) => Sugg::MaybeParen(format!("-({self})").into()), + Self::BinOp(AssocOp::Cast, ..) => Sugg::MaybeParen(format!("-({self})").into()), _ => make_unop("-", self), } } @@ -576,14 +576,14 @@ enum Associativity { /// associative. #[must_use] fn associativity(op: AssocOp) -> Associativity { - use rustc_ast::util::parser::AssocOp::{As, Assign, AssignOp, Binary, Range}; + use rustc_ast::util::parser::AssocOp::{Assign, AssignOp, Binary, Cast, Range}; use ast::BinOpKind::{ Add, BitAnd, BitOr, BitXor, Div, Eq, Gt, Ge, And, Or, Lt, Le, Rem, Mul, Ne, Shl, Shr, Sub, }; match op { Assign | AssignOp(_) => Associativity::Right, - Binary(Add | BitAnd | BitOr | BitXor | And | Or | Mul) | As => Associativity::Both, + Binary(Add | BitAnd | BitOr | BitXor | And | Or | Mul) | Cast => Associativity::Both, Binary(Div | Eq | Gt | Ge | Lt | Le | Rem | Ne | Shl | Shr | Sub) => Associativity::Left, Range(_) => Associativity::None, } From ef66cbb27bc2a911edf4541e1c56102dc03e42d9 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 7 Feb 2025 21:58:08 +0800 Subject: [PATCH 15/15] require trait impls to have matching const stabilities as the traits --- .../rustc_const_eval/src/check_consts/mod.rs | 5 ++ compiler/rustc_passes/messages.ftl | 6 ++ compiler/rustc_passes/src/errors.rs | 39 +++++++++++ compiler/rustc_passes/src/stability.rs | 60 ++++++++++++---- library/core/src/ops/arith.rs | 1 + library/core/src/ops/deref.rs | 3 + tests/ui/traits/const-traits/staged-api.rs | 32 +++++++++ .../ui/traits/const-traits/staged-api.stderr | 69 ++++++++++++++++++- 8 files changed, 200 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index 659d4a304563e..607cb2e497d8d 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -94,6 +94,11 @@ pub fn rustc_allow_const_fn_unstable( /// world into two functions: those that are safe to expose on stable (and hence may not use /// unstable features, not even recursively), and those that are not. pub fn is_fn_or_trait_safe_to_expose_on_stable(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + // A default body in a `#[const_trait]` is const-stable when the trait is const-stable. + if tcx.is_const_default_method(def_id) { + return is_fn_or_trait_safe_to_expose_on_stable(tcx, tcx.parent(def_id)); + } + match tcx.lookup_const_stability(def_id) { None => { // In a `staged_api` crate, we do enforce recursive const stability for all unmarked diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index bc43580a7f00d..b65430c348027 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -725,6 +725,12 @@ passes_target_feature_on_statement = .warn = {-passes_previously_accepted} .label = {passes_should_be_applied_to_fn.label} +passes_trait_impl_const_stability_mismatch = const stability on the impl does not match the const stability on the trait +passes_trait_impl_const_stability_mismatch_impl_stable = this impl is (implicitly) stable... +passes_trait_impl_const_stability_mismatch_impl_unstable = this impl is unstable... +passes_trait_impl_const_stability_mismatch_trait_stable = ...but the trait is stable +passes_trait_impl_const_stability_mismatch_trait_unstable = ...but the trait is unstable + passes_trait_impl_const_stable = trait implementations cannot be const stable yet .note = see issue #67792 for more information diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 51b5861ee0ae6..9bb9b2353dc86 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1552,6 +1552,45 @@ pub(crate) struct TraitImplConstStable { pub span: Span, } +#[derive(Diagnostic)] +#[diag(passes_trait_impl_const_stability_mismatch)] +pub(crate) struct TraitImplConstStabilityMismatch { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub impl_stability: ImplConstStability, + #[subdiagnostic] + pub trait_stability: TraitConstStability, +} + +#[derive(Subdiagnostic)] +pub(crate) enum TraitConstStability { + #[note(passes_trait_impl_const_stability_mismatch_trait_stable)] + Stable { + #[primary_span] + span: Span, + }, + #[note(passes_trait_impl_const_stability_mismatch_trait_unstable)] + Unstable { + #[primary_span] + span: Span, + }, +} + +#[derive(Subdiagnostic)] +pub(crate) enum ImplConstStability { + #[note(passes_trait_impl_const_stability_mismatch_impl_stable)] + Stable { + #[primary_span] + span: Span, + }, + #[note(passes_trait_impl_const_stability_mismatch_impl_unstable)] + Unstable { + #[primary_span] + span: Span, + }, +} + #[derive(Diagnostic)] #[diag(passes_unknown_feature, code = E0635)] pub(crate) struct UnknownFeature { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 8a4bdf3875c4b..0fcf6a80ec4de 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -313,7 +313,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { .map(|(stab, _span)| ConstStability::from_partial(stab, const_stability_indirect)); // If this is a const fn but not annotated with stability markers, see if we can inherit regular stability. - if fn_sig.is_some_and(|s| s.header.is_const()) && const_stab.is_none() && + if fn_sig.is_some_and(|s| s.header.is_const()) && const_stab.is_none() && // We only ever inherit unstable features. let Some(inherit_regular_stab) = final_stab.filter(|s| s.is_unstable()) @@ -826,24 +826,56 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { } } - // `#![feature(const_trait_impl)]` is unstable, so any impl declared stable - // needs to have an error emitted. if features.const_trait_impl() - && self.tcx.is_const_trait_impl(item.owner_id.to_def_id()) - && const_stab.is_some_and(|stab| stab.is_const_stable()) + && let hir::Constness::Const = constness { - self.tcx.dcx().emit_err(errors::TraitImplConstStable { span: item.span }); + let stable_or_implied_stable = match const_stab { + None => true, + Some(stab) if stab.is_const_stable() => { + // `#![feature(const_trait_impl)]` is unstable, so any impl declared stable + // needs to have an error emitted. + // Note: Remove this error once `const_trait_impl` is stabilized + self.tcx + .dcx() + .emit_err(errors::TraitImplConstStable { span: item.span }); + true + } + Some(_) => false, + }; + + if let Some(trait_id) = t.trait_def_id() + && let Some(const_stab) = self.tcx.lookup_const_stability(trait_id) + { + // the const stability of a trait impl must match the const stability on the trait. + if const_stab.is_const_stable() != stable_or_implied_stable { + let trait_span = self.tcx.def_ident_span(trait_id).unwrap(); + + let impl_stability = if stable_or_implied_stable { + errors::ImplConstStability::Stable { span: item.span } + } else { + errors::ImplConstStability::Unstable { span: item.span } + }; + let trait_stability = if const_stab.is_const_stable() { + errors::TraitConstStability::Stable { span: trait_span } + } else { + errors::TraitConstStability::Unstable { span: trait_span } + }; + + self.tcx.dcx().emit_err(errors::TraitImplConstStabilityMismatch { + span: item.span, + impl_stability, + trait_stability, + }); + } + } } } - match constness { - rustc_hir::Constness::Const => { - if let Some(def_id) = t.trait_def_id() { - // FIXME(const_trait_impl): Improve the span here. - self.tcx.check_const_stability(def_id, t.path.span, t.path.span); - } - } - rustc_hir::Constness::NotConst => {} + if let hir::Constness::Const = constness + && let Some(def_id) = t.trait_def_id() + { + // FIXME(const_trait_impl): Improve the span here. + self.tcx.check_const_stability(def_id, t.path.span, t.path.span); } for impl_item_ref in *items { diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index fe7ff2d9ede6a..54d79beca95ab 100644 --- a/library/core/src/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -96,6 +96,7 @@ pub trait Add { macro_rules! add_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_ops", issue = "90080")] impl const Add for $t { type Output = $t; diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index 11490ea2bfcb4..e74f5443ac2d8 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -150,6 +150,7 @@ pub trait Deref { } #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_deref", issue = "88955")] impl const Deref for &T { type Target = T; @@ -163,6 +164,7 @@ impl const Deref for &T { impl !DerefMut for &T {} #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_deref", issue = "88955")] impl const Deref for &mut T { type Target = T; @@ -273,6 +275,7 @@ pub trait DerefMut: ~const Deref { } #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_deref", issue = "88955")] impl const DerefMut for &mut T { fn deref_mut(&mut self) -> &mut T { *self diff --git a/tests/ui/traits/const-traits/staged-api.rs b/tests/ui/traits/const-traits/staged-api.rs index 9a030dafd6bc4..8dd7226fc298e 100644 --- a/tests/ui/traits/const-traits/staged-api.rs +++ b/tests/ui/traits/const-traits/staged-api.rs @@ -85,4 +85,36 @@ const fn implicitly_stable_const_context() { //~^ ERROR cannot use `#[feature(const_trait_impl)]` } +// check that const stability of impls and traits must match +#[const_trait] +#[rustc_const_unstable(feature = "beef", issue = "none")] +trait U {} + +#[const_trait] +#[rustc_const_stable(since = "0.0.0", feature = "beef2")] +trait S {} + +// implied stable +impl const U for u8 {} +//~^ const stability on the impl does not match the const stability on the trait + +#[rustc_const_stable(since = "0.0.0", feature = "beef2")] +impl const U for u16 {} +//~^ const stability on the impl does not match the const stability on the trait +//~| trait implementations cannot be const stable yet + +#[rustc_const_unstable(feature = "beef", issue = "none")] +impl const U for u32 {} + +// implied stable +impl const S for u8 {} + +#[rustc_const_stable(since = "0.0.0", feature = "beef2")] +impl const S for u16 {} +//~^ trait implementations cannot be const stable yet + +#[rustc_const_unstable(feature = "beef", issue = "none")] +impl const S for u32 {} +//~^ const stability on the impl does not match the const stability on the trait + fn main() {} diff --git a/tests/ui/traits/const-traits/staged-api.stderr b/tests/ui/traits/const-traits/staged-api.stderr index a7a7a1ee7215b..cdf577287eec5 100644 --- a/tests/ui/traits/const-traits/staged-api.stderr +++ b/tests/ui/traits/const-traits/staged-api.stderr @@ -1,3 +1,70 @@ +error: const stability on the impl does not match the const stability on the trait + --> $DIR/staged-api.rs:98:1 + | +LL | impl const U for u8 {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: this impl is (implicitly) stable... + --> $DIR/staged-api.rs:98:1 + | +LL | impl const U for u8 {} + | ^^^^^^^^^^^^^^^^^^^^^^ +note: ...but the trait is unstable + --> $DIR/staged-api.rs:91:7 + | +LL | trait U {} + | ^ + +error: trait implementations cannot be const stable yet + --> $DIR/staged-api.rs:102:1 + | +LL | impl const U for u16 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #67792 for more information + +error: const stability on the impl does not match the const stability on the trait + --> $DIR/staged-api.rs:102:1 + | +LL | impl const U for u16 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this impl is (implicitly) stable... + --> $DIR/staged-api.rs:102:1 + | +LL | impl const U for u16 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +note: ...but the trait is unstable + --> $DIR/staged-api.rs:91:7 + | +LL | trait U {} + | ^ + +error: trait implementations cannot be const stable yet + --> $DIR/staged-api.rs:113:1 + | +LL | impl const S for u16 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #67792 for more information + +error: const stability on the impl does not match the const stability on the trait + --> $DIR/staged-api.rs:117:1 + | +LL | impl const S for u32 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this impl is unstable... + --> $DIR/staged-api.rs:117:1 + | +LL | impl const S for u32 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +note: ...but the trait is stable + --> $DIR/staged-api.rs:95:7 + | +LL | trait S {} + | ^ + error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` --> $DIR/staged-api.rs:38:5 | @@ -323,5 +390,5 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn implicitly_stable_const_context() { | -error: aborting due to 19 previous errors +error: aborting due to 24 previous errors