diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 7ebb1e85cdb23..f942970278399 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -790,6 +790,7 @@ declare_lint! { /// ### Example /// /// ```rust + /// #[warn(unused_macro_rules)] /// macro_rules! unused_empty { /// (hello) => { println!("Hello, world!") }; // This rule is unused /// () => { println!("empty") }; // This rule is used @@ -814,7 +815,7 @@ declare_lint! { /// /// [`macro_export` attribute]: https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope pub UNUSED_MACRO_RULES, - Warn, + Allow, "detects macro rules that were not used" } diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 2a49017de3cc8..fa0206c349af6 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -194,10 +194,10 @@ use crate::cmp::Ordering; use crate::fmt::{self, Debug, Display}; -use crate::marker::Unsize; +use crate::marker::{PhantomData, Unsize}; use crate::mem; use crate::ops::{CoerceUnsized, Deref, DerefMut}; -use crate::ptr; +use crate::ptr::{self, NonNull}; /// A mutable memory location. /// @@ -896,7 +896,8 @@ impl RefCell { // SAFETY: `BorrowRef` ensures that there is only immutable access // to the value while borrowed. - Ok(Ref { value: unsafe { &*self.value.get() }, borrow: b }) + let value = unsafe { NonNull::new_unchecked(self.value.get()) }; + Ok(Ref { value, borrow: b }) } None => Err(BorrowError { // If a borrow occurred, then we must already have an outstanding borrow, @@ -980,8 +981,9 @@ impl RefCell { self.borrowed_at.set(Some(crate::panic::Location::caller())); } - // SAFETY: `BorrowRef` guarantees unique access. - Ok(RefMut { value: unsafe { &mut *self.value.get() }, borrow: b }) + // SAFETY: `BorrowRefMut` guarantees unique access. + let value = unsafe { NonNull::new_unchecked(self.value.get()) }; + Ok(RefMut { value, borrow: b, marker: PhantomData }) } None => Err(BorrowMutError { // If a borrow occurred, then we must already have an outstanding borrow, @@ -1314,7 +1316,9 @@ impl Clone for BorrowRef<'_> { #[stable(feature = "rust1", since = "1.0.0")] #[must_not_suspend = "holding a Ref across suspend points can cause BorrowErrors"] pub struct Ref<'b, T: ?Sized + 'b> { - value: &'b T, + // NB: we use a pointer instead of `&'b T` to avoid `noalias` violations, because a + // `Ref` argument doesn't hold immutability for its whole scope, only until it drops. + value: NonNull, borrow: BorrowRef<'b>, } @@ -1324,7 +1328,8 @@ impl Deref for Ref<'_, T> { #[inline] fn deref(&self) -> &T { - self.value + // SAFETY: the value is accessible as long as we hold our borrow. + unsafe { self.value.as_ref() } } } @@ -1368,7 +1373,7 @@ impl<'b, T: ?Sized> Ref<'b, T> { where F: FnOnce(&T) -> &U, { - Ref { value: f(orig.value), borrow: orig.borrow } + Ref { value: NonNull::from(f(&*orig)), borrow: orig.borrow } } /// Makes a new `Ref` for an optional component of the borrowed data. The @@ -1399,8 +1404,8 @@ impl<'b, T: ?Sized> Ref<'b, T> { where F: FnOnce(&T) -> Option<&U>, { - match f(orig.value) { - Some(value) => Ok(Ref { value, borrow: orig.borrow }), + match f(&*orig) { + Some(value) => Ok(Ref { value: NonNull::from(value), borrow: orig.borrow }), None => Err(orig), } } @@ -1431,9 +1436,12 @@ impl<'b, T: ?Sized> Ref<'b, T> { where F: FnOnce(&T) -> (&U, &V), { - let (a, b) = f(orig.value); + let (a, b) = f(&*orig); let borrow = orig.borrow.clone(); - (Ref { value: a, borrow }, Ref { value: b, borrow: orig.borrow }) + ( + Ref { value: NonNull::from(a), borrow }, + Ref { value: NonNull::from(b), borrow: orig.borrow }, + ) } /// Convert into a reference to the underlying data. @@ -1467,7 +1475,8 @@ impl<'b, T: ?Sized> Ref<'b, T> { // unique reference to the borrowed RefCell. No further mutable references can be created // from the original cell. mem::forget(orig.borrow); - orig.value + // SAFETY: after forgetting, we can form a reference for the rest of lifetime `'b`. + unsafe { orig.value.as_ref() } } } @@ -1507,13 +1516,13 @@ impl<'b, T: ?Sized> RefMut<'b, T> { /// ``` #[stable(feature = "cell_map", since = "1.8.0")] #[inline] - pub fn map(orig: RefMut<'b, T>, f: F) -> RefMut<'b, U> + pub fn map(mut orig: RefMut<'b, T>, f: F) -> RefMut<'b, U> where F: FnOnce(&mut T) -> &mut U, { // FIXME(nll-rfc#40): fix borrow-check - let RefMut { value, borrow } = orig; - RefMut { value: f(value), borrow } + let value = NonNull::from(f(&mut *orig)); + RefMut { value, borrow: orig.borrow, marker: PhantomData } } /// Makes a new `RefMut` for an optional component of the borrowed data. The @@ -1548,23 +1557,20 @@ impl<'b, T: ?Sized> RefMut<'b, T> { /// ``` #[unstable(feature = "cell_filter_map", reason = "recently added", issue = "81061")] #[inline] - pub fn filter_map(orig: RefMut<'b, T>, f: F) -> Result, Self> + pub fn filter_map(mut orig: RefMut<'b, T>, f: F) -> Result, Self> where F: FnOnce(&mut T) -> Option<&mut U>, { // FIXME(nll-rfc#40): fix borrow-check - let RefMut { value, borrow } = orig; - let value = value as *mut T; // SAFETY: function holds onto an exclusive reference for the duration // of its call through `orig`, and the pointer is only de-referenced // inside of the function call never allowing the exclusive reference to // escape. - match f(unsafe { &mut *value }) { - Some(value) => Ok(RefMut { value, borrow }), - None => { - // SAFETY: same as above. - Err(RefMut { value: unsafe { &mut *value }, borrow }) + match f(&mut *orig) { + Some(value) => { + Ok(RefMut { value: NonNull::from(value), borrow: orig.borrow, marker: PhantomData }) } + None => Err(orig), } } @@ -1596,15 +1602,18 @@ impl<'b, T: ?Sized> RefMut<'b, T> { #[stable(feature = "refcell_map_split", since = "1.35.0")] #[inline] pub fn map_split( - orig: RefMut<'b, T>, + mut orig: RefMut<'b, T>, f: F, ) -> (RefMut<'b, U>, RefMut<'b, V>) where F: FnOnce(&mut T) -> (&mut U, &mut V), { - let (a, b) = f(orig.value); let borrow = orig.borrow.clone(); - (RefMut { value: a, borrow }, RefMut { value: b, borrow: orig.borrow }) + let (a, b) = f(&mut *orig); + ( + RefMut { value: NonNull::from(a), borrow, marker: PhantomData }, + RefMut { value: NonNull::from(b), borrow: orig.borrow, marker: PhantomData }, + ) } /// Convert into a mutable reference to the underlying data. @@ -1630,14 +1639,15 @@ impl<'b, T: ?Sized> RefMut<'b, T> { /// assert!(cell.try_borrow_mut().is_err()); /// ``` #[unstable(feature = "cell_leak", issue = "69099")] - pub fn leak(orig: RefMut<'b, T>) -> &'b mut T { + pub fn leak(mut orig: RefMut<'b, T>) -> &'b mut T { // By forgetting this BorrowRefMut we ensure that the borrow counter in the RefCell can't // go back to UNUSED within the lifetime `'b`. Resetting the reference tracking state would // require a unique reference to the borrowed RefCell. No further references can be created // from the original cell within that lifetime, making the current borrow the only // reference for the remaining lifetime. mem::forget(orig.borrow); - orig.value + // SAFETY: after forgetting, we can form a reference for the rest of lifetime `'b`. + unsafe { orig.value.as_mut() } } } @@ -1692,8 +1702,12 @@ impl<'b> BorrowRefMut<'b> { #[stable(feature = "rust1", since = "1.0.0")] #[must_not_suspend = "holding a RefMut across suspend points can cause BorrowErrors"] pub struct RefMut<'b, T: ?Sized + 'b> { - value: &'b mut T, + // NB: we use a pointer instead of `&'b mut T` to avoid `noalias` violations, because a + // `RefMut` argument doesn't hold exclusivity for its whole scope, only until it drops. + value: NonNull, borrow: BorrowRefMut<'b>, + // NonNull is covariant over T, so we need to reintroduce invariance. + marker: PhantomData<&'b mut T>, } #[stable(feature = "rust1", since = "1.0.0")] @@ -1702,7 +1716,8 @@ impl Deref for RefMut<'_, T> { #[inline] fn deref(&self) -> &T { - self.value + // SAFETY: the value is accessible as long as we hold our borrow. + unsafe { self.value.as_ref() } } } @@ -1710,7 +1725,8 @@ impl Deref for RefMut<'_, T> { impl DerefMut for RefMut<'_, T> { #[inline] fn deref_mut(&mut self) -> &mut T { - self.value + // SAFETY: the value is accessible as long as we hold our borrow. + unsafe { self.value.as_mut() } } } diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index a231f533d190b..dbc3d2923ed59 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -496,9 +496,10 @@ macro_rules! r#try { #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "write_macro")] macro_rules! write { - ($dst:expr, $($arg:tt)*) => { - $dst.write_fmt($crate::format_args!($($arg)*)) - }; + ($dst:expr, $($arg:tt)*) => {{ + let result = $dst.write_fmt($crate::format_args!($($arg)*)); + result + }}; } /// Write formatted data into a buffer, with a newline appended. @@ -553,9 +554,10 @@ macro_rules! writeln { ($dst:expr $(,)?) => { $crate::write!($dst, "\n") }; - ($dst:expr, $($arg:tt)*) => { - $dst.write_fmt($crate::format_args_nl!($($arg)*)) - }; + ($dst:expr, $($arg:tt)*) => {{ + let result = $dst.write_fmt($crate::format_args_nl!($($arg)*)); + result + }}; } /// Indicates unreachable code. diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index e512c0d81a0f3..646744a50eb88 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -62,9 +62,9 @@ macro_rules! panic { #[cfg_attr(not(test), rustc_diagnostic_item = "print_macro")] #[allow_internal_unstable(print_internals)] macro_rules! print { - ($($arg:tt)*) => { - $crate::io::_print($crate::format_args!($($arg)*)) - }; + ($($arg:tt)*) => {{ + $crate::io::_print($crate::format_args!($($arg)*)); + }}; } /// Prints to the standard output, with a newline. @@ -133,9 +133,9 @@ macro_rules! println { #[cfg_attr(not(test), rustc_diagnostic_item = "eprint_macro")] #[allow_internal_unstable(print_internals)] macro_rules! eprint { - ($($arg:tt)*) => { - $crate::io::_eprint($crate::format_args!($($arg)*)) - }; + ($($arg:tt)*) => {{ + $crate::io::_eprint($crate::format_args!($($arg)*)); + }}; } /// Prints to the standard error, with a newline. diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js index 52577b228aa13..7634a15b9bd19 100644 --- a/src/librustdoc/html/static/.eslintrc.js +++ b/src/librustdoc/html/static/.eslintrc.js @@ -29,5 +29,10 @@ module.exports = { "no-var": ["error"], "prefer-const": ["error"], "prefer-arrow-callback": ["error"], + "brace-style": [ + "error", + "1tbs", + { "allowSingleLine": false } + ], } }; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 336223ad28f32..f748616acca04 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -841,8 +841,8 @@ function loadCss(cssFileName) { onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => { if (e.parentNode.id !== "implementations-list" || (!hasClass(e, "implementors-toggle") && - !hasClass(e, "type-contents-toggle"))) - { + !hasClass(e, "type-contents-toggle")) + ) { e.open = false; } }); diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 408b7e19feadd..7b9d86a851b19 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -98,7 +98,9 @@ // visible. This is necessary since updateScrapedExample calls scrollToLoc which // depends on offsetHeight, a property that requires an element to be visible to // compute correctly. - setTimeout(() => { onEachLazy(moreExamples, updateScrapedExample); }); + setTimeout(() => { + onEachLazy(moreExamples, updateScrapedExample); + }); }, {once: true}); }); })(); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index b596adf32c6fd..0be70d77d06e4 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -320,8 +320,8 @@ window.initSearch = rawSearchIndex => { if (foundExclamation) { throw new Error("Cannot have more than one `!` in an ident"); } else if (parserState.pos + 1 < parserState.length && - isIdentCharacter(parserState.userQuery[parserState.pos + 1])) - { + isIdentCharacter(parserState.userQuery[parserState.pos + 1]) + ) { throw new Error("`!` can only be at the end of an ident"); } foundExclamation = true; @@ -330,12 +330,10 @@ window.initSearch = rawSearchIndex => { } else if ( isStopCharacter(c) || isSpecialStartCharacter(c) || - isSeparatorCharacter(c)) - { + isSeparatorCharacter(c) + ) { break; - } - // If we allow paths ("str::string" for example). - else if (c === ":") { + } else if (c === ":") { // If we allow paths ("str::string" for example). if (!isPathStart(parserState)) { break; } @@ -372,8 +370,8 @@ window.initSearch = rawSearchIndex => { end = getIdentEndPosition(parserState); } if (parserState.pos < parserState.length && - parserState.userQuery[parserState.pos] === "<") - { + parserState.userQuery[parserState.pos] === "<" + ) { if (isInGenerics) { throw new Error("Unexpected `<` after `<`"); } else if (start >= end) { @@ -592,8 +590,8 @@ window.initSearch = rawSearchIndex => { if (elem && elem.value !== "All crates" && - hasOwnPropertyRustdoc(rawSearchIndex, elem.value)) - { + hasOwnPropertyRustdoc(rawSearchIndex, elem.value) + ) { return elem.value; } return null; @@ -786,37 +784,51 @@ window.initSearch = rawSearchIndex => { // sort by exact match with regard to the last word (mismatch goes later) a = (aaa.word !== userQuery); b = (bbb.word !== userQuery); - if (a !== b) { return a - b; } + if (a !== b) { + return a - b; + } // Sort by non levenshtein results and then levenshtein results by the distance // (less changes required to match means higher rankings) a = (aaa.lev); b = (bbb.lev); - if (a !== b) { return a - b; } + if (a !== b) { + return a - b; + } // sort by crate (non-current crate goes later) a = (aaa.item.crate !== window.currentCrate); b = (bbb.item.crate !== window.currentCrate); - if (a !== b) { return a - b; } + if (a !== b) { + return a - b; + } // sort by item name length (longer goes later) a = aaa.word.length; b = bbb.word.length; - if (a !== b) { return a - b; } + if (a !== b) { + return a - b; + } // sort by item name (lexicographically larger goes later) a = aaa.word; b = bbb.word; - if (a !== b) { return (a > b ? +1 : -1); } + if (a !== b) { + return (a > b ? +1 : -1); + } // sort by index of keyword in item name (no literal occurrence goes later) a = (aaa.index < 0); b = (bbb.index < 0); - if (a !== b) { return a - b; } + if (a !== b) { + return a - b; + } // (later literal occurrence, if any, goes later) a = aaa.index; b = bbb.index; - if (a !== b) { return a - b; } + if (a !== b) { + return a - b; + } // special precedence for primitive and keyword pages if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) || @@ -831,17 +843,23 @@ window.initSearch = rawSearchIndex => { // sort by description (no description goes later) a = (aaa.item.desc === ""); b = (bbb.item.desc === ""); - if (a !== b) { return a - b; } + if (a !== b) { + return a - b; + } // sort by type (later occurrence in `itemTypes` goes later) a = aaa.item.ty; b = bbb.item.ty; - if (a !== b) { return a - b; } + if (a !== b) { + return a - b; + } // sort by path (lexicographically larger goes later) a = aaa.item.path; b = bbb.item.path; - if (a !== b) { return (a > b ? +1 : -1); } + if (a !== b) { + return (a > b ? +1 : -1); + } // que sera, sera return 0; @@ -1315,16 +1333,15 @@ window.initSearch = rawSearchIndex => { } if (searchWord.indexOf(elem.pathLast) > -1 || - row.normalizedName.indexOf(elem.pathLast) > -1) - { + row.normalizedName.indexOf(elem.pathLast) > -1 + ) { // filter type: ... queries if (!results_others[fullId] !== undefined) { index = row.normalizedName.indexOf(elem.pathLast); } } lev = levenshtein(searchWord, elem.pathLast); - if (lev > 0 && elem.pathLast.length > 2 && searchWord.indexOf(elem.pathLast) > -1) - { + if (lev > 0 && elem.pathLast.length > 2 && searchWord.indexOf(elem.pathLast) > -1) { if (elem.pathLast.length < 6) { lev = 1; } else { @@ -1670,8 +1687,8 @@ window.initSearch = rawSearchIndex => { // By default, the search DOM element is "empty" (meaning it has no children not // text content). Once a search has been run, it won't be empty, even if you press // ESC or empty the search input (which also "cancels" the search). - && (!search.firstChild || search.firstChild.innerText !== searchState.loadingText))) - { + && (!search.firstChild || search.firstChild.innerText !== searchState.loadingText)) + ) { const elem = document.createElement("a"); elem.href = results.others[0].href; removeClass(elem, "active"); @@ -1766,7 +1783,7 @@ window.initSearch = rawSearchIndex => { let i = 0; for (const elem of elems) { const j = i; - elem.onclick = () => { printTab(j); }; + elem.onclick = () => printTab(j); searchState.focusedByTab.push(null); i += 1; } diff --git a/src/test/codegen/noalias-refcell.rs b/src/test/codegen/noalias-refcell.rs new file mode 100644 index 0000000000000..dba73937abf17 --- /dev/null +++ b/src/test/codegen/noalias-refcell.rs @@ -0,0 +1,14 @@ +// compile-flags: -O -C no-prepopulate-passes -Z mutable-noalias=yes + +#![crate_type = "lib"] + +use std::cell::{Ref, RefCell, RefMut}; + +// Make sure that none of the arguments get a `noalias` attribute, because +// the `RefCell` might alias writes after either `Ref`/`RefMut` is dropped. + +// CHECK-LABEL: @maybe_aliased( +// CHECK-NOT: noalias +// CHECK-SAME: %_refcell +#[no_mangle] +pub unsafe fn maybe_aliased(_: Ref<'_, i32>, _: RefMut<'_, i32>, _refcell: &RefCell) {} diff --git a/src/test/ui/issues/issue-63787.rs b/src/test/ui/issues/issue-63787.rs new file mode 100644 index 0000000000000..cba079b231522 --- /dev/null +++ b/src/test/ui/issues/issue-63787.rs @@ -0,0 +1,36 @@ +// run-pass +// compile-flags: -O + +// Make sure that `Ref` and `RefMut` do not make false promises about aliasing, +// because once they drop, their reference/pointer can alias other writes. + +// Adapted from comex's proof of concept: +// https://github.com/rust-lang/rust/issues/63787#issuecomment-523588164 + +use std::cell::RefCell; +use std::ops::Deref; + +pub fn break_if_r_is_noalias(rc: &RefCell, r: impl Deref) -> i32 { + let ptr1 = &*r as *const i32; + let a = *r; + drop(r); + *rc.borrow_mut() = 2; + let r2 = rc.borrow(); + let ptr2 = &*r2 as *const i32; + if ptr2 != ptr1 { + panic!(); + } + // If LLVM knows the pointers are the same, and if `r` was `noalias`, + // then it may replace this with `a + a`, ignoring the earlier write. + a + *r2 +} + +fn main() { + let mut rc = RefCell::new(1); + let res = break_if_r_is_noalias(&rc, rc.borrow()); + assert_eq!(res, 3); + + *rc.get_mut() = 1; + let res = break_if_r_is_noalias(&rc, rc.borrow_mut()); + assert_eq!(res, 3); +} diff --git a/src/test/ui/macros/format-args-temporaries.rs b/src/test/ui/macros/format-args-temporaries.rs new file mode 100644 index 0000000000000..ddd4c9754bfa4 --- /dev/null +++ b/src/test/ui/macros/format-args-temporaries.rs @@ -0,0 +1,70 @@ +// check-pass + +use std::fmt::{self, Display}; + +struct Mutex; + +impl Mutex { + fn lock(&self) -> MutexGuard { + MutexGuard(self) + } +} + +struct MutexGuard<'a>(&'a Mutex); + +impl<'a> Drop for MutexGuard<'a> { + fn drop(&mut self) { + // Empty but this is a necessary part of the repro. Otherwise borrow + // checker is fine with 'a dangling at the time that MutexGuard goes out + // of scope. + } +} + +impl<'a> MutexGuard<'a> { + fn write_fmt(&self, _args: fmt::Arguments) {} +} + +impl<'a> Display for MutexGuard<'a> { + fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { + Ok(()) + } +} + +fn main() { + let _write = { + let out = Mutex; + let mutex = Mutex; + write!(out.lock(), "{}", mutex.lock()) /* no semicolon */ + }; + + let _writeln = { + let out = Mutex; + let mutex = Mutex; + writeln!(out.lock(), "{}", mutex.lock()) /* no semicolon */ + }; + + let _print = { + let mutex = Mutex; + print!("{}", mutex.lock()) /* no semicolon */ + }; + + let _println = { + let mutex = Mutex; + println!("{}", mutex.lock()) /* no semicolon */ + }; + + let _eprint = { + let mutex = Mutex; + eprint!("{}", mutex.lock()) /* no semicolon */ + }; + + let _eprintln = { + let mutex = Mutex; + eprintln!("{}", mutex.lock()) /* no semicolon */ + }; + + let _panic = { + let mutex = Mutex; + panic!("{}", mutex.lock()) /* no semicolon */ + }; +}