diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 21078f79f634a..dc181ecda5b05 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -2,7 +2,6 @@ #![feature(associated_type_bounds)] #![feature(associated_type_defaults)] #![feature(crate_visibility_modifier)] -#![feature(decl_macro)] #![feature(if_let_guard)] #![feature(let_chains)] #![feature(let_else)] diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index fbacebf99c050..4298475767e6f 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -110,7 +110,7 @@ use rustc_ast::token::{DelimToken, Token, TokenKind}; use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::MultiSpan; -use rustc_session::lint::builtin::META_VARIABLE_MISUSE; +use rustc_session::lint::builtin::{META_VARIABLE_MISUSE, MISSING_FRAGMENT_SPECIFIER}; use rustc_session::parse::ParseSess; use rustc_span::symbol::kw; use rustc_span::{symbol::MacroRulesNormalizedIdent, Span}; @@ -261,7 +261,18 @@ fn check_binders( } } // Similarly, this can only happen when checking a toplevel macro. - TokenTree::MetaVarDecl(span, name, _kind) => { + TokenTree::MetaVarDecl(span, name, kind) => { + if kind.is_none() && node_id != DUMMY_NODE_ID { + // FIXME: Report this as a hard error eventually and remove equivalent errors from + // `parse_tt_inner` and `nameize`. Until then the error may be reported twice, once + // as a hard error and then once as a buffered lint. + sess.buffer_lint( + MISSING_FRAGMENT_SPECIFIER, + span, + node_id, + &format!("missing fragment specifier"), + ); + } if !macros.is_empty() { sess.span_diagnostic.span_bug(span, "unexpected MetaVarDecl in nested lhs"); } diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index a137e0c92e8a2..ce243b4a67272 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -411,7 +411,6 @@ impl TtParser { /// track of through the mps generated. fn parse_tt_inner( &mut self, - sess: &ParseSess, matcher: &[MatcherLoc], token: &Token, ) -> Option { @@ -519,11 +518,9 @@ impl TtParser { self.bb_mps.push(mp); } } else { + // E.g. `$e` instead of `$e:expr`, reported as a hard error if actually used. // Both this check and the one in `nameize` are necessary, surprisingly. - if sess.missing_fragment_specifiers.borrow_mut().remove(&span).is_some() { - // E.g. `$e` instead of `$e:expr`. - return Some(Error(span, "missing fragment specifier".to_string())); - } + return Some(Error(span, "missing fragment specifier".to_string())); } } MatcherLoc::Eof => { @@ -549,7 +546,7 @@ impl TtParser { // Need to take ownership of the matches from within the `Lrc`. Lrc::make_mut(&mut eof_mp.matches); let matches = Lrc::try_unwrap(eof_mp.matches).unwrap().into_iter(); - self.nameize(sess, matcher, matches) + self.nameize(matcher, matches) } EofMatcherPositions::Multiple => { Error(token.span, "ambiguity: multiple successful parses".to_string()) @@ -587,7 +584,7 @@ impl TtParser { // Process `cur_mps` until either we have finished the input or we need to get some // parsing from the black-box parser done. - if let Some(res) = self.parse_tt_inner(&parser.sess, matcher, &parser.token) { + if let Some(res) = self.parse_tt_inner(matcher, &parser.token) { return res; } @@ -694,7 +691,6 @@ impl TtParser { fn nameize>( &self, - sess: &ParseSess, matcher: &[MatcherLoc], mut res: I, ) -> NamedParseResult { @@ -711,11 +707,9 @@ impl TtParser { } }; } else { + // E.g. `$e` instead of `$e:expr`, reported as a hard error if actually used. // Both this check and the one in `parse_tt_inner` are necessary, surprisingly. - if sess.missing_fragment_specifiers.borrow_mut().remove(&span).is_some() { - // E.g. `$e` instead of `$e:expr`. - return Error(span, "missing fragment specifier".to_string()); - } + return Error(span, "missing fragment specifier".to_string()); } } } diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index a99a18aae119a..024299fbd9c01 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -2,8 +2,7 @@ use crate::mbe::macro_parser::count_metavar_decls; use crate::mbe::{Delimited, KleeneOp, KleeneToken, MetaVarExpr, SequenceRepetition, TokenTree}; use rustc_ast::token::{self, Token}; -use rustc_ast::tokenstream; -use rustc_ast::{NodeId, DUMMY_NODE_ID}; +use rustc_ast::{tokenstream, NodeId}; use rustc_ast_pretty::pprust; use rustc_feature::Features; use rustc_session::parse::{feature_err, ParseSess}; @@ -104,10 +103,7 @@ pub(super) fn parse( } tree => tree.as_ref().map_or(start_sp, tokenstream::TokenTree::span), }; - if node_id != DUMMY_NODE_ID { - // Macros loaded from other crates have dummy node ids. - sess.missing_fragment_specifiers.borrow_mut().insert(span, node_id); - } + result.push(TokenTree::MetaVarDecl(span, ident, None)); } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index eac6a33cf2298..2a01b677e33bf 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -30,7 +30,6 @@ use rustc_resolve::{Resolver, ResolverArenas}; use rustc_serialize::json; use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType}; use rustc_session::cstore::{MetadataLoader, MetadataLoaderDyn}; -use rustc_session::lint; use rustc_session::output::{filename_for_input, filename_for_metadata}; use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; @@ -349,23 +348,8 @@ pub fn configure_and_expand( ecx.check_unused_macros(); }); - let mut missing_fragment_specifiers: Vec<_> = ecx - .sess - .parse_sess - .missing_fragment_specifiers - .borrow() - .iter() - .map(|(span, node_id)| (*span, *node_id)) - .collect(); - missing_fragment_specifiers.sort_unstable_by_key(|(span, _)| *span); - let recursion_limit_hit = ecx.reduced_recursion_limit.is_some(); - for (span, node_id) in missing_fragment_specifiers { - let lint = lint::builtin::MISSING_FRAGMENT_SPECIFIER; - let msg = "missing fragment specifier"; - resolver.lint_buffer().buffer_lint(lint, node_id, span, msg); - } if cfg!(windows) { env::set_var("PATH", &old_path); } diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 0b9c27c2cd6e3..1fa180b320cd8 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -140,7 +140,6 @@ pub struct ParseSess { pub config: CrateConfig, pub check_config: CrateCheckConfig, pub edition: Edition, - pub missing_fragment_specifiers: Lock>, /// Places where raw identifiers were used. This is used to avoid complaining about idents /// clashing with keywords in new editions. pub raw_identifier_spans: Lock>, @@ -195,7 +194,6 @@ impl ParseSess { config: FxHashSet::default(), check_config: CrateCheckConfig::default(), edition: ExpnId::root().expn_data().edition, - missing_fragment_specifiers: Default::default(), raw_identifier_spans: Lock::new(Vec::new()), bad_unicode_identifiers: Lock::new(Default::default()), source_map, diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 0b57c36247e43..d0d37c08d1306 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -393,7 +393,7 @@ impl Rc { /// # Examples /// /// ``` - /// #![allow(dead_code)] + /// # #![allow(dead_code)] /// use std::rc::{Rc, Weak}; /// /// struct Gadget { diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 71b6b9b41f5c5..e97c1637fd5a2 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -117,27 +117,99 @@ use crate::vec::Vec; /// /// # UTF-8 /// -/// `String`s are always valid UTF-8. This has a few implications, the first of -/// which is that if you need a non-UTF-8 string, consider [`OsString`]. It is -/// similar, but without the UTF-8 constraint. The second implication is that -/// you cannot index into a `String`: +/// `String`s are always valid UTF-8. If you need a non-UTF-8 string, consider +/// [`OsString`]. It is similar, but without the UTF-8 constraint. Because UTF-8 +/// is a variable width encoding, `String`s are typically smaller than an array of +/// the same `chars`: +/// +/// ``` +/// use std::mem; +/// +/// // `s` is ASCII which represents each `char` as one byte +/// let s = "hello"; +/// assert_eq!(s.len(), 5); +/// +/// // A `char` array with the same contents would be longer because +/// // every `char` is four bytes +/// let s = ['h', 'e', 'l', 'l', 'o']; +/// let size: usize = s.into_iter().map(|c| mem::size_of_val(&c)).sum(); +/// assert_eq!(size, 20); +/// +/// // However, for non-ASCII strings, the difference will be smaller +/// // and sometimes they are the same +/// let s = "💖💖💖💖💖"; +/// assert_eq!(s.len(), 20); +/// +/// let s = ['💖', '💖', '💖', '💖', '💖']; +/// let size: usize = s.into_iter().map(|c| mem::size_of_val(&c)).sum(); +/// assert_eq!(size, 20); +/// ``` +/// +/// This raises interesting questions as to how `s[i]` should work. +/// What should `i` be here? Several options include byte indices and +/// `char` indices but, because of UTF-8 encoding, only byte indices +/// would provide constant time indexing. Getting the `i`th `char`, for +/// example, is available using [`chars`]: +/// +/// ``` +/// let s = "hello"; +/// let third_character = s.chars().nth(2); +/// assert_eq!(third_character, Some('l')); +/// +/// let s = "💖💖💖💖💖"; +/// let third_character = s.chars().nth(2); +/// assert_eq!(third_character, Some('💖')); +/// ``` +/// +/// Next, what should `s[i]` return? Because indexing returns a reference +/// to underlying data it could be `&u8`, `&[u8]`, or something else similar. +/// Since we're only providing one index, `&u8` makes the most sense but that +/// might not be what the user expects and can be explicitly achieved with +/// [`as_bytes()`]: +/// +/// ``` +/// // The first byte is 104 - the byte value of `'h'` +/// let s = "hello"; +/// assert_eq!(s.as_bytes()[0], 104); +/// // or +/// assert_eq!(s.as_bytes()[0], b'h'); +/// +/// // The first byte is 240 which isn't obviously useful +/// let s = "💖💖💖💖💖"; +/// assert_eq!(s.as_bytes()[0], 240); +/// ``` +/// +/// Due to these ambiguities/restrictions, indexing with a `usize` is simply +/// forbidden: /// /// ```compile_fail,E0277 /// let s = "hello"; /// -/// println!("The first letter of s is {}", s[0]); // ERROR!!! +/// // The following will not compile! +/// println!("The first letter of s is {}", s[0]); /// ``` /// +/// It is more clear, however, how `&s[i..j]` should work (that is, +/// indexing with a range). It should accept byte indices (to be constant-time) +/// and return a `&str` which is UTF-8 encoded. This is also called "string slicing". +/// Note this will panic if the byte indices provided are not character +/// boundaries - see [`is_char_boundary`] for more details. See the implementations +/// for [`SliceIndex`] for more details on string slicing. For a non-panicking +/// version of string slicing, see [`get`]. +/// /// [`OsString`]: ../../std/ffi/struct.OsString.html "ffi::OsString" +/// [`SliceIndex`]: core::slice::SliceIndex +/// [`as_bytes()`]: str::as_bytes +/// [`get`]: str::get +/// [`is_char_boundary`]: str::is_char_boundary /// -/// Indexing is intended to be a constant-time operation, but UTF-8 encoding -/// does not allow us to do this. Furthermore, it's not clear what sort of -/// thing the index should return: a byte, a codepoint, or a grapheme cluster. -/// The [`bytes`] and [`chars`] methods return iterators over the first -/// two, respectively. +/// The [`bytes`] and [`chars`] methods return iterators over the bytes and +/// codepoints of the string, respectively. To iterate over codepoints along +/// with byte indices, use [`char_indices`]. /// /// [`bytes`]: str::bytes /// [`chars`]: str::chars +/// [`char_indices`]: str::char_indices /// /// # Deref /// diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index db6898c1308a2..612e366cedf78 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -1,7 +1,6 @@ use crate::cmp; use crate::fmt; -use crate::mem; -use crate::num::NonZeroUsize; +use crate::mem::{self, ValidAlign}; use crate::ptr::NonNull; // While this function is used in one place and its implementation @@ -40,7 +39,7 @@ pub struct Layout { // // (However, we do not analogously require `align >= sizeof(void*)`, // even though that is *also* a requirement of `posix_memalign`.) - align_: NonZeroUsize, + align_: ValidAlign, } impl Layout { @@ -97,8 +96,8 @@ impl Layout { #[must_use] #[inline] pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self { - // SAFETY: the caller must ensure that `align` is greater than zero. - Layout { size_: size, align_: unsafe { NonZeroUsize::new_unchecked(align) } } + // SAFETY: the caller must ensure that `align` is a power of two. + Layout { size_: size, align_: unsafe { ValidAlign::new_unchecked(align) } } } /// The minimum size in bytes for a memory block of this layout. @@ -117,7 +116,7 @@ impl Layout { without modifying the layout"] #[inline] pub const fn align(&self) -> usize { - self.align_.get() + self.align_.as_nonzero().get() } /// Constructs a `Layout` suitable for holding a value of type `T`. diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 0d33fea75f407..712d06f217034 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -21,6 +21,12 @@ mod maybe_uninit; #[stable(feature = "maybe_uninit", since = "1.36.0")] pub use maybe_uninit::MaybeUninit; +mod valid_align; +// For now this type is left crate-local. It could potentially make sense to expose +// it publicly, as it would be a nice parameter type for methods which need to take +// alignment as a parameter, such as `Layout::padding_needed_for`. +pub(crate) use valid_align::ValidAlign; + #[stable(feature = "rust1", since = "1.0.0")] #[doc(inline)] pub use crate::intrinsics::transmute; diff --git a/library/core/src/mem/valid_align.rs b/library/core/src/mem/valid_align.rs new file mode 100644 index 0000000000000..596a67f255a1b --- /dev/null +++ b/library/core/src/mem/valid_align.rs @@ -0,0 +1,240 @@ +use crate::convert::TryFrom; +use crate::num::NonZeroUsize; +use crate::{cmp, fmt, mem, num}; + +/// A type storing a `usize` which is a power of two, and thus +/// represents a possible alignment in the rust abstract machine. +/// +/// Note that particularly large alignments, while representable in this type, +/// are likely not to be supported by actual allocators and linkers. +#[derive(Copy, Clone)] +#[repr(transparent)] +pub(crate) struct ValidAlign(ValidAlignEnum); + +// ValidAlign is `repr(usize)`, but via extra steps. +const _: () = assert!(mem::size_of::() == mem::size_of::()); +const _: () = assert!(mem::align_of::() == mem::align_of::()); + +impl ValidAlign { + /// Creates a `ValidAlign` from a power-of-two `usize`. + /// + /// # Safety + /// + /// `align` must be a power of two. + /// + /// Equivalently, it must be `1 << exp` for some `exp` in `0..usize::BITS`. + /// It must *not* be zero. + #[inline] + pub(crate) const unsafe fn new_unchecked(align: usize) -> Self { + debug_assert!(align.is_power_of_two()); + + // SAFETY: By precondition, this must be a power of two, and + // our variants encompass all possible powers of two. + unsafe { mem::transmute::(align) } + } + + #[inline] + pub(crate) const fn as_nonzero(self) -> NonZeroUsize { + // SAFETY: All the discriminants are non-zero. + unsafe { NonZeroUsize::new_unchecked(self.0 as usize) } + } + + /// Returns the base 2 logarithm of the alignment. + /// + /// This is always exact, as `self` represents a power of two. + #[inline] + pub(crate) fn log2(self) -> u32 { + self.as_nonzero().trailing_zeros() + } +} + +impl fmt::Debug for ValidAlign { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?} (1 << {:?})", self.as_nonzero(), self.log2()) + } +} + +impl TryFrom for ValidAlign { + type Error = num::TryFromIntError; + + #[inline] + fn try_from(align: NonZeroUsize) -> Result { + if align.is_power_of_two() { + // SAFETY: Just checked for power-of-two + unsafe { Ok(ValidAlign::new_unchecked(align.get())) } + } else { + Err(num::TryFromIntError(())) + } + } +} + +impl TryFrom for ValidAlign { + type Error = num::TryFromIntError; + + #[inline] + fn try_from(align: usize) -> Result { + if align.is_power_of_two() { + // SAFETY: Just checked for power-of-two + unsafe { Ok(ValidAlign::new_unchecked(align)) } + } else { + Err(num::TryFromIntError(())) + } + } +} + +impl cmp::Eq for ValidAlign {} + +impl cmp::PartialEq for ValidAlign { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_nonzero() == other.as_nonzero() + } +} + +impl cmp::Ord for ValidAlign { + #[inline] + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.as_nonzero().cmp(&other.as_nonzero()) + } +} + +impl cmp::PartialOrd for ValidAlign { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +#[cfg(target_pointer_width = "16")] +type ValidAlignEnum = ValidAlignEnum16; +#[cfg(target_pointer_width = "32")] +type ValidAlignEnum = ValidAlignEnum32; +#[cfg(target_pointer_width = "64")] +type ValidAlignEnum = ValidAlignEnum64; + +#[derive(Copy, Clone)] +#[repr(u16)] +enum ValidAlignEnum16 { + _Align1Shl0 = 1 << 0, + _Align1Shl1 = 1 << 1, + _Align1Shl2 = 1 << 2, + _Align1Shl3 = 1 << 3, + _Align1Shl4 = 1 << 4, + _Align1Shl5 = 1 << 5, + _Align1Shl6 = 1 << 6, + _Align1Shl7 = 1 << 7, + _Align1Shl8 = 1 << 8, + _Align1Shl9 = 1 << 9, + _Align1Shl10 = 1 << 10, + _Align1Shl11 = 1 << 11, + _Align1Shl12 = 1 << 12, + _Align1Shl13 = 1 << 13, + _Align1Shl14 = 1 << 14, + _Align1Shl15 = 1 << 15, +} + +#[derive(Copy, Clone)] +#[repr(u32)] +enum ValidAlignEnum32 { + _Align1Shl0 = 1 << 0, + _Align1Shl1 = 1 << 1, + _Align1Shl2 = 1 << 2, + _Align1Shl3 = 1 << 3, + _Align1Shl4 = 1 << 4, + _Align1Shl5 = 1 << 5, + _Align1Shl6 = 1 << 6, + _Align1Shl7 = 1 << 7, + _Align1Shl8 = 1 << 8, + _Align1Shl9 = 1 << 9, + _Align1Shl10 = 1 << 10, + _Align1Shl11 = 1 << 11, + _Align1Shl12 = 1 << 12, + _Align1Shl13 = 1 << 13, + _Align1Shl14 = 1 << 14, + _Align1Shl15 = 1 << 15, + _Align1Shl16 = 1 << 16, + _Align1Shl17 = 1 << 17, + _Align1Shl18 = 1 << 18, + _Align1Shl19 = 1 << 19, + _Align1Shl20 = 1 << 20, + _Align1Shl21 = 1 << 21, + _Align1Shl22 = 1 << 22, + _Align1Shl23 = 1 << 23, + _Align1Shl24 = 1 << 24, + _Align1Shl25 = 1 << 25, + _Align1Shl26 = 1 << 26, + _Align1Shl27 = 1 << 27, + _Align1Shl28 = 1 << 28, + _Align1Shl29 = 1 << 29, + _Align1Shl30 = 1 << 30, + _Align1Shl31 = 1 << 31, +} + +#[derive(Copy, Clone)] +#[repr(u64)] +enum ValidAlignEnum64 { + _Align1Shl0 = 1 << 0, + _Align1Shl1 = 1 << 1, + _Align1Shl2 = 1 << 2, + _Align1Shl3 = 1 << 3, + _Align1Shl4 = 1 << 4, + _Align1Shl5 = 1 << 5, + _Align1Shl6 = 1 << 6, + _Align1Shl7 = 1 << 7, + _Align1Shl8 = 1 << 8, + _Align1Shl9 = 1 << 9, + _Align1Shl10 = 1 << 10, + _Align1Shl11 = 1 << 11, + _Align1Shl12 = 1 << 12, + _Align1Shl13 = 1 << 13, + _Align1Shl14 = 1 << 14, + _Align1Shl15 = 1 << 15, + _Align1Shl16 = 1 << 16, + _Align1Shl17 = 1 << 17, + _Align1Shl18 = 1 << 18, + _Align1Shl19 = 1 << 19, + _Align1Shl20 = 1 << 20, + _Align1Shl21 = 1 << 21, + _Align1Shl22 = 1 << 22, + _Align1Shl23 = 1 << 23, + _Align1Shl24 = 1 << 24, + _Align1Shl25 = 1 << 25, + _Align1Shl26 = 1 << 26, + _Align1Shl27 = 1 << 27, + _Align1Shl28 = 1 << 28, + _Align1Shl29 = 1 << 29, + _Align1Shl30 = 1 << 30, + _Align1Shl31 = 1 << 31, + _Align1Shl32 = 1 << 32, + _Align1Shl33 = 1 << 33, + _Align1Shl34 = 1 << 34, + _Align1Shl35 = 1 << 35, + _Align1Shl36 = 1 << 36, + _Align1Shl37 = 1 << 37, + _Align1Shl38 = 1 << 38, + _Align1Shl39 = 1 << 39, + _Align1Shl40 = 1 << 40, + _Align1Shl41 = 1 << 41, + _Align1Shl42 = 1 << 42, + _Align1Shl43 = 1 << 43, + _Align1Shl44 = 1 << 44, + _Align1Shl45 = 1 << 45, + _Align1Shl46 = 1 << 46, + _Align1Shl47 = 1 << 47, + _Align1Shl48 = 1 << 48, + _Align1Shl49 = 1 << 49, + _Align1Shl50 = 1 << 50, + _Align1Shl51 = 1 << 51, + _Align1Shl52 = 1 << 52, + _Align1Shl53 = 1 << 53, + _Align1Shl54 = 1 << 54, + _Align1Shl55 = 1 << 55, + _Align1Shl56 = 1 << 56, + _Align1Shl57 = 1 << 57, + _Align1Shl58 = 1 << 58, + _Align1Shl59 = 1 << 59, + _Align1Shl60 = 1 << 60, + _Align1Shl61 = 1 << 61, + _Align1Shl62 = 1 << 62, + _Align1Shl63 = 1 << 63, +} diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index a30d2ff0ea642..98c9bf556bb07 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -299,8 +299,8 @@ impl u8 { #[rustc_const_stable(feature = "const_ascii_methods_on_intrinsics", since = "1.52.0")] #[inline] pub const fn to_ascii_uppercase(&self) -> u8 { - // Unset the fifth bit if this is a lowercase letter - *self & !((self.is_ascii_lowercase() as u8) * ASCII_CASE_MASK) + // Toggle the fifth bit if this is a lowercase letter + *self ^ ((self.is_ascii_lowercase() as u8) * ASCII_CASE_MASK) } /// Makes a copy of the value in its ASCII lower case equivalent. diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index fdf58c50e4863..f9a317f663c0b 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -5,7 +5,7 @@ use crate::cmp::*; // macro for implementing n-ary tuple functions and operations macro_rules! tuple_impls { - ( $( $Tuple:ident( $( $T:ident )+ ) )+ ) => { + ( $( ( $( $T:ident )+ ) )+ ) => { $( #[stable(feature = "rust1", since = "1.0.0")] impl<$($T:PartialEq),+> PartialEq for ($($T,)+) where last_type!($($T,)+): ?Sized { @@ -106,16 +106,16 @@ macro_rules! last_type { } tuple_impls! { - Tuple1(A) - Tuple2(A B) - Tuple3(A B C) - Tuple4(A B C D) - Tuple5(A B C D E) - Tuple6(A B C D E F) - Tuple7(A B C D E F G) - Tuple8(A B C D E F G H) - Tuple9(A B C D E F G H I) - Tuple10(A B C D E F G H I J) - Tuple11(A B C D E F G H I J K) - Tuple12(A B C D E F G H I J K L) + (A) + (A B) + (A B C) + (A B C D) + (A B C D E) + (A B C D E F) + (A B C D E F G) + (A B C D E F G H) + (A B C D E F G H I) + (A B C D E F G H I J) + (A B C D E F G H I J K) + (A B C D E F G H I J K L) } diff --git a/library/core/tests/alloc.rs b/library/core/tests/alloc.rs index c8592e40a69a0..628ac3a45ff04 100644 --- a/library/core/tests/alloc.rs +++ b/library/core/tests/alloc.rs @@ -11,3 +11,21 @@ fn const_unchecked_layout() { assert_eq!(LAYOUT.align(), ALIGN); assert_eq!(Some(DANGLING), NonNull::new(ALIGN as *mut u8)); } + +#[test] +fn layout_debug_shows_log2_of_alignment() { + // `Debug` is not stable, but here's what it does right now + let layout = Layout::from_size_align(24576, 8192).unwrap(); + let s = format!("{:?}", layout); + assert_eq!(s, "Layout { size_: 24576, align_: 8192 (1 << 13) }"); +} + +// Running this normally doesn't do much, but it's also run in Miri, which +// will double-check that these are allowed by the validity invariants. +#[test] +fn layout_accepts_all_valid_alignments() { + for align in 0..usize::BITS { + let layout = Layout::from_size_align(0, 1_usize << align).unwrap(); + assert_eq!(layout.align(), 1_usize << align); + } +} diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 339b7a8d1e6fb..81200ba60b026 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -2065,6 +2065,7 @@ impl Step for Crate { } } +/// Rustdoc is special in various ways, which is why this step is different from `Crate`. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct CrateRustdoc { host: TargetSelection, @@ -2092,11 +2093,15 @@ impl Step for CrateRustdoc { let test_kind = self.test_kind; let target = self.host; - // Use the previous stage compiler to reuse the artifacts that are - // created when running compiletest for src/test/rustdoc. If this used - // `compiler`, then it would cause rustdoc to be built *again*, which - // isn't really necessary. - let compiler = builder.compiler_for(builder.top_stage, target, target); + let compiler = if builder.config.download_rustc { + builder.compiler(builder.top_stage, target) + } else { + // Use the previous stage compiler to reuse the artifacts that are + // created when running compiletest for src/test/rustdoc. If this used + // `compiler`, then it would cause rustdoc to be built *again*, which + // isn't really necessary. + builder.compiler_for(builder.top_stage, target, target) + }; builder.ensure(compile::Rustc { compiler, target }); let mut cargo = tool::prepare_tool_cargo( @@ -2112,6 +2117,15 @@ impl Step for CrateRustdoc { if test_kind.subcommand() == "test" && !builder.fail_fast { cargo.arg("--no-fail-fast"); } + match builder.doc_tests { + DocTests::Only => { + cargo.arg("--doc"); + } + DocTests::No => { + cargo.args(&["--lib", "--bins", "--examples", "--tests", "--benches"]); + } + DocTests::Yes => {} + } cargo.arg("-p").arg("rustdoc:0.0.0"); @@ -2136,6 +2150,8 @@ impl Step for CrateRustdoc { // sets up the dylib path for the *host* (stage1/lib), which is the // wrong directory. // + // Recall that we special-cased `compiler_for(top_stage)` above, so we always use stage1. + // // It should be considered to just stop running doctests on // librustdoc. There is only one test, and it doesn't look too // important. There might be other ways to avoid this, but it seems @@ -2144,8 +2160,15 @@ impl Step for CrateRustdoc { // See also https://github.com/rust-lang/rust/issues/13983 where the // host vs target dylibs for rustdoc are consistently tricky to deal // with. + // + // Note that this set the host libdir for `download_rustc`, which uses a normal rust distribution. + let libdir = if builder.config.download_rustc { + builder.rustc_libdir(compiler) + } else { + builder.sysroot_libdir(compiler, target).to_path_buf() + }; let mut dylib_path = dylib_path(); - dylib_path.insert(0, PathBuf::from(&*builder.sysroot_libdir(compiler, target))); + dylib_path.insert(0, PathBuf::from(&*libdir)); cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); if !builder.config.verbose_tests { diff --git a/src/test/ui/consts/std/alloc.32bit.stderr b/src/test/ui/consts/std/alloc.32bit.stderr index 67bc3202c105c..1490314a76788 100644 --- a/src/test/ui/consts/std/alloc.32bit.stderr +++ b/src/test/ui/consts/std/alloc.32bit.stderr @@ -1,14 +1,25 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/alloc.rs:8:1 + --> $DIR/alloc.rs:9:1 | -LL | const LAYOUT_INVALID: Layout = unsafe { Layout::from_size_align_unchecked(0x1000, 0x00) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .align_: encountered 0, but expected something greater or equal to 1 +LL | const LAYOUT_INVALID_ZERO: Layout = unsafe { Layout::from_size_align_unchecked(0x1000, 0x00) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .align_.0.: encountered 0x00000000, but expected a valid enum tag | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { 00 10 00 00 00 00 00 00 │ ........ } -error: aborting due to previous error +error[E0080]: it is undefined behavior to use this value + --> $DIR/alloc.rs:13:1 + | +LL | const LAYOUT_INVALID_THREE: Layout = unsafe { Layout::from_size_align_unchecked(9, 3) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .align_.0.: encountered 0x00000003, but expected a valid enum tag + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + 09 00 00 00 03 00 00 00 │ ........ + } + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/std/alloc.64bit.stderr b/src/test/ui/consts/std/alloc.64bit.stderr index ec89dec272dbc..58349feec63d4 100644 --- a/src/test/ui/consts/std/alloc.64bit.stderr +++ b/src/test/ui/consts/std/alloc.64bit.stderr @@ -1,14 +1,25 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/alloc.rs:8:1 + --> $DIR/alloc.rs:9:1 | -LL | const LAYOUT_INVALID: Layout = unsafe { Layout::from_size_align_unchecked(0x1000, 0x00) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .align_: encountered 0, but expected something greater or equal to 1 +LL | const LAYOUT_INVALID_ZERO: Layout = unsafe { Layout::from_size_align_unchecked(0x1000, 0x00) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .align_.0.: encountered 0x0000000000000000, but expected a valid enum tag | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ } -error: aborting due to previous error +error[E0080]: it is undefined behavior to use this value + --> $DIR/alloc.rs:13:1 + | +LL | const LAYOUT_INVALID_THREE: Layout = unsafe { Layout::from_size_align_unchecked(9, 3) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .align_.0.: encountered 0x0000000000000003, but expected a valid enum tag + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + 09 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 │ ................ + } + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/std/alloc.rs b/src/test/ui/consts/std/alloc.rs index 14eadc4487f9e..708b954e84aea 100644 --- a/src/test/ui/consts/std/alloc.rs +++ b/src/test/ui/consts/std/alloc.rs @@ -1,11 +1,16 @@ // stderr-per-bitwidth +// ignore-debug (the debug assertions change the error) use std::alloc::Layout; // ok const LAYOUT_VALID: Layout = unsafe { Layout::from_size_align_unchecked(0x1000, 0x08) }; // not ok, since alignment needs to be non-zero. -const LAYOUT_INVALID: Layout = unsafe { Layout::from_size_align_unchecked(0x1000, 0x00) }; +const LAYOUT_INVALID_ZERO: Layout = unsafe { Layout::from_size_align_unchecked(0x1000, 0x00) }; +//~^ ERROR it is undefined behavior to use this value + +// not ok, since alignment needs to be a power of two. +const LAYOUT_INVALID_THREE: Layout = unsafe { Layout::from_size_align_unchecked(9, 3) }; //~^ ERROR it is undefined behavior to use this value fn main() {} diff --git a/src/test/ui/macros/macro-match-nonterminal.rs b/src/test/ui/macros/macro-match-nonterminal.rs index b23e5c71c03f0..5d9eb55fee036 100644 --- a/src/test/ui/macros/macro-match-nonterminal.rs +++ b/src/test/ui/macros/macro-match-nonterminal.rs @@ -2,6 +2,8 @@ macro_rules! test { ($a, $b) => { //~^ ERROR missing fragment //~| ERROR missing fragment + //~| ERROR missing fragment + //~| WARN this was previously accepted //~| WARN this was previously accepted () }; diff --git a/src/test/ui/macros/macro-match-nonterminal.stderr b/src/test/ui/macros/macro-match-nonterminal.stderr index 674ce3434aac6..48b9bc6ff6ae0 100644 --- a/src/test/ui/macros/macro-match-nonterminal.stderr +++ b/src/test/ui/macros/macro-match-nonterminal.stderr @@ -4,15 +4,24 @@ error: missing fragment specifier LL | ($a, $b) => { | ^ +error: missing fragment specifier + --> $DIR/macro-match-nonterminal.rs:2:8 + | +LL | ($a, $b) => { + | ^ + | + = note: `#[deny(missing_fragment_specifier)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #40107 + error: missing fragment specifier --> $DIR/macro-match-nonterminal.rs:2:10 | LL | ($a, $b) => { | ^^ | - = note: `#[deny(missing_fragment_specifier)]` on by default = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #40107 -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/src/test/ui/macros/macro-missing-fragment-deduplication.rs b/src/test/ui/macros/macro-missing-fragment-deduplication.rs new file mode 100644 index 0000000000000..c1e6ba7464772 --- /dev/null +++ b/src/test/ui/macros/macro-missing-fragment-deduplication.rs @@ -0,0 +1,15 @@ +// compile-flags: -Zdeduplicate-diagnostics=yes + +macro_rules! m { + ($name) => {} + //~^ ERROR missing fragment + //~| ERROR missing fragment + //~| WARN this was previously accepted +} + +fn main() { + m!(); + m!(); + m!(); + m!(); +} diff --git a/src/test/ui/macros/macro-missing-fragment-deduplication.stderr b/src/test/ui/macros/macro-missing-fragment-deduplication.stderr new file mode 100644 index 0000000000000..7622ca054c8ab --- /dev/null +++ b/src/test/ui/macros/macro-missing-fragment-deduplication.stderr @@ -0,0 +1,18 @@ +error: missing fragment specifier + --> $DIR/macro-missing-fragment-deduplication.rs:4:6 + | +LL | ($name) => {} + | ^^^^^ + +error: missing fragment specifier + --> $DIR/macro-missing-fragment-deduplication.rs:4:6 + | +LL | ($name) => {} + | ^^^^^ + | + = note: `#[deny(missing_fragment_specifier)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #40107 + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/macros/macro-missing-fragment.rs b/src/test/ui/macros/macro-missing-fragment.rs index 1d0b0889b4cef..210c85ebbf2f3 100644 --- a/src/test/ui/macros/macro-missing-fragment.rs +++ b/src/test/ui/macros/macro-missing-fragment.rs @@ -1,7 +1,26 @@ -macro_rules! m { - ( $( any_token $field_rust_type )* ) => {}; //~ ERROR missing fragment +#![warn(missing_fragment_specifier)] + +macro_rules! used_arm { + ( $( any_token $field_rust_type )* ) => {}; + //~^ ERROR missing fragment + //~| WARN missing fragment + //~| WARN this was previously accepted +} + +macro_rules! used_macro_unused_arm { + () => {}; + ( $name ) => {}; + //~^ WARN missing fragment + //~| WARN this was previously accepted +} + +macro_rules! unused_macro { + ( $name ) => {}; + //~^ WARN missing fragment + //~| WARN this was previously accepted } fn main() { - m!(); + used_arm!(); + used_macro_unused_arm!(); } diff --git a/src/test/ui/macros/macro-missing-fragment.stderr b/src/test/ui/macros/macro-missing-fragment.stderr index b7871c0ec3a6f..1bf6f04ec7f14 100644 --- a/src/test/ui/macros/macro-missing-fragment.stderr +++ b/src/test/ui/macros/macro-missing-fragment.stderr @@ -1,8 +1,40 @@ error: missing fragment specifier - --> $DIR/macro-missing-fragment.rs:2:20 + --> $DIR/macro-missing-fragment.rs:4:20 | LL | ( $( any_token $field_rust_type )* ) => {}; | ^^^^^^^^^^^^^^^^ -error: aborting due to previous error +warning: missing fragment specifier + --> $DIR/macro-missing-fragment.rs:4:20 + | +LL | ( $( any_token $field_rust_type )* ) => {}; + | ^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/macro-missing-fragment.rs:1:9 + | +LL | #![warn(missing_fragment_specifier)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #40107 + +warning: missing fragment specifier + --> $DIR/macro-missing-fragment.rs:12:7 + | +LL | ( $name ) => {}; + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #40107 + +warning: missing fragment specifier + --> $DIR/macro-missing-fragment.rs:18:7 + | +LL | ( $name ) => {}; + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #40107 + +error: aborting due to previous error; 3 warnings emitted diff --git a/src/test/ui/parser/macro/issue-33569.rs b/src/test/ui/parser/macro/issue-33569.rs index 80e2d7c6545ba..069d181e96267 100644 --- a/src/test/ui/parser/macro/issue-33569.rs +++ b/src/test/ui/parser/macro/issue-33569.rs @@ -1,6 +1,8 @@ macro_rules! foo { { $+ } => { //~ ERROR expected identifier, found `+` //~^ ERROR missing fragment specifier + //~| ERROR missing fragment specifier + //~| WARN this was previously accepted $(x)(y) //~ ERROR expected one of: `*`, `+`, or `?` } } diff --git a/src/test/ui/parser/macro/issue-33569.stderr b/src/test/ui/parser/macro/issue-33569.stderr index b4d38d3ce4806..39d49fd03f1bf 100644 --- a/src/test/ui/parser/macro/issue-33569.stderr +++ b/src/test/ui/parser/macro/issue-33569.stderr @@ -5,7 +5,7 @@ LL | { $+ } => { | ^ error: expected one of: `*`, `+`, or `?` - --> $DIR/issue-33569.rs:4:13 + --> $DIR/issue-33569.rs:6:13 | LL | $(x)(y) | ^^^ @@ -16,5 +16,15 @@ error: missing fragment specifier LL | { $+ } => { | ^ -error: aborting due to 3 previous errors +error: missing fragment specifier + --> $DIR/issue-33569.rs:2:8 + | +LL | { $+ } => { + | ^ + | + = note: `#[deny(missing_fragment_specifier)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #40107 + +error: aborting due to 4 previous errors