From e40a4461eae543e067e6133817919f6ab7f04ac8 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 16 Apr 2023 21:27:25 -0400 Subject: [PATCH 01/20] Support loading version information from xz tarballs --- Cargo.lock | 9 ++++---- src/tools/build-manifest/Cargo.toml | 1 + src/tools/build-manifest/src/versions.rs | 28 +++++++++++++++++++++--- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 358b2f2e924c3..ed245dd3e7ece 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -288,6 +288,7 @@ dependencies = [ "sha2", "tar", "toml", + "xz2", ] [[package]] @@ -1939,9 +1940,9 @@ dependencies = [ [[package]] name = "lzma-sys" -version = "0.1.16" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f24f76ec44a8ac23a31915d6e326bca17ce88da03096f1ff194925dc714dac99" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" dependencies = [ "cc", "libc", @@ -5386,9 +5387,9 @@ dependencies = [ [[package]] name = "xz2" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c179869f34fc7c01830d3ce7ea2086bc3a07e0d35289b667d0a8bf910258926c" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" dependencies = [ "lzma-sys", ] diff --git a/src/tools/build-manifest/Cargo.toml b/src/tools/build-manifest/Cargo.toml index c437bde5ae69a..6c3b5bb00a347 100644 --- a/src/tools/build-manifest/Cargo.toml +++ b/src/tools/build-manifest/Cargo.toml @@ -9,6 +9,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" anyhow = "1.0.32" flate2 = "1.0.16" +xz2 = "0.1.7" tar = "0.4.29" sha2 = "0.10.1" rayon = "1.5.1" diff --git a/src/tools/build-manifest/src/versions.rs b/src/tools/build-manifest/src/versions.rs index dde9745afb785..7a4c15d01eadc 100644 --- a/src/tools/build-manifest/src/versions.rs +++ b/src/tools/build-manifest/src/versions.rs @@ -5,6 +5,7 @@ use std::fs::File; use std::io::Read; use std::path::{Path, PathBuf}; use tar::Archive; +use xz2::read::XzDecoder; const DEFAULT_TARGET: &str = "x86_64-unknown-linux-gnu"; @@ -175,9 +176,23 @@ impl Versions { } fn load_version_from_tarball(&mut self, package: &PkgType) -> Result { - let tarball_name = self.tarball_name(package, DEFAULT_TARGET)?; - let tarball = self.dist_path.join(tarball_name); + for ext in ["xz", "gz"] { + let info = + self.load_version_from_tarball_inner(&self.dist_path.join(self.archive_name( + package, + DEFAULT_TARGET, + &format!("tar.{}", ext), + )?))?; + if info.present { + return Ok(info); + } + } + + // If neither tarball is present, we fallback to returning the non-present info. + Ok(VersionInfo::default()) + } + fn load_version_from_tarball_inner(&mut self, tarball: &Path) -> Result { let file = match File::open(&tarball) { Ok(file) => file, Err(err) if err.kind() == std::io::ErrorKind::NotFound => { @@ -187,7 +202,14 @@ impl Versions { } Err(err) => return Err(err.into()), }; - let mut tar = Archive::new(GzDecoder::new(file)); + let mut tar: Archive> = + Archive::new(if tarball.extension().map_or(false, |e| e == "gz") { + Box::new(GzDecoder::new(file)) + } else if tarball.extension().map_or(false, |e| e == "xz") { + Box::new(XzDecoder::new(file)) + } else { + unimplemented!("tarball extension not recognized: {}", tarball.display()) + }); let mut version = None; let mut git_commit = None; From 20742ea21ab0556d22321481bed1e8a6dad88c56 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 26 Apr 2023 23:17:54 +0000 Subject: [PATCH 02/20] Adjust obligation cause code for find_and_report_unsatisfied_index_impl --- compiler/rustc_hir_typeck/src/expr.rs | 25 ++++++++++++------- .../ui/typeck/bad-index-due-to-nested.stderr | 18 ++++++++----- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 3ffc583d43f61..82913602f2d5c 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2821,7 +2821,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // but has nested obligations which are unsatisfied. for (base_t, _) in self.autoderef(base.span, base_t).silence_errors() { if let Some((_, index_ty, element_ty)) = - self.find_and_report_unsatisfied_index_impl(expr.hir_id, base, base_t) + self.find_and_report_unsatisfied_index_impl(base, base_t) { self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No); return element_ty; @@ -2880,7 +2880,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// predicates cause this to be, so that the user can add them to fix their code. fn find_and_report_unsatisfied_index_impl( &self, - index_expr_hir_id: HirId, base_expr: &hir::Expr<'_>, base_ty: Ty<'tcx>, ) -> Option<(ErrorGuaranteed, Ty<'tcx>, Ty<'tcx>)> { @@ -2913,13 +2912,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // in the first place. ocx.register_obligations(traits::predicates_for_generics( |idx, span| { - traits::ObligationCause::new( - base_expr.span, - self.body_id, - if span.is_dummy() { - traits::ExprItemObligation(impl_def_id, index_expr_hir_id, idx) - } else { - traits::ExprBindingObligation(impl_def_id, span, index_expr_hir_id, idx) + cause.clone().derived_cause( + ty::Binder::dummy(ty::TraitPredicate { + trait_ref: impl_trait_ref, + polarity: ty::ImplPolarity::Positive, + constness: ty::BoundConstness::NotConst, + }), + |derived| { + traits::ImplDerivedObligation(Box::new( + traits::ImplDerivedObligationCause { + derived, + impl_or_alias_def_id: impl_def_id, + impl_def_predicate_index: Some(idx), + span, + }, + )) }, ) }, diff --git a/tests/ui/typeck/bad-index-due-to-nested.stderr b/tests/ui/typeck/bad-index-due-to-nested.stderr index e03b06b336eef..cdb23372c4ba5 100644 --- a/tests/ui/typeck/bad-index-due-to-nested.stderr +++ b/tests/ui/typeck/bad-index-due-to-nested.stderr @@ -4,11 +4,14 @@ error[E0277]: the trait bound `K: Hash` is not satisfied LL | map[k] | ^^^ the trait `Hash` is not implemented for `K` | -note: required by a bound in ` as Index<&K>>` - --> $DIR/bad-index-due-to-nested.rs:9:8 +note: required for `HashMap` to implement `Index<&K>` + --> $DIR/bad-index-due-to-nested.rs:7:12 | +LL | impl Index<&K> for HashMap + | ^^^^^^^^^ ^^^^^^^^^^^^^ +LL | where LL | K: Hash, - | ^^^^ required by this bound in ` as Index<&K>>` + | ---- unsatisfied trait bound introduced here help: consider restricting type parameter `K` | LL | fn index<'a, K: std::hash::Hash, V>(map: &'a HashMap, k: K) -> &'a V { @@ -20,11 +23,14 @@ error[E0277]: the trait bound `V: Copy` is not satisfied LL | map[k] | ^^^ the trait `Copy` is not implemented for `V` | -note: required by a bound in ` as Index<&K>>` - --> $DIR/bad-index-due-to-nested.rs:10:8 +note: required for `HashMap` to implement `Index<&K>` + --> $DIR/bad-index-due-to-nested.rs:7:12 | +LL | impl Index<&K> for HashMap + | ^^^^^^^^^ ^^^^^^^^^^^^^ +... LL | V: Copy, - | ^^^^ required by this bound in ` as Index<&K>>` + | ---- unsatisfied trait bound introduced here help: consider restricting type parameter `V` | LL | fn index<'a, K, V: std::marker::Copy>(map: &'a HashMap, k: K) -> &'a V { From 25e9b79060d67f1ac0dc2315ff627b6359192bfc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 27 Apr 2023 12:22:21 +0200 Subject: [PATCH 03/20] interpret: fail more gracefully on uninit unsized locals --- compiler/rustc_const_eval/src/const_eval/valtrees.rs | 2 +- compiler/rustc_const_eval/src/interpret/operand.rs | 6 ++++++ compiler/rustc_middle/src/mir/interpret/error.rs | 4 ++++ tests/ui/const_prop/unsized-local-ice.rs | 9 +++++++++ 4 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 tests/ui/const_prop/unsized-local-ice.rs diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 4d54c01830bc0..b10f2e9f862db 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -337,7 +337,7 @@ fn valtree_into_mplace<'tcx>( match ty.kind() { ty::FnDef(_, _) => { - ecx.write_immediate(Immediate::Uninit, &place.into()).unwrap(); + // Zero-sized type, nothing to do. } ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { let scalar_int = valtree.unwrap_leaf(); diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 5310ef0bb3ed0..a7f66071fe2bb 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -245,6 +245,12 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { if self.layout.is_unsized() { + if matches!(self.op, Operand::Immediate(Immediate::Uninit)) { + // Uninit unsized places shouldn't occur. In the interpreter we have them + // temporarily for unsized arguments before their value is put in; in ConstProp they + // remain uninit and this code can actually be reached. + throw_inval!(UninitUnsizedLocal); + } // There are no unsized immediates. self.assert_mem_place().len(cx) } else { diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index c5137cf0666ea..e45284ca50655 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -134,6 +134,9 @@ pub enum InvalidProgramInfo<'tcx> { FnAbiAdjustForForeignAbi(call::AdjustForForeignAbiError), /// SizeOf of unsized type was requested. SizeOfUnsizedType(Ty<'tcx>), + /// An unsized local was accessed without having been initialized. + /// This is not meaningful as we can't even have backing memory for such locals. + UninitUnsizedLocal, } impl fmt::Display for InvalidProgramInfo<'_> { @@ -150,6 +153,7 @@ impl fmt::Display for InvalidProgramInfo<'_> { Layout(ref err) => write!(f, "{err}"), FnAbiAdjustForForeignAbi(ref err) => write!(f, "{err}"), SizeOfUnsizedType(ty) => write!(f, "size_of called on unsized type `{ty}`"), + UninitUnsizedLocal => write!(f, "unsized local is used while uninitialized"), } } } diff --git a/tests/ui/const_prop/unsized-local-ice.rs b/tests/ui/const_prop/unsized-local-ice.rs new file mode 100644 index 0000000000000..c725b3238ea64 --- /dev/null +++ b/tests/ui/const_prop/unsized-local-ice.rs @@ -0,0 +1,9 @@ +// build-pass +//! Regression test for . +#![feature(unsized_fn_params)] + +pub fn take_unsized_slice(s: [u8]) { + s[0]; +} + +fn main() {} From 6fcf165586364dc3feff57f3aa4eb4f637a6d4d5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 28 Apr 2023 12:54:39 +0200 Subject: [PATCH 04/20] move some const-prop tests to appropriate folder --- tests/ui/{consts => const_prop}/const-prop-ice.rs | 0 tests/ui/{consts => const_prop}/const-prop-ice.stderr | 0 tests/ui/{consts => const_prop}/const-prop-ice2.rs | 0 tests/ui/{consts => const_prop}/const-prop-ice2.stderr | 0 tests/ui/{consts => const_prop}/const-prop-ice3.rs | 0 tests/ui/{consts => const_prop}/const-prop-overflowing-casts.rs | 0 .../ui/{consts => const_prop}/const-prop-read-static-in-const.rs | 0 .../{consts => const_prop}/const-prop-read-static-in-const.stderr | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{consts => const_prop}/const-prop-ice.rs (100%) rename tests/ui/{consts => const_prop}/const-prop-ice.stderr (100%) rename tests/ui/{consts => const_prop}/const-prop-ice2.rs (100%) rename tests/ui/{consts => const_prop}/const-prop-ice2.stderr (100%) rename tests/ui/{consts => const_prop}/const-prop-ice3.rs (100%) rename tests/ui/{consts => const_prop}/const-prop-overflowing-casts.rs (100%) rename tests/ui/{consts => const_prop}/const-prop-read-static-in-const.rs (100%) rename tests/ui/{consts => const_prop}/const-prop-read-static-in-const.stderr (100%) diff --git a/tests/ui/consts/const-prop-ice.rs b/tests/ui/const_prop/const-prop-ice.rs similarity index 100% rename from tests/ui/consts/const-prop-ice.rs rename to tests/ui/const_prop/const-prop-ice.rs diff --git a/tests/ui/consts/const-prop-ice.stderr b/tests/ui/const_prop/const-prop-ice.stderr similarity index 100% rename from tests/ui/consts/const-prop-ice.stderr rename to tests/ui/const_prop/const-prop-ice.stderr diff --git a/tests/ui/consts/const-prop-ice2.rs b/tests/ui/const_prop/const-prop-ice2.rs similarity index 100% rename from tests/ui/consts/const-prop-ice2.rs rename to tests/ui/const_prop/const-prop-ice2.rs diff --git a/tests/ui/consts/const-prop-ice2.stderr b/tests/ui/const_prop/const-prop-ice2.stderr similarity index 100% rename from tests/ui/consts/const-prop-ice2.stderr rename to tests/ui/const_prop/const-prop-ice2.stderr diff --git a/tests/ui/consts/const-prop-ice3.rs b/tests/ui/const_prop/const-prop-ice3.rs similarity index 100% rename from tests/ui/consts/const-prop-ice3.rs rename to tests/ui/const_prop/const-prop-ice3.rs diff --git a/tests/ui/consts/const-prop-overflowing-casts.rs b/tests/ui/const_prop/const-prop-overflowing-casts.rs similarity index 100% rename from tests/ui/consts/const-prop-overflowing-casts.rs rename to tests/ui/const_prop/const-prop-overflowing-casts.rs diff --git a/tests/ui/consts/const-prop-read-static-in-const.rs b/tests/ui/const_prop/const-prop-read-static-in-const.rs similarity index 100% rename from tests/ui/consts/const-prop-read-static-in-const.rs rename to tests/ui/const_prop/const-prop-read-static-in-const.rs diff --git a/tests/ui/consts/const-prop-read-static-in-const.stderr b/tests/ui/const_prop/const-prop-read-static-in-const.stderr similarity index 100% rename from tests/ui/consts/const-prop-read-static-in-const.stderr rename to tests/ui/const_prop/const-prop-read-static-in-const.stderr From b4ba2f0bf469da7a5fea38f2ef2a9bd069736eba Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 1 May 2023 17:09:59 +1000 Subject: [PATCH 05/20] Change rlink serialization from `MemEncoder` to `FileEncoder`. Because we're writing to a file, so `FileEncoder` is better because we don't have to write all the data to memory first. --- compiler/rustc_codegen_ssa/src/lib.rs | 10 +++++++--- compiler/rustc_interface/src/queries.rs | 3 +-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 26d55618b490a..c3cc17c255b46 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -31,7 +31,7 @@ use rustc_middle::dep_graph::WorkProduct; use rustc_middle::middle::dependency_format::Dependencies; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_middle::ty::query::{ExternProviders, Providers}; -use rustc_serialize::opaque::{MemDecoder, MemEncoder}; +use rustc_serialize::opaque::{FileEncoder, MemDecoder}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT}; use rustc_session::cstore::{self, CrateSource}; @@ -39,6 +39,7 @@ use rustc_session::utils::NativeLibKind; use rustc_span::symbol::Symbol; use rustc_span::DebuggerVisualizerFile; use std::collections::BTreeSet; +use std::io; use std::path::{Path, PathBuf}; pub mod back; @@ -215,8 +216,11 @@ const RLINK_MAGIC: &[u8] = b"rustlink"; const RUSTC_VERSION: Option<&str> = option_env!("CFG_VERSION"); impl CodegenResults { - pub fn serialize_rlink(codegen_results: &CodegenResults) -> Vec { - let mut encoder = MemEncoder::new(); + pub fn serialize_rlink( + rlink_file: &Path, + codegen_results: &CodegenResults, + ) -> Result { + let mut encoder = FileEncoder::new(rlink_file)?; encoder.emit_raw_bytes(RLINK_MAGIC); // `emit_raw_bytes` is used to make sure that the version representation does not depend on // Encoder's inner representation of `u32`. diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 77fbbf64a0ad2..6483d51a0b9a9 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -368,9 +368,8 @@ impl Linker { } if sess.opts.unstable_opts.no_link { - let encoded = CodegenResults::serialize_rlink(&codegen_results); let rlink_file = self.prepare_outputs.with_extension(config::RLINK_EXT); - std::fs::write(&rlink_file, encoded) + CodegenResults::serialize_rlink(&rlink_file, &codegen_results) .map_err(|error| sess.emit_fatal(FailedWritingFile { path: &rlink_file, error }))?; return Ok(()); } From 0453cda59e4eb09e2bc632c41420da22bfeac242 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Mon, 1 May 2023 14:36:52 +0100 Subject: [PATCH 06/20] Don't bail out early when checking invalid `repr` attr --- compiler/rustc_passes/src/check_attr.rs | 8 +------- tests/ui/repr/invalid_repr_list_help.rs | 5 +++++ tests/ui/repr/invalid_repr_list_help.stderr | 20 +++++++++++++++++++- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 3f28ac26f8617..06aa273791526 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -101,12 +101,11 @@ impl CheckAttrVisitor<'_> { item: Option>, ) { let mut doc_aliases = FxHashMap::default(); - let mut is_valid = true; let mut specified_inline = None; let mut seen = FxHashMap::default(); let attrs = self.tcx.hir().attrs(hir_id); for attr in attrs { - let attr_is_valid = match attr.name_or_empty() { + match attr.name_or_empty() { sym::do_not_recommend => self.check_do_not_recommend(attr.span, target), sym::inline => self.check_inline(hir_id, attr, span, target), sym::no_coverage => self.check_no_coverage(hir_id, attr, span, target), @@ -188,7 +187,6 @@ impl CheckAttrVisitor<'_> { sym::link_ordinal => self.check_link_ordinal(&attr, span, target), _ => true, }; - is_valid &= attr_is_valid; // lint-only checks match attr.name_or_empty() { @@ -255,10 +253,6 @@ impl CheckAttrVisitor<'_> { self.check_unused_attribute(hir_id, attr) } - if !is_valid { - return; - } - self.check_repr(attrs, span, target, item, hir_id); self.check_used(attrs, target); } diff --git a/tests/ui/repr/invalid_repr_list_help.rs b/tests/ui/repr/invalid_repr_list_help.rs index c320984536cac..785ffb1e0f4cb 100644 --- a/tests/ui/repr/invalid_repr_list_help.rs +++ b/tests/ui/repr/invalid_repr_list_help.rs @@ -15,3 +15,8 @@ pub struct OwO3 { pub enum OwO4 { UwU = 1, } + +#[repr(uwu)] //~ERROR: unrecognized representation hint +#[doc(owo)] //~WARN: unknown `doc` attribute + //~^ WARN: this was previously +pub struct Owo5; diff --git a/tests/ui/repr/invalid_repr_list_help.stderr b/tests/ui/repr/invalid_repr_list_help.stderr index 2acd56d9a3234..48a6af3dd4c11 100644 --- a/tests/ui/repr/invalid_repr_list_help.stderr +++ b/tests/ui/repr/invalid_repr_list_help.stderr @@ -30,6 +30,24 @@ LL | #[repr(uwu, u8)] | = help: valid reprs are `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` -error: aborting due to 4 previous errors +warning: unknown `doc` attribute `owo` + --> $DIR/invalid_repr_list_help.rs:20:7 + | +LL | #[doc(owo)] + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 + = note: `#[warn(invalid_doc_attributes)]` on by default + +error[E0552]: unrecognized representation hint + --> $DIR/invalid_repr_list_help.rs:19:8 + | +LL | #[repr(uwu)] + | ^^^ + | + = help: valid reprs are `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` + +error: aborting due to 5 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0552`. From bc68de94c87537edd43f22606d84e8a2ad48e413 Mon Sep 17 00:00:00 2001 From: ozkanonur Date: Mon, 1 May 2023 18:36:30 +0300 Subject: [PATCH 07/20] remove pointless `FIXME` in `bootstrap::download` The suggestion given by `FIXME` to use `CompilerMetadata` for `download_toolchain` in `bootstrap::download` can result in more confusion. This is because `stamp_key` is not always a date; it can also be a commit hash. Additionally, unlike in `download_beta_toolchain`, in the `download_ci_rustc` function, `version` and `commit` values are calculated separately. Signed-off-by: ozkanonur --- src/bootstrap/download.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bootstrap/download.rs b/src/bootstrap/download.rs index c1cf9b93fb356..3e82a381a1b2d 100644 --- a/src/bootstrap/download.rs +++ b/src/bootstrap/download.rs @@ -427,7 +427,6 @@ impl Config { fn download_toolchain( &self, - // FIXME(ozkanonur) use CompilerMetadata instead of `version: &str` version: &str, sysroot: &str, stamp_key: &str, From 8d359e4385052f012d8d0c2e57a0bcfe54462d44 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 2 May 2023 11:01:58 +1000 Subject: [PATCH 08/20] Move some `Encodable`/`Decodable` tests. Round-trip encoding/decoding of many types is tested in `compiler/rustc_serialize/tests/opaque.rs`. There is also a small amount of encoding/decoding testing in three files in `tests/ui-fulldeps`. There is no obvious reason why these three files are necessary. They were originally added in 2014. Maybe it wasn't possible for a proc macro to run in a unit test back then? This commit just moves the testing from those three files into the unit test. --- compiler/rustc_serialize/tests/opaque.rs | 39 ++++++++++++++++ .../deriving-encodable-decodable-box.rs | 34 -------------- ...riving-encodable-decodable-cell-refcell.rs | 44 ------------------- tests/ui-fulldeps/issue-14021.rs | 33 -------------- 4 files changed, 39 insertions(+), 111 deletions(-) delete mode 100644 tests/ui-fulldeps/deriving-encodable-decodable-box.rs delete mode 100644 tests/ui-fulldeps/deriving-encodable-decodable-cell-refcell.rs delete mode 100644 tests/ui-fulldeps/issue-14021.rs diff --git a/compiler/rustc_serialize/tests/opaque.rs b/compiler/rustc_serialize/tests/opaque.rs index 5e7dd18aa8408..7a7db99b168e6 100644 --- a/compiler/rustc_serialize/tests/opaque.rs +++ b/compiler/rustc_serialize/tests/opaque.rs @@ -251,3 +251,42 @@ fn test_tuples() { check_round_trip(vec![(1234567isize, 100000000000000u64, 99999999999999i64)]); check_round_trip(vec![(String::new(), "some string".to_string())]); } + +#[test] +fn test_unit_like_struct() { + #[derive(Encodable, Decodable, PartialEq, Debug)] + struct UnitLikeStruct; + + check_round_trip(vec![UnitLikeStruct]); +} + +#[test] +fn test_box() { + #[derive(Encodable, Decodable, PartialEq, Debug)] + struct A { + foo: Box<[bool]>, + } + + let obj = A { foo: Box::new([true, false]) }; + check_round_trip(vec![obj]); +} + +#[test] +fn test_cell() { + use std::cell::{Cell, RefCell}; + + #[derive(Encodable, Decodable, PartialEq, Debug)] + struct A { + baz: isize, + } + + #[derive(Encodable, Decodable, PartialEq, Debug)] + struct B { + foo: Cell, + bar: RefCell, + } + + let obj = B { foo: Cell::new(true), bar: RefCell::new(A { baz: 2 }) }; + check_round_trip(vec![obj]); +} + diff --git a/tests/ui-fulldeps/deriving-encodable-decodable-box.rs b/tests/ui-fulldeps/deriving-encodable-decodable-box.rs deleted file mode 100644 index 1c376f59e5174..0000000000000 --- a/tests/ui-fulldeps/deriving-encodable-decodable-box.rs +++ /dev/null @@ -1,34 +0,0 @@ -// run-pass - -#![allow(unused_imports)] -#![feature(rustc_private)] - -extern crate rustc_macros; -extern crate rustc_serialize; - -// Necessary to pull in object code as the rest of the rustc crates are shipped only as rmeta -// files. -#[allow(unused_extern_crates)] -extern crate rustc_driver; - -use rustc_macros::{Decodable, Encodable}; -use rustc_serialize::opaque::{MemDecoder, MemEncoder}; -use rustc_serialize::{Decodable, Encodable, Encoder}; - -#[derive(Encodable, Decodable)] -struct A { - foo: Box<[bool]>, -} - -fn main() { - let obj = A { foo: Box::new([true, false]) }; - - let mut encoder = MemEncoder::new(); - obj.encode(&mut encoder); - let data = encoder.finish(); - - let mut decoder = MemDecoder::new(&data, 0); - let obj2 = A::decode(&mut decoder); - - assert_eq!(obj.foo, obj2.foo); -} diff --git a/tests/ui-fulldeps/deriving-encodable-decodable-cell-refcell.rs b/tests/ui-fulldeps/deriving-encodable-decodable-cell-refcell.rs deleted file mode 100644 index 844d40f2ecd6a..0000000000000 --- a/tests/ui-fulldeps/deriving-encodable-decodable-cell-refcell.rs +++ /dev/null @@ -1,44 +0,0 @@ -// run-pass - -#![allow(unused_imports)] -// This briefly tests the capability of `Cell` and `RefCell` to implement the -// `Encodable` and `Decodable` traits via `#[derive(Encodable, Decodable)]` -#![feature(rustc_private)] - -extern crate rustc_macros; -extern crate rustc_serialize; - -// Necessary to pull in object code as the rest of the rustc crates are shipped only as rmeta -// files. -#[allow(unused_extern_crates)] -extern crate rustc_driver; - -use rustc_macros::{Decodable, Encodable}; -use rustc_serialize::opaque::{MemDecoder, MemEncoder}; -use rustc_serialize::{Decodable, Encodable, Encoder}; -use std::cell::{Cell, RefCell}; - -#[derive(Encodable, Decodable)] -struct A { - baz: isize, -} - -#[derive(Encodable, Decodable)] -struct B { - foo: Cell, - bar: RefCell, -} - -fn main() { - let obj = B { foo: Cell::new(true), bar: RefCell::new(A { baz: 2 }) }; - - let mut encoder = MemEncoder::new(); - obj.encode(&mut encoder); - let data = encoder.finish(); - - let mut decoder = MemDecoder::new(&data, 0); - let obj2 = B::decode(&mut decoder); - - assert_eq!(obj.foo.get(), obj2.foo.get()); - assert_eq!(obj.bar.borrow().baz, obj2.bar.borrow().baz); -} diff --git a/tests/ui-fulldeps/issue-14021.rs b/tests/ui-fulldeps/issue-14021.rs deleted file mode 100644 index 309b5c4a03d57..0000000000000 --- a/tests/ui-fulldeps/issue-14021.rs +++ /dev/null @@ -1,33 +0,0 @@ -// run-pass - -#![allow(unused_mut)] -#![allow(unused_imports)] -#![feature(rustc_private)] - -extern crate rustc_macros; -extern crate rustc_serialize; - -// Necessary to pull in object code as the rest of the rustc crates are shipped only as rmeta -// files. -#[allow(unused_extern_crates)] -extern crate rustc_driver; - -use rustc_macros::{Decodable, Encodable}; -use rustc_serialize::opaque::{MemDecoder, MemEncoder}; -use rustc_serialize::{Decodable, Encodable, Encoder}; - -#[derive(Encodable, Decodable, PartialEq, Debug)] -struct UnitLikeStruct; - -pub fn main() { - let obj = UnitLikeStruct; - - let mut encoder = MemEncoder::new(); - obj.encode(&mut encoder); - let data = encoder.finish(); - - let mut decoder = MemDecoder::new(&data, 0); - let obj2 = UnitLikeStruct::decode(&mut decoder); - - assert_eq!(obj, obj2); -} From ebee3f8515c6f5189b69ae56919ab5bba934aabe Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 1 May 2023 18:51:05 +1000 Subject: [PATCH 09/20] Remove `MemEncoder`. It's only used in tests. Which is bad, because it means that `FileEncoder` is used in the compiler but isn't used in tests! `tests/opaque.rs` now tests encoding/decoding round-trips via file. Because this is slower than memory, this commit also adjusts the `u16`/`i16` tests so they are more like the `u32`/`i32` tests, i.e. they don't test every possible value. --- Cargo.lock | 1 + compiler/rustc_serialize/Cargo.toml | 1 + compiler/rustc_serialize/src/opaque.rs | 129 +---------------------- compiler/rustc_serialize/tests/opaque.rs | 19 ++-- 4 files changed, 17 insertions(+), 133 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f6ee8157b40ef..bff68df401425 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4059,6 +4059,7 @@ dependencies = [ "indexmap", "rustc_macros", "smallvec", + "tempfile", "thin-vec", ] diff --git a/compiler/rustc_serialize/Cargo.toml b/compiler/rustc_serialize/Cargo.toml index e4dbb8a637cea..6046780685ad8 100644 --- a/compiler/rustc_serialize/Cargo.toml +++ b/compiler/rustc_serialize/Cargo.toml @@ -10,3 +10,4 @@ thin-vec = "0.2.12" [dev-dependencies] rustc_macros = { path = "../rustc_macros" } +tempfile = "3.2" diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index 0f6e4b329b87e..a2ec318df6d83 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -12,118 +12,14 @@ use std::ptr; // Encoder // ----------------------------------------------------------------------------- -pub struct MemEncoder { - pub data: Vec, -} - -impl MemEncoder { - pub fn new() -> MemEncoder { - MemEncoder { data: vec![] } - } - - #[inline] - pub fn position(&self) -> usize { - self.data.len() - } - - pub fn finish(self) -> Vec { - self.data - } -} - -macro_rules! write_leb128 { - ($enc:expr, $value:expr, $int_ty:ty, $fun:ident) => {{ - const MAX_ENCODED_LEN: usize = $crate::leb128::max_leb128_len::<$int_ty>(); - let old_len = $enc.data.len(); - - if MAX_ENCODED_LEN > $enc.data.capacity() - old_len { - $enc.data.reserve(MAX_ENCODED_LEN); - } - - // SAFETY: The above check and `reserve` ensures that there is enough - // room to write the encoded value to the vector's internal buffer. - unsafe { - let buf = &mut *($enc.data.as_mut_ptr().add(old_len) - as *mut [MaybeUninit; MAX_ENCODED_LEN]); - let encoded = leb128::$fun(buf, $value); - $enc.data.set_len(old_len + encoded.len()); - } - }}; -} - -impl Encoder for MemEncoder { - #[inline] - fn emit_usize(&mut self, v: usize) { - write_leb128!(self, v, usize, write_usize_leb128) - } - - #[inline] - fn emit_u128(&mut self, v: u128) { - write_leb128!(self, v, u128, write_u128_leb128); - } - - #[inline] - fn emit_u64(&mut self, v: u64) { - write_leb128!(self, v, u64, write_u64_leb128); - } - - #[inline] - fn emit_u32(&mut self, v: u32) { - write_leb128!(self, v, u32, write_u32_leb128); - } - - #[inline] - fn emit_u16(&mut self, v: u16) { - self.data.extend_from_slice(&v.to_le_bytes()); - } - - #[inline] - fn emit_u8(&mut self, v: u8) { - self.data.push(v); - } - - #[inline] - fn emit_isize(&mut self, v: isize) { - write_leb128!(self, v, isize, write_isize_leb128) - } - - #[inline] - fn emit_i128(&mut self, v: i128) { - write_leb128!(self, v, i128, write_i128_leb128) - } - - #[inline] - fn emit_i64(&mut self, v: i64) { - write_leb128!(self, v, i64, write_i64_leb128) - } - - #[inline] - fn emit_i32(&mut self, v: i32) { - write_leb128!(self, v, i32, write_i32_leb128) - } - - #[inline] - fn emit_i16(&mut self, v: i16) { - self.data.extend_from_slice(&v.to_le_bytes()); - } - - #[inline] - fn emit_raw_bytes(&mut self, s: &[u8]) { - self.data.extend_from_slice(s); - } -} - pub type FileEncodeResult = Result; /// `FileEncoder` encodes data to file via fixed-size buffer. /// -/// When encoding large amounts of data to a file, using `FileEncoder` may be -/// preferred over using `MemEncoder` to encode to a `Vec`, and then writing the -/// `Vec` to file, as the latter uses as much memory as there is encoded data, -/// while the former uses the fixed amount of memory allocated to the buffer. -/// `FileEncoder` also has the advantage of not needing to reallocate as data -/// is appended to it, but the disadvantage of requiring more error handling, -/// which has some runtime overhead. +/// There used to be a `MemEncoder` type that encoded all the data into a +/// `Vec`. `FileEncoder` is better because its memory use is determined by the +/// size of the buffer, rather than the full length of the encoded data, and +/// because it doesn't need to reallocate memory along the way. pub struct FileEncoder { /// The input buffer. For adequate performance, we need more control over /// buffering than `BufWriter` offers. If `BufWriter` ever offers a raw @@ -645,13 +541,6 @@ impl<'a> Decoder for MemDecoder<'a> { // Specialize encoding byte slices. This specialization also applies to encoding `Vec`s, etc., // since the default implementations call `encode` on their slices internally. -impl Encodable for [u8] { - fn encode(&self, e: &mut MemEncoder) { - Encoder::emit_usize(e, self.len()); - e.emit_raw_bytes(self); - } -} - impl Encodable for [u8] { fn encode(&self, e: &mut FileEncoder) { Encoder::emit_usize(e, self.len()); @@ -675,16 +564,6 @@ impl IntEncodedWithFixedSize { pub const ENCODED_SIZE: usize = 8; } -impl Encodable for IntEncodedWithFixedSize { - #[inline] - fn encode(&self, e: &mut MemEncoder) { - let _start_pos = e.position(); - e.emit_raw_bytes(&self.0.to_le_bytes()); - let _end_pos = e.position(); - debug_assert_eq!((_end_pos - _start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); - } -} - impl Encodable for IntEncodedWithFixedSize { #[inline] fn encode(&self, e: &mut FileEncoder) { diff --git a/compiler/rustc_serialize/tests/opaque.rs b/compiler/rustc_serialize/tests/opaque.rs index 7a7db99b168e6..861091688bb2d 100644 --- a/compiler/rustc_serialize/tests/opaque.rs +++ b/compiler/rustc_serialize/tests/opaque.rs @@ -1,9 +1,10 @@ #![allow(rustc::internal)] use rustc_macros::{Decodable, Encodable}; -use rustc_serialize::opaque::{MemDecoder, MemEncoder}; +use rustc_serialize::opaque::{MemDecoder, FileEncoder}; use rustc_serialize::{Decodable, Encodable}; use std::fmt::Debug; +use std::fs; #[derive(PartialEq, Clone, Debug, Encodable, Decodable)] struct Struct { @@ -27,18 +28,21 @@ struct Struct { } fn check_round_trip< - T: Encodable + for<'a> Decodable> + PartialEq + Debug, + T: Encodable + for<'a> Decodable> + PartialEq + Debug, >( values: Vec, ) { - let mut encoder = MemEncoder::new(); + let tmpfile = tempfile::NamedTempFile::new().unwrap(); + let tmpfile = tmpfile.path(); + + let mut encoder = FileEncoder::new(&tmpfile).unwrap(); for value in &values { Encodable::encode(value, &mut encoder); } + encoder.finish().unwrap(); - let data = encoder.finish(); + let data = fs::read(&tmpfile).unwrap(); let mut decoder = MemDecoder::new(&data[..], 0); - for value in values { let decoded = Decodable::decode(&mut decoder); assert_eq!(value, decoded); @@ -61,7 +65,7 @@ fn test_u8() { #[test] fn test_u16() { - for i in u16::MIN..u16::MAX { + for i in [u16::MIN, 111, 3333, 55555, u16::MAX] { check_round_trip(vec![1, 2, 3, i, i, i]); } } @@ -92,7 +96,7 @@ fn test_i8() { #[test] fn test_i16() { - for i in i16::MIN..i16::MAX { + for i in [i16::MIN, -100, 0, 101, i16::MAX] { check_round_trip(vec![-1, 2, -3, i, i, i, 2]); } } @@ -289,4 +293,3 @@ fn test_cell() { let obj = B { foo: Cell::new(true), bar: RefCell::new(A { baz: 2 }) }; check_round_trip(vec![obj]); } - From ef77dd232d7eed9d82b0719d7fc683924a3dc2de Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 27 Apr 2023 18:34:43 +0300 Subject: [PATCH 10/20] resolve: One more attempt to simplify `module_children` --- compiler/rustc_metadata/src/rmeta/encoder.rs | 16 ++++++++------- compiler/rustc_metadata/src/rmeta/mod.rs | 6 ++++++ compiler/rustc_middle/src/ty/context.rs | 21 ++++++-------------- compiler/rustc_middle/src/ty/mod.rs | 3 +-- compiler/rustc_privacy/src/lib.rs | 8 +++++--- compiler/rustc_resolve/src/imports.rs | 19 ++++++------------ compiler/rustc_resolve/src/lib.rs | 9 +++------ src/librustdoc/clean/inline.rs | 3 ++- src/librustdoc/clean/mod.rs | 4 ++-- src/librustdoc/visit_ast.rs | 9 +++++---- 10 files changed, 45 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index f5ffdd27cae3f..3253d0a905744 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1364,9 +1364,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record!(self.tables.params_in_repr[def_id] <- params_in_repr); if adt_def.is_enum() { - let module_children = tcx.module_children_non_reexports(local_def_id); + let module_children = tcx.module_children_local(local_def_id); record_array!(self.tables.module_children_non_reexports[def_id] <- - module_children.iter().map(|def_id| def_id.local_def_index)); + module_children.iter().map(|child| child.res.def_id().index)); } else { // For non-enum, there is only one variant, and its def_id is the adt's. debug_assert_eq!(adt_def.variants().len(), 1); @@ -1412,12 +1412,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // Encode this here because we don't do it in encode_def_ids. record!(self.tables.expn_that_defined[def_id] <- tcx.expn_that_defined(local_def_id)); } else { - let non_reexports = tcx.module_children_non_reexports(local_def_id); + let module_children = tcx.module_children_local(local_def_id); + record_array!(self.tables.module_children_non_reexports[def_id] <- - non_reexports.iter().map(|def_id| def_id.local_def_index)); + module_children.iter().filter(|child| child.reexport_chain.is_empty()) + .map(|child| child.res.def_id().index)); record_defaulted_array!(self.tables.module_children_reexports[def_id] <- - tcx.module_children_reexports(local_def_id)); + module_children.iter().filter(|child| !child.reexport_chain.is_empty())); } } @@ -1676,9 +1678,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { hir::ItemKind::Trait(..) => { record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); - let module_children = tcx.module_children_non_reexports(item.owner_id.def_id); + let module_children = tcx.module_children_local(item.owner_id.def_id); record_array!(self.tables.module_children_non_reexports[def_id] <- - module_children.iter().map(|def_id| def_id.local_def_index)); + module_children.iter().map(|child| child.res.def_id().index)); let associated_item_def_ids = self.tcx.associated_item_def_ids(def_id); record_associated_item_def_ids(self, associated_item_def_ids); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index dd02463e16a07..84f6b7f934dc3 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -357,10 +357,16 @@ define_tables! { associated_types_for_impl_traits_in_associated_fn: Table>, opt_rpitit_info: Table>>, unused_generic_params: Table, + // Reexported names are not associated with individual `DefId`s, + // e.g. a glob import can introduce a lot of names, all with the same `DefId`. + // That's why the encoded list needs to contain `ModChild` structures describing all the names + // individually instead of `DefId`s. module_children_reexports: Table>, - optional: attributes: Table>, + // For non-reexported names in a module every name is associated with a separate `DefId`, + // so we can take their names, visibilities etc from other encoded tables. module_children_non_reexports: Table>, associated_item_or_field_def_ids: Table>, opt_def_kind: Table, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index a309eaf048d2a..bf78b379986d8 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2414,26 +2414,17 @@ impl<'tcx> TyCtxt<'tcx> { } } - /// Named module children from all items except `use` and `extern crate` imports. - /// - /// In addition to regular items this list also includes struct or variant constructors, and + /// Named module children from all kinds of items, including imports. + /// In addition to regular items this list also includes struct and variant constructors, and /// items inside `extern {}` blocks because all of them introduce names into parent module. - /// For non-reexported children every such name is associated with a separate `DefId`. /// /// Module here is understood in name resolution sense - it can be a `mod` item, /// or a crate root, or an enum, or a trait. - pub fn module_children_non_reexports(self, def_id: LocalDefId) -> &'tcx [LocalDefId] { - self.resolutions(()).module_children_non_reexports.get(&def_id).map_or(&[], |v| &v[..]) - } - - /// Named module children from `use` and `extern crate` imports. /// - /// Reexported names are not associated with individual `DefId`s, - /// e.g. a glob import can introduce a lot of names, all with the same `DefId`. - /// That's why the list needs to contain `ModChild` structures describing all the names - /// individually instead of `DefId`s. - pub fn module_children_reexports(self, def_id: LocalDefId) -> &'tcx [ModChild] { - self.resolutions(()).module_children_reexports.get(&def_id).map_or(&[], |v| &v[..]) + /// This is not a query, making it a query causes perf regressions + /// (probably due to hashing spans in `ModChild`ren). + pub fn module_children_local(self, def_id: LocalDefId) -> &'tcx [ModChild] { + self.resolutions(()).module_children.get(&def_id).map_or(&[], |v| &v[..]) } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index db6b35026a8e7..8986defacc7ba 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -165,8 +165,7 @@ pub struct ResolverGlobalCtxt { pub effective_visibilities: EffectiveVisibilities, pub extern_crate_map: FxHashMap, pub maybe_unused_trait_imports: FxIndexSet, - pub module_children_non_reexports: LocalDefIdMap>, - pub module_children_reexports: LocalDefIdMap>, + pub module_children: LocalDefIdMap>, pub glob_map: FxHashMap>, pub main_def: Option, pub trait_impls: FxIndexMap>, diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index c607c7fd5f4a7..7e60870fef0ff 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -515,9 +515,11 @@ impl<'tcx> EmbargoVisitor<'tcx> { let vis = self.tcx.local_visibility(item_id.owner_id.def_id); self.update_macro_reachable_def(item_id.owner_id.def_id, def_kind, vis, defining_mod); } - for export in self.tcx.module_children_reexports(module_def_id) { - if export.vis.is_accessible_from(defining_mod, self.tcx) - && let Res::Def(def_kind, def_id) = export.res + for child in self.tcx.module_children_local(module_def_id) { + // FIXME: Use module children for the logic above too. + if !child.reexport_chain.is_empty() + && child.vis.is_accessible_from(defining_mod, self.tcx) + && let Res::Def(def_kind, def_id) = child.res && let Some(def_id) = def_id.as_local() { let vis = self.tcx.local_visibility(def_id); self.update_macro_reachable_def(def_id, def_kind, vis, defining_mod); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index d7c518fbdd0dc..1685468715f15 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1261,14 +1261,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { *module.globs.borrow_mut() = Vec::new(); if let Some(def_id) = module.opt_def_id() { - let mut non_reexports = Vec::new(); - let mut reexports = Vec::new(); + let mut children = Vec::new(); module.for_each_child(self, |this, ident, _, binding| { let res = binding.res().expect_non_local(); - if !binding.is_import() { - non_reexports.push(res.def_id().expect_local()); - } else if res != def::Res::Err && !binding.is_ambiguity() { + if res != def::Res::Err && !binding.is_ambiguity() { let mut reexport_chain = SmallVec::new(); let mut next_binding = binding; while let NameBindingKind::Import { binding, import, .. } = next_binding.kind { @@ -1276,17 +1273,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { next_binding = binding; } - reexports.push(ModChild { ident, res, vis: binding.vis, reexport_chain }); + children.push(ModChild { ident, res, vis: binding.vis, reexport_chain }); } }); - // Should be fine because this code is only called for local modules. - let def_id = def_id.expect_local(); - if !non_reexports.is_empty() { - self.module_children_non_reexports.insert(def_id, non_reexports); - } - if !reexports.is_empty() { - self.module_children_reexports.insert(def_id, reexports); + if !children.is_empty() { + // Should be fine because this code is only called for local modules. + self.module_children.insert(def_id.expect_local(), children); } } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 125f5ce761174..e46463579fe45 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -909,8 +909,7 @@ pub struct Resolver<'a, 'tcx> { /// `CrateNum` resolutions of `extern crate` items. extern_crate_map: FxHashMap, - module_children_non_reexports: LocalDefIdMap>, - module_children_reexports: LocalDefIdMap>, + module_children: LocalDefIdMap>, trait_map: NodeMap>, /// A map from nodes to anonymous modules. @@ -1260,8 +1259,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { lifetimes_res_map: Default::default(), extra_lifetime_params_map: Default::default(), extern_crate_map: Default::default(), - module_children_non_reexports: Default::default(), - module_children_reexports: Default::default(), + module_children: Default::default(), trait_map: NodeMap::default(), underscore_disambiguator: 0, empty_module, @@ -1399,8 +1397,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { has_pub_restricted, effective_visibilities, extern_crate_map, - module_children_non_reexports: self.module_children_non_reexports, - module_children_reexports: self.module_children_reexports, + module_children: self.module_children, glob_map, maybe_unused_trait_imports, main_def, diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 3f6a5d6d9017a..951f54e93663c 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -152,8 +152,9 @@ pub(crate) fn try_inline_glob( // reexported by the glob, e.g. because they are shadowed by something else. let reexports = cx .tcx - .module_children_reexports(current_mod) + .module_children_local(current_mod) .iter() + .filter(|child| !child.reexport_chain.is_empty()) .filter_map(|child| child.res.opt_def_id()) .collect(); let mut items = build_module_items(cx, did, visited, inlined_names, Some(&reexports)); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1531e7fc7b91d..f9a46e33f9a8c 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2089,9 +2089,9 @@ pub(crate) fn reexport_chain<'tcx>( import_def_id: LocalDefId, target_def_id: LocalDefId, ) -> &'tcx [Reexport] { - for child in tcx.module_children_reexports(tcx.local_parent(import_def_id)) { + for child in tcx.module_children_local(tcx.local_parent(import_def_id)) { if child.res.opt_def_id() == Some(target_def_id.to_def_id()) - && child.reexport_chain[0].id() == Some(import_def_id.to_def_id()) + && child.reexport_chain.first().and_then(|r| r.id()) == Some(import_def_id.to_def_id()) { return &child.reexport_chain; } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index a6089680fae9d..841c7a78b2d4f 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -136,14 +136,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // is declared but also a reexport of itself producing two exports of the same // macro in the same module. let mut inserted = FxHashSet::default(); - for export in self.cx.tcx.module_children_reexports(CRATE_DEF_ID) { - if let Res::Def(DefKind::Macro(_), def_id) = export.res && + for child in self.cx.tcx.module_children_local(CRATE_DEF_ID) { + if !child.reexport_chain.is_empty() && + let Res::Def(DefKind::Macro(_), def_id) = child.res && let Some(local_def_id) = def_id.as_local() && self.cx.tcx.has_attr(def_id, sym::macro_export) && inserted.insert(def_id) { - let item = self.cx.tcx.hir().expect_item(local_def_id); - top_level_module.items.insert((local_def_id, Some(item.ident.name)), (item, None, None)); + let item = self.cx.tcx.hir().expect_item(local_def_id); + top_level_module.items.insert((local_def_id, Some(item.ident.name)), (item, None, None)); } } From 7bc6d598f94744751a249ed18885592a6f077bcd Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 2 May 2023 16:14:20 +0000 Subject: [PATCH 11/20] Avoid ICEing miri on layout query cycles --- compiler/rustc_middle/messages.ftl | 3 + compiler/rustc_middle/src/ty/layout.rs | 5 + compiler/rustc_middle/src/values.rs | 6 ++ .../html/templates/type_layout.html | 93 ++++++++++--------- src/tools/miri/tests/fail/layout_cycle.rs | 28 ++++++ src/tools/miri/tests/fail/layout_cycle.stderr | 28 ++++++ 6 files changed, 119 insertions(+), 44 deletions(-) create mode 100644 src/tools/miri/tests/fail/layout_cycle.rs create mode 100644 src/tools/miri/tests/fail/layout_cycle.stderr diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index bd9d89deee179..c6bbf2ef0cdb5 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -32,6 +32,9 @@ middle_values_too_big = middle_cannot_be_normalized = unable to determine layout for `{$ty}` because `{$failure_ty}` cannot be normalized +middle_cycle = + a cycle occurred during layout computation + middle_strict_coherence_needs_negative_coherence = to use `strict_coherence` on this trait, the `with_negative_coherence` feature must be enabled .label = due to this attribute diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 6e6b7c39ecb19..f2a2e67cf82dc 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -210,6 +210,7 @@ pub enum LayoutError<'tcx> { Unknown(Ty<'tcx>), SizeOverflow(Ty<'tcx>), NormalizationFailure(Ty<'tcx>, NormalizationError<'tcx>), + Cycle, } impl IntoDiagnostic<'_, !> for LayoutError<'_> { @@ -230,6 +231,9 @@ impl IntoDiagnostic<'_, !> for LayoutError<'_> { diag.set_arg("failure_ty", e.get_type_for_failure()); diag.set_primary_message(fluent::middle_cannot_be_normalized); } + LayoutError::Cycle => { + diag.set_primary_message(fluent::middle_cycle); + } } diag } @@ -250,6 +254,7 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> { t, e.get_type_for_failure() ), + LayoutError::Cycle => write!(f, "a cycle occurred during layout computation"), } } } diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 5c38c0acc7f86..c62c33d4dfc18 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -106,6 +106,12 @@ impl<'tcx> Value, DepKind> for ty::EarlyBinder Value, DepKind> for Result> { + fn from_cycle_error(_tcx: TyCtxt<'tcx>, _cycle: &[QueryInfo]) -> Self { + Err(ty::layout::LayoutError::Cycle) + } +} + // item_and_field_ids should form a cycle where each field contains the // type in the next element in the list pub fn recursive_type_error( diff --git a/src/librustdoc/html/templates/type_layout.html b/src/librustdoc/html/templates/type_layout.html index 58b220c7428f4..20e09a548058d 100644 --- a/src/librustdoc/html/templates/type_layout.html +++ b/src/librustdoc/html/templates/type_layout.html @@ -1,53 +1,58 @@ -

{# #} +

{# #} Layout§ {# #}

{# #}
{# #} {% match type_layout_size %} - {% when Ok(type_layout_size) %} -
{# #} -

{# #} - Note: Most layout information is completely {#+ #} - unstable and may even differ between compilations. {#+ #} - The only exception is types with certain repr(...) {#+ #} - attributes. Please see the Rust Reference’s {#+ #} - “Type Layout” {#+ #} - chapter for details on type layout guarantees. {# #} -

{# #} -
{# #} -

Size: {{ type_layout_size|safe }}

{# #} - {% if !variants.is_empty() %} -

{# #} - Size for each variant: {# #} -

{# #} -
    {# #} - {% for (name, layout_size) in variants %} -
  • {# #} - {{ name }}: {#+ #} - {{ layout_size|safe }} -
  • {# #} - {% endfor %} -
{# #} - {% endif %} - {# This kind of layout error can occur with valid code, e.g. if you try to - get the layout of a generic type such as `Vec`. #} + {% when Ok(type_layout_size) %} +
{# #} +

{# #} + Note: Most layout information is completely {#+ #} + unstable and may even differ between compilations. {#+ #} + The only exception is types with certain repr(...) {#+ #} + attributes. Please see the Rust Reference’s {#+ #} + “Type Layout” {#+ #} + chapter for details on type layout guarantees. {# #} +

{# #} +
{# #} +

Size: {{ type_layout_size|safe }}

{# #} + {% if !variants.is_empty() %} +

{# #} + Size for each variant: {# #} +

{# #} +
    {# #} + {% for (name, layout_size) in variants %} +
  • {# #} + {{ name }}: {#+ #} + {{ layout_size|safe }} +
  • {# #} + {% endfor %} +
{# #} + {% endif %} + {# This kind of layout error can occur with valid code, e.g. if you try to + get the layout of a generic type such as `Vec`. #} {% when Err(LayoutError::Unknown(_)) %} -

{# #} - Note: Unable to compute type layout, {#+ #} - possibly due to this type having generic parameters. {#+ #} - Layout can only be computed for concrete, fully-instantiated types. {# #} -

{# #} +

{# #} + Note: Unable to compute type layout, {#+ #} + possibly due to this type having generic parameters. {#+ #} + Layout can only be computed for concrete, fully-instantiated types. {# #} +

{# #} {# This kind of error probably can't happen with valid code, but we don't - want to panic and prevent the docs from building, so we just let the - user know that we couldn't compute the layout. #} + want to panic and prevent the docs from building, so we just let the + user know that we couldn't compute the layout. #} {% when Err(LayoutError::SizeOverflow(_)) %} -

{# #} - Note: Encountered an error during type layout; {#+ #} - the type was too big. {# #} -

{# #} +

{# #} + Note: Encountered an error during type layout; {#+ #} + the type was too big. {# #} +

{# #} {% when Err(LayoutError::NormalizationFailure(_, _)) %} -

{# #} - Note: Encountered an error during type layout; {#+ #} - the type failed to be normalized. {# #} -

{# #} - {% endmatch %} +

{# #} + Note: Encountered an error during type layout; {#+ #} + the type failed to be normalized. {# #} +

{# #} + {% when Err(LayoutError::Cycle) %} +

{# #} + Note: Encountered an error during type layout; {#+ #} + the type's layout depended on the type's layout itself. {# #} +

{# #} + {% endmatch %}
{# #} diff --git a/src/tools/miri/tests/fail/layout_cycle.rs b/src/tools/miri/tests/fail/layout_cycle.rs new file mode 100644 index 0000000000000..d050310bd805c --- /dev/null +++ b/src/tools/miri/tests/fail/layout_cycle.rs @@ -0,0 +1,28 @@ +//@error-pattern: a cycle occurred during layout computation +//~^ ERROR: cycle detected when computing layout of + +use std::mem; + +pub struct S { + pub f: ::I, +} + +pub trait Tr { + type I: Tr; +} + +impl Tr for S { + type I = S>; +} + +impl Tr for () { + type I = (); +} + +fn foo() -> usize { + mem::size_of::>() +} + +fn main() { + println!("{}", foo::>()); +} diff --git a/src/tools/miri/tests/fail/layout_cycle.stderr b/src/tools/miri/tests/fail/layout_cycle.stderr new file mode 100644 index 0000000000000..62b7d5fb77d12 --- /dev/null +++ b/src/tools/miri/tests/fail/layout_cycle.stderr @@ -0,0 +1,28 @@ +error[E0391]: cycle detected when computing layout of `S>` + | + = note: ...which requires computing layout of ` as Tr>::I`... + = note: ...which again requires computing layout of `S>`, completing the cycle + +error: post-monomorphization error: a cycle occurred during layout computation + --> RUSTLIB/core/src/mem/mod.rs:LL:CC + | +LL | intrinsics::size_of::() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation + | + = note: inside `std::mem::size_of::>>` at RUSTLIB/core/src/mem/mod.rs:LL:CC +note: inside `foo::>` + --> $DIR/layout_cycle.rs:LL:CC + | +LL | mem::size_of::>() + | ^^^^^^^^^^^^^^^^^^^^^^ +note: inside `main` + --> $DIR/layout_cycle.rs:LL:CC + | +LL | println!("{}", foo::>()); + | ^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0391`. From 6af761a5aa96682183766703fa761451719f3ead Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 3 May 2023 08:22:05 +1000 Subject: [PATCH 12/20] Add some triagebot notifications for nnethercote. --- triagebot.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 89867b63d64f8..2ab6e03e7eee0 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -489,6 +489,14 @@ message = "This PR changes src/bootstrap/defaults/config.compiler.toml. If appro [mentions."src/bootstrap/defaults/config.codegen.toml"] message = "This PR changes src/bootstrap/defaults/config.codegen.toml. If appropriate, please also update `config.compiler.toml` so the defaults are in sync." +[mentions."tests/ui/deriving/deriving-all-codegen.stdout"] +message = "Changes to the code generated for builtin derived traits." +cc = ["@nnethercote"] + +[mentions."tests/ui/stats/hir-stats.stderr"] +message = "Changes to the size of AST and/or HIR nodes." +cc = ["@nnethercote"] + [assign] warn_non_default_branch = true contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html" From 51b9f7862648286258282440a2d788e8ba80af10 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 3 May 2023 08:25:02 +1000 Subject: [PATCH 13/20] Amend the triagebot comment for `Cargo.lock` changes. I don't like the current wording. It's obnoxious to be told by a bot that a change I made intentionally is "probably unintentional"! I also don't like describing unintentional changes as "Random", it's not the right word. --- triagebot.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index 2ab6e03e7eee0..54c8b2060c526 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -475,10 +475,10 @@ cc = ["@rust-lang/style"] [mentions."Cargo.lock"] message = """ -These commits modify the `Cargo.lock` file. Random changes to `Cargo.lock` can be introduced when switching branches and rebasing PRs. -This was probably unintentional and should be reverted before this PR is merged. +These commits modify the `Cargo.lock` file. Unintentional changes to `Cargo.lock` can be introduced when switching branches and rebasing PRs. -If this was intentional then you can ignore this comment. +If this was unintentional then you should revert the changes before this PR is merged. +Otherwise, you can ignore this comment. """ [mentions."src/tools/x"] From 6e01e910cb8ab0109235be7cc7ab7ef465724255 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 25 Apr 2023 05:15:50 +0000 Subject: [PATCH 14/20] Implement negative bounds --- compiler/rustc_ast/src/ast.rs | 18 +++ compiler/rustc_ast_lowering/src/lib.rs | 17 ++- compiler/rustc_ast_passes/messages.ftl | 5 +- .../rustc_ast_passes/src/ast_validation.rs | 5 +- compiler/rustc_ast_passes/src/errors.rs | 8 ++ compiler/rustc_ast_passes/src/feature_gate.rs | 6 + compiler/rustc_ast_pretty/src/pprust/state.rs | 7 + compiler/rustc_feature/src/active.rs | 2 + compiler/rustc_hir/src/hir.rs | 1 + .../rustc_hir_analysis/src/astconv/mod.rs | 26 +++- compiler/rustc_hir_analysis/src/bounds.rs | 8 +- compiler/rustc_hir_analysis/src/lib.rs | 1 + compiler/rustc_middle/src/ty/print/pretty.rs | 3 + compiler/rustc_parse/messages.ftl | 10 +- compiler/rustc_parse/src/errors.rs | 31 +---- .../rustc_parse/src/parser/diagnostics.rs | 2 +- compiler/rustc_parse/src/parser/generics.rs | 4 +- compiler/rustc_parse/src/parser/item.rs | 10 +- compiler/rustc_parse/src/parser/path.rs | 2 +- compiler/rustc_parse/src/parser/ty.rs | 125 +++++++----------- compiler/rustc_span/src/symbol.rs | 1 + .../src/traits/error_reporting/suggestions.rs | 4 + .../feature-gate-negative_bounds.rs | 4 + .../feature-gate-negative_bounds.stderr | 8 ++ tests/ui/issues/issue-58857.stderr | 4 +- tests/ui/parser/issues/issue-33418.fixed | 19 --- tests/ui/parser/issues/issue-33418.rs | 8 +- tests/ui/parser/issues/issue-33418.stderr | 38 +++--- ...gative-outlives-bound-syntactic-fail.fixed | 9 +- ...-negative-outlives-bound-syntactic-fail.rs | 3 + ...ative-outlives-bound-syntactic-fail.stderr | 32 ++++- tests/ui/traits/negative-bounds/simple.rs | 42 ++++++ tests/ui/traits/negative-bounds/simple.stderr | 82 ++++++++++++ 33 files changed, 360 insertions(+), 185 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-negative_bounds.rs create mode 100644 tests/ui/feature-gates/feature-gate-negative_bounds.stderr delete mode 100644 tests/ui/parser/issues/issue-33418.fixed create mode 100644 tests/ui/traits/negative-bounds/simple.rs create mode 100644 tests/ui/traits/negative-bounds/simple.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index dce7106d12804..b5dba0713bfa0 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -287,12 +287,20 @@ pub enum TraitBoundModifier { /// No modifiers None, + /// `!Trait` + Negative, + /// `?Trait` Maybe, /// `~const Trait` MaybeConst, + /// `~const !Trait` + // + // This parses but will be rejected during AST validation. + MaybeConstNegative, + /// `~const ?Trait` // // This parses but will be rejected during AST validation. @@ -2446,6 +2454,16 @@ impl fmt::Debug for ImplPolarity { } } +#[derive(Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)] +pub enum BoundPolarity { + /// `Type: Trait` + Positive, + /// `Type: !Trait` + Negative(Span), + /// `Type: ?Trait` + Maybe(Span), +} + #[derive(Clone, Encodable, Decodable, Debug)] pub enum FnRetTy { /// Returns type is not specified. diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index b5b28bf8e31e5..4100efb6eb330 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1368,13 +1368,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { this.arena.alloc_from_iter(bounds.iter().filter_map(|bound| match bound { GenericBound::Trait( ty, - TraitBoundModifier::None | TraitBoundModifier::MaybeConst, + TraitBoundModifier::None + | TraitBoundModifier::MaybeConst + | TraitBoundModifier::Negative, ) => Some(this.lower_poly_trait_ref(ty, itctx)), // `~const ?Bound` will cause an error during AST validation // anyways, so treat it like `?Bound` as compilation proceeds. GenericBound::Trait( _, - TraitBoundModifier::Maybe | TraitBoundModifier::MaybeConstMaybe, + TraitBoundModifier::Maybe + | TraitBoundModifier::MaybeConstMaybe + | TraitBoundModifier::MaybeConstNegative, ) => None, GenericBound::Outlives(lifetime) => { if lifetime_bound.is_none() { @@ -2421,11 +2425,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { TraitBoundModifier::None => hir::TraitBoundModifier::None, TraitBoundModifier::MaybeConst => hir::TraitBoundModifier::MaybeConst, + TraitBoundModifier::Negative => { + if self.tcx.features().negative_bounds { + hir::TraitBoundModifier::Negative + } else { + hir::TraitBoundModifier::None + } + } + // `MaybeConstMaybe` will cause an error during AST validation, but we need to pick a // placeholder for compilation to proceed. TraitBoundModifier::MaybeConstMaybe | TraitBoundModifier::Maybe => { hir::TraitBoundModifier::Maybe } + TraitBoundModifier::MaybeConstNegative => hir::TraitBoundModifier::MaybeConst, } } diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index ffca37ae9d5f6..90535af781d90 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -206,7 +206,7 @@ ast_passes_tilde_const_disallowed = `~const` is not allowed here .closure = closures cannot have `~const` trait bounds .function = this function is not `const`, so it cannot have `~const` trait bounds -ast_passes_optional_const_exclusive = `~const` and `?` are mutually exclusive +ast_passes_optional_const_exclusive = `~const` and `{$modifier}` are mutually exclusive ast_passes_const_and_async = functions cannot be both `const` and `async` .const = `const` because of this @@ -235,3 +235,6 @@ ast_passes_incompatible_features = `{$f1}` and `{$f2}` are incompatible, using t .help = remove one of these features ast_passes_show_span = {$msg} + +ast_passes_negative_bound_not_supported = + negative bounds are not supported diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index c79626ccd76f0..6e9d33516ce5d 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1168,7 +1168,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }); } (_, TraitBoundModifier::MaybeConstMaybe) => { - self.err_handler().emit_err(errors::OptionalConstExclusive {span: bound.span()}); + self.err_handler().emit_err(errors::OptionalConstExclusive {span: bound.span(), modifier: "?" }); + } + (_, TraitBoundModifier::MaybeConstNegative) => { + self.err_handler().emit_err(errors::OptionalConstExclusive {span: bound.span(), modifier: "!" }); } _ => {} } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 1732865f0bb64..66a47db90744f 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -567,6 +567,7 @@ pub enum TildeConstReason { pub struct OptionalConstExclusive { #[primary_span] pub span: Span, + pub modifier: &'static str, } #[derive(Diagnostic)] @@ -693,3 +694,10 @@ pub struct ShowSpan { pub span: Span, pub msg: &'static str, } + +#[derive(Diagnostic)] +#[diag(ast_passes_negative_bound_not_supported)] +pub struct NegativeBoundUnsupported { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 17bcd24ee39fd..77e83fdbbaaf8 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -603,6 +603,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(dyn_star, "`dyn*` trait objects are experimental"); gate_all!(const_closures, "const closures are experimental"); + if !visitor.features.negative_bounds { + for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() { + sess.emit_err(errors::NegativeBoundUnsupported { span }); + } + } + // All uses of `gate_all!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). // We emit an early future-incompatible warning for these. diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 849336c8669a1..ae346510ccc82 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1570,12 +1570,19 @@ impl<'a> State<'a> { GenericBound::Trait(tref, modifier) => { match modifier { TraitBoundModifier::None => {} + TraitBoundModifier::Negative => { + self.word("!"); + } TraitBoundModifier::Maybe => { self.word("?"); } TraitBoundModifier::MaybeConst => { self.word_space("~const"); } + TraitBoundModifier::MaybeConstNegative => { + self.word_space("~const"); + self.word("!"); + } TraitBoundModifier::MaybeConstMaybe => { self.word_space("~const"); self.word("?"); diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 6201e5b619b87..294ed18a239f5 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -164,6 +164,8 @@ declare_features! ( (active, link_cfg, "1.14.0", None, None), /// Allows the `multiple_supertrait_upcastable` lint. (active, multiple_supertrait_upcastable, "1.69.0", None, None), + /// Allow negative trait bounds. This is an internal-only feature for testing the trait solver! + (incomplete, negative_bounds, "CURRENT_RUSTC_VERSION", None, None), /// Allows using `#[omit_gdb_pretty_printer_section]`. (active, omit_gdb_pretty_printer_section, "1.5.0", None, None), /// Allows using `#[prelude_import]` on glob `use` items. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e220a0293393e..38cd5865cc326 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -435,6 +435,7 @@ pub enum GenericArgsParentheses { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] pub enum TraitBoundModifier { None, + Negative, Maybe, MaybeConst, } diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 6cb008bc5f8a0..0faf7a4ba4519 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -665,6 +665,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span: Span, binding_span: Option, constness: ty::BoundConstness, + polarity: ty::ImplPolarity, bounds: &mut Bounds<'tcx>, speculative: bool, trait_ref_span: Span, @@ -696,10 +697,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty::Binder::bind_with_vars(tcx.mk_trait_ref(trait_def_id, substs), bound_vars); debug!(?poly_trait_ref, ?assoc_bindings); - bounds.push_trait_bound(tcx, poly_trait_ref, span, constness); + bounds.push_trait_bound(tcx, poly_trait_ref, span, constness, polarity); let mut dup_bindings = FxHashMap::default(); for binding in &assoc_bindings { + // TODO: negative polarity can't have associated type bindings! + // Specify type to assert that error was already reported in `Err` case. let _: Result<_, ErrorGuaranteed> = self.add_predicates_for_ast_type_binding( hir_id, @@ -711,6 +714,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { binding_span.unwrap_or(binding.span), constness, only_self_bounds, + polarity, ); // Okay to ignore `Err` because of `ErrorGuaranteed` (see above). } @@ -743,6 +747,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { trait_ref: &hir::TraitRef<'_>, span: Span, constness: ty::BoundConstness, + polarity: ty::ImplPolarity, self_ty: Ty<'tcx>, bounds: &mut Bounds<'tcx>, speculative: bool, @@ -764,6 +769,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span, binding_span, constness, + polarity, bounds, speculative, trait_ref_span, @@ -799,6 +805,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span, binding_span, constness, + ty::ImplPolarity::Positive, bounds, speculative, trait_ref_span, @@ -961,16 +968,23 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { for ast_bound in ast_bounds { match ast_bound { hir::GenericBound::Trait(poly_trait_ref, modifier) => { - let constness = match modifier { - hir::TraitBoundModifier::MaybeConst => ty::BoundConstness::ConstIfConst, - hir::TraitBoundModifier::None => ty::BoundConstness::NotConst, + let (constness, polarity) = match modifier { + hir::TraitBoundModifier::MaybeConst => { + (ty::BoundConstness::ConstIfConst, ty::ImplPolarity::Positive) + } + hir::TraitBoundModifier::None => { + (ty::BoundConstness::NotConst, ty::ImplPolarity::Positive) + } + hir::TraitBoundModifier::Negative => { + (ty::BoundConstness::NotConst, ty::ImplPolarity::Negative) + } hir::TraitBoundModifier::Maybe => continue, }; - let _ = self.instantiate_poly_trait_ref( &poly_trait_ref.trait_ref, poly_trait_ref.span, constness, + polarity, param_ty, bounds, false, @@ -1088,6 +1102,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { path_span: Span, constness: ty::BoundConstness, only_self_bounds: OnlySelfBounds, + polarity: ty::ImplPolarity, ) -> Result<(), ErrorGuaranteed> { // Given something like `U: SomeTrait`, we want to produce a // predicate like `::T = X`. This is somewhat @@ -1438,6 +1453,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &trait_bound.trait_ref, trait_bound.span, ty::BoundConstness::NotConst, + ty::ImplPolarity::Positive, dummy_self, &mut bounds, false, diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index 284b099e7bc71..7156fea8f896c 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -42,8 +42,14 @@ impl<'tcx> Bounds<'tcx> { trait_ref: ty::PolyTraitRef<'tcx>, span: Span, constness: ty::BoundConstness, + polarity: ty::ImplPolarity, ) { - self.predicates.push((trait_ref.with_constness(constness).to_predicate(tcx), span)); + self.predicates.push(( + trait_ref + .map_bound(|trait_ref| ty::TraitPredicate { trait_ref, constness, polarity }) + .to_predicate(tcx), + span, + )); } pub fn push_projection_bound( diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 961457b75794a..3fe34f23aef42 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -528,6 +528,7 @@ pub fn hir_trait_to_predicates<'tcx>( hir_trait, DUMMY_SP, ty::BoundConstness::NotConst, + ty::ImplPolarity::Positive, self_ty, &mut bounds, true, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 2aced27f7bbdc..6ac9f95045069 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2816,6 +2816,9 @@ define_print_and_forward_display! { if let ty::BoundConstness::ConstIfConst = self.constness && cx.tcx().features().const_trait_impl { p!("~const "); } + if let ty::ImplPolarity::Negative = self.polarity { + p!("!"); + } p!(print(self.trait_ref.print_only_trait_path())) } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 9a5232b1bcdd9..cd296dca133f5 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -615,13 +615,6 @@ parse_invalid_dyn_keyword = invalid `dyn` keyword .help = `dyn` is only needed at the start of a trait `+`-separated list .suggestion = remove this keyword -parse_negative_bounds_not_supported = negative bounds are not supported - .label = negative bounds are not supported - .suggestion = {$num_bounds -> - [one] remove the bound - *[other] remove the bounds - } - parse_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml` parse_help_set_edition_standalone = pass `--edition {$edition}` to `rustc` parse_note_edition_guide = for more on editions, read https://doc.rust-lang.org/edition-guide @@ -772,7 +765,8 @@ parse_assoc_lifetime = associated lifetimes are not supported parse_tilde_const_lifetime = `~const` may only modify trait bounds, not lifetime bounds -parse_maybe_lifetime = `?` may only modify trait bounds, not lifetime bounds +parse_modifier_lifetime = `{$sigil}` may only modify trait bounds, not lifetime bounds + .suggestion = remove the `{$sigil}` parse_parenthesized_lifetime = parenthesized lifetime bounds are not supported .suggestion = remove the parentheses diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index b445ccc7ad0aa..010a13aefa420 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2280,31 +2280,6 @@ pub(crate) struct InvalidDynKeyword { pub span: Span, } -#[derive(Diagnostic)] -#[diag(parse_negative_bounds_not_supported)] -pub(crate) struct NegativeBoundsNotSupported { - #[primary_span] - pub negative_bounds: Vec, - #[label] - pub last_span: Span, - #[subdiagnostic] - pub sub: Option, -} - -#[derive(Subdiagnostic)] -#[suggestion( - parse_suggestion, - style = "tool-only", - code = "{fixed}", - applicability = "machine-applicable" -)] -pub(crate) struct NegativeBoundsNotSupportedSugg { - #[primary_span] - pub bound_list: Span, - pub num_bounds: usize, - pub fixed: String, -} - #[derive(Subdiagnostic)] pub enum HelpUseLatestEdition { #[help(parse_help_set_edition_cargo)] @@ -2412,10 +2387,12 @@ pub(crate) struct TildeConstLifetime { } #[derive(Diagnostic)] -#[diag(parse_maybe_lifetime)] -pub(crate) struct MaybeLifetime { +#[diag(parse_modifier_lifetime)] +pub(crate) struct ModifierLifetime { #[primary_span] + #[suggestion(style = "tool-only", applicability = "maybe-incorrect", code = "")] pub span: Span, + pub sigil: &'static str, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 638a432cea5f1..e974df61dc966 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1284,7 +1284,7 @@ impl<'a> Parser<'a> { } self.bump(); // `+` - let bounds = self.parse_generic_bounds(None)?; + let bounds = self.parse_generic_bounds()?; let sum_span = ty.span.to(self.prev_token.span); let sub = match &ty.kind { diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 61a7ae93bfa8b..e6d0f9fbc76d8 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -78,7 +78,7 @@ impl<'a> Parser<'a> { } self.restore_snapshot(snapshot); } - self.parse_generic_bounds(colon_span)? + self.parse_generic_bounds()? } else { Vec::new() }; @@ -419,7 +419,7 @@ impl<'a> Parser<'a> { // or with mandatory equality sign and the second type. let ty = self.parse_ty_for_where_clause()?; if self.eat(&token::Colon) { - let bounds = self.parse_generic_bounds(Some(self.prev_token.span))?; + let bounds = self.parse_generic_bounds()?; Ok(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { span: lo.to(self.prev_token.span), bound_generic_params: lifetime_defs, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 64ff7f1fb2c10..10a95cae3e11f 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -788,11 +788,7 @@ impl<'a> Parser<'a> { // Parse optional colon and supertrait bounds. let had_colon = self.eat(&token::Colon); let span_at_colon = self.prev_token.span; - let bounds = if had_colon { - self.parse_generic_bounds(Some(self.prev_token.span))? - } else { - Vec::new() - }; + let bounds = if had_colon { self.parse_generic_bounds()? } else { Vec::new() }; let span_before_eq = self.prev_token.span; if self.eat(&token::Eq) { @@ -802,7 +798,7 @@ impl<'a> Parser<'a> { self.sess.emit_err(errors::BoundsNotAllowedOnTraitAliases { span }); } - let bounds = self.parse_generic_bounds(None)?; + let bounds = self.parse_generic_bounds()?; generics.where_clause = self.parse_where_clause()?; self.expect_semi()?; @@ -883,7 +879,7 @@ impl<'a> Parser<'a> { // Parse optional colon and param bounds. let bounds = - if self.eat(&token::Colon) { self.parse_generic_bounds(None)? } else { Vec::new() }; + if self.eat(&token::Colon) { self.parse_generic_bounds()? } else { Vec::new() }; let before_where_clause = self.parse_where_clause()?; let ty = if self.eat(&token::Eq) { Some(self.parse_ty()?) } else { None }; diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index b9a2b141bce38..205d4d15e2e87 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -606,7 +606,7 @@ impl<'a> Parser<'a> { let kind = if self.eat(&token::Colon) { // Parse associated type constraint bound. - let bounds = self.parse_generic_bounds(Some(self.prev_token.span))?; + let bounds = self.parse_generic_bounds()?; AssocConstraintKind::Bound { bounds } } else if self.eat(&token::Eq) { self.parse_assoc_equality_term(ident, self.prev_token.span)? diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 37c441fbecb96..04f7eea90ed56 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -3,8 +3,7 @@ use super::{Parser, PathStyle, TokenType}; use crate::errors::{ self, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType, FnPointerCannotBeAsync, FnPointerCannotBeConst, FnPtrWithGenerics, FnPtrWithGenericsSugg, - InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, - NegativeBoundsNotSupported, NegativeBoundsNotSupportedSugg, NestedCVariadicType, + InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, NestedCVariadicType, ReturnTypesUseThinArrow, }; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; @@ -14,8 +13,9 @@ use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::util::case::Case; use rustc_ast::{ - self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, - MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind, + self as ast, BareFnTy, BoundPolarity, FnRetTy, GenericBound, GenericBounds, GenericParam, + Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, + TraitObjectSyntax, Ty, TyKind, }; use rustc_errors::{Applicability, PResult}; use rustc_span::source_map::Span; @@ -23,10 +23,10 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; use thin_vec::{thin_vec, ThinVec}; -/// Any `?` or `~const` modifiers that appear at the start of a bound. +/// Any `?`, `!`, or `~const` modifiers that appear at the start of a bound. struct BoundModifiers { /// `?Trait`. - maybe: Option, + bound_polarity: BoundPolarity, /// `~const Trait`. maybe_const: Option, @@ -34,11 +34,13 @@ struct BoundModifiers { impl BoundModifiers { fn to_trait_bound_modifier(&self) -> TraitBoundModifier { - match (self.maybe, self.maybe_const) { - (None, None) => TraitBoundModifier::None, - (Some(_), None) => TraitBoundModifier::Maybe, - (None, Some(_)) => TraitBoundModifier::MaybeConst, - (Some(_), Some(_)) => TraitBoundModifier::MaybeConstMaybe, + match (self.bound_polarity, self.maybe_const) { + (BoundPolarity::Positive, None) => TraitBoundModifier::None, + (BoundPolarity::Negative(_), None) => TraitBoundModifier::Negative, + (BoundPolarity::Maybe(_), None) => TraitBoundModifier::Maybe, + (BoundPolarity::Positive, Some(_)) => TraitBoundModifier::MaybeConst, + (BoundPolarity::Negative(_), Some(_)) => TraitBoundModifier::MaybeConstNegative, + (BoundPolarity::Maybe(_), Some(_)) => TraitBoundModifier::MaybeConstMaybe, } } } @@ -368,7 +370,7 @@ impl<'a> Parser<'a> { fn parse_bare_trait_object(&mut self, lo: Span, allow_plus: AllowPlus) -> PResult<'a, TyKind> { let lt_no_plus = self.check_lifetime() && !self.look_ahead(1, |t| t.is_like_plus()); - let bounds = self.parse_generic_bounds_common(allow_plus, None)?; + let bounds = self.parse_generic_bounds_common(allow_plus)?; if lt_no_plus { self.sess.emit_err(NeedPlusAfterTraitObjectLifetime { span: lo }); } @@ -395,7 +397,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, TyKind> { if plus { self.eat_plus(); // `+`, or `+=` gets split and `+` is discarded - bounds.append(&mut self.parse_generic_bounds(Some(self.prev_token.span))?); + bounds.append(&mut self.parse_generic_bounds()?); } Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None)) } @@ -598,7 +600,7 @@ impl<'a> Parser<'a> { } }) } - let bounds = self.parse_generic_bounds(None)?; + let bounds = self.parse_generic_bounds()?; *impl_dyn_multi = bounds.len() > 1 || self.prev_token.kind == TokenKind::BinOp(token::Plus); Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds)) } @@ -629,7 +631,7 @@ impl<'a> Parser<'a> { }; // Always parse bounds greedily for better error recovery. - let bounds = self.parse_generic_bounds(None)?; + let bounds = self.parse_generic_bounds()?; *impl_dyn_multi = bounds.len() > 1 || self.prev_token.kind == TokenKind::BinOp(token::Plus); Ok(TyKind::TraitObject(bounds, syntax)) } @@ -660,23 +662,15 @@ impl<'a> Parser<'a> { } } - pub(super) fn parse_generic_bounds( - &mut self, - colon_span: Option, - ) -> PResult<'a, GenericBounds> { - self.parse_generic_bounds_common(AllowPlus::Yes, colon_span) + pub(super) fn parse_generic_bounds(&mut self) -> PResult<'a, GenericBounds> { + self.parse_generic_bounds_common(AllowPlus::Yes) } /// Parses bounds of a type parameter `BOUND + BOUND + ...`, possibly with trailing `+`. /// /// See `parse_generic_bound` for the `BOUND` grammar. - fn parse_generic_bounds_common( - &mut self, - allow_plus: AllowPlus, - colon_span: Option, - ) -> PResult<'a, GenericBounds> { + fn parse_generic_bounds_common(&mut self, allow_plus: AllowPlus) -> PResult<'a, GenericBounds> { let mut bounds = Vec::new(); - let mut negative_bounds = Vec::new(); // In addition to looping while we find generic bounds: // We continue even if we find a keyword. This is necessary for error recovery on, @@ -693,19 +687,12 @@ impl<'a> Parser<'a> { self.sess.emit_err(InvalidDynKeyword { span: self.token.span }); self.bump(); } - match self.parse_generic_bound()? { - Ok(bound) => bounds.push(bound), - Err(neg_sp) => negative_bounds.push(neg_sp), - } + bounds.push(self.parse_generic_bound()?); if allow_plus == AllowPlus::No || !self.eat_plus() { break; } } - if !negative_bounds.is_empty() { - self.error_negative_bounds(colon_span, &bounds, negative_bounds); - } - Ok(bounds) } @@ -713,55 +700,22 @@ impl<'a> Parser<'a> { fn can_begin_bound(&mut self) -> bool { // This needs to be synchronized with `TokenKind::can_begin_bound`. self.check_path() - || self.check_lifetime() - || self.check(&token::Not) // Used for error reporting only. - || self.check(&token::Question) - || self.check(&token::Tilde) - || self.check_keyword(kw::For) - || self.check(&token::OpenDelim(Delimiter::Parenthesis)) - } - - fn error_negative_bounds( - &self, - colon_span: Option, - bounds: &[GenericBound], - negative_bounds: Vec, - ) { - let sub = if let Some(bound_list) = colon_span { - let bound_list = bound_list.to(self.prev_token.span); - let mut new_bound_list = String::new(); - if !bounds.is_empty() { - let mut snippets = bounds.iter().map(|bound| self.span_to_snippet(bound.span())); - while let Some(Ok(snippet)) = snippets.next() { - new_bound_list.push_str(" + "); - new_bound_list.push_str(&snippet); - } - new_bound_list = new_bound_list.replacen(" +", ":", 1); - } - - Some(NegativeBoundsNotSupportedSugg { - bound_list, - num_bounds: negative_bounds.len(), - fixed: new_bound_list, - }) - } else { - None - }; - - let last_span = *negative_bounds.last().expect("no negative bounds, but still error?"); - self.sess.emit_err(NegativeBoundsNotSupported { negative_bounds, last_span, sub }); + || self.check_lifetime() + || self.check(&token::Not) + || self.check(&token::Question) + || self.check(&token::Tilde) + || self.check_keyword(kw::For) + || self.check(&token::OpenDelim(Delimiter::Parenthesis)) } /// Parses a bound according to the grammar: /// ```ebnf /// BOUND = TY_BOUND | LT_BOUND /// ``` - fn parse_generic_bound(&mut self) -> PResult<'a, Result> { - let anchor_lo = self.prev_token.span; + fn parse_generic_bound(&mut self) -> PResult<'a, GenericBound> { let lo = self.token.span; let has_parens = self.eat(&token::OpenDelim(Delimiter::Parenthesis)); let inner_lo = self.token.span; - let is_negative = self.eat(&token::Not); let modifiers = self.parse_ty_bound_modifiers()?; let bound = if self.token.is_lifetime() { @@ -771,7 +725,7 @@ impl<'a> Parser<'a> { self.parse_generic_ty_bound(lo, has_parens, modifiers)? }; - Ok(if is_negative { Err(anchor_lo.to(self.prev_token.span)) } else { Ok(bound) }) + Ok(bound) } /// Parses a lifetime ("outlives") bound, e.g. `'a`, according to: @@ -799,8 +753,14 @@ impl<'a> Parser<'a> { self.sess.emit_err(errors::TildeConstLifetime { span }); } - if let Some(span) = modifiers.maybe { - self.sess.emit_err(errors::MaybeLifetime { span }); + match modifiers.bound_polarity { + BoundPolarity::Positive => {} + BoundPolarity::Negative(span) => { + self.sess.emit_err(errors::ModifierLifetime { span, sigil: "!" }); + } + BoundPolarity::Maybe(span) => { + self.sess.emit_err(errors::ModifierLifetime { span, sigil: "?" }); + } } } @@ -843,9 +803,16 @@ impl<'a> Parser<'a> { None }; - let maybe = self.eat(&token::Question).then_some(self.prev_token.span); + let bound_polarity = if self.eat(&token::Question) { + BoundPolarity::Maybe(self.prev_token.span) + } else if self.eat(&token::Not) { + self.sess.gated_spans.gate(sym::negative_bounds, self.prev_token.span); + BoundPolarity::Negative(self.prev_token.span) + } else { + BoundPolarity::Positive + }; - Ok(BoundModifiers { maybe, maybe_const }) + Ok(BoundModifiers { bound_polarity, maybe_const }) } /// Parses a type bound according to: diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7969b848fd956..714d10f234107 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -984,6 +984,7 @@ symbols! { needs_panic_runtime, neg, negate_unsigned, + negative_bounds, negative_impls, neon, never, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 595f6e0b9271b..d87a297942829 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -530,6 +530,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { associated_ty: Option<(&'static str, Ty<'tcx>)>, mut body_id: LocalDefId, ) { + if trait_pred.skip_binder().polarity == ty::ImplPolarity::Negative { + return; + } + let trait_pred = self.resolve_numeric_literals_with_default(trait_pred); let self_ty = trait_pred.skip_binder().self_ty(); diff --git a/tests/ui/feature-gates/feature-gate-negative_bounds.rs b/tests/ui/feature-gates/feature-gate-negative_bounds.rs new file mode 100644 index 0000000000000..533cb0ce5bcc5 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-negative_bounds.rs @@ -0,0 +1,4 @@ +fn test() {} +//~^ ERROR negative bounds are not supported + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-negative_bounds.stderr b/tests/ui/feature-gates/feature-gate-negative_bounds.stderr new file mode 100644 index 0000000000000..ae010fdf3f848 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-negative_bounds.stderr @@ -0,0 +1,8 @@ +error: negative bounds are not supported + --> $DIR/feature-gate-negative_bounds.rs:1:12 + | +LL | fn test() {} + | ^ + +error: aborting due to previous error + diff --git a/tests/ui/issues/issue-58857.stderr b/tests/ui/issues/issue-58857.stderr index e2acec47e5abf..6aef35f0bb96a 100644 --- a/tests/ui/issues/issue-58857.stderr +++ b/tests/ui/issues/issue-58857.stderr @@ -1,8 +1,8 @@ error: negative bounds are not supported - --> $DIR/issue-58857.rs:4:7 + --> $DIR/issue-58857.rs:4:9 | LL | impl Conj{} - | ^^^^^^^^ negative bounds are not supported + | ^ error: aborting due to previous error diff --git a/tests/ui/parser/issues/issue-33418.fixed b/tests/ui/parser/issues/issue-33418.fixed deleted file mode 100644 index ed885ae143566..0000000000000 --- a/tests/ui/parser/issues/issue-33418.fixed +++ /dev/null @@ -1,19 +0,0 @@ -// run-rustfix - -trait Tr {} -//~^ ERROR negative bounds are not supported -trait Tr2: SuperA {} -//~^ ERROR negative bounds are not supported -trait Tr3: SuperB {} -//~^ ERROR negative bounds are not supported -trait Tr4: SuperB + SuperD {} -//~^ ERROR negative bounds are not supported -trait Tr5 {} -//~^ ERROR negative bounds are not supported - -trait SuperA {} -trait SuperB {} -trait SuperC {} -trait SuperD {} - -fn main() {} diff --git a/tests/ui/parser/issues/issue-33418.rs b/tests/ui/parser/issues/issue-33418.rs index 9934284abfbbe..4ebd5871e53ca 100644 --- a/tests/ui/parser/issues/issue-33418.rs +++ b/tests/ui/parser/issues/issue-33418.rs @@ -1,5 +1,3 @@ -// run-rustfix - trait Tr: !SuperA {} //~^ ERROR negative bounds are not supported trait Tr2: SuperA + !SuperB {} @@ -7,10 +5,12 @@ trait Tr2: SuperA + !SuperB {} trait Tr3: !SuperA + SuperB {} //~^ ERROR negative bounds are not supported trait Tr4: !SuperA + SuperB - + !SuperC + SuperD {} +//~^ ERROR negative bounds are not supported ++ !SuperC + SuperD {} //~^ ERROR negative bounds are not supported trait Tr5: !SuperA - + !SuperB {} +//~^ ERROR negative bounds are not supported ++ !SuperB {} //~^ ERROR negative bounds are not supported trait SuperA {} diff --git a/tests/ui/parser/issues/issue-33418.stderr b/tests/ui/parser/issues/issue-33418.stderr index 9a8733e89292e..b111bcfd24093 100644 --- a/tests/ui/parser/issues/issue-33418.stderr +++ b/tests/ui/parser/issues/issue-33418.stderr @@ -1,36 +1,44 @@ error: negative bounds are not supported - --> $DIR/issue-33418.rs:3:9 + --> $DIR/issue-33418.rs:1:11 | LL | trait Tr: !SuperA {} - | ^^^^^^^^^ negative bounds are not supported + | ^ error: negative bounds are not supported - --> $DIR/issue-33418.rs:5:19 + --> $DIR/issue-33418.rs:3:21 | LL | trait Tr2: SuperA + !SuperB {} - | ^^^^^^^^^ negative bounds are not supported + | ^ error: negative bounds are not supported - --> $DIR/issue-33418.rs:7:10 + --> $DIR/issue-33418.rs:5:12 | LL | trait Tr3: !SuperA + SuperB {} - | ^^^^^^^^^ negative bounds are not supported + | ^ error: negative bounds are not supported - --> $DIR/issue-33418.rs:9:10 + --> $DIR/issue-33418.rs:7:12 | LL | trait Tr4: !SuperA + SuperB - | ^^^^^^^^^ -LL | + !SuperC + SuperD {} - | ^^^^^^^^^ negative bounds are not supported + | ^ error: negative bounds are not supported - --> $DIR/issue-33418.rs:12:10 + --> $DIR/issue-33418.rs:9:3 + | +LL | + !SuperC + SuperD {} + | ^ + +error: negative bounds are not supported + --> $DIR/issue-33418.rs:11:12 | LL | trait Tr5: !SuperA - | ^^^^^^^^^ -LL | + !SuperB {} - | ^^^^^^^^^ negative bounds are not supported + | ^ + +error: negative bounds are not supported + --> $DIR/issue-33418.rs:13:3 + | +LL | + !SuperB {} + | ^ -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.fixed b/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.fixed index 95019b2786925..2c42f9731743e 100644 --- a/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.fixed +++ b/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.fixed @@ -6,9 +6,12 @@ fn main() {} -pub fn f1() {} +pub fn f1() {} //~^ ERROR negative bounds are not supported -pub fn f2<'a, T: Ord>() {} +//~| ERROR `!` may only modify trait bounds, not lifetime bound +pub fn f2<'a, T: Ord + 'a>() {} //~^ ERROR negative bounds are not supported -pub fn f3<'a, T: Ord>() {} +//~| ERROR `!` may only modify trait bounds, not lifetime bound +pub fn f3<'a, T: 'a + Ord>() {} //~^ ERROR negative bounds are not supported +//~| ERROR `!` may only modify trait bounds, not lifetime bound diff --git a/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.rs b/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.rs index 82f54f8faa98c..e510efaae5ba2 100644 --- a/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.rs +++ b/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.rs @@ -8,7 +8,10 @@ fn main() {} pub fn f1() {} //~^ ERROR negative bounds are not supported +//~| ERROR `!` may only modify trait bounds, not lifetime bound pub fn f2<'a, T: Ord + !'a>() {} //~^ ERROR negative bounds are not supported +//~| ERROR `!` may only modify trait bounds, not lifetime bound pub fn f3<'a, T: !'a + Ord>() {} //~^ ERROR negative bounds are not supported +//~| ERROR `!` may only modify trait bounds, not lifetime bound diff --git a/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.stderr b/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.stderr index a4a422948aca6..91fe02db3a606 100644 --- a/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.stderr +++ b/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.stderr @@ -1,20 +1,38 @@ +error: `!` may only modify trait bounds, not lifetime bounds + --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:9:14 + | +LL | pub fn f1() {} + | ^ + +error: `!` may only modify trait bounds, not lifetime bounds + --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:12:24 + | +LL | pub fn f2<'a, T: Ord + !'a>() {} + | ^ + +error: `!` may only modify trait bounds, not lifetime bounds + --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:15:18 + | +LL | pub fn f3<'a, T: !'a + Ord>() {} + | ^ + error: negative bounds are not supported - --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:9:12 + --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:9:14 | LL | pub fn f1() {} - | ^^^^^^^^^^ negative bounds are not supported + | ^ error: negative bounds are not supported - --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:11:22 + --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:12:24 | LL | pub fn f2<'a, T: Ord + !'a>() {} - | ^^^^^ negative bounds are not supported + | ^ error: negative bounds are not supported - --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:13:16 + --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:15:18 | LL | pub fn f3<'a, T: !'a + Ord>() {} - | ^^^^^ negative bounds are not supported + | ^ -error: aborting due to 3 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/traits/negative-bounds/simple.rs b/tests/ui/traits/negative-bounds/simple.rs new file mode 100644 index 0000000000000..f6d1d5169c4fc --- /dev/null +++ b/tests/ui/traits/negative-bounds/simple.rs @@ -0,0 +1,42 @@ +#![feature(negative_bounds, negative_impls)] +//~^ WARN the feature `negative_bounds` is incomplete and may not be safe to use and/or cause compiler crashes + +fn not_copy() {} + +fn neg_param_env() { + not_copy::(); +} + +fn pos_param_env() { + not_copy::(); + //~^ ERROR the trait bound `T: !Copy` is not satisfied +} + +fn unknown() { + not_copy::(); + //~^ ERROR the trait bound `T: !Copy` is not satisfied +} + +struct NotCopyable; +impl !Copy for NotCopyable {} + +fn neg_impl() { + not_copy::(); +} + +#[derive(Copy, Clone)] +struct Copyable; + +fn pos_impl() { + not_copy::(); + //~^ ERROR the trait bound `Copyable: !Copy` is not satisfied +} + +struct NotNecessarilyCopyable; + +fn unknown_impl() { + not_copy::(); + //~^ ERROR the trait bound `NotNecessarilyCopyable: !Copy` is not satisfied +} + +fn main() {} diff --git a/tests/ui/traits/negative-bounds/simple.stderr b/tests/ui/traits/negative-bounds/simple.stderr new file mode 100644 index 0000000000000..efd82c09d6258 --- /dev/null +++ b/tests/ui/traits/negative-bounds/simple.stderr @@ -0,0 +1,82 @@ +warning: the feature `negative_bounds` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/simple.rs:1:12 + | +LL | #![feature(negative_bounds, negative_impls)] + | ^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: the trait bound `T: !Copy` is not satisfied + --> $DIR/simple.rs:6:16 + | +LL | not_copy::(); + | ^ the trait `!Copy` is not implemented for `T` + | +note: required by a bound in `not_copy` + --> $DIR/simple.rs:3:16 + | +LL | fn not_copy() {} + | ^^^^^ required by this bound in `not_copy` + +error[E0277]: the trait bound `T: !Copy` is not satisfied + --> $DIR/simple.rs:10:16 + | +LL | not_copy::(); + | ^ the trait `!Copy` is not implemented for `T` + | +note: required by a bound in `not_copy` + --> $DIR/simple.rs:4:16 + | +LL | fn not_copy() {} + | ^^^^^ required by this bound in `not_copy` + +error[E0277]: the trait bound `T: !Copy` is not satisfied + --> $DIR/simple.rs:16:16 + | +LL | not_copy::(); + | ^ the trait `!Copy` is not implemented for `T` + | +note: required by a bound in `not_copy` + --> $DIR/simple.rs:4:16 + | +LL | fn not_copy() {} + | ^^^^^ required by this bound in `not_copy` + +error[E0277]: the trait bound `Copyable: !Copy` is not satisfied + --> $DIR/simple.rs:31:16 + | +LL | not_copy::(); + | ^^^^^^^^ the trait `!Copy` is not implemented for `Copyable` + | + = help: the trait `Copy` is implemented for `Copyable` +note: required by a bound in `not_copy` + --> $DIR/simple.rs:4:16 + | +LL | fn not_copy() {} + | ^^^^^ required by this bound in `not_copy` +help: consider annotating `Copyable` with `#[derive(Copy)]` + | +LL + #[derive(Copy)] +LL | struct Copyable; + | + +error[E0277]: the trait bound `NotNecessarilyCopyable: !Copy` is not satisfied + --> $DIR/simple.rs:38:16 + | +LL | not_copy::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `!Copy` is not implemented for `NotNecessarilyCopyable` + | +note: required by a bound in `not_copy` + --> $DIR/simple.rs:4:16 + | +LL | fn not_copy() {} + | ^^^^^ required by this bound in `not_copy` +help: consider annotating `NotNecessarilyCopyable` with `#[derive(Copy)]` + | +LL + #[derive(Copy)] +LL | struct NotNecessarilyCopyable; + | + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. From 86f50b9f5c56982f4f50404539c56395ce811f18 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 25 Apr 2023 05:47:05 +0000 Subject: [PATCH 15/20] Disallow associated type constraints on negative bounds --- compiler/rustc_ast_passes/messages.ftl | 3 ++ .../rustc_ast_passes/src/ast_validation.rs | 12 +++++++ compiler/rustc_ast_passes/src/errors.rs | 7 ++++ .../rustc_hir_analysis/src/astconv/mod.rs | 2 -- .../negative-bounds/associated-constraints.rs | 20 +++++++++++ .../associated-constraints.stderr | 34 +++++++++++++++++++ 6 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 tests/ui/traits/negative-bounds/associated-constraints.rs create mode 100644 tests/ui/traits/negative-bounds/associated-constraints.stderr diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 90535af781d90..2f413789e7704 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -238,3 +238,6 @@ ast_passes_show_span = {$msg} ast_passes_negative_bound_not_supported = negative bounds are not supported + +ast_passes_constraint_on_negative_bound = + associated type constraints not allowed on negative bounds diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 6e9d33516ce5d..bf43bbdbbeeba 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1177,6 +1177,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } + // Negative trait bounds are not allowed to have associated constraints + if let GenericBound::Trait(trait_ref, TraitBoundModifier::Negative) = bound + && let Some(segment) = trait_ref.trait_ref.path.segments.last() + && let Some(ast::GenericArgs::AngleBracketed(args)) = segment.args.as_deref() + { + for arg in &args.args { + if let ast::AngleBracketedArg::Constraint(constraint) = arg { + self.err_handler().emit_err(errors::ConstraintOnNegativeBound { span: constraint.span }); + } + } + } + visit::walk_param_bound(self, bound) } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 66a47db90744f..82fe2a21d0876 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -701,3 +701,10 @@ pub struct NegativeBoundUnsupported { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(ast_passes_constraint_on_negative_bound)] +pub struct ConstraintOnNegativeBound { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 0faf7a4ba4519..fda59d000eaa2 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -701,8 +701,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut dup_bindings = FxHashMap::default(); for binding in &assoc_bindings { - // TODO: negative polarity can't have associated type bindings! - // Specify type to assert that error was already reported in `Err` case. let _: Result<_, ErrorGuaranteed> = self.add_predicates_for_ast_type_binding( hir_id, diff --git a/tests/ui/traits/negative-bounds/associated-constraints.rs b/tests/ui/traits/negative-bounds/associated-constraints.rs new file mode 100644 index 0000000000000..bc1a0ef170837 --- /dev/null +++ b/tests/ui/traits/negative-bounds/associated-constraints.rs @@ -0,0 +1,20 @@ +#![feature(negative_bounds, associated_type_bounds)] +//~^ WARN the feature `negative_bounds` is incomplete and may not be safe to use and/or cause compiler crashes + +trait Trait { + type Assoc; +} + +fn test>() {} +//~^ ERROR associated type constraints not allowed on negative bounds + +fn test2() where T: !Trait {} +//~^ ERROR associated type constraints not allowed on negative bounds + +fn test3>() {} +//~^ ERROR associated type constraints not allowed on negative bounds + +fn test4() where T: !Trait {} +//~^ ERROR associated type constraints not allowed on negative bounds + +fn main() {} diff --git a/tests/ui/traits/negative-bounds/associated-constraints.stderr b/tests/ui/traits/negative-bounds/associated-constraints.stderr new file mode 100644 index 0000000000000..335ac7e5ad903 --- /dev/null +++ b/tests/ui/traits/negative-bounds/associated-constraints.stderr @@ -0,0 +1,34 @@ +error: associated type constraints not allowed on negative bounds + --> $DIR/associated-constraints.rs:8:19 + | +LL | fn test>() {} + | ^^^^^^^^^^^ + +error: associated type constraints not allowed on negative bounds + --> $DIR/associated-constraints.rs:11:31 + | +LL | fn test2() where T: !Trait {} + | ^^^^^^^^^^^ + +error: associated type constraints not allowed on negative bounds + --> $DIR/associated-constraints.rs:14:20 + | +LL | fn test3>() {} + | ^^^^^^^^^^^ + +error: associated type constraints not allowed on negative bounds + --> $DIR/associated-constraints.rs:17:31 + | +LL | fn test4() where T: !Trait {} + | ^^^^^^^^^^^ + +warning: the feature `negative_bounds` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/associated-constraints.rs:1:12 + | +LL | #![feature(negative_bounds, associated_type_bounds)] + | ^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + +error: aborting due to 4 previous errors; 1 warning emitted + From 40a63cb06ff21d7cd38f35c92a29162e43f9aadf Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 25 Apr 2023 06:02:49 +0000 Subject: [PATCH 16/20] Make tools happy --- src/librustdoc/html/format.rs | 1 + src/librustdoc/json/conversions.rs | 4 ++++ src/tools/rustfmt/src/types.rs | 2 ++ 3 files changed, 7 insertions(+) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 297120da284b9..1c6810bdaf9be 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -439,6 +439,7 @@ impl clean::GenericBound { let modifier_str = match modifier { hir::TraitBoundModifier::None => "", hir::TraitBoundModifier::Maybe => "?", + hir::TraitBoundModifier::Negative => "!", // ~const is experimental; do not display those bounds in rustdoc hir::TraitBoundModifier::MaybeConst => "", }; diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 62aab46fa7e8b..b5bebb7059380 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -533,6 +533,10 @@ pub(crate) fn from_trait_bound_modifier( None => TraitBoundModifier::None, Maybe => TraitBoundModifier::Maybe, MaybeConst => TraitBoundModifier::MaybeConst, + // FIXME(negative-bounds): This bound should be rendered negative, but + // since that's experimental, maybe let's not add it to the rustdoc json + // API just now... + Negative => TraitBoundModifier::None, } } diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 01e2fb6e61e15..9ebe38cc25f7f 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -552,6 +552,8 @@ impl Rewrite for ast::GenericBound { ast::TraitBoundModifier::MaybeConstMaybe => poly_trait_ref .rewrite(context, shape.offset_left(8)?) .map(|s| format!("~const ?{}", s)), + rustc_ast::TraitBoundModifier::Negative + | rustc_ast::TraitBoundModifier::MaybeConstNegative => None, }; rewrite.map(|s| if has_paren { format!("({})", s) } else { s }) } From 03469c3f2e5f7318b9dd47483f5656b321880408 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 25 Apr 2023 06:37:24 +0000 Subject: [PATCH 17/20] Make negative trait bounds work with the old trait solver --- compiler/rustc_hir_analysis/src/astconv/mod.rs | 10 ++++++++++ compiler/rustc_infer/src/traits/util.rs | 4 ++++ .../src/traits/select/candidate_assembly.rs | 5 +++++ compiler/rustc_trait_selection/src/traits/wf.rs | 15 +++++++++++++++ tests/ui/traits/negative-bounds/simple.stderr | 16 ++-------------- 5 files changed, 36 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index fda59d000eaa2..1f01ad009dc39 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -701,6 +701,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut dup_bindings = FxHashMap::default(); for binding in &assoc_bindings { + // Don't register additional associated type bounds for negative bounds, + // since we should have emitten an error for them earlier, and they will + // not be well-formed! + if polarity == ty::ImplPolarity::Negative { + self.tcx() + .sess + .delay_span_bug(binding.span, "negative trait bounds should not have bindings"); + continue; + } + // Specify type to assert that error was already reported in `Err` case. let _: Result<_, ErrorGuaranteed> = self.add_predicates_for_ast_type_binding( hir_id, diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index ef01d5d513bbc..f54e5e5e56ffc 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -200,6 +200,10 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> { let bound_predicate = elaboratable.predicate().kind(); match bound_predicate.skip_binder() { ty::PredicateKind::Clause(ty::Clause::Trait(data)) => { + // Negative trait bounds do not imply any supertrait bounds + if data.polarity == ty::ImplPolarity::Negative { + return; + } // Get predicates implied by the trait, or only super predicates if we only care about self predicates. let predicates = if self.only_self { tcx.super_predicates_of(data.def_id()) diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 1db9b8ce92e4f..a8864f47ef036 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -57,6 +57,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if obligation.polarity() == ty::ImplPolarity::Negative { self.assemble_candidates_for_trait_alias(obligation, &mut candidates); self.assemble_candidates_from_impls(obligation, &mut candidates); + self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?; } else { self.assemble_candidates_for_trait_alias(obligation, &mut candidates); @@ -187,6 +188,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Keep only those bounds which may apply, and propagate overflow if it occurs. for bound in matching_bounds { + if bound.skip_binder().polarity != stack.obligation.predicate.skip_binder().polarity { + continue; + } + // FIXME(oli-obk): it is suspicious that we are dropping the constness and // polarity here. let wc = self.where_clause_may_apply(stack, bound.map_bound(|t| t.trait_ref))?; diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 40e19abc0d06a..0590e02d84ac0 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -328,6 +328,13 @@ impl<'tcx> WfPredicates<'tcx> { let tcx = self.tcx; let trait_ref = &trait_pred.trait_ref; + // Negative trait predicates don't require supertraits to hold, just + // that their substs are WF. + if trait_pred.polarity == ty::ImplPolarity::Negative { + self.compute_negative_trait_pred(trait_ref); + return; + } + // if the trait predicate is not const, the wf obligations should not be const as well. let obligations = if trait_pred.constness == ty::BoundConstness::NotConst { self.nominal_obligations_without_const(trait_ref.def_id, trait_ref.substs) @@ -393,6 +400,14 @@ impl<'tcx> WfPredicates<'tcx> { ); } + // Compute the obligations that are required for `trait_ref` to be WF, + // given that it is a *negative* trait predicate. + fn compute_negative_trait_pred(&mut self, trait_ref: &ty::TraitRef<'tcx>) { + for arg in trait_ref.substs { + self.compute(arg); + } + } + /// Pushes the obligations required for `trait_ref::Item` to be WF /// into `self.out`. fn compute_projection(&mut self, data: ty::AliasTy<'tcx>) { diff --git a/tests/ui/traits/negative-bounds/simple.stderr b/tests/ui/traits/negative-bounds/simple.stderr index efd82c09d6258..a3cab41a2ce0f 100644 --- a/tests/ui/traits/negative-bounds/simple.stderr +++ b/tests/ui/traits/negative-bounds/simple.stderr @@ -7,19 +7,7 @@ LL | #![feature(negative_bounds, negative_impls)] = note: `#[warn(incomplete_features)]` on by default error[E0277]: the trait bound `T: !Copy` is not satisfied - --> $DIR/simple.rs:6:16 - | -LL | not_copy::(); - | ^ the trait `!Copy` is not implemented for `T` - | -note: required by a bound in `not_copy` - --> $DIR/simple.rs:3:16 - | -LL | fn not_copy() {} - | ^^^^^ required by this bound in `not_copy` - -error[E0277]: the trait bound `T: !Copy` is not satisfied - --> $DIR/simple.rs:10:16 + --> $DIR/simple.rs:11:16 | LL | not_copy::(); | ^ the trait `!Copy` is not implemented for `T` @@ -77,6 +65,6 @@ LL + #[derive(Copy)] LL | struct NotNecessarilyCopyable; | -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0277`. From 6fca051b76342f8c21ec85fb9a77103b7c05adb9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 26 Apr 2023 22:37:36 +0000 Subject: [PATCH 18/20] Rustfmt support for negative bounds, test --- src/tools/rustfmt/src/types.rs | 8 ++++++-- src/tools/rustfmt/tests/target/negative-bounds.rs | 11 +++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 src/tools/rustfmt/tests/target/negative-bounds.rs diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 9ebe38cc25f7f..f548388ed8ba2 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -552,8 +552,12 @@ impl Rewrite for ast::GenericBound { ast::TraitBoundModifier::MaybeConstMaybe => poly_trait_ref .rewrite(context, shape.offset_left(8)?) .map(|s| format!("~const ?{}", s)), - rustc_ast::TraitBoundModifier::Negative - | rustc_ast::TraitBoundModifier::MaybeConstNegative => None, + ast::TraitBoundModifier::Negative => poly_trait_ref + .rewrite(context, shape.offset_left(1)?) + .map(|s| format!("!{}", s)), + ast::TraitBoundModifier::MaybeConstNegative => poly_trait_ref + .rewrite(context, shape.offset_left(8)?) + .map(|s| format!("~const !{}", s)), }; rewrite.map(|s| if has_paren { format!("({})", s) } else { s }) } diff --git a/src/tools/rustfmt/tests/target/negative-bounds.rs b/src/tools/rustfmt/tests/target/negative-bounds.rs new file mode 100644 index 0000000000000..4fb35cccf6684 --- /dev/null +++ b/src/tools/rustfmt/tests/target/negative-bounds.rs @@ -0,0 +1,11 @@ +fn negative() +where + i32: !Copy, +{ +} + +fn maybe_const_negative() +where + i32: ~const !Copy, +{ +} From 6e377849c09a310b6eef50ebd91c1f014d41ab73 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Sun, 12 Feb 2023 12:05:56 +0000 Subject: [PATCH 19/20] Correctly convert an NT path to a Win32 path This can be done by simply changing the `\??\` prefix to `\\?\` and then attempting to convert to a user path. Currently it simply strips off the prefix which could lead to the wrong path being returned (e.g. if it's not a drive path or if the path contains trailing spaces, etc). --- library/std/src/fs/tests.rs | 6 +++++- library/std/src/sys/windows/fs.rs | 27 ++++++++++++++++----------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 401def1845827..a8a0b9f122d66 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -919,6 +919,7 @@ fn symlink_noexist() { #[test] fn read_link() { + let tmpdir = tmpdir(); if cfg!(windows) { // directory symlink assert_eq!(check!(fs::read_link(r"C:\Users\All Users")), Path::new(r"C:\ProgramData")); @@ -933,8 +934,11 @@ fn read_link() { Path::new(r"C:\Users") ); } + // Check that readlink works with non-drive paths on Windows. + let link = tmpdir.join("link_unc"); + check!(symlink_dir(r"\\localhost\c$\", &link)); + assert_eq!(check!(fs::read_link(&link)), Path::new(r"\\localhost\c$\")); } - let tmpdir = tmpdir(); let link = tmpdir.join("link"); if !got_symlink_permission(&tmpdir) { return; diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index f99cdfbecfb69..ff05d31915cb0 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -477,7 +477,7 @@ impl File { fn reparse_point( &self, space: &mut Align8<[MaybeUninit]>, - ) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> { + ) -> io::Result<(c::DWORD, *mut c::REPARSE_DATA_BUFFER)> { unsafe { let mut bytes = 0; cvt({ @@ -496,7 +496,7 @@ impl File { ) })?; const _: () = assert!(core::mem::align_of::() <= 8); - Ok((bytes, space.0.as_ptr().cast::())) + Ok((bytes, space.0.as_mut_ptr().cast::())) } } @@ -506,22 +506,22 @@ impl File { unsafe { let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag { c::IO_REPARSE_TAG_SYMLINK => { - let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER = - ptr::addr_of!((*buf).rest).cast(); + let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER = + ptr::addr_of_mut!((*buf).rest).cast(); assert!(info.is_aligned()); ( - ptr::addr_of!((*info).PathBuffer).cast::(), + ptr::addr_of_mut!((*info).PathBuffer).cast::(), (*info).SubstituteNameOffset / 2, (*info).SubstituteNameLength / 2, (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0, ) } c::IO_REPARSE_TAG_MOUNT_POINT => { - let info: *const c::MOUNT_POINT_REPARSE_BUFFER = - ptr::addr_of!((*buf).rest).cast(); + let info: *mut c::MOUNT_POINT_REPARSE_BUFFER = + ptr::addr_of_mut!((*buf).rest).cast(); assert!(info.is_aligned()); ( - ptr::addr_of!((*info).PathBuffer).cast::(), + ptr::addr_of_mut!((*info).PathBuffer).cast::(), (*info).SubstituteNameOffset / 2, (*info).SubstituteNameLength / 2, false, @@ -535,13 +535,18 @@ impl File { } }; let subst_ptr = path_buffer.add(subst_off.into()); - let mut subst = slice::from_raw_parts(subst_ptr, subst_len as usize); + let subst = slice::from_raw_parts_mut(subst_ptr, subst_len as usize); // Absolute paths start with an NT internal namespace prefix `\??\` // We should not let it leak through. if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) { - subst = &subst[4..]; + // Turn `\??\` into `\\?\` (a verbatim path). + subst[1] = b'\\' as u16; + // Attempt to convert to a more user-friendly path. + let user = super::args::to_user_path(subst.iter().copied().chain([0]).collect())?; + Ok(PathBuf::from(OsString::from_wide(&user))) + } else { + Ok(PathBuf::from(OsString::from_wide(subst))) } - Ok(PathBuf::from(OsString::from_wide(subst))) } } From 109a47fc9d63a5e093bd36423e290fe8bc18ae25 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Wed, 3 May 2023 11:20:59 +0100 Subject: [PATCH 20/20] Use `from_wide_to_user_path` in `read_link` --- library/std/src/sys/windows/args.rs | 5 +++-- library/std/src/sys/windows/fs.rs | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs index 43c0cdb657ef1..5bfd8b52ed08d 100644 --- a/library/std/src/sys/windows/args.rs +++ b/library/std/src/sys/windows/args.rs @@ -313,6 +313,9 @@ pub(crate) fn make_bat_command_line( /// /// This is necessary because cmd.exe does not support verbatim paths. pub(crate) fn to_user_path(path: &Path) -> io::Result> { + from_wide_to_user_path(to_u16s(path)?) +} +pub(crate) fn from_wide_to_user_path(mut path: Vec) -> io::Result> { use crate::ptr; use crate::sys::windows::fill_utf16_buf; @@ -325,8 +328,6 @@ pub(crate) fn to_user_path(path: &Path) -> io::Result> { const N: u16 = b'N' as _; const C: u16 = b'C' as _; - let mut path = to_u16s(path)?; - // Early return if the path is too long to remove the verbatim prefix. const LEGACY_MAX_PATH: usize = 260; if path.len() > LEGACY_MAX_PATH { diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index ff05d31915cb0..fe052c8281b50 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -542,8 +542,10 @@ impl File { // Turn `\??\` into `\\?\` (a verbatim path). subst[1] = b'\\' as u16; // Attempt to convert to a more user-friendly path. - let user = super::args::to_user_path(subst.iter().copied().chain([0]).collect())?; - Ok(PathBuf::from(OsString::from_wide(&user))) + let user = super::args::from_wide_to_user_path( + subst.iter().copied().chain([0]).collect(), + )?; + Ok(PathBuf::from(OsString::from_wide(&user.strip_suffix(&[0]).unwrap_or(&user)))) } else { Ok(PathBuf::from(OsString::from_wide(subst))) }