Auto merge of #104702 - Manishearth:rollup-75hagzd, r=Manishearth

Rollup of 7 pull requests

Successful merges:

 - #83608 (Add slice methods for indexing via an array of indices.)
 - #95583 (Deprecate the unstable `ptr_to_from_bits` feature)
 - #101655 (Make the Box one-liner more descriptive)
 - #102207 (Constify remaining `Layout` methods)
 - #103193 (mark sys_common::once::generic::Once::new const-stable)
 - #104622 (Use clang for the UEFI targets)
 - #104638 (Move macro_rules diagnostics to diagnostics module)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2022-11-22 07:15:48 +00:00
commit a78c9bee4d
22 changed files with 597 additions and 259 deletions

View File

@ -789,9 +789,9 @@ dependencies = [
[[package]]
name = "compiler_builtins"
version = "0.1.82"
version = "0.1.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18cd7635fea7bb481ea543b392789844c1ad581299da70184c7175ce3af76603"
checksum = "989b2c1ca6e90ad06fdc69d1d1862fa28d27a977be6d92ae2fa762cf61fe0b10"
dependencies = [
"cc",
"rustc-std-workspace-core",

View File

@ -1,7 +1,7 @@
use crate::base::*;
use crate::config::StripUnconfigured;
use crate::hygiene::SyntaxContext;
use crate::mbe::macro_rules::annotate_err_with_kind;
use crate::mbe::diagnostics::annotate_err_with_kind;
use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod};
use crate::placeholders::{placeholder, PlaceholderExpander};

View File

@ -3,6 +3,7 @@
//! why we call this module `mbe`. For external documentation, prefer the
//! official terminology: "declarative macros".
pub(crate) mod diagnostics;
pub(crate) mod macro_check;
pub(crate) mod macro_parser;
pub(crate) mod macro_rules;

View File

@ -0,0 +1,257 @@
use std::borrow::Cow;
use crate::base::{DummyResult, ExtCtxt, MacResult};
use crate::expand::{parse_ast_fragment, AstFragmentKind};
use crate::mbe::{
macro_parser::{MatcherLoc, NamedParseResult, ParseResult::*, TtParser},
macro_rules::{try_match_macro, Tracker},
};
use rustc_ast::token::{self, Token};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage};
use rustc_parse::parser::{Parser, Recovery};
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::Ident;
use rustc_span::Span;
use super::macro_rules::{parser_from_cx, NoopTracker};
pub(super) fn failed_to_match_macro<'cx>(
cx: &'cx mut ExtCtxt<'_>,
sp: Span,
def_span: Span,
name: Ident,
arg: TokenStream,
lhses: &[Vec<MatcherLoc>],
) -> Box<dyn MacResult + 'cx> {
let sess = &cx.sess.parse_sess;
// An error occurred, try the expansion again, tracking the expansion closely for better diagnostics.
let mut tracker = CollectTrackerAndEmitter::new(cx, sp);
let try_success_result = try_match_macro(sess, name, &arg, lhses, &mut tracker);
if try_success_result.is_ok() {
// Nonterminal parser recovery might turn failed matches into successful ones,
// but for that it must have emitted an error already
tracker.cx.sess.delay_span_bug(sp, "Macro matching returned a success on the second try");
}
if let Some(result) = tracker.result {
// An irrecoverable error occurred and has been emitted.
return result;
}
let Some((token, label, remaining_matcher)) = tracker.best_failure else {
return DummyResult::any(sp);
};
let span = token.span.substitute_dummy(sp);
let mut err = cx.struct_span_err(span, &parse_failure_msg(&token));
err.span_label(span, label);
if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) {
err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro");
}
annotate_doc_comment(&mut err, sess.source_map(), span);
if let Some(span) = remaining_matcher.span() {
err.span_note(span, format!("while trying to match {remaining_matcher}"));
} else {
err.note(format!("while trying to match {remaining_matcher}"));
}
// Check whether there's a missing comma in this macro call, like `println!("{}" a);`
if let Some((arg, comma_span)) = arg.add_comma() {
for lhs in lhses {
let parser = parser_from_cx(sess, arg.clone(), Recovery::Allowed);
let mut tt_parser = TtParser::new(name);
if let Success(_) =
tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, &mut NoopTracker)
{
if comma_span.is_dummy() {
err.note("you might be missing a comma");
} else {
err.span_suggestion_short(
comma_span,
"missing comma here",
", ",
Applicability::MachineApplicable,
);
}
}
}
}
err.emit();
cx.trace_macros_diag();
DummyResult::any(sp)
}
/// The tracker used for the slow error path that collects useful info for diagnostics.
struct CollectTrackerAndEmitter<'a, 'cx, 'matcher> {
cx: &'a mut ExtCtxt<'cx>,
remaining_matcher: Option<&'matcher MatcherLoc>,
/// Which arm's failure should we report? (the one furthest along)
best_failure: Option<(Token, &'static str, MatcherLoc)>,
root_span: Span,
result: Option<Box<dyn MacResult + 'cx>>,
}
impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, 'matcher> {
fn before_match_loc(&mut self, parser: &TtParser, matcher: &'matcher MatcherLoc) {
if self.remaining_matcher.is_none()
|| (parser.has_no_remaining_items_for_step() && *matcher != MatcherLoc::Eof)
{
self.remaining_matcher = Some(matcher);
}
}
fn after_arm(&mut self, result: &NamedParseResult) {
match result {
Success(_) => {
// Nonterminal parser recovery might turn failed matches into successful ones,
// but for that it must have emitted an error already
self.cx.sess.delay_span_bug(
self.root_span,
"should not collect detailed info for successful macro match",
);
}
Failure(token, msg) => match self.best_failure {
Some((ref best_token, _, _)) if best_token.span.lo() >= token.span.lo() => {}
_ => {
self.best_failure = Some((
token.clone(),
msg,
self.remaining_matcher
.expect("must have collected matcher already")
.clone(),
))
}
},
Error(err_sp, msg) => {
let span = err_sp.substitute_dummy(self.root_span);
self.cx.struct_span_err(span, msg).emit();
self.result = Some(DummyResult::any(span));
}
ErrorReported(_) => self.result = Some(DummyResult::any(self.root_span)),
}
}
fn description() -> &'static str {
"detailed"
}
fn recovery() -> Recovery {
Recovery::Allowed
}
}
impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx, '_> {
fn new(cx: &'a mut ExtCtxt<'cx>, root_span: Span) -> Self {
Self { cx, remaining_matcher: None, best_failure: None, root_span, result: None }
}
}
pub(super) fn emit_frag_parse_err(
mut e: DiagnosticBuilder<'_, rustc_errors::ErrorGuaranteed>,
parser: &Parser<'_>,
orig_parser: &mut Parser<'_>,
site_span: Span,
arm_span: Span,
kind: AstFragmentKind,
) {
// FIXME(davidtwco): avoid depending on the error message text
if parser.token == token::Eof
&& let DiagnosticMessage::Str(message) = &e.message[0].0
&& message.ends_with(", found `<eof>`")
{
let msg = &e.message[0];
e.message[0] = (
DiagnosticMessage::Str(format!(
"macro expansion ends with an incomplete expression: {}",
message.replace(", found `<eof>`", ""),
)),
msg.1,
);
if !e.span.is_dummy() {
// early end of macro arm (#52866)
e.replace_span_with(parser.token.span.shrink_to_hi());
}
}
if e.span.is_dummy() {
// Get around lack of span in error (#30128)
e.replace_span_with(site_span);
if !parser.sess.source_map().is_imported(arm_span) {
e.span_label(arm_span, "in this macro arm");
}
} else if parser.sess.source_map().is_imported(parser.token.span) {
e.span_label(site_span, "in this macro invocation");
}
match kind {
// Try a statement if an expression is wanted but failed and suggest adding `;` to call.
AstFragmentKind::Expr => match parse_ast_fragment(orig_parser, AstFragmentKind::Stmts) {
Err(err) => err.cancel(),
Ok(_) => {
e.note(
"the macro call doesn't expand to an expression, but it can expand to a statement",
);
e.span_suggestion_verbose(
site_span.shrink_to_hi(),
"add `;` to interpret the expansion as a statement",
";",
Applicability::MaybeIncorrect,
);
}
},
_ => annotate_err_with_kind(&mut e, kind, site_span),
};
e.emit();
}
pub(crate) fn annotate_err_with_kind(err: &mut Diagnostic, kind: AstFragmentKind, span: Span) {
match kind {
AstFragmentKind::Ty => {
err.span_label(span, "this macro call doesn't expand to a type");
}
AstFragmentKind::Pat => {
err.span_label(span, "this macro call doesn't expand to a pattern");
}
_ => {}
};
}
#[derive(Subdiagnostic)]
enum ExplainDocComment {
#[label(expand_explain_doc_comment_inner)]
Inner {
#[primary_span]
span: Span,
},
#[label(expand_explain_doc_comment_outer)]
Outer {
#[primary_span]
span: Span,
},
}
pub(super) fn annotate_doc_comment(err: &mut Diagnostic, sm: &SourceMap, span: Span) {
if let Ok(src) = sm.span_to_snippet(span) {
if src.starts_with("///") || src.starts_with("/**") {
err.subdiagnostic(ExplainDocComment::Outer { span });
} else if src.starts_with("//!") || src.starts_with("/*!") {
err.subdiagnostic(ExplainDocComment::Inner { span });
}
}
}
/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
/// other tokens, this is "unexpected token...".
pub(super) fn parse_failure_msg(tok: &Token) -> String {
match tok.kind {
token::Eof => "unexpected end of macro invocation".to_string(),
_ => format!("no rules expected the token `{}`", pprust::token_to_string(tok),),
}
}

View File

@ -2,6 +2,7 @@
use crate::base::{SyntaxExtension, SyntaxExtensionKind};
use crate::expand::{ensure_complete_parse, parse_ast_fragment, AstFragment, AstFragmentKind};
use crate::mbe;
use crate::mbe::diagnostics::{annotate_doc_comment, parse_failure_msg};
use crate::mbe::macro_check;
use crate::mbe::macro_parser::{Error, ErrorReported, Failure, Success, TtParser};
use crate::mbe::macro_parser::{MatchedSeq, MatchedTokenTree, MatcherLoc};
@ -14,9 +15,7 @@
use rustc_ast_pretty::pprust;
use rustc_attr::{self as attr, TransparencyError};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_errors::{
Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
};
use rustc_errors::{Applicability, ErrorGuaranteed};
use rustc_feature::Features;
use rustc_lint_defs::builtin::{
RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
@ -27,7 +26,6 @@
use rustc_session::Session;
use rustc_span::edition::Edition;
use rustc_span::hygiene::Transparency;
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{kw, sym, Ident, MacroRulesNormalizedIdent};
use rustc_span::Span;
@ -35,6 +33,7 @@
use std::collections::hash_map::Entry;
use std::{mem, slice};
use super::diagnostics;
use super::macro_parser::{NamedMatches, NamedParseResult};
pub(crate) struct ParserAnyMacro<'a> {
@ -51,74 +50,6 @@ pub(crate) struct ParserAnyMacro<'a> {
is_local: bool,
}
pub(crate) fn annotate_err_with_kind(err: &mut Diagnostic, kind: AstFragmentKind, span: Span) {
match kind {
AstFragmentKind::Ty => {
err.span_label(span, "this macro call doesn't expand to a type");
}
AstFragmentKind::Pat => {
err.span_label(span, "this macro call doesn't expand to a pattern");
}
_ => {}
};
}
fn emit_frag_parse_err(
mut e: DiagnosticBuilder<'_, rustc_errors::ErrorGuaranteed>,
parser: &Parser<'_>,
orig_parser: &mut Parser<'_>,
site_span: Span,
arm_span: Span,
kind: AstFragmentKind,
) {
// FIXME(davidtwco): avoid depending on the error message text
if parser.token == token::Eof
&& let DiagnosticMessage::Str(message) = &e.message[0].0
&& message.ends_with(", found `<eof>`")
{
let msg = &e.message[0];
e.message[0] = (
DiagnosticMessage::Str(format!(
"macro expansion ends with an incomplete expression: {}",
message.replace(", found `<eof>`", ""),
)),
msg.1,
);
if !e.span.is_dummy() {
// early end of macro arm (#52866)
e.replace_span_with(parser.token.span.shrink_to_hi());
}
}
if e.span.is_dummy() {
// Get around lack of span in error (#30128)
e.replace_span_with(site_span);
if !parser.sess.source_map().is_imported(arm_span) {
e.span_label(arm_span, "in this macro arm");
}
} else if parser.sess.source_map().is_imported(parser.token.span) {
e.span_label(site_span, "in this macro invocation");
}
match kind {
// Try a statement if an expression is wanted but failed and suggest adding `;` to call.
AstFragmentKind::Expr => match parse_ast_fragment(orig_parser, AstFragmentKind::Stmts) {
Err(err) => err.cancel(),
Ok(_) => {
e.note(
"the macro call doesn't expand to an expression, but it can expand to a statement",
);
e.span_suggestion_verbose(
site_span.shrink_to_hi(),
"add `;` to interpret the expansion as a statement",
";",
Applicability::MaybeIncorrect,
);
}
},
_ => annotate_err_with_kind(&mut e, kind, site_span),
};
e.emit();
}
impl<'a> ParserAnyMacro<'a> {
pub(crate) fn make(mut self: Box<ParserAnyMacro<'a>>, kind: AstFragmentKind) -> AstFragment {
let ParserAnyMacro {
@ -134,7 +65,7 @@ pub(crate) fn make(mut self: Box<ParserAnyMacro<'a>>, kind: AstFragmentKind) ->
let fragment = match parse_ast_fragment(parser, kind) {
Ok(f) => f,
Err(err) => {
emit_frag_parse_err(err, parser, snapshot, site_span, arm_span, kind);
diagnostics::emit_frag_parse_err(err, parser, snapshot, site_span, arm_span, kind);
return kind.dummy(site_span);
}
};
@ -224,7 +155,7 @@ pub(super) trait Tracker<'matcher> {
}
/// A noop tracker that is used in the hot path of the expansion, has zero overhead thanks to monomorphization.
struct NoopTracker;
pub(super) struct NoopTracker;
impl<'matcher> Tracker<'matcher> for NoopTracker {
fn before_match_loc(&mut self, _: &TtParser, _: &'matcher MatcherLoc) {}
@ -331,135 +262,10 @@ fn expand_macro<'cx>(
}
}
// An error occurred, try the expansion again, tracking the expansion closely for better diagnostics.
let mut tracker = CollectTrackerAndEmitter::new(cx, sp);
let try_success_result = try_match_macro(sess, name, &arg, lhses, &mut tracker);
if try_success_result.is_ok() {
// Nonterminal parser recovery might turn failed matches into successful ones,
// but for that it must have emitted an error already
tracker.cx.sess.delay_span_bug(sp, "Macro matching returned a success on the second try");
}
if let Some(result) = tracker.result {
// An irrecoverable error occurred and has been emitted.
return result;
}
let Some((token, label, remaining_matcher)) = tracker.best_failure else {
return DummyResult::any(sp);
};
let span = token.span.substitute_dummy(sp);
let mut err = cx.struct_span_err(span, &parse_failure_msg(&token));
err.span_label(span, label);
if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) {
err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro");
}
annotate_doc_comment(&mut err, sess.source_map(), span);
if let Some(span) = remaining_matcher.span() {
err.span_note(span, format!("while trying to match {remaining_matcher}"));
} else {
err.note(format!("while trying to match {remaining_matcher}"));
}
// Check whether there's a missing comma in this macro call, like `println!("{}" a);`
if let Some((arg, comma_span)) = arg.add_comma() {
for lhs in lhses {
let parser = parser_from_cx(sess, arg.clone(), Recovery::Allowed);
let mut tt_parser = TtParser::new(name);
if let Success(_) =
tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, &mut NoopTracker)
{
if comma_span.is_dummy() {
err.note("you might be missing a comma");
} else {
err.span_suggestion_short(
comma_span,
"missing comma here",
", ",
Applicability::MachineApplicable,
);
}
}
}
}
err.emit();
cx.trace_macros_diag();
DummyResult::any(sp)
diagnostics::failed_to_match_macro(cx, sp, def_span, name, arg, lhses)
}
/// The tracker used for the slow error path that collects useful info for diagnostics.
struct CollectTrackerAndEmitter<'a, 'cx, 'matcher> {
cx: &'a mut ExtCtxt<'cx>,
remaining_matcher: Option<&'matcher MatcherLoc>,
/// Which arm's failure should we report? (the one furthest along)
best_failure: Option<(Token, &'static str, MatcherLoc)>,
root_span: Span,
result: Option<Box<dyn MacResult + 'cx>>,
}
impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, 'matcher> {
fn before_match_loc(&mut self, parser: &TtParser, matcher: &'matcher MatcherLoc) {
if self.remaining_matcher.is_none()
|| (parser.has_no_remaining_items_for_step() && *matcher != MatcherLoc::Eof)
{
self.remaining_matcher = Some(matcher);
}
}
fn after_arm(&mut self, result: &NamedParseResult) {
match result {
Success(_) => {
// Nonterminal parser recovery might turn failed matches into successful ones,
// but for that it must have emitted an error already
self.cx.sess.delay_span_bug(
self.root_span,
"should not collect detailed info for successful macro match",
);
}
Failure(token, msg) => match self.best_failure {
Some((ref best_token, _, _)) if best_token.span.lo() >= token.span.lo() => {}
_ => {
self.best_failure = Some((
token.clone(),
msg,
self.remaining_matcher
.expect("must have collected matcher already")
.clone(),
))
}
},
Error(err_sp, msg) => {
let span = err_sp.substitute_dummy(self.root_span);
self.cx.struct_span_err(span, msg).emit();
self.result = Some(DummyResult::any(span));
}
ErrorReported(_) => self.result = Some(DummyResult::any(self.root_span)),
}
}
fn description() -> &'static str {
"detailed"
}
fn recovery() -> Recovery {
Recovery::Allowed
}
}
impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx, '_> {
fn new(cx: &'a mut ExtCtxt<'cx>, root_span: Span) -> Self {
Self { cx, remaining_matcher: None, best_failure: None, root_span, result: None }
}
}
enum CanRetry {
pub(super) enum CanRetry {
Yes,
/// We are not allowed to retry macro expansion as a fatal error has been emitted already.
No(ErrorGuaranteed),
@ -469,7 +275,7 @@ enum CanRetry {
/// and nothing if it failed. On failure, it's the callers job to use `track` accordingly to record all errors
/// correctly.
#[instrument(level = "debug", skip(sess, arg, lhses, track), fields(tracking = %T::description()))]
fn try_match_macro<'matcher, T: Tracker<'matcher>>(
pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
sess: &ParseSess,
name: Ident,
arg: &TokenStream,
@ -769,30 +575,6 @@ pub fn compile_declarative_macro(
(mk_syn_ext(expander), rule_spans)
}
#[derive(Subdiagnostic)]
enum ExplainDocComment {
#[label(expand_explain_doc_comment_inner)]
Inner {
#[primary_span]
span: Span,
},
#[label(expand_explain_doc_comment_outer)]
Outer {
#[primary_span]
span: Span,
},
}
fn annotate_doc_comment(err: &mut Diagnostic, sm: &SourceMap, span: Span) {
if let Ok(src) = sm.span_to_snippet(span) {
if src.starts_with("///") || src.starts_with("/**") {
err.subdiagnostic(ExplainDocComment::Outer { span });
} else if src.starts_with("//!") || src.starts_with("/*!") {
err.subdiagnostic(ExplainDocComment::Inner { span });
}
}
}
fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree) -> bool {
// lhs is going to be like TokenTree::Delimited(...), where the
// entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens.
@ -1577,15 +1359,6 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
}
}
fn parser_from_cx(sess: &ParseSess, tts: TokenStream, recovery: Recovery) -> Parser<'_> {
pub(super) fn parser_from_cx(sess: &ParseSess, tts: TokenStream, recovery: Recovery) -> Parser<'_> {
Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS).recovery(recovery)
}
/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
/// other tokens, this is "unexpected token...".
fn parse_failure_msg(tok: &Token) -> String {
match tok.kind {
token::Eof => "unexpected end of macro invocation".to_string(),
_ => format!("no rules expected the token `{}`", pprust::token_to_string(tok),),
}
}

View File

@ -187,7 +187,7 @@
mod thin;
/// A pointer type for heap allocation.
/// A pointer type that uniquely owns a heap allocation of type `T`.
///
/// See the [module-level documentation](../../std/boxed/index.html) for more.
#[lang = "owned_box"]

View File

@ -157,9 +157,10 @@ pub const fn new<T>() -> Self {
/// allocate backing structure for `T` (which could be a trait
/// or other unsized type like a slice).
#[stable(feature = "alloc_layout", since = "1.28.0")]
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[must_use]
#[inline]
pub fn for_value<T: ?Sized>(t: &T) -> Self {
pub const fn for_value<T: ?Sized>(t: &T) -> Self {
let (size, align) = (mem::size_of_val(t), mem::align_of_val(t));
// SAFETY: see rationale in `new` for why this is using the unsafe variant
unsafe { Layout::from_size_align_unchecked(size, align) }
@ -191,8 +192,9 @@ pub fn for_value<T: ?Sized>(t: &T) -> Self {
/// [trait object]: ../../book/ch17-02-trait-objects.html
/// [extern type]: ../../unstable-book/language-features/extern-types.html
#[unstable(feature = "layout_for_ptr", issue = "69835")]
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[must_use]
pub unsafe fn for_value_raw<T: ?Sized>(t: *const T) -> Self {
pub const unsafe fn for_value_raw<T: ?Sized>(t: *const T) -> Self {
// SAFETY: we pass along the prerequisites of these functions to the caller
let (size, align) = unsafe { (mem::size_of_val_raw(t), mem::align_of_val_raw(t)) };
// SAFETY: see rationale in `new` for why this is using the unsafe variant
@ -229,8 +231,9 @@ pub const fn dangling(&self) -> NonNull<u8> {
/// Returns an error if the combination of `self.size()` and the given
/// `align` violates the conditions listed in [`Layout::from_size_align`].
#[stable(feature = "alloc_layout_manipulation", since = "1.44.0")]
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[inline]
pub fn align_to(&self, align: usize) -> Result<Self, LayoutError> {
pub const fn align_to(&self, align: usize) -> Result<Self, LayoutError> {
Layout::from_size_align(self.size(), cmp::max(self.align(), align))
}
@ -287,10 +290,11 @@ pub const fn padding_needed_for(&self, align: usize) -> usize {
/// This is equivalent to adding the result of `padding_needed_for`
/// to the layout's current size.
#[stable(feature = "alloc_layout_manipulation", since = "1.44.0")]
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[must_use = "this returns a new `Layout`, \
without modifying the original"]
#[inline]
pub fn pad_to_align(&self) -> Layout {
pub const fn pad_to_align(&self) -> Layout {
let pad = self.padding_needed_for(self.align());
// This cannot overflow. Quoting from the invariant of Layout:
// > `size`, when rounded up to the nearest multiple of `align`,
@ -311,8 +315,9 @@ pub fn pad_to_align(&self) -> Layout {
///
/// On arithmetic overflow, returns `LayoutError`.
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[inline]
pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError> {
pub const fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError> {
// This cannot overflow. Quoting from the invariant of Layout:
// > `size`, when rounded up to the nearest multiple of `align`,
// > must not overflow isize (i.e., the rounded value must be
@ -321,7 +326,8 @@ pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError> {
let alloc_size = padded_size.checked_mul(n).ok_or(LayoutError)?;
// The safe constructor is called here to enforce the isize size limit.
Layout::from_size_alignment(alloc_size, self.align).map(|layout| (layout, padded_size))
let layout = Layout::from_size_alignment(alloc_size, self.align)?;
Ok((layout, padded_size))
}
/// Creates a layout describing the record for `self` followed by
@ -370,8 +376,9 @@ pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError> {
/// # assert_eq!(repr_c(&[u64, u32, u16, u32]), Ok((s, vec![0, 8, 12, 16])));
/// ```
#[stable(feature = "alloc_layout_manipulation", since = "1.44.0")]
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[inline]
pub fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError> {
pub const fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError> {
let new_align = cmp::max(self.align, next.align);
let pad = self.padding_needed_for(next.align());
@ -396,8 +403,9 @@ pub fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError> {
///
/// On arithmetic overflow, returns `LayoutError`.
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[inline]
pub fn repeat_packed(&self, n: usize) -> Result<Self, LayoutError> {
pub const fn repeat_packed(&self, n: usize) -> Result<Self, LayoutError> {
let size = self.size().checked_mul(n).ok_or(LayoutError)?;
// The safe constructor is called here to enforce the isize size limit.
Layout::from_size_alignment(size, self.align)
@ -410,8 +418,9 @@ pub fn repeat_packed(&self, n: usize) -> Result<Self, LayoutError> {
///
/// On arithmetic overflow, returns `LayoutError`.
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[inline]
pub fn extend_packed(&self, next: Self) -> Result<Self, LayoutError> {
pub const fn extend_packed(&self, next: Self) -> Result<Self, LayoutError> {
let new_size = self.size().checked_add(next.size()).ok_or(LayoutError)?;
// The safe constructor is called here to enforce the isize size limit.
Layout::from_size_alignment(new_size, self.align)
@ -422,13 +431,18 @@ pub fn extend_packed(&self, next: Self) -> Result<Self, LayoutError> {
/// On arithmetic overflow or when the total size would exceed
/// `isize::MAX`, returns `LayoutError`.
#[stable(feature = "alloc_layout_manipulation", since = "1.44.0")]
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[inline]
pub fn array<T>(n: usize) -> Result<Self, LayoutError> {
pub const fn array<T>(n: usize) -> Result<Self, LayoutError> {
// Reduce the amount of code we need to monomorphize per `T`.
return inner(mem::size_of::<T>(), Alignment::of::<T>(), n);
#[inline]
fn inner(element_size: usize, align: Alignment, n: usize) -> Result<Layout, LayoutError> {
const fn inner(
element_size: usize,
align: Alignment,
n: usize,
) -> Result<Layout, LayoutError> {
// We need to check two things about the size:
// - That the total size won't overflow a `usize`, and
// - That the total size still fits in an `isize`.

View File

@ -506,3 +506,6 @@ fn description(&self) -> &str {
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
impl Error for crate::ffi::FromBytesUntilNulError {}
#[unstable(feature = "get_many_mut", issue = "104642")]
impl<const N: usize> Error for crate::slice::GetManyMutError<N> {}

View File

@ -99,6 +99,8 @@
// Library features:
#![feature(const_align_offset)]
#![feature(const_align_of_val)]
#![feature(const_align_of_val_raw)]
#![feature(const_alloc_layout)]
#![feature(const_arguments_as_str)]
#![feature(const_array_into_iter_constructors)]
#![feature(const_bigint_helper_methods)]
@ -141,6 +143,7 @@
#![feature(const_ptr_write)]
#![feature(const_raw_ptr_comparison)]
#![feature(const_size_of_val)]
#![feature(const_size_of_val_raw)]
#![feature(const_slice_from_raw_parts_mut)]
#![feature(const_slice_ptr_len)]
#![feature(const_slice_split_at_mut)]

View File

@ -9,7 +9,9 @@
/// Note that particularly large alignments, while representable in this type,
/// are likely not to be supported by actual allocators and linkers.
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
#[derive(Copy, Clone, Eq, PartialEq)]
#[derive(Copy, Clone, Eq)]
#[cfg_attr(bootstrap, derive(PartialEq))]
#[cfg_attr(not(bootstrap), derive_const(PartialEq))]
#[repr(transparent)]
pub struct Alignment(AlignmentEnum);
@ -167,16 +169,18 @@ fn from(align: Alignment) -> usize {
}
}
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
impl cmp::Ord for Alignment {
impl const cmp::Ord for Alignment {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.as_nonzero().cmp(&other.as_nonzero())
self.as_nonzero().get().cmp(&other.as_nonzero().get())
}
}
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
impl cmp::PartialOrd for Alignment {
impl const cmp::PartialOrd for Alignment {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
@ -198,7 +202,9 @@ fn hash<H: hash::Hasher>(&self, state: &mut H) {
#[cfg(target_pointer_width = "64")]
type AlignmentEnum = AlignmentEnum64;
#[derive(Copy, Clone, Eq, PartialEq)]
#[derive(Copy, Clone, Eq)]
#[cfg_attr(bootstrap, derive(PartialEq))]
#[cfg_attr(not(bootstrap), derive_const(PartialEq))]
#[repr(u16)]
enum AlignmentEnum16 {
_Align1Shl0 = 1 << 0,
@ -219,7 +225,9 @@ enum AlignmentEnum16 {
_Align1Shl15 = 1 << 15,
}
#[derive(Copy, Clone, Eq, PartialEq)]
#[derive(Copy, Clone, Eq)]
#[cfg_attr(bootstrap, derive(PartialEq))]
#[cfg_attr(not(bootstrap), derive_const(PartialEq))]
#[repr(u32)]
enum AlignmentEnum32 {
_Align1Shl0 = 1 << 0,
@ -256,7 +264,9 @@ enum AlignmentEnum32 {
_Align1Shl31 = 1 << 31,
}
#[derive(Copy, Clone, Eq, PartialEq)]
#[derive(Copy, Clone, Eq)]
#[cfg_attr(bootstrap, derive(PartialEq))]
#[cfg_attr(not(bootstrap), derive_const(PartialEq))]
#[repr(u64)]
enum AlignmentEnum64 {
_Align1Shl0 = 1 << 0,

View File

@ -119,6 +119,11 @@ pub const fn cast_mut(self) -> *mut T {
/// assert_eq!(p1.to_bits() - p0.to_bits(), 4);
/// ```
#[unstable(feature = "ptr_to_from_bits", issue = "91126")]
#[deprecated(
since = "1.67",
note = "replaced by the `exposed_addr` method, or update your code \
to follow the strict provenance rules using its APIs"
)]
pub fn to_bits(self) -> usize
where
T: Sized,
@ -140,6 +145,11 @@ pub fn to_bits(self) -> usize
/// assert_eq!(<*const u8>::from_bits(1), dangling);
/// ```
#[unstable(feature = "ptr_to_from_bits", issue = "91126")]
#[deprecated(
since = "1.67",
note = "replaced by the `ptr::from_exposed_addr` function, or update \
your code to follow the strict provenance rules using its APIs"
)]
#[allow(fuzzy_provenance_casts)] // this is an unstable and semi-deprecated cast function
pub fn from_bits(bits: usize) -> Self
where

View File

@ -125,6 +125,11 @@ pub const fn cast_const(self) -> *const T {
/// assert_eq!(p1.to_bits() - p0.to_bits(), 4);
/// ```
#[unstable(feature = "ptr_to_from_bits", issue = "91126")]
#[deprecated(
since = "1.67",
note = "replaced by the `exposed_addr` method, or update your code \
to follow the strict provenance rules using its APIs"
)]
pub fn to_bits(self) -> usize
where
T: Sized,
@ -146,6 +151,11 @@ pub fn to_bits(self) -> usize
/// assert_eq!(<*mut u8>::from_bits(1), dangling);
/// ```
#[unstable(feature = "ptr_to_from_bits", issue = "91126")]
#[deprecated(
since = "1.67",
note = "replaced by the `ptr::from_exposed_addr_mut` function, or \
update your code to follow the strict provenance rules using its APIs"
)]
#[allow(fuzzy_provenance_casts)] // this is an unstable and semi-deprecated cast function
pub fn from_bits(bits: usize) -> Self
where

View File

@ -7,6 +7,7 @@
#![stable(feature = "rust1", since = "1.0.0")]
use crate::cmp::Ordering::{self, Greater, Less};
use crate::fmt;
use crate::intrinsics::{assert_unsafe_precondition, exact_div};
use crate::marker::Copy;
use crate::mem::{self, SizedTypeProperties};
@ -4082,6 +4083,88 @@ pub fn take_last_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> {
*self = rem;
Some(last)
}
/// Returns mutable references to many indices at once, without doing any checks.
///
/// For a safe alternative see [`get_many_mut`].
///
/// # Safety
///
/// Calling this method with overlapping or out-of-bounds indices is *[undefined behavior]*
/// even if the resulting references are not used.
///
/// # Examples
///
/// ```
/// #![feature(get_many_mut)]
///
/// let x = &mut [1, 2, 4];
///
/// unsafe {
/// let [a, b] = x.get_many_unchecked_mut([0, 2]);
/// *a *= 10;
/// *b *= 100;
/// }
/// assert_eq!(x, &[10, 2, 400]);
/// ```
///
/// [`get_many_mut`]: slice::get_many_mut
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
#[unstable(feature = "get_many_mut", issue = "104642")]
#[inline]
pub unsafe fn get_many_unchecked_mut<const N: usize>(
&mut self,
indices: [usize; N],
) -> [&mut T; N] {
// NB: This implementation is written as it is because any variation of
// `indices.map(|i| self.get_unchecked_mut(i))` would make miri unhappy,
// or generate worse code otherwise. This is also why we need to go
// through a raw pointer here.
let slice: *mut [T] = self;
let mut arr: mem::MaybeUninit<[&mut T; N]> = mem::MaybeUninit::uninit();
let arr_ptr = arr.as_mut_ptr();
// SAFETY: We expect `indices` to contain disjunct values that are
// in bounds of `self`.
unsafe {
for i in 0..N {
let idx = *indices.get_unchecked(i);
*(*arr_ptr).get_unchecked_mut(i) = &mut *slice.get_unchecked_mut(idx);
}
arr.assume_init()
}
}
/// Returns mutable references to many indices at once.
///
/// Returns an error if any index is out-of-bounds, or if the same index was
/// passed more than once.
///
/// # Examples
///
/// ```
/// #![feature(get_many_mut)]
///
/// let v = &mut [1, 2, 3];
/// if let Ok([a, b]) = v.get_many_mut([0, 2]) {
/// *a = 413;
/// *b = 612;
/// }
/// assert_eq!(v, &[413, 2, 612]);
/// ```
#[unstable(feature = "get_many_mut", issue = "104642")]
#[inline]
pub fn get_many_mut<const N: usize>(
&mut self,
indices: [usize; N],
) -> Result<[&mut T; N], GetManyMutError<N>> {
if !get_many_check_valid(&indices, self.len()) {
return Err(GetManyMutError { _private: () });
}
// SAFETY: The `get_many_check_valid()` call checked that all indices
// are disjunct and in bounds.
unsafe { Ok(self.get_many_unchecked_mut(indices)) }
}
}
impl<T, const N: usize> [[T; N]] {
@ -4304,3 +4387,56 @@ fn as_slice(&self) -> &[Self::Item] {
self
}
}
/// This checks every index against each other, and against `len`.
///
/// This will do `binomial(N + 1, 2) = N * (N + 1) / 2 = 0, 1, 3, 6, 10, ..`
/// comparison operations.
fn get_many_check_valid<const N: usize>(indices: &[usize; N], len: usize) -> bool {
// NB: The optimzer should inline the loops into a sequence
// of instructions without additional branching.
let mut valid = true;
for (i, &idx) in indices.iter().enumerate() {
valid &= idx < len;
for &idx2 in &indices[..i] {
valid &= idx != idx2;
}
}
valid
}
/// The error type returned by [`get_many_mut<N>`][`slice::get_many_mut`].
///
/// It indicates one of two possible errors:
/// - An index is out-of-bounds.
/// - The same index appeared multiple times in the array.
///
/// # Examples
///
/// ```
/// #![feature(get_many_mut)]
///
/// let v = &mut [1, 2, 3];
/// assert!(v.get_many_mut([0, 999]).is_err());
/// assert!(v.get_many_mut([1, 1]).is_err());
/// ```
#[unstable(feature = "get_many_mut", issue = "104642")]
// NB: The N here is there to be forward-compatible with adding more details
// to the error type at a later point
pub struct GetManyMutError<const N: usize> {
_private: (),
}
#[unstable(feature = "get_many_mut", issue = "104642")]
impl<const N: usize> fmt::Debug for GetManyMutError<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GetManyMutError").finish_non_exhaustive()
}
}
#[unstable(feature = "get_many_mut", issue = "104642")]
impl<const N: usize> fmt::Display for GetManyMutError<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt("an index is out of bounds or appeared multiple times in the array", f)
}
}

View File

@ -108,6 +108,7 @@
#![feature(provide_any)]
#![feature(utf8_chunks)]
#![feature(is_ascii_octdigit)]
#![feature(get_many_mut)]
#![deny(unsafe_op_in_unsafe_fn)]
#![deny(fuzzy_provenance_casts)]

View File

@ -2595,3 +2595,63 @@ fn test_flatten_mut_size_overflow() {
let x = &mut [[(); usize::MAX]; 2][..];
let _ = x.flatten_mut();
}
#[test]
fn test_get_many_mut_normal_2() {
let mut v = vec![1, 2, 3, 4, 5];
let [a, b] = v.get_many_mut([3, 0]).unwrap();
*a += 10;
*b += 100;
assert_eq!(v, vec![101, 2, 3, 14, 5]);
}
#[test]
fn test_get_many_mut_normal_3() {
let mut v = vec![1, 2, 3, 4, 5];
let [a, b, c] = v.get_many_mut([0, 4, 2]).unwrap();
*a += 10;
*b += 100;
*c += 1000;
assert_eq!(v, vec![11, 2, 1003, 4, 105]);
}
#[test]
fn test_get_many_mut_empty() {
let mut v = vec![1, 2, 3, 4, 5];
let [] = v.get_many_mut([]).unwrap();
assert_eq!(v, vec![1, 2, 3, 4, 5]);
}
#[test]
fn test_get_many_mut_single_first() {
let mut v = vec![1, 2, 3, 4, 5];
let [a] = v.get_many_mut([0]).unwrap();
*a += 10;
assert_eq!(v, vec![11, 2, 3, 4, 5]);
}
#[test]
fn test_get_many_mut_single_last() {
let mut v = vec![1, 2, 3, 4, 5];
let [a] = v.get_many_mut([4]).unwrap();
*a += 10;
assert_eq!(v, vec![1, 2, 3, 4, 15]);
}
#[test]
fn test_get_many_mut_oob_nonempty() {
let mut v = vec![1, 2, 3, 4, 5];
assert!(v.get_many_mut([5]).is_err());
}
#[test]
fn test_get_many_mut_oob_empty() {
let mut v: Vec<i32> = vec![];
assert!(v.get_many_mut([0]).is_err());
}
#[test]
fn test_get_many_mut_duplicate() {
let mut v = vec![1, 2, 3, 4, 5];
assert!(v.get_many_mut([1, 3, 3, 4]).is_err());
}

View File

@ -347,6 +347,7 @@
#![feature(stdsimd)]
#![feature(test)]
#![feature(trace_macros)]
#![feature(get_many_mut)]
//
// Only used in tests/benchmarks:
//

View File

@ -107,6 +107,7 @@ struct WaiterQueue<'a> {
impl Once {
#[inline]
#[rustc_const_stable(feature = "const_once_new", since = "1.32.0")]
pub const fn new() -> Once {
Once { state_and_queue: AtomicPtr::new(ptr::invalid_mut(INCOMPLETE)) }
}

View File

@ -53,6 +53,7 @@ dependencies = [
"hex",
"ignore",
"libc",
"object",
"once_cell",
"opener",
"pretty_assertions",
@ -400,6 +401,15 @@ dependencies = [
"libc",
]
[[package]]
name = "object"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.12.0"

View File

@ -42,6 +42,7 @@ getopts = "0.2.19"
cc = "1.0.69"
libc = "0.2"
hex = "0.4"
object = { version = "0.29.0", default-features = false, features = ["archive", "coff", "read_core", "unaligned"] }
serde = { version = "1.0.8", features = ["derive"] }
serde_json = "1.0.2"
sha2 = "0.10"

View File

@ -10,10 +10,14 @@
use std::collections::HashSet;
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use object::read::archive::ArchiveFile;
use object::BinaryFormat;
use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
use crate::cache::{Interned, INTERNER};
use crate::channel;
@ -555,6 +559,39 @@ fn skip_host_target_lib(builder: &Builder<'_>, compiler: Compiler) -> bool {
}
}
/// Check that all objects in rlibs for UEFI targets are COFF. This
/// ensures that the C compiler isn't producing ELF objects, which would
/// not link correctly with the COFF objects.
fn verify_uefi_rlib_format(builder: &Builder<'_>, target: TargetSelection, stamp: &Path) {
if !target.ends_with("-uefi") {
return;
}
for (path, _) in builder.read_stamp_file(stamp) {
if path.extension() != Some(OsStr::new("rlib")) {
continue;
}
let data = t!(fs::read(&path));
let data = data.as_slice();
let archive = t!(ArchiveFile::parse(data));
for member in archive.members() {
let member = t!(member);
let member_data = t!(member.data(data));
let is_coff = match object::File::parse(member_data) {
Ok(member_file) => member_file.format() == BinaryFormat::Coff,
Err(_) => false,
};
if !is_coff {
let member_name = String::from_utf8_lossy(member.name());
panic!("member {} in {} is not COFF", member_name, path.display());
}
}
}
}
/// Copy stamped files into an image's `target/lib` directory.
fn copy_target_libs(builder: &Builder<'_>, target: TargetSelection, image: &Path, stamp: &Path) {
let dst = image.join("lib/rustlib").join(target.triple).join("lib");
@ -610,6 +647,7 @@ fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
let stamp = compile::libstd_stamp(builder, compiler_to_use, target);
verify_uefi_rlib_format(builder, target, &stamp);
copy_target_libs(builder, target, &tarball.image_dir(), &stamp);
Some(tarball.generate())

View File

@ -61,6 +61,12 @@ ENV \
AR_i686_unknown_freebsd=i686-unknown-freebsd12-ar \
CC_i686_unknown_freebsd=i686-unknown-freebsd12-clang \
CXX_i686_unknown_freebsd=i686-unknown-freebsd12-clang++ \
CC_aarch64_unknown_uefi=clang-11 \
CXX_aarch64_unknown_uefi=clang++-11 \
CC_i686_unknown_uefi=clang-11 \
CXX_i686_unknown_uefi=clang++-11 \
CC_x86_64_unknown_uefi=clang-11 \
CXX_x86_64_unknown_uefi=clang++-11 \
CC=gcc-8 \
CXX=g++-8

View File

@ -1,6 +1,7 @@
FROM ubuntu:20.04
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
clang-11 \
g++ \
make \
ninja-build \
@ -67,7 +68,9 @@ ENV MUSL_TARGETS=x86_64-unknown-linux-musl \
ENV MUSL_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $MUSL_TARGETS
COPY host-x86_64/test-various/uefi_qemu_test /uefi_qemu_test
ENV UEFI_TARGETS=x86_64-unknown-uefi
ENV UEFI_TARGETS=x86_64-unknown-uefi \
CC_x86_64_unknown_uefi=clang-11 \
CXX_x86_64_unknown_uefi=clang++-11
ENV UEFI_SCRIPT python3 /checkout/x.py --stage 2 build --host='' --target $UEFI_TARGETS && \
python3 -u /uefi_qemu_test/run.py