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:
commit
a78c9bee4d
@ -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",
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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;
|
||||
|
257
compiler/rustc_expand/src/mbe/diagnostics.rs
Normal file
257
compiler/rustc_expand/src/mbe/diagnostics.rs
Normal 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),),
|
||||
}
|
||||
}
|
@ -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),),
|
||||
}
|
||||
}
|
||||
|
@ -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"]
|
||||
|
@ -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`.
|
||||
|
@ -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> {}
|
||||
|
@ -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)]
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)]
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -347,6 +347,7 @@
|
||||
#![feature(stdsimd)]
|
||||
#![feature(test)]
|
||||
#![feature(trace_macros)]
|
||||
#![feature(get_many_mut)]
|
||||
//
|
||||
// Only used in tests/benchmarks:
|
||||
//
|
||||
|
@ -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)) }
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user