From 86a4b27475aab52b998c15f5758540697cc9cff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 4 Feb 2021 10:23:01 +0200 Subject: [PATCH 01/20] Increment `self.index` before calling `Iterator::self.a.__iterator_get_unchecked` in `Zip` `TrustedRandomAccess` specialization Otherwise if `Iterator::self.a.__iterator_get_unchecked` panics the index would not have been incremented yet and another call to `Iterator::next` would read from the same index again, which is not allowed according to the API contract of `TrustedRandomAccess` for `!Clone`. Fixes https://github.com/rust-lang/rust/issues/81740 --- library/core/src/iter/adapters/zip.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/library/core/src/iter/adapters/zip.rs b/library/core/src/iter/adapters/zip.rs index 98b8dca961407..9f98353452006 100644 --- a/library/core/src/iter/adapters/zip.rs +++ b/library/core/src/iter/adapters/zip.rs @@ -198,12 +198,13 @@ where Some((self.a.__iterator_get_unchecked(i), self.b.__iterator_get_unchecked(i))) } } else if A::may_have_side_effect() && self.index < self.a.size() { + let i = self.index; + self.index += 1; // match the base implementation's potential side effects - // SAFETY: we just checked that `self.index` < `self.a.len()` + // SAFETY: we just checked that `i` < `self.a.len()` unsafe { - self.a.__iterator_get_unchecked(self.index); + self.a.__iterator_get_unchecked(i); } - self.index += 1; None } else { None From 55ca27faa78434d81d3f59535c96bbb2a08f0d5c Mon Sep 17 00:00:00 2001 From: The8472 Date: Sat, 6 Feb 2021 22:38:16 +0100 Subject: [PATCH 02/20] use rwlock for accessing ENV --- library/std/src/sys/unix/os.rs | 16 +++---- .../std/src/sys/unix/process/process_unix.rs | 6 +-- library/std/src/sys_common/rwlock.rs | 44 +++++++++++++++++++ 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index d5e14bec76572..33921180cb17c 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -22,6 +22,7 @@ use crate::str; use crate::sys::cvt; use crate::sys::fd; use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard}; +use crate::sys_common::rwlock::{RWLock, RWLockGuard}; use crate::vec; use libc::{c_char, c_int, c_void}; @@ -493,17 +494,16 @@ pub unsafe fn environ() -> *mut *const *const c_char { &mut environ } -pub unsafe fn env_lock() -> StaticMutexGuard { - // It is UB to attempt to acquire this mutex reentrantly! - static ENV_LOCK: StaticMutex = StaticMutex::new(); - ENV_LOCK.lock() +pub unsafe fn env_rwlock(readonly: bool) -> RWLockGuard { + static ENV_LOCK: RWLock = RWLock::new(); + if readonly { ENV_LOCK.read_with_guard() } else { ENV_LOCK.write_with_guard() } } /// Returns a vector of (variable, value) byte-vector pairs for all the /// environment variables of the current process. pub fn env() -> Env { unsafe { - let _guard = env_lock(); + let _guard = env_rwlock(true); let mut environ = *environ(); let mut result = Vec::new(); if !environ.is_null() { @@ -540,7 +540,7 @@ pub fn getenv(k: &OsStr) -> io::Result> { // always None as well let k = CString::new(k.as_bytes())?; unsafe { - let _guard = env_lock(); + let _guard = env_rwlock(true); let s = libc::getenv(k.as_ptr()) as *const libc::c_char; let ret = if s.is_null() { None @@ -556,7 +556,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { let v = CString::new(v.as_bytes())?; unsafe { - let _guard = env_lock(); + let _guard = env_rwlock(false); cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) } } @@ -565,7 +565,7 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> { let nbuf = CString::new(n.as_bytes())?; unsafe { - let _guard = env_lock(); + let _guard = env_rwlock(false); cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) } } diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 2746f87468dca..0e4e66389fb3b 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -47,7 +47,7 @@ impl Command { // a lock any more because the parent won't do anything and the child is // in its own process. let result = unsafe { - let _env_lock = sys::os::env_lock(); + let _env_lock = sys::os::env_rwlock(true); cvt(libc::fork())? }; @@ -124,7 +124,7 @@ impl Command { // Similar to when forking, we want to ensure that access to // the environment is synchronized, so make sure to grab the // environment lock before we try to exec. - let _lock = sys::os::env_lock(); + let _lock = sys::os::env_rwlock(true); let Err(e) = self.do_exec(theirs, envp.as_ref()); e @@ -404,7 +404,7 @@ impl Command { cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?; // Make sure we synchronize access to the global `environ` resource - let _env_lock = sys::os::env_lock(); + let _env_lock = sys::os::env_rwlock(true); let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _); cvt_nz(libc::posix_spawnp( &mut p.pid, diff --git a/library/std/src/sys_common/rwlock.rs b/library/std/src/sys_common/rwlock.rs index 3705d641a1be6..1f2765ffbcca6 100644 --- a/library/std/src/sys_common/rwlock.rs +++ b/library/std/src/sys_common/rwlock.rs @@ -1,5 +1,23 @@ use crate::sys::rwlock as imp; +enum GuardType { + Read, + Write, +} + +pub struct RWLockGuard(&'static RWLock, GuardType); + +impl Drop for RWLockGuard { + fn drop(&mut self) { + unsafe { + match &self.1 { + GuardType::Read => self.0.read_unlock(), + GuardType::Write => self.0.write_unlock(), + } + } + } +} + /// An OS-based reader-writer lock. /// /// This structure is entirely unsafe and serves as the lowest layer of a @@ -26,6 +44,19 @@ impl RWLock { self.0.read() } + /// Acquires shared access to the underlying lock, blocking the current + /// thread to do so. + /// + /// The lock is automatically unlocked when the returned guard is dropped. + /// + /// Behavior is undefined if the rwlock has been moved between this and any + /// previous method call. + #[inline] + pub unsafe fn read_with_guard(&'static self) -> RWLockGuard { + self.read(); + RWLockGuard(&self, GuardType::Read) + } + /// Attempts to acquire shared access to this lock, returning whether it /// succeeded or not. /// @@ -48,6 +79,19 @@ impl RWLock { self.0.write() } + /// Acquires write access to the underlying lock, blocking the current thread + /// to do so. + /// + /// The lock is automatically unlocked when the returned guard is dropped. + /// + /// Behavior is undefined if the rwlock has been moved between this and any + /// previous method call. + #[inline] + pub unsafe fn write_with_guard(&'static self) -> RWLockGuard { + self.write(); + RWLockGuard(&self, GuardType::Write) + } + /// Attempts to acquire exclusive access to this lock, returning whether it /// succeeded or not. /// From 406fd3a2772e62ff1b466e59de45d5caa4cfd975 Mon Sep 17 00:00:00 2001 From: The8472 Date: Sun, 7 Feb 2021 09:45:49 +0100 Subject: [PATCH 03/20] silence dead code warnings on windows --- library/std/src/sys_common/rwlock.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/std/src/sys_common/rwlock.rs b/library/std/src/sys_common/rwlock.rs index 1f2765ffbcca6..7fd902ee0fe2e 100644 --- a/library/std/src/sys_common/rwlock.rs +++ b/library/std/src/sys_common/rwlock.rs @@ -1,12 +1,15 @@ use crate::sys::rwlock as imp; +#[cfg(unix)] enum GuardType { Read, Write, } +#[cfg(unix)] pub struct RWLockGuard(&'static RWLock, GuardType); +#[cfg(unix)] impl Drop for RWLockGuard { fn drop(&mut self) { unsafe { @@ -52,6 +55,7 @@ impl RWLock { /// Behavior is undefined if the rwlock has been moved between this and any /// previous method call. #[inline] + #[cfg(unix)] pub unsafe fn read_with_guard(&'static self) -> RWLockGuard { self.read(); RWLockGuard(&self, GuardType::Read) @@ -87,6 +91,7 @@ impl RWLock { /// Behavior is undefined if the rwlock has been moved between this and any /// previous method call. #[inline] + #[cfg(unix)] pub unsafe fn write_with_guard(&'static self) -> RWLockGuard { self.write(); RWLockGuard(&self, GuardType::Write) From c7d9bffe76477a2f79c468b07e5eaf82525eea99 Mon Sep 17 00:00:00 2001 From: Tri Vo Date: Fri, 22 Jan 2021 18:32:38 -0800 Subject: [PATCH 04/20] HWASan support --- compiler/rustc_codegen_llvm/src/attributes.rs | 3 +++ compiler/rustc_codegen_llvm/src/back/write.rs | 6 +++++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 4 +++ compiler/rustc_codegen_ssa/src/back/link.rs | 3 +++ .../rustc_llvm/llvm-wrapper/LLVMWrapper.h | 1 + .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 26 +++++++++++++++++++ .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 2 ++ compiler/rustc_session/src/config.rs | 20 +++++++++----- compiler/rustc_session/src/options.rs | 3 ++- compiler/rustc_session/src/session.rs | 6 ++++- compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_typeck/src/collect.rs | 4 ++- src/bootstrap/configure.py | 2 +- src/bootstrap/native.rs | 2 +- .../ui/invalid/invalid-no-sanitize.stderr | 2 +- src/test/ui/sanitize/hwaddress.rs | 19 ++++++++++++++ src/tools/compiletest/src/header.rs | 5 ++++ src/tools/compiletest/src/util.rs | 3 +++ 18 files changed, 100 insertions(+), 12 deletions(-) create mode 100644 src/test/ui/sanitize/hwaddress.rs diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index a78d692aaa7fb..26111729ba5b2 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -53,6 +53,9 @@ pub fn sanitize(cx: &CodegenCx<'ll, '_>, no_sanitize: SanitizerSet, llfn: &'ll V if enabled.contains(SanitizerSet::THREAD) { llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn); } + if enabled.contains(SanitizerSet::HWADDRESS) { + llvm::Attribute::SanitizeHWAddress.apply_llfn(Function, llfn); + } } /// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function. diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 326ae354ccf48..8b737c9a2e557 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -440,6 +440,8 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager( sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY), sanitize_memory_track_origins: config.sanitizer_memory_track_origins as c_int, sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD), + sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS), + sanitize_hwaddress_recover: config.sanitizer_recover.contains(SanitizerSet::HWADDRESS), }) } else { None @@ -652,6 +654,10 @@ unsafe fn add_sanitizer_passes(config: &ModuleConfig, passes: &mut Vec<&'static if config.sanitizer.contains(SanitizerSet::THREAD) { passes.push(llvm::LLVMRustCreateThreadSanitizerPass()); } + if config.sanitizer.contains(SanitizerSet::HWADDRESS) { + let recover = config.sanitizer_recover.contains(SanitizerSet::HWADDRESS); + passes.push(llvm::LLVMRustCreateHWAddressSanitizerPass(recover)); + } } pub(crate) fn link( diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index e82198f8f0c06..8c1740d8f25f0 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -131,6 +131,7 @@ pub enum Attribute { ReturnsTwice = 25, ReadNone = 26, InaccessibleMemOnly = 27, + SanitizeHWAddress = 28, } /// LLVMIntPredicate @@ -439,6 +440,8 @@ pub struct SanitizerOptions { pub sanitize_memory_recover: bool, pub sanitize_memory_track_origins: c_int, pub sanitize_thread: bool, + pub sanitize_hwaddress: bool, + pub sanitize_hwaddress_recover: bool, } /// LLVMRelocMode @@ -2128,6 +2131,7 @@ extern "C" { Recover: bool, ) -> &'static mut Pass; pub fn LLVMRustCreateThreadSanitizerPass() -> &'static mut Pass; + pub fn LLVMRustCreateHWAddressSanitizerPass(Recover: bool) -> &'static mut Pass; pub fn LLVMRustAddPass(PM: &PassManager<'_>, Pass: &'static mut Pass); pub fn LLVMRustAddLastExtensionPasses( PMB: &PassManagerBuilder, diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 8bc4e64422370..6c58417590e69 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -893,6 +893,9 @@ fn link_sanitizers(sess: &Session, crate_type: CrateType, linker: &mut dyn Linke if sanitizer.contains(SanitizerSet::THREAD) { link_sanitizer_runtime(sess, linker, "tsan"); } + if sanitizer.contains(SanitizerSet::HWADDRESS) { + link_sanitizer_runtime(sess, linker, "hwasan"); + } } fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index 57b8664d3b605..0e3bf5615af7b 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -85,6 +85,7 @@ enum LLVMRustAttribute { ReturnsTwice = 25, ReadNone = 26, InaccessibleMemOnly = 27, + SanitizeHWAddress = 28, }; typedef struct OpaqueRustString *RustStringRef; diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 2264908995bb7..5263d5dcf3e8f 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -33,6 +33,7 @@ #include "llvm/Support/TimeProfiler.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" #include "llvm/Transforms/Instrumentation/MemorySanitizer.h" +#include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h" #include "llvm/Transforms/Utils/CanonicalizeAliases.h" #include "llvm/Transforms/Utils/NameAnonGlobals.h" @@ -133,6 +134,12 @@ extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() { return wrap(createThreadSanitizerLegacyPassPass()); } +extern "C" LLVMPassRef LLVMRustCreateHWAddressSanitizerPass(bool Recover) { + const bool CompileKernel = false; + + return wrap(createHWAddressSanitizerLegacyPassPass(CompileKernel, Recover)); +} + extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) { assert(RustPass); Pass *Pass = unwrap(RustPass); @@ -722,6 +729,8 @@ struct LLVMRustSanitizerOptions { bool SanitizeMemoryRecover; int SanitizeMemoryTrackOrigins; bool SanitizeThread; + bool SanitizeHWAddress; + bool SanitizeHWAddressRecover; }; extern "C" void @@ -886,6 +895,23 @@ LLVMRustOptimizeWithNewPassManager( /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover)); } ); +#endif + } + if (SanitizerOptions->SanitizeHWAddress) { +#if LLVM_VERSION_GE(11, 0) + OptimizerLastEPCallbacks.push_back( + [SanitizerOptions](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { + MPM.addPass(HWAddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeHWAddressRecover)); + } + ); +#else + PipelineStartEPCallbacks.push_back( + [SanitizerOptions](ModulePassManager &MPM) { + MPM.addPass(HWAddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeHWAddressRecover)); + } + ); #endif } } diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 4118e93074563..45835990cecbb 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -205,6 +205,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { return Attribute::ReadNone; case InaccessibleMemOnly: return Attribute::InaccessibleMemOnly; + case SanitizeHWAddress: + return Attribute::SanitizeHWAddress; } report_fatal_error("bad AttributeKind"); } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index e9ea0ab6f98f5..210dbb0ee9939 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -43,6 +43,7 @@ bitflags! { const LEAK = 1 << 1; const MEMORY = 1 << 2; const THREAD = 1 << 3; + const HWADDRESS = 1 << 4; } } @@ -56,6 +57,7 @@ impl fmt::Display for SanitizerSet { SanitizerSet::LEAK => "leak", SanitizerSet::MEMORY => "memory", SanitizerSet::THREAD => "thread", + SanitizerSet::HWADDRESS => "hwaddress", _ => panic!("unrecognized sanitizer {:?}", s), }; if !first { @@ -73,12 +75,18 @@ impl IntoIterator for SanitizerSet { type IntoIter = std::vec::IntoIter; fn into_iter(self) -> Self::IntoIter { - [SanitizerSet::ADDRESS, SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::THREAD] - .iter() - .copied() - .filter(|&s| self.contains(s)) - .collect::>() - .into_iter() + [ + SanitizerSet::ADDRESS, + SanitizerSet::LEAK, + SanitizerSet::MEMORY, + SanitizerSet::THREAD, + SanitizerSet::HWADDRESS, + ] + .iter() + .copied() + .filter(|&s| self.contains(s)) + .collect::>() + .into_iter() } } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index f78df8a7e29fa..baa0502521da7 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -253,7 +253,7 @@ macro_rules! options { pub const parse_passes: &str = "a space-separated list of passes, or `all`"; pub const parse_panic_strategy: &str = "either `unwind` or `abort`"; pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; - pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `leak`, `memory` or `thread`"; + pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `hwaddress`, `leak`, `memory` or `thread`"; pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2"; pub const parse_cfguard: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`"; @@ -476,6 +476,7 @@ macro_rules! options { "leak" => SanitizerSet::LEAK, "memory" => SanitizerSet::MEMORY, "thread" => SanitizerSet::THREAD, + "hwaddress" => SanitizerSet::HWADDRESS, _ => return false, } } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 69aa72d899fb3..a7ceb9e06a519 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1126,7 +1126,8 @@ impl Session { self.opts.optimize != config::OptLevel::No // AddressSanitizer uses lifetimes to detect use after scope bugs. // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. - || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY) + // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future. + || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS) } pub fn link_dead_code(&self) -> bool { @@ -1562,6 +1563,8 @@ fn validate_commandline_args_with_session_available(sess: &Session) { "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu", ]; + const HWASAN_SUPPORTED_TARGETS: &[&str] = + &["aarch64-linux-android", "aarch64-unknown-linux-gnu"]; // Sanitizers can only be used on some tested platforms. for s in sess.opts.debugging_opts.sanitizer { @@ -1570,6 +1573,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { SanitizerSet::LEAK => LSAN_SUPPORTED_TARGETS, SanitizerSet::MEMORY => MSAN_SUPPORTED_TARGETS, SanitizerSet::THREAD => TSAN_SUPPORTED_TARGETS, + SanitizerSet::HWADDRESS => HWASAN_SUPPORTED_TARGETS, _ => panic!("unrecognized sanitizer {}", s), }; if !supported_targets.contains(&&*sess.opts.target_triple.triple()) { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 20e4f7262acb9..1c37a6b2aca18 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -593,6 +593,7 @@ symbols! { html_no_source, html_playground_url, html_root_url, + hwaddress, i, i128, i128_type, diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index b1d98d75196d5..b89cbed3282ee 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -2628,10 +2628,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY; } else if item.has_name(sym::thread) { codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD; + } else if item.has_name(sym::hwaddress) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS; } else { tcx.sess .struct_span_err(item.span(), "invalid argument for `no_sanitize`") - .note("expected one of: `address`, `memory` or `thread`") + .note("expected one of: `address`, `hwaddress`, `memory` or `thread`") .emit(); } } diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 2cabaee68ea67..2e6e9142afe6c 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -51,7 +51,7 @@ def v(*args): o("ninja", "llvm.ninja", "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)") o("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date") o("vendor", "build.vendor", "enable usage of vendored Rust crates") -o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, lsan, msan, tsan)") +o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, lsan, msan, tsan, hwasan)") o("dist-src", "rust.dist-src", "when building tarballs enables building a source tarball") o("cargo-native-static", "build.cargo-native-static", "static native libraries in cargo") o("profiler", "build.profiler", "build the profiler runtime") diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 609ac8b366952..b5a8b694c9420 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -804,7 +804,7 @@ fn supported_sanitizers( "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "aarch64-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]), "aarch64-unknown-linux-gnu" => { - common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan"]) + common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"]) } "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "x86_64-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]), diff --git a/src/test/ui/invalid/invalid-no-sanitize.stderr b/src/test/ui/invalid/invalid-no-sanitize.stderr index e9983e5fbd24d..4c0b17c7d3769 100644 --- a/src/test/ui/invalid/invalid-no-sanitize.stderr +++ b/src/test/ui/invalid/invalid-no-sanitize.stderr @@ -4,7 +4,7 @@ error: invalid argument for `no_sanitize` LL | #[no_sanitize(brontosaurus)] | ^^^^^^^^^^^^ | - = note: expected one of: `address`, `memory` or `thread` + = note: expected one of: `address`, `hwaddress`, `memory` or `thread` error: aborting due to previous error diff --git a/src/test/ui/sanitize/hwaddress.rs b/src/test/ui/sanitize/hwaddress.rs new file mode 100644 index 0000000000000..ad5d0245457ec --- /dev/null +++ b/src/test/ui/sanitize/hwaddress.rs @@ -0,0 +1,19 @@ +// needs-sanitizer-support +// needs-sanitizer-hwaddress +// +// compile-flags: -Z sanitizer=hwaddress -O -g +// +// run-fail +// error-pattern: HWAddressSanitizer: tag-mismatch + +#![feature(test)] + +use std::hint::black_box; + +fn main() { + let xs = vec![0, 1, 2, 3]; + // Avoid optimizing everything out. + let xs = black_box(xs.as_ptr()); + let code = unsafe { *xs.offset(4) }; + std::process::exit(code); +} diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 2eba91fd1f4cf..becb6037e0bf4 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -48,6 +48,7 @@ impl EarlyProps { let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target); + let has_hwasan = util::HWASAN_SUPPORTED_TARGETS.contains(&&*config.target); iter_header(testfile, None, rdr, &mut |ln| { // we should check if any only- exists and if it exists @@ -101,6 +102,10 @@ impl EarlyProps { props.ignore = true; } + if !has_hwasan && config.parse_name_directive(ln, "needs-sanitizer-hwaddress") { + props.ignore = true; + } + if config.target == "wasm32-unknown-unknown" && config.parse_check_run_results(ln) { props.ignore = true; } diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 292850bd9e277..b302953708c18 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -110,6 +110,9 @@ pub const TSAN_SUPPORTED_TARGETS: &[&str] = &[ "x86_64-unknown-linux-gnu", ]; +pub const HWASAN_SUPPORTED_TARGETS: &[&str] = + &["aarch64-linux-android", "aarch64-unknown-linux-gnu"]; + const BIG_ENDIAN: &[&str] = &[ "aarch64_be", "armebv7r", From 9c34c140a76f4ddd26c6042e2d847827fee304b6 Mon Sep 17 00:00:00 2001 From: Tri Vo Date: Sun, 7 Feb 2021 23:48:21 -0800 Subject: [PATCH 05/20] HWASan documentation --- .../src/compiler-flags/sanitizer.md | 89 ++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index d03d5c7501424..4f7a101d2acbd 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -7,12 +7,15 @@ The tracking issue for this feature is: [#39699](https://github.com/rust-lang/ru This feature allows for use of one of following sanitizers: * [AddressSanitizer][clang-asan] a fast memory error detector. +* [HWAddressSanitizer][clang-hwasan] a memory error detector similar to + AddressSanitizer, but based on partial hardware assistance. * [LeakSanitizer][clang-lsan] a run-time memory leak detector. * [MemorySanitizer][clang-msan] a detector of uninitialized reads. * [ThreadSanitizer][clang-tsan] a fast data race detector. -To enable a sanitizer compile with `-Zsanitizer=address`, `-Zsanitizer=leak`, -`-Zsanitizer=memory` or `-Zsanitizer=thread`. +To enable a sanitizer compile with `-Zsanitizer=address`, +`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory` or +`-Zsanitizer=thread`. # AddressSanitizer @@ -174,6 +177,86 @@ Shadow byte legend (one shadow byte represents 8 application bytes): ==39249==ABORTING ``` +# HWAddressSanitizer + +HWAddressSanitizer is a newer variant of AddressSanitizer that consumes much +less memory. + +HWAddressSanitizer is supported on the following targets: + +* `aarch64-linux-android` +* `aarch64-unknown-linux-gnu` + +HWAddressSanitizer requires `tagged-globals` target feature to instrument +globals. To enable this target feature compile with `-C +target-feature=+tagged-globals` + +## Example + +Heap buffer overflow: + +```rust +fn main() { + let xs = vec![0, 1, 2, 3]; + let _y = unsafe { *xs.as_ptr().offset(4) }; +} +``` + +```shell +$ rustc main.rs -Zsanitizer=hwaddress -C target-feature=+tagged-globals -C +linker=aarch64-linux-gnu-gcc -C link-arg=-fuse-ld=lld --target +aarch64-unknown-linux-gnu +``` + +```shell +$ ./main +==241==ERROR: HWAddressSanitizer: tag-mismatch on address 0xefdeffff0050 at pc 0xaaaae0ae4a98 +READ of size 4 at 0xefdeffff0050 tags: 2c/00 (ptr/mem) in thread T0 + #0 0xaaaae0ae4a94 (/.../main+0x54a94) + ... + +[0xefdeffff0040,0xefdeffff0060) is a small allocated heap chunk; size: 32 offset: 16 +0xefdeffff0050 is located 0 bytes to the right of 16-byte region [0xefdeffff0040,0xefdeffff0050) +allocated here: + #0 0xaaaae0acb80c (/.../main+0x3b80c) + ... + +Thread: T0 0xeffe00002000 stack: [0xffffc28ad000,0xffffc30ad000) sz: 8388608 tls: [0xffffaa10a020,0xffffaa10a7d0) +Memory tags around the buggy address (one tag corresponds to 16 bytes): + 0xfefcefffef80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefcefffef90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefcefffefa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefcefffefb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefcefffefc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefcefffefd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefcefffefe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefcefffeff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +=>0xfefceffff000: d7 d7 05 00 2c [00] 00 00 00 00 00 00 00 00 00 00 + 0xfefceffff010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefceffff020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefceffff030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefceffff040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefceffff050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefceffff060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefceffff070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefceffff080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Tags for short granules around the buggy address (one tag corresponds to 16 bytes): + 0xfefcefffeff0: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. +=>0xfefceffff000: .. .. 8c .. .. [..] .. .. .. .. .. .. .. .. .. .. + 0xfefceffff010: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. +See https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#short-granules for a description of short granule tags +Registers where the failure occurred (pc 0xaaaae0ae4a98): + x0 2c00efdeffff0050 x1 0000000000000004 x2 0000000000000004 x3 0000000000000000 + x4 0000fffefc30ac37 x5 000000000000005d x6 00000ffffc30ac37 x7 0000efff00000000 + x8 2c00efdeffff0050 x9 0200efff00000000 x10 0000000000000000 x11 0200efff00000000 + x12 0200effe00000310 x13 0200effe00000310 x14 0000000000000008 x15 5d00ffffc30ac360 + x16 0000aaaae0ad062c x17 0000000000000003 x18 0000000000000001 x19 0000ffffc30ac658 + x20 4e00ffffc30ac6e0 x21 0000aaaae0ac5e10 x22 0000000000000000 x23 0000000000000000 + x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 0000000000000000 + x28 0000000000000000 x29 0000ffffc30ac5a0 x30 0000aaaae0ae4a98 +SUMMARY: HWAddressSanitizer: tag-mismatch (/.../main+0x54a94) +``` + # LeakSanitizer LeakSanitizer is run-time memory leak detector. @@ -321,11 +404,13 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT * [Sanitizers project page](https://github.com/google/sanitizers/wiki/) * [AddressSanitizer in Clang][clang-asan] +* [HWAddressSanitizer in Clang][clang-hwasan] * [LeakSanitizer in Clang][clang-lsan] * [MemorySanitizer in Clang][clang-msan] * [ThreadSanitizer in Clang][clang-tsan] [clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html +[clang-hwasan]: https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html [clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html [clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html [clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html From 2200cf10d8051f535f726f2800b935a527696de8 Mon Sep 17 00:00:00 2001 From: The8472 Date: Mon, 8 Feb 2021 23:31:49 +0100 Subject: [PATCH 06/20] avoid &mut on the read path since it now allows concurrent readers --- library/std/src/sys/unix/os.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 33921180cb17c..4f42ce8eafcbc 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -491,7 +491,7 @@ pub unsafe fn environ() -> *mut *const *const c_char { extern "C" { static mut environ: *const *const c_char; } - &mut environ + ptr::addr_of_mut!(environ) } pub unsafe fn env_rwlock(readonly: bool) -> RWLockGuard { From 44abad5b12afa58b9f495593f1c8b090e644fd7e Mon Sep 17 00:00:00 2001 From: The8472 Date: Mon, 8 Feb 2021 23:34:23 +0100 Subject: [PATCH 07/20] introduce StaticRWLock wrapper to make methods safe --- library/std/src/sys/unix/os.rs | 17 +-- .../std/src/sys/unix/process/process_unix.rs | 6 +- library/std/src/sys_common/rwlock.rs | 109 ++++++++++-------- 3 files changed, 72 insertions(+), 60 deletions(-) diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 4f42ce8eafcbc..f5a607561c0f7 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -22,7 +22,7 @@ use crate::str; use crate::sys::cvt; use crate::sys::fd; use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard}; -use crate::sys_common::rwlock::{RWLock, RWLockGuard}; +use crate::sys_common::rwlock::{RWLockGuard, StaticRWLock}; use crate::vec; use libc::{c_char, c_int, c_void}; @@ -494,16 +494,17 @@ pub unsafe fn environ() -> *mut *const *const c_char { ptr::addr_of_mut!(environ) } -pub unsafe fn env_rwlock(readonly: bool) -> RWLockGuard { - static ENV_LOCK: RWLock = RWLock::new(); - if readonly { ENV_LOCK.read_with_guard() } else { ENV_LOCK.write_with_guard() } +static ENV_LOCK: StaticRWLock = StaticRWLock::new(); + +pub fn env_read_lock() -> RWLockGuard { + ENV_LOCK.read_with_guard() } /// Returns a vector of (variable, value) byte-vector pairs for all the /// environment variables of the current process. pub fn env() -> Env { unsafe { - let _guard = env_rwlock(true); + let _guard = env_read_lock(); let mut environ = *environ(); let mut result = Vec::new(); if !environ.is_null() { @@ -540,7 +541,7 @@ pub fn getenv(k: &OsStr) -> io::Result> { // always None as well let k = CString::new(k.as_bytes())?; unsafe { - let _guard = env_rwlock(true); + let _guard = env_read_lock(); let s = libc::getenv(k.as_ptr()) as *const libc::c_char; let ret = if s.is_null() { None @@ -556,7 +557,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { let v = CString::new(v.as_bytes())?; unsafe { - let _guard = env_rwlock(false); + let _guard = ENV_LOCK.write_with_guard(); cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) } } @@ -565,7 +566,7 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> { let nbuf = CString::new(n.as_bytes())?; unsafe { - let _guard = env_rwlock(false); + let _guard = ENV_LOCK.write_with_guard(); cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) } } diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 0e4e66389fb3b..9e82df7755e89 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -47,7 +47,7 @@ impl Command { // a lock any more because the parent won't do anything and the child is // in its own process. let result = unsafe { - let _env_lock = sys::os::env_rwlock(true); + let _env_lock = sys::os::env_read_lock(); cvt(libc::fork())? }; @@ -124,7 +124,7 @@ impl Command { // Similar to when forking, we want to ensure that access to // the environment is synchronized, so make sure to grab the // environment lock before we try to exec. - let _lock = sys::os::env_rwlock(true); + let _lock = sys::os::env_read_lock(); let Err(e) = self.do_exec(theirs, envp.as_ref()); e @@ -404,7 +404,7 @@ impl Command { cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?; // Make sure we synchronize access to the global `environ` resource - let _env_lock = sys::os::env_rwlock(true); + let _env_lock = sys::os::env_read_lock(); let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _); cvt_nz(libc::posix_spawnp( &mut p.pid, diff --git a/library/std/src/sys_common/rwlock.rs b/library/std/src/sys_common/rwlock.rs index 7fd902ee0fe2e..cc13771009fab 100644 --- a/library/std/src/sys_common/rwlock.rs +++ b/library/std/src/sys_common/rwlock.rs @@ -1,26 +1,5 @@ use crate::sys::rwlock as imp; -#[cfg(unix)] -enum GuardType { - Read, - Write, -} - -#[cfg(unix)] -pub struct RWLockGuard(&'static RWLock, GuardType); - -#[cfg(unix)] -impl Drop for RWLockGuard { - fn drop(&mut self) { - unsafe { - match &self.1 { - GuardType::Read => self.0.read_unlock(), - GuardType::Write => self.0.write_unlock(), - } - } - } -} - /// An OS-based reader-writer lock. /// /// This structure is entirely unsafe and serves as the lowest layer of a @@ -47,20 +26,6 @@ impl RWLock { self.0.read() } - /// Acquires shared access to the underlying lock, blocking the current - /// thread to do so. - /// - /// The lock is automatically unlocked when the returned guard is dropped. - /// - /// Behavior is undefined if the rwlock has been moved between this and any - /// previous method call. - #[inline] - #[cfg(unix)] - pub unsafe fn read_with_guard(&'static self) -> RWLockGuard { - self.read(); - RWLockGuard(&self, GuardType::Read) - } - /// Attempts to acquire shared access to this lock, returning whether it /// succeeded or not. /// @@ -83,20 +48,6 @@ impl RWLock { self.0.write() } - /// Acquires write access to the underlying lock, blocking the current thread - /// to do so. - /// - /// The lock is automatically unlocked when the returned guard is dropped. - /// - /// Behavior is undefined if the rwlock has been moved between this and any - /// previous method call. - #[inline] - #[cfg(unix)] - pub unsafe fn write_with_guard(&'static self) -> RWLockGuard { - self.write(); - RWLockGuard(&self, GuardType::Write) - } - /// Attempts to acquire exclusive access to this lock, returning whether it /// succeeded or not. /// @@ -135,3 +86,63 @@ impl RWLock { self.0.destroy() } } + +// the cfg annotations only exist due to dead code warnings. the code itself is portable +#[cfg(unix)] +pub struct StaticRWLock(RWLock); + +#[cfg(unix)] +impl StaticRWLock { + pub const fn new() -> StaticRWLock { + StaticRWLock(RWLock::new()) + } + + /// Acquires shared access to the underlying lock, blocking the current + /// thread to do so. + /// + /// The lock is automatically unlocked when the returned guard is dropped. + #[inline] + pub fn read_with_guard(&'static self) -> RWLockGuard { + // Safety: All methods require static references, therefore self + // cannot be moved between invocations. + unsafe { + self.0.read(); + } + RWLockGuard(&self.0, GuardType::Read) + } + + /// Acquires write access to the underlying lock, blocking the current thread + /// to do so. + /// + /// The lock is automatically unlocked when the returned guard is dropped. + #[inline] + pub fn write_with_guard(&'static self) -> RWLockGuard { + // Safety: All methods require static references, therefore self + // cannot be moved between invocations. + unsafe { + self.0.write(); + } + RWLockGuard(&self.0, GuardType::Write) + } +} + +#[cfg(unix)] +enum GuardType { + Read, + Write, +} + +#[cfg(unix)] +pub struct RWLockGuard(&'static RWLock, GuardType); + +#[cfg(unix)] +impl Drop for RWLockGuard { + fn drop(&mut self) { + unsafe { + match &self.1 { + GuardType::Read => self.0.read_unlock(), + GuardType::Write => self.0.write_unlock(), + } + } + } +} From 1d9ac3c22c98b468eb3d6c9770f1ab4be782dc74 Mon Sep 17 00:00:00 2001 From: Ellen Date: Tue, 9 Feb 2021 08:57:42 +0000 Subject: [PATCH 08/20] Fix const generics in GAT --- compiler/rustc_typeck/src/collect/type_of.rs | 42 +++++++++++++++++++ compiler/rustc_typeck/src/lib.rs | 1 + ...nst-generics-gat-in-trait-return-type-1.rs | 22 ++++++++++ ...nst-generics-gat-in-trait-return-type-2.rs | 22 ++++++++++ ...nst-generics-gat-in-trait-return-type-3.rs | 27 ++++++++++++ 5 files changed, 114 insertions(+) create mode 100644 src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-1.rs create mode 100644 src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-2.rs create mode 100644 src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-3.rs diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index e4eabca9c3b76..ef17ab9ccd137 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -29,6 +29,48 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< let parent_node = tcx.hir().get(parent_node_id); match parent_node { + Node::Ty(hir_ty @ Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => { + let id = tcx + .hir() + .parent_iter(hir_id) + .filter(|(_, node)| matches!(node, Node::Item(_))) + .map(|(id, _)| id) + .next() + .unwrap(); + + let item_did = tcx.hir().local_def_id(id).to_def_id(); + let item_ctxt = &ItemCtxt::new(tcx, item_did) as &dyn crate::astconv::AstConv<'_>; + let ty = item_ctxt.ast_ty_to_ty(hir_ty); + + if let ty::Projection(projection) = ty.kind() { + let generics = tcx.generics_of(projection.item_def_id); + + let arg_index = segment + .args + .and_then(|args| { + args.args + .iter() + .filter(|arg| arg.is_const()) + .position(|arg| arg.id() == hir_id) + }) + .unwrap_or_else(|| { + bug!("no arg matching AnonConst in segment"); + }); + + return generics + .params + .iter() + .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const)) + .nth(arg_index) + .map(|param| param.def_id); + } + + tcx.sess.delay_span_bug( + tcx.def_span(def_id), + "unexpected non-GAT usage of an anon const", + ); + return None; + } Node::Expr(&Expr { kind: ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)), diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index fd44bafab6f76..2284767debb4d 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -56,6 +56,7 @@ This API is completely unstable and subject to change. */ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(bindings_after_at)] #![feature(bool_to_option)] #![feature(box_syntax)] #![feature(crate_visibility_modifier)] diff --git a/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-1.rs b/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-1.rs new file mode 100644 index 0000000000000..ab33ef6f2442c --- /dev/null +++ b/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-1.rs @@ -0,0 +1,22 @@ +// run-pass +#![feature(generic_associated_types)] +#![allow(incomplete_features)] + +// This test unsures that with_opt_const_param returns the +// def_id of the N param in the Foo::Assoc GAT. + +trait Foo { + type Assoc; + fn foo(&self) -> Self::Assoc<3>; +} + +impl Foo for () { + type Assoc = [(); N]; + fn foo(&self) -> Self::Assoc<3> { + [(); 3] + } +} + +fn main() { + assert_eq!(().foo(), [(); 3]); +} diff --git a/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-2.rs b/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-2.rs new file mode 100644 index 0000000000000..ba9a82ae72109 --- /dev/null +++ b/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-2.rs @@ -0,0 +1,22 @@ +// run-pass +#![feature(generic_associated_types)] +#![allow(incomplete_features)] + +// This test unsures that with_opt_const_param returns the +// def_id of the N param in the Foo::Assoc GAT. + +trait Foo { + type Assoc; + fn foo(&self) -> Self::Assoc; +} + +impl Foo for () { + type Assoc = [(); N]; + fn foo(&self) -> Self::Assoc { + [(); N] + } +} + +fn main() { + assert_eq!(().foo::<10>(), [(); 10]); +} diff --git a/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-3.rs b/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-3.rs new file mode 100644 index 0000000000000..9da5334056a37 --- /dev/null +++ b/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-3.rs @@ -0,0 +1,27 @@ +// run-pass +#![feature(generic_associated_types)] +#![allow(incomplete_features)] + +// This test unsures that with_opt_const_param returns the +// def_id of the N param in the Bar::Assoc GAT. + +trait Bar { + type Assoc; +} +trait Foo: Bar { + fn foo(&self) -> Self::Assoc<3>; +} + +impl Bar for () { + type Assoc = [(); N]; +} + +impl Foo for () { + fn foo(&self) -> Self::Assoc<3> { + [(); 3] + } +} + +fn main() { + assert_eq!(().foo(), [(); 3]); +} From 4fc181dd62f38c7b424e5261756a2f01ded68a5b Mon Sep 17 00:00:00 2001 From: The8472 Date: Tue, 9 Feb 2021 18:49:29 +0100 Subject: [PATCH 09/20] split guard into read and write types --- library/std/src/sys/unix/os.rs | 4 ++-- library/std/src/sys_common/rwlock.rs | 29 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index f5a607561c0f7..1d1118aa69434 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -22,7 +22,7 @@ use crate::str; use crate::sys::cvt; use crate::sys::fd; use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard}; -use crate::sys_common::rwlock::{RWLockGuard, StaticRWLock}; +use crate::sys_common::rwlock::{RWLockReadGuard, StaticRWLock}; use crate::vec; use libc::{c_char, c_int, c_void}; @@ -496,7 +496,7 @@ pub unsafe fn environ() -> *mut *const *const c_char { static ENV_LOCK: StaticRWLock = StaticRWLock::new(); -pub fn env_read_lock() -> RWLockGuard { +pub fn env_read_lock() -> RWLockReadGuard { ENV_LOCK.read_with_guard() } diff --git a/library/std/src/sys_common/rwlock.rs b/library/std/src/sys_common/rwlock.rs index cc13771009fab..41e8ad7729463 100644 --- a/library/std/src/sys_common/rwlock.rs +++ b/library/std/src/sys_common/rwlock.rs @@ -102,13 +102,13 @@ impl StaticRWLock { /// /// The lock is automatically unlocked when the returned guard is dropped. #[inline] - pub fn read_with_guard(&'static self) -> RWLockGuard { + pub fn read_with_guard(&'static self) -> RWLockReadGuard { // Safety: All methods require static references, therefore self // cannot be moved between invocations. unsafe { self.0.read(); } - RWLockGuard(&self.0, GuardType::Read) + RWLockReadGuard(&self.0) } /// Acquires write access to the underlying lock, blocking the current thread @@ -116,33 +116,32 @@ impl StaticRWLock { /// /// The lock is automatically unlocked when the returned guard is dropped. #[inline] - pub fn write_with_guard(&'static self) -> RWLockGuard { + pub fn write_with_guard(&'static self) -> RWLockWriteGuard { // Safety: All methods require static references, therefore self // cannot be moved between invocations. unsafe { self.0.write(); } - RWLockGuard(&self.0, GuardType::Write) + RWLockWriteGuard(&self.0) } } #[cfg(unix)] -enum GuardType { - Read, - Write, +pub struct RWLockReadGuard(&'static RWLock); + +#[cfg(unix)] +impl Drop for RWLockReadGuard { + fn drop(&mut self) { + unsafe { self.0.read_unlock() } + } } #[cfg(unix)] -pub struct RWLockGuard(&'static RWLock, GuardType); +pub struct RWLockWriteGuard(&'static RWLock); #[cfg(unix)] -impl Drop for RWLockGuard { +impl Drop for RWLockWriteGuard { fn drop(&mut self) { - unsafe { - match &self.1 { - GuardType::Read => self.0.read_unlock(), - GuardType::Write => self.0.write_unlock(), - } - } + unsafe { self.0.write_unlock() } } } From 2c4337a70a4d4451fc440b1bb60ff5ca8b7eb5b2 Mon Sep 17 00:00:00 2001 From: Ellen Date: Wed, 10 Feb 2021 05:15:35 +0000 Subject: [PATCH 10/20] Comments :3 --- compiler/rustc_typeck/src/collect/type_of.rs | 22 ++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index ef17ab9ccd137..b5f37c8398ef3 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -29,19 +29,32 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< let parent_node = tcx.hir().get(parent_node_id); match parent_node { + // This matches on types who's paths couldn't be resolved without typeck'ing e.g. + // + // trait Foo { + // type Assoc;; + // fn foo() -> Self::Assoc<3>; + // // note: if the def_id argument is the 3 then in this example + // // parent_node would be the node for Self::Assoc<_> + // } + // We didnt write ::Assoc so the Self::Assoc<_> is lowered to QPath::TypeRelative. + // I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU Node::Ty(hir_ty @ Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => { - let id = tcx + // Walk up from the parent_node to find an item so that + // we can resolve the relative path to an actual associated type + let item_hir_id = tcx .hir() .parent_iter(hir_id) .filter(|(_, node)| matches!(node, Node::Item(_))) .map(|(id, _)| id) .next() .unwrap(); - - let item_did = tcx.hir().local_def_id(id).to_def_id(); + let item_did = tcx.hir().local_def_id(item_hir_id).to_def_id(); let item_ctxt = &ItemCtxt::new(tcx, item_did) as &dyn crate::astconv::AstConv<'_>; - let ty = item_ctxt.ast_ty_to_ty(hir_ty); + // This ty will be the actual associated type so that we can + // go through its generics to find which param our def_id corresponds to + let ty = item_ctxt.ast_ty_to_ty(hir_ty); if let ty::Projection(projection) = ty.kind() { let generics = tcx.generics_of(projection.item_def_id); @@ -65,6 +78,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< .map(|param| param.def_id); } + // I dont think it's possible to reach this but I'm not 100% sure - BoxyUwU tcx.sess.delay_span_bug( tcx.def_span(def_id), "unexpected non-GAT usage of an anon const", From 042274558563623b7656df0a413bd3ccaf83678d Mon Sep 17 00:00:00 2001 From: Ellen Date: Wed, 10 Feb 2021 05:23:02 +0000 Subject: [PATCH 11/20] Fix comment smol mistakes --- compiler/rustc_typeck/src/collect/type_of.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index b5f37c8398ef3..c2c9984f2afb9 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -32,7 +32,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< // This matches on types who's paths couldn't be resolved without typeck'ing e.g. // // trait Foo { - // type Assoc;; + // type Assoc; // fn foo() -> Self::Assoc<3>; // // note: if the def_id argument is the 3 then in this example // // parent_node would be the node for Self::Assoc<_> @@ -41,7 +41,8 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< // I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU Node::Ty(hir_ty @ Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => { // Walk up from the parent_node to find an item so that - // we can resolve the relative path to an actual associated type + // we can resolve the relative path to an actual associated type. + // For the code example above this item would be the Foo trait. let item_hir_id = tcx .hir() .parent_iter(hir_id) @@ -53,7 +54,8 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< let item_ctxt = &ItemCtxt::new(tcx, item_did) as &dyn crate::astconv::AstConv<'_>; // This ty will be the actual associated type so that we can - // go through its generics to find which param our def_id corresponds to + // go through its generics to find which param our def_id corresponds to. + // For the code example above, this ty would be the Assoc. let ty = item_ctxt.ast_ty_to_ty(hir_ty); if let ty::Projection(projection) = ty.kind() { let generics = tcx.generics_of(projection.item_def_id); From 0ffa2da18648afa75f272dfbd0182232c2ca87c1 Mon Sep 17 00:00:00 2001 From: Ellen Date: Wed, 10 Feb 2021 05:29:45 +0000 Subject: [PATCH 12/20] comma... --- compiler/rustc_typeck/src/collect/type_of.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index c2c9984f2afb9..571b91cf25446 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -42,7 +42,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< Node::Ty(hir_ty @ Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => { // Walk up from the parent_node to find an item so that // we can resolve the relative path to an actual associated type. - // For the code example above this item would be the Foo trait. + // For the code example above, this item would be the Foo trait. let item_hir_id = tcx .hir() .parent_iter(hir_id) From d64b749f2cebcfec942ecbbb87e24a9f8cc28469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Thu, 28 Jan 2021 20:22:33 +0300 Subject: [PATCH 13/20] Allow casting mut array ref to mut ptr We now allow two new casts: - mut array reference to mut ptr. Example: let mut x: [usize; 2] = [0, 0]; let p = &mut x as *mut usize; We allow casting const array references to const pointers so not allowing mut references to mut pointers was inconsistent. - mut array reference to const ptr. Example: let mut x: [usize; 2] = [0, 0]; let p = &mut x as *const usize; This was similarly inconsistent as we allow casting mut references to const pointers. Existing test 'vector-cast-weirdness' updated to test both cases. Fixes #24151 --- .../src/borrow_check/type_check/mod.rs | 39 ++++++++++++------- compiler/rustc_typeck/src/check/cast.rs | 5 +-- .../array-slice-vec/vector-cast-weirdness.rs | 14 ++++++- .../vector-cast-weirdness.stderr | 22 ++++++++--- 4 files changed, 55 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs index 3ba06bdd6e05f..52b1ff3877da7 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs @@ -2191,19 +2191,18 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { CastKind::Pointer(PointerCast::ArrayToPointer) => { let ty_from = op.ty(body, tcx); - let opt_ty_elem = match ty_from.kind() { - ty::RawPtr(ty::TypeAndMut { - mutbl: hir::Mutability::Not, - ty: array_ty, - }) => match array_ty.kind() { - ty::Array(ty_elem, _) => Some(ty_elem), - _ => None, - }, + let opt_ty_elem_mut = match ty_from.kind() { + ty::RawPtr(ty::TypeAndMut { mutbl: array_mut, ty: array_ty }) => { + match array_ty.kind() { + ty::Array(ty_elem, _) => Some((ty_elem, *array_mut)), + _ => None, + } + } _ => None, }; - let ty_elem = match opt_ty_elem { - Some(ty_elem) => ty_elem, + let (ty_elem, ty_mut) = match opt_ty_elem_mut { + Some(ty_elem_mut) => ty_elem_mut, None => { span_mirbug!( self, @@ -2215,11 +2214,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } }; - let ty_to = match ty.kind() { - ty::RawPtr(ty::TypeAndMut { - mutbl: hir::Mutability::Not, - ty: ty_to, - }) => ty_to, + let (ty_to, ty_to_mut) = match ty.kind() { + ty::RawPtr(ty::TypeAndMut { mutbl: ty_to_mut, ty: ty_to }) => { + (ty_to, *ty_to_mut) + } _ => { span_mirbug!( self, @@ -2231,6 +2229,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } }; + if ty_to_mut == Mutability::Mut && ty_mut == Mutability::Not { + span_mirbug!( + self, + rvalue, + "ArrayToPointer cast from const {:?} to mut {:?}", + ty, + ty_to + ); + return; + } + if let Err(terr) = self.sub_types( ty_elem, ty_to, diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 7924ffe8a6fd5..16c344e8e2b9e 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -765,9 +765,8 @@ impl<'a, 'tcx> CastCheck<'tcx> { m_expr: ty::TypeAndMut<'tcx>, m_cast: ty::TypeAndMut<'tcx>, ) -> Result { - // array-ptr-cast. - - if m_expr.mutbl == hir::Mutability::Not && m_cast.mutbl == hir::Mutability::Not { + // array-ptr-cast: allow mut-to-mut, mut-to-const, const-to-const + if m_expr.mutbl == hir::Mutability::Mut || m_cast.mutbl == hir::Mutability::Not { if let ty::Array(ety, _) = m_expr.ty.kind() { // Due to the limitations of LLVM global constants, // region pointers end up pointing at copies of diff --git a/src/test/ui/array-slice-vec/vector-cast-weirdness.rs b/src/test/ui/array-slice-vec/vector-cast-weirdness.rs index 79b9243765b95..e8f2c71477a5f 100644 --- a/src/test/ui/array-slice-vec/vector-cast-weirdness.rs +++ b/src/test/ui/array-slice-vec/vector-cast-weirdness.rs @@ -1,7 +1,11 @@ // Issue #14893. Tests that casts from vectors don't behave strangely in the // presence of the `_` type shorthand notation. +// // Update: after a change to the way casts are done, we have more type information // around and so the errors here are no longer exactly the same. +// +// Update: With PR #81479 some of the previously rejected cases are now allowed. +// New test cases added. struct X { y: [u8; 2], @@ -12,13 +16,19 @@ fn main() { // No longer a type mismatch - the `_` can be fully resolved by type inference. let p1: *const u8 = &x1.y as *const _; + let p1: *mut u8 = &x1.y as *mut _; + //~^ ERROR: casting `&[u8; 2]` as `*mut u8` is invalid let t1: *const [u8; 2] = &x1.y as *const _; + let t1: *mut [u8; 2] = &x1.y as *mut _; + //~^ ERROR: casting `&[u8; 2]` as `*mut [u8; 2]` is invalid let h1: *const [u8; 2] = &x1.y as *const [u8; 2]; + let t1: *mut [u8; 2] = &x1.y as *mut [u8; 2]; + //~^ ERROR: casting `&[u8; 2]` as `*mut [u8; 2]` is invalid let mut x1 = X { y: [0, 0] }; - // This is still an error since we don't allow casts from &mut [T; n] to *mut T. - let p1: *mut u8 = &mut x1.y as *mut _; //~ ERROR casting + let p1: *mut u8 = &mut x1.y as *mut _; + let p2: *const u8 = &mut x1.y as *const _; let t1: *mut [u8; 2] = &mut x1.y as *mut _; let h1: *mut [u8; 2] = &mut x1.y as *mut [u8; 2]; } diff --git a/src/test/ui/array-slice-vec/vector-cast-weirdness.stderr b/src/test/ui/array-slice-vec/vector-cast-weirdness.stderr index 37055bb75f559..6fdb1ac9e3059 100644 --- a/src/test/ui/array-slice-vec/vector-cast-weirdness.stderr +++ b/src/test/ui/array-slice-vec/vector-cast-weirdness.stderr @@ -1,9 +1,21 @@ -error[E0606]: casting `&mut [u8; 2]` as `*mut u8` is invalid - --> $DIR/vector-cast-weirdness.rs:21:23 +error[E0606]: casting `&[u8; 2]` as `*mut u8` is invalid + --> $DIR/vector-cast-weirdness.rs:19:23 | -LL | let p1: *mut u8 = &mut x1.y as *mut _; - | ^^^^^^^^^^^^^^^^^^^ +LL | let p1: *mut u8 = &x1.y as *mut _; + | ^^^^^^^^^^^^^^^ -error: aborting due to previous error +error[E0606]: casting `&[u8; 2]` as `*mut [u8; 2]` is invalid + --> $DIR/vector-cast-weirdness.rs:22:28 + | +LL | let t1: *mut [u8; 2] = &x1.y as *mut _; + | ^^^^^^^^^^^^^^^ + +error[E0606]: casting `&[u8; 2]` as `*mut [u8; 2]` is invalid + --> $DIR/vector-cast-weirdness.rs:25:28 + | +LL | let t1: *mut [u8; 2] = &x1.y as *mut [u8; 2]; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0606`. From 7ca96ed2af7552cf67be36befe8f6e25e5fe63f8 Mon Sep 17 00:00:00 2001 From: Ellen Date: Wed, 10 Feb 2021 13:11:12 +0000 Subject: [PATCH 14/20] rewrite the comments --- compiler/rustc_typeck/src/collect/type_of.rs | 33 +++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index 571b91cf25446..7fa58dcd5f44f 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -29,20 +29,28 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< let parent_node = tcx.hir().get(parent_node_id); match parent_node { - // This matches on types who's paths couldn't be resolved without typeck'ing e.g. + // This match arm is for when the def_id appears in a GAT whose + // path can't be resolved without typechecking e.g. // // trait Foo { - // type Assoc; + // type Assoc; // fn foo() -> Self::Assoc<3>; - // // note: if the def_id argument is the 3 then in this example - // // parent_node would be the node for Self::Assoc<_> // } - // We didnt write ::Assoc so the Self::Assoc<_> is lowered to QPath::TypeRelative. + // + // In the above code we would call this query with the def_id of 3 and + // the parent_node we match on would be the hir node for Self::Assoc<3> + // + // `Self::Assoc<3>` cant be resolved without typchecking here as we + // didnt write ::Assoc<3>. If we did then another match + // arm would handle this. + // // I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU Node::Ty(hir_ty @ Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => { - // Walk up from the parent_node to find an item so that - // we can resolve the relative path to an actual associated type. - // For the code example above, this item would be the Foo trait. + // Find the Item containing the associated type so we can create an ItemCtxt. + // Using the ItemCtxt convert the HIR for the unresolved assoc type into a + // ty which is a fully resolved projection. + // For the code example above, this would mean converting Self::Assoc<3> + // into a ty::Projection(::Assoc<3>) let item_hir_id = tcx .hir() .parent_iter(hir_id) @@ -52,11 +60,12 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< .unwrap(); let item_did = tcx.hir().local_def_id(item_hir_id).to_def_id(); let item_ctxt = &ItemCtxt::new(tcx, item_did) as &dyn crate::astconv::AstConv<'_>; - - // This ty will be the actual associated type so that we can - // go through its generics to find which param our def_id corresponds to. - // For the code example above, this ty would be the Assoc. let ty = item_ctxt.ast_ty_to_ty(hir_ty); + + // Iterate through the generics of the projection to find the one that corresponds to + // the def_id that this query was called with. We filter to only const args here as a + // precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't + // but it can't hurt to be safe ^^ if let ty::Projection(projection) = ty.kind() { let generics = tcx.generics_of(projection.item_def_id); From 02ffe9ef0101cb7b0540ca6195d63442ad3df5ec Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sun, 6 Dec 2020 17:56:01 -0500 Subject: [PATCH 15/20] Fix injected errors when running doctests on a crate named after a keyword Unfortunately, this can't currently be tested. The problem is that we need the file to be compiled first to then be used as dependency, which cannot be done currently unfortunately in the rustdoc test suites. Example: ```rust // name this file "foo.rs" /// ``` /// let x = foo::foo(); /// ``` pub fn foo() {} ``` If you run `rustdoc --test foo.rs`, you'll get: ``` running 1 test test foo.rs - foo (line 1) ... FAILED failures: ---- foo.rs - foo (line 1) stdout ---- error[E0463]: can't find crate for `foo` --> foo.rs:0:1 | 2 | extern crate foo; | ^^^^^^^^^^^^^^^^^ can't find crate ``` If a test were possible, it would look something like ````rust #![crate_name = "mod"] #![crate_type = "lib"] //! ``` //! // NOTE: requires that the literal string 'mod' appears in the doctest for //! // the bug to appear //! assert_eq!(1, 1); //! ``` ```` --- src/librustdoc/doctest.rs | 7 +++++-- src/librustdoc/doctest/tests.rs | 8 ++++---- src/test/rustdoc/playground-arg.rs | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index eecfd337cdf84..fb4774ae19246 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -546,9 +546,12 @@ crate fn make_test( // compiler. if !already_has_extern_crate && !opts.no_crate_inject && cratename != Some("std") { if let Some(cratename) = cratename { - // Make sure its actually used if not included. + // Don't inject `extern crate` if the crate is never used. + // NOTE: this is terribly inaccurate because it doesn't actually + // parse the source, but only has false positives, not false + // negatives. if s.contains(cratename) { - prog.push_str(&format!("extern crate {};\n", cratename)); + prog.push_str(&format!("extern crate r#{};\n", cratename)); line_offset += 1; } } diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index 465b2b1d69b34..c49e45c0e25a0 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -38,7 +38,7 @@ fn make_test_crate_name() { let input = "use asdf::qwop; assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] -extern crate asdf; +extern crate r#asdf; fn main() { use asdf::qwop; assert_eq!(2+2, 4); @@ -128,7 +128,7 @@ fn make_test_opts_attrs() { let input = "use asdf::qwop; assert_eq!(2+2, 4);"; let expected = "#![feature(sick_rad)] -extern crate asdf; +extern crate r#asdf; fn main() { use asdf::qwop; assert_eq!(2+2, 4); @@ -141,7 +141,7 @@ assert_eq!(2+2, 4); opts.attrs.push("feature(hella_dope)".to_string()); let expected = "#![feature(sick_rad)] #![feature(hella_dope)] -extern crate asdf; +extern crate r#asdf; fn main() { use asdf::qwop; assert_eq!(2+2, 4); @@ -250,7 +250,7 @@ assert_eq!(asdf::foo, 4);"; let expected = "#![allow(unused)] extern crate hella_qwop; -extern crate asdf; +extern crate r#asdf; fn main() { assert_eq!(asdf::foo, 4); }" diff --git a/src/test/rustdoc/playground-arg.rs b/src/test/rustdoc/playground-arg.rs index 018716ad45af3..dbe2297f81887 100644 --- a/src/test/rustdoc/playground-arg.rs +++ b/src/test/rustdoc/playground-arg.rs @@ -11,4 +11,4 @@ pub fn dummy() {} // ensure that `extern crate foo;` was inserted into code snips automatically: -// @matches foo/index.html '//a[@class="test-arrow"][@href="https://example.com/?code=%23!%5Ballow(unused)%5D%0Aextern%20crate%20foo%3B%0Afn%20main()%20%7B%0Ause%20foo%3A%3Adummy%3B%0Adummy()%3B%0A%7D&edition=2015"]' "Run" +// @matches foo/index.html '//a[@class="test-arrow"][@href="https://example.com/?code=%23!%5Ballow(unused)%5D%0Aextern%20crate%20r%23foo%3B%0Afn%20main()%20%7B%0Ause%20foo%3A%3Adummy%3B%0Adummy()%3B%0A%7D&edition=2015"]' "Run" From fda71d676d07415f78c7835a6fa67b6aacb62c97 Mon Sep 17 00:00:00 2001 From: LingMan Date: Fri, 12 Feb 2021 10:27:08 +0100 Subject: [PATCH 16/20] Push a `char` instead of a `str` with len one into a String --- compiler/rustc_typeck/src/check/upvar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 04a9e65e6647d..4f90f2877ed91 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -1211,7 +1211,7 @@ fn construct_place_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String { ProjectionKind::Subslice => String::from("Subslice"), }; if i != 0 { - projections_str.push_str(","); + projections_str.push(','); } projections_str.push_str(proj.as_str()); } From f546633cf82a339402b2180c20141cc1b72d1c87 Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Fri, 12 Feb 2021 12:46:02 +0300 Subject: [PATCH 17/20] Remove unnecessary lint allow attrs on example --- library/alloc/src/boxed.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 354efdefc2103..04033905728ab 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -99,13 +99,11 @@ //! pub struct Foo; //! //! #[no_mangle] -//! #[allow(improper_ctypes_definitions)] //! pub extern "C" fn foo_new() -> Box { //! Box::new(Foo) //! } //! //! #[no_mangle] -//! #[allow(improper_ctypes_definitions)] //! pub extern "C" fn foo_delete(_: Option>) {} //! ``` //! From de21cdf792fcdb1795f42b4676bcfa68f6d3cae9 Mon Sep 17 00:00:00 2001 From: VillSnow Date: Sun, 31 Jan 2021 09:31:17 +0900 Subject: [PATCH 18/20] update documents --- library/core/src/slice/mod.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 19a3b45e568c0..6ed461aba7693 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2082,6 +2082,12 @@ impl [T] { /// [`Result::Err`] is returned, containing the index where a matching /// element could be inserted while maintaining sorted order. /// + /// See also [`binary_search_by`], [`binary_search_by_key`], and [`partition_point`]. + /// + /// [`binary_search_by`]: #method.binary_search_by + /// [`binary_search_by_key`]: #method.binary_search_by_key + /// [`partition_point`]: #method.partition_point + /// /// # Examples /// /// Looks up a series of four elements. The first is found, with a @@ -2129,6 +2135,12 @@ impl [T] { /// [`Result::Err`] is returned, containing the index where a matching /// element could be inserted while maintaining sorted order. /// + /// See also [`binary_search`], [`binary_search_by_key`], and [`partition_point`]. + /// + /// [`binary_search`]: #method.binary_search + /// [`binary_search_by_key`]: #method.binary_search_by_key + /// [`partition_point`]: #method.partition_point + /// /// # Examples /// /// Looks up a series of four elements. The first is found, with a @@ -2186,7 +2198,12 @@ impl [T] { /// [`Result::Err`] is returned, containing the index where a matching /// element could be inserted while maintaining sorted order. /// + /// See also [`binary_search`], [`binary_search_by`], and [`partition_point`]. + /// /// [`sort_by_key`]: #method.sort_by_key + /// [`binary_search`]: #method.binary_search + /// [`binary_search_by`]: #method.binary_search_by + /// [`partition_point`]: #method.partition_point /// /// # Examples /// @@ -3399,6 +3416,12 @@ impl [T] { /// If this slice is not partitioned, the returned result is unspecified and meaningless, /// as this method performs a kind of binary search. /// + /// See also [`binary_search`], [`binary_search_by`], and [`binary_search_by_key`]. + /// + /// [`binary_search`]: #method.binary_search + /// [`binary_search_by`]: #method.binary_search_by + /// [`binary_search_by_key`]: #method.binary_search_by_key + /// /// # Examples /// /// ``` From afdc8c79184dad881e618026c1ce91f2b9bae138 Mon Sep 17 00:00:00 2001 From: VillSnow Date: Thu, 14 Jan 2021 23:36:33 +0900 Subject: [PATCH 19/20] stabilize partition_point --- library/core/src/slice/mod.rs | 4 +--- library/core/tests/lib.rs | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 6ed461aba7693..c99572d98ff82 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -3425,8 +3425,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(partition_point)] - /// /// let v = [1, 2, 3, 3, 5, 6, 7]; /// let i = v.partition_point(|&x| x < 5); /// @@ -3434,7 +3432,7 @@ impl [T] { /// assert!(v[..i].iter().all(|&x| x < 5)); /// assert!(v[i..].iter().all(|&x| !(x < 5))); /// ``` - #[unstable(feature = "partition_point", reason = "new API", issue = "73831")] + #[stable(feature = "partition_point", since = "1.52.0")] pub fn partition_point

(&self, mut pred: P) -> usize where P: FnMut(&T) -> bool, diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 339691b117694..40dc6473b7d40 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -67,7 +67,6 @@ #![feature(option_result_unwrap_unchecked)] #![feature(option_unwrap_none)] #![feature(peekable_peek_mut)] -#![feature(partition_point)] #![feature(once_cell)] #![feature(unsafe_block_in_unsafe_fn)] #![feature(int_bits_const)] From fde59a8cb73f87ced306a29649b80c3776bab526 Mon Sep 17 00:00:00 2001 From: LingMan Date: Fri, 12 Feb 2021 13:58:13 +0100 Subject: [PATCH 20/20] Use `Iterator::all` instead of open-coding it Shorter code and by initializing to the final value directly, the variable doesn't need to be mut. --- compiler/rustc_typeck/src/check/upvar.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 04a9e65e6647d..10a7c1a394d3f 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -1382,14 +1382,8 @@ fn determine_place_ancestry_relation( // Assume of length of projections_b = m let projections_b = &place_b.projections; - let mut same_initial_projections = true; - - for (proj_a, proj_b) in projections_a.iter().zip(projections_b.iter()) { - if proj_a != proj_b { - same_initial_projections = false; - break; - } - } + let same_initial_projections = + projections_a.iter().zip(projections_b.iter()).all(|(proj_a, proj_b)| proj_a == proj_b); if same_initial_projections { // First min(n, m) projections are the same