Auto merge of #126878 - matthiaskrgr:rollup-oufemqp, r=matthiaskrgr

Rollup of 9 pull requests

Successful merges:

 - #126230 (tidy: skip submodules if not present for non-CI environments)
 - #126612 (Update outdated README in build-manifest.)
 - #126616 (less bootstrap warnings)
 - #126663 (remove `GIT_DIR` handling in pre-push hook)
 - #126830 (make unsized_fn_params an internal feature)
 - #126833 (don't ICE when encountering an extern type field during validation)
 - #126837 (delegation: Do not crash on qpaths without a trait)
 - #126851 (Rework pattern and expression nonterminal kinds.)
 - #126862 (Add needs-symlink directive to compiletest)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-06-23 21:23:55 +00:00
commit bcf94dec5b
43 changed files with 344 additions and 212 deletions

View file

@ -5678,6 +5678,7 @@ dependencies = [
name = "tidy"
version = "0.1.0"
dependencies = [
"build_helper",
"cargo_metadata 0.15.4",
"fluent-syntax",
"ignore",

View file

@ -1,6 +1,8 @@
pub use BinOpToken::*;
pub use LitKind::*;
pub use Nonterminal::*;
pub use NtExprKind::*;
pub use NtPatKind::*;
pub use TokenKind::*;
use crate::ast;
@ -871,6 +873,27 @@ fn eq(&self, rhs: &TokenKind) -> bool {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable)]
pub enum NtPatKind {
// Matches or-patterns. Was written using `pat` in edition 2021 or later.
PatWithOr,
// Doesn't match or-patterns.
// - `inferred`: was written using `pat` in edition 2015 or 2018.
// - `!inferred`: was written using `pat_param`.
PatParam { inferred: bool },
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable)]
pub enum NtExprKind {
// Matches expressions using the post-edition 2024. Was written using
// `expr` in edition 2024 or later.
Expr,
// Matches expressions using the pre-edition 2024 rules.
// - `inferred`: was written using `expr` in edition 2021 or earlier.
// - `!inferred`: was written using `expr_2021`.
Expr2021 { inferred: bool },
}
#[derive(Clone, Encodable, Decodable)]
/// For interpolation during macro expansion.
pub enum Nonterminal {
@ -892,19 +915,8 @@ pub enum NonterminalKind {
Item,
Block,
Stmt,
PatParam {
/// Keep track of whether the user used `:pat_param` or `:pat` and we inferred it from the
/// edition of the span. This is used for diagnostics.
inferred: bool,
},
PatWithOr,
Expr,
/// Matches an expression using the rules from edition 2021 and earlier.
Expr2021 {
/// Keep track of whether the user used `:expr` or `:expr_2021` and we inferred it from the
/// edition of the span. This is used for diagnostics AND feature gating.
inferred: bool,
},
Pat(NtPatKind),
Expr(NtExprKind),
Ty,
Ident,
Lifetime,
@ -926,20 +938,22 @@ pub fn from_symbol(
sym::item => NonterminalKind::Item,
sym::block => NonterminalKind::Block,
sym::stmt => NonterminalKind::Stmt,
sym::pat => match edition() {
Edition::Edition2015 | Edition::Edition2018 => {
NonterminalKind::PatParam { inferred: true }
sym::pat => {
if edition().at_least_rust_2021() {
NonterminalKind::Pat(PatWithOr)
} else {
NonterminalKind::Pat(PatParam { inferred: true })
}
Edition::Edition2021 | Edition::Edition2024 => NonterminalKind::PatWithOr,
},
sym::pat_param => NonterminalKind::PatParam { inferred: false },
sym::expr => match edition() {
Edition::Edition2015 | Edition::Edition2018 | Edition::Edition2021 => {
NonterminalKind::Expr2021 { inferred: true }
}
sym::pat_param => NonterminalKind::Pat(PatParam { inferred: false }),
sym::expr => {
if edition().at_least_rust_2024() {
NonterminalKind::Expr(Expr)
} else {
NonterminalKind::Expr(Expr2021 { inferred: true })
}
Edition::Edition2024 => NonterminalKind::Expr,
},
sym::expr_2021 => NonterminalKind::Expr2021 { inferred: false },
}
sym::expr_2021 => NonterminalKind::Expr(Expr2021 { inferred: false }),
sym::ty => NonterminalKind::Ty,
sym::ident => NonterminalKind::Ident,
sym::lifetime => NonterminalKind::Lifetime,
@ -951,15 +965,16 @@ pub fn from_symbol(
_ => return None,
})
}
fn symbol(self) -> Symbol {
match self {
NonterminalKind::Item => sym::item,
NonterminalKind::Block => sym::block,
NonterminalKind::Stmt => sym::stmt,
NonterminalKind::PatParam { inferred: false } => sym::pat_param,
NonterminalKind::PatParam { inferred: true } | NonterminalKind::PatWithOr => sym::pat,
NonterminalKind::Expr | NonterminalKind::Expr2021 { inferred: true } => sym::expr,
NonterminalKind::Expr2021 { inferred: false } => sym::expr_2021,
NonterminalKind::Pat(PatParam { inferred: true } | PatWithOr) => sym::pat,
NonterminalKind::Pat(PatParam { inferred: false }) => sym::pat_param,
NonterminalKind::Expr(Expr2021 { inferred: true } | Expr) => sym::expr,
NonterminalKind::Expr(Expr2021 { inferred: false }) => sym::expr_2021,
NonterminalKind::Ty => sym::ty,
NonterminalKind::Ident => sym::ident,
NonterminalKind::Lifetime => sym::lifetime,

View file

@ -89,6 +89,8 @@ const_eval_exact_div_has_remainder =
const_eval_extern_static =
cannot access extern static ({$did})
const_eval_extern_type_field = `extern type` field does not have a known offset
const_eval_fn_ptr_call =
function pointers need an RFC before allowed to be called in {const_eval_const_context}s
const_eval_for_loop_into_iter_non_const =

View file

@ -386,33 +386,8 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(
CompileTimeMachine::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
);
let res = ecx.load_mir(cid.instance.def, cid.promoted);
res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)).map_err(|error| {
let (error, backtrace) = error.into_parts();
backtrace.print_backtrace();
let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) {
("static", String::new())
} else {
// If the current item has generics, we'd like to enrich the message with the
// instance and its args: to show the actual compile-time values, in addition to
// the expression, leading to the const eval error.
let instance = &cid.instance;
if !instance.args.is_empty() {
let instance = with_no_trimmed_paths!(instance.to_string());
("const_with_path", instance)
} else {
("const", String::new())
}
};
super::report(
*ecx.tcx,
error,
DUMMY_SP,
|| super::get_span_and_frames(ecx.tcx, ecx.stack()),
|span, frames| ConstEvalError { span, error_kind: kind, instance, frame_notes: frames },
)
})
res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body))
.map_err(|error| report_eval_error(&ecx, cid, error))
}
#[inline(always)]
@ -438,23 +413,60 @@ fn const_validate_mplace<'tcx>(
ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)
// Instead of just reporting the `InterpError` via the usual machinery, we give a more targeted
// error about the validation failure.
.map_err(|error| report_validation_error(&ecx, error, alloc_id))?;
.map_err(|error| report_validation_error(&ecx, cid, error, alloc_id))?;
inner = true;
}
Ok(())
}
#[inline(always)]
fn report_validation_error<'tcx>(
#[inline(never)]
fn report_eval_error<'tcx>(
ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
cid: GlobalId<'tcx>,
error: InterpErrorInfo<'tcx>,
alloc_id: AllocId,
) -> ErrorHandled {
let (error, backtrace) = error.into_parts();
backtrace.print_backtrace();
let ub_note = matches!(error, InterpError::UndefinedBehavior(_)).then(|| {});
let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) {
("static", String::new())
} else {
// If the current item has generics, we'd like to enrich the message with the
// instance and its args: to show the actual compile-time values, in addition to
// the expression, leading to the const eval error.
let instance = &cid.instance;
if !instance.args.is_empty() {
let instance = with_no_trimmed_paths!(instance.to_string());
("const_with_path", instance)
} else {
("const", String::new())
}
};
super::report(
*ecx.tcx,
error,
DUMMY_SP,
|| super::get_span_and_frames(ecx.tcx, ecx.stack()),
|span, frames| ConstEvalError { span, error_kind: kind, instance, frame_notes: frames },
)
}
#[inline(never)]
fn report_validation_error<'tcx>(
ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
cid: GlobalId<'tcx>,
error: InterpErrorInfo<'tcx>,
alloc_id: AllocId,
) -> ErrorHandled {
if !matches!(error.kind(), InterpError::UndefinedBehavior(_)) {
// Some other error happened during validation, e.g. an unsupported operation.
return report_eval_error(ecx, cid, error);
}
let (error, backtrace) = error.into_parts();
backtrace.print_backtrace();
let bytes = ecx.print_alloc_bytes_for_diagnostics(alloc_id);
let (size, align, _) = ecx.get_alloc_info(alloc_id);
@ -465,6 +477,6 @@ fn report_validation_error<'tcx>(
error,
DUMMY_SP,
|| crate::const_eval::get_span_and_frames(ecx.tcx, ecx.stack()),
move |span, frames| errors::ValidationFailure { span, ub_note, frames, raw_bytes },
move |span, frames| errors::ValidationFailure { span, ub_note: (), frames, raw_bytes },
)
}

View file

@ -425,7 +425,7 @@ pub struct ValidationFailure {
#[primary_span]
pub span: Span,
#[note(const_eval_validation_failure_note)]
pub ub_note: Option<()>,
pub ub_note: (),
#[subdiagnostic]
pub frames: Vec<FrameNote>,
#[subdiagnostic]
@ -825,6 +825,7 @@ fn diagnostic_message(&self) -> DiagMessage {
use crate::fluent_generated::*;
match self {
UnsupportedOpInfo::Unsupported(s) => s.clone().into(),
UnsupportedOpInfo::ExternTypeField => const_eval_extern_type_field,
UnsupportedOpInfo::UnsizedLocal => const_eval_unsized_local,
UnsupportedOpInfo::OverwritePartialPointer(_) => const_eval_partial_pointer_overwrite,
UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_copy,
@ -845,7 +846,10 @@ fn add_args<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
// `ReadPointerAsInt(Some(info))` is never printed anyway, it only serves as an error to
// be further processed by validity checking which then turns it into something nice to
// print. So it's not worth the effort of having diagnostics that can print the `info`.
UnsizedLocal | Unsupported(_) | ReadPointerAsInt(_) => {}
UnsizedLocal
| UnsupportedOpInfo::ExternTypeField
| Unsupported(_)
| ReadPointerAsInt(_) => {}
OverwritePartialPointer(ptr) | ReadPartialPointer(ptr) => {
diag.arg("ptr", ptr);
}

View file

@ -21,7 +21,7 @@
use tracing::{debug, instrument};
use super::{
throw_ub, throw_unsup_format, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy,
throw_ub, throw_unsup, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy,
Provenance, Scalar,
};
@ -186,8 +186,8 @@ pub fn project_field<P: Projectable<'tcx, M::Provenance>>(
(base_meta, offset)
}
None => {
// We don't know the alignment of this field, so we cannot adjust.
throw_unsup_format!("`extern type` does not have a known offset")
// We cannot know the alignment of this field, so we cannot adjust.
throw_unsup!(ExternTypeField)
}
}
} else {

View file

@ -5,6 +5,7 @@
//! to be const-safe.
use std::fmt::Write;
use std::hash::Hash;
use std::num::NonZero;
use either::{Left, Right};
@ -17,7 +18,8 @@
use rustc_middle::bug;
use rustc_middle::mir::interpret::{
ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance,
ValidationErrorInfo, ValidationErrorKind, ValidationErrorKind::*,
UnsupportedOpInfo, ValidationErrorInfo,
ValidationErrorKind::{self, *},
};
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, Ty};
@ -26,8 +28,6 @@
Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
};
use std::hash::Hash;
use super::{
err_ub, format_interp_error, machine::AllocMap, throw_ub, AllocId, AllocKind, CheckInAllocMsg,
GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy,
@ -1028,7 +1028,9 @@ fn validate_operand_internal(
Err(err)
if matches!(
err.kind(),
err_ub!(ValidationError { .. }) | InterpError::InvalidProgram(_)
err_ub!(ValidationError { .. })
| InterpError::InvalidProgram(_)
| InterpError::Unsupported(UnsupportedOpInfo::ExternTypeField)
) =>
{
Err(err)

View file

@ -61,6 +61,9 @@ expand_feature_removed =
expand_glob_delegation_outside_impls =
glob delegation is only supported in impls
expand_glob_delegation_traitless_qpath =
qualified path without a trait in glob delegation
expand_helper_attribute_name_invalid =
`{$name}` cannot be a name of derive helper attribute

View file

@ -449,6 +449,13 @@ pub(crate) struct GlobDelegationOutsideImpls {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(expand_glob_delegation_traitless_qpath)]
pub(crate) struct GlobDelegationTraitlessQpath {
#[primary_span]
pub span: Span,
}
// This used to be the `proc_macro_back_compat` lint (#83125). It was later
// turned into a hard error.
#[derive(Diagnostic)]

View file

@ -1,8 +1,9 @@
use crate::base::*;
use crate::config::StripUnconfigured;
use crate::errors::{
EmptyDelegationMac, GlobDelegationOutsideImpls, IncompleteParse, RecursionLimitReached,
RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue, WrongFragmentKind,
EmptyDelegationMac, GlobDelegationOutsideImpls, GlobDelegationTraitlessQpath, IncompleteParse,
RecursionLimitReached, RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue,
WrongFragmentKind,
};
use crate::mbe::diagnostics::annotate_err_with_kind;
use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod};
@ -1989,6 +1990,8 @@ fn flat_map_node<Node: InvocationCollectorNode<OutputTy: Default>>(
}
None if let Some((deleg, item)) = node.delegation() => {
let Some(suffixes) = &deleg.suffixes else {
let traitless_qself =
matches!(&deleg.qself, Some(qself) if qself.position == 0);
let item = match node.to_annotatable() {
Annotatable::ImplItem(item) => item,
ann @ (Annotatable::Item(_)
@ -2000,6 +2003,11 @@ fn flat_map_node<Node: InvocationCollectorNode<OutputTy: Default>>(
}
_ => unreachable!(),
};
if traitless_qself {
let span = item.span;
self.cx.dcx().emit_err(GlobDelegationTraitlessQpath { span });
return Default::default();
}
return self.collect_glob_delegation(item, Node::KIND).make_ast::<Node>();
};

View file

@ -10,7 +10,9 @@
use ast::token::IdentIsRaw;
use rustc_ast as ast;
use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind, TokenKind::*};
use rustc_ast::token::{
self, Delimiter, NonterminalKind, NtPatKind::*, Token, TokenKind, TokenKind::*,
};
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast::{NodeId, DUMMY_NODE_ID};
use rustc_ast_pretty::pprust;
@ -1145,14 +1147,17 @@ fn check_matcher_core<'tt>(
// Macros defined in the current crate have a real node id,
// whereas macros from an external crate have a dummy id.
if def.id != DUMMY_NODE_ID
&& matches!(kind, NonterminalKind::PatParam { inferred: true })
&& matches!(next_token, TokenTree::Token(token) if token.kind == BinOp(token::BinOpToken::Or))
&& matches!(kind, NonterminalKind::Pat(PatParam { inferred: true }))
&& matches!(
next_token,
TokenTree::Token(token) if token.kind == BinOp(token::BinOpToken::Or)
)
{
// It is suggestion to use pat_param, for example: $x:pat -> $x:pat_param.
let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
span,
name,
Some(NonterminalKind::PatParam { inferred: false }),
Some(NonterminalKind::Pat(PatParam { inferred: false })),
));
sess.psess.buffer_lint(
RUST_2021_INCOMPATIBLE_OR_PATTERNS,
@ -1185,14 +1190,14 @@ fn check_matcher_core<'tt>(
);
err.span_label(sp, format!("not allowed after `{kind}` fragments"));
if kind == NonterminalKind::PatWithOr
if kind == NonterminalKind::Pat(PatWithOr)
&& sess.psess.edition.at_least_rust_2021()
&& next_token.is_token(&BinOp(token::BinOpToken::Or))
{
let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
span,
name,
Some(NonterminalKind::PatParam { inferred: false }),
Some(NonterminalKind::Pat(PatParam { inferred: false })),
));
err.span_suggestion(
span,
@ -1292,9 +1297,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
// maintain
IsInFollow::Yes
}
NonterminalKind::Stmt
| NonterminalKind::Expr
| NonterminalKind::Expr2021 { inferred: _ } => {
NonterminalKind::Stmt | NonterminalKind::Expr(_) => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`;`"];
match tok {
TokenTree::Token(token) => match token.kind {
@ -1304,7 +1307,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
_ => IsInFollow::No(TOKENS),
}
}
NonterminalKind::PatParam { .. } => {
NonterminalKind::Pat(PatParam { .. }) => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"];
match tok {
TokenTree::Token(token) => match token.kind {
@ -1317,7 +1320,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
_ => IsInFollow::No(TOKENS),
}
}
NonterminalKind::PatWithOr => {
NonterminalKind::Pat(PatWithOr) => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"];
match tok {
TokenTree::Token(token) => match token.kind {

View file

@ -2,7 +2,7 @@
use crate::mbe::macro_parser::count_metavar_decls;
use crate::mbe::{Delimited, KleeneOp, KleeneToken, MetaVarExpr, SequenceRepetition, TokenTree};
use rustc_ast::token::{self, Delimiter, IdentIsRaw, Token};
use rustc_ast::token::{self, Delimiter, IdentIsRaw, NonterminalKind, NtExprKind::*, Token};
use rustc_ast::{tokenstream, NodeId};
use rustc_ast_pretty::pprust;
use rustc_feature::Features;
@ -85,36 +85,31 @@ pub(super) fn parse(
span.edition()
}
};
let kind =
token::NonterminalKind::from_symbol(fragment.name, edition)
.unwrap_or_else(|| {
let help = match fragment.name {
sym::expr_2021 => {
format!(
"fragment specifier `expr_2021` \
requires Rust 2021 or later\n\
{VALID_FRAGMENT_NAMES_MSG}"
)
}
_ if edition().at_least_rust_2021()
&& features
.expr_fragment_specifier_2024 =>
{
VALID_FRAGMENT_NAMES_MSG_2021.into()
}
_ => VALID_FRAGMENT_NAMES_MSG.into(),
};
sess.dcx().emit_err(
errors::InvalidFragmentSpecifier {
span,
fragment,
help,
},
);
token::NonterminalKind::Ident
let kind = NonterminalKind::from_symbol(fragment.name, edition)
.unwrap_or_else(|| {
let help = match fragment.name {
sym::expr_2021 => {
format!(
"fragment specifier `expr_2021` \
requires Rust 2021 or later\n\
{VALID_FRAGMENT_NAMES_MSG}"
)
}
_ if edition().at_least_rust_2021()
&& features.expr_fragment_specifier_2024 =>
{
VALID_FRAGMENT_NAMES_MSG_2021.into()
}
_ => VALID_FRAGMENT_NAMES_MSG.into(),
};
sess.dcx().emit_err(errors::InvalidFragmentSpecifier {
span,
fragment,
help,
});
if kind
== (token::NonterminalKind::Expr2021 { inferred: false })
NonterminalKind::Ident
});
if kind == NonterminalKind::Expr(Expr2021 { inferred: false })
&& !features.expr_fragment_specifier_2024
{
rustc_session::parse::feature_err(

View file

@ -629,7 +629,7 @@ pub fn internal(&self, feature: Symbol) -> bool {
/// Allows unsafe on extern declarations and safety qualifiers over internal items.
(unstable, unsafe_extern_blocks, "1.80.0", Some(123743)),
/// Allows unsized fn parameters.
(unstable, unsized_fn_params, "1.49.0", Some(48055)),
(internal, unsized_fn_params, "1.49.0", Some(48055)),
/// Allows unsized rvalues at arguments and parameters.
(incomplete, unsized_locals, "1.30.0", Some(48055)),
/// Allows unsized tuple coercion.

View file

@ -520,6 +520,8 @@ pub enum UnsupportedOpInfo {
Unsupported(String),
/// Unsized local variables.
UnsizedLocal,
/// Extern type field with an indeterminate offset.
ExternTypeField,
//
// The variants below are only reachable from CTFE/const prop, miri will never emit them.
//

View file

@ -1,5 +1,7 @@
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Nonterminal::*, NonterminalKind, Token};
use rustc_ast::token::{
self, Delimiter, Nonterminal::*, NonterminalKind, NtExprKind::*, NtPatKind::*, Token,
};
use rustc_ast::HasTokens;
use rustc_ast_pretty::pprust;
use rustc_data_structures::sync::Lrc;
@ -36,14 +38,14 @@ fn may_be_ident(nt: &token::Nonterminal) -> bool {
}
match kind {
NonterminalKind::Expr2021 { inferred: _ } => {
NonterminalKind::Expr(Expr2021 { .. }) => {
token.can_begin_expr()
// This exception is here for backwards compatibility.
&& !token.is_keyword(kw::Let)
// This exception is here for backwards compatibility.
&& !token.is_keyword(kw::Const)
}
NonterminalKind::Expr => {
NonterminalKind::Expr(Expr) => {
token.can_begin_expr()
// This exception is here for backwards compatibility.
&& !token.is_keyword(kw::Let)
@ -74,7 +76,7 @@ fn may_be_ident(nt: &token::Nonterminal) -> bool {
token::Interpolated(nt) => may_be_ident(nt),
_ => false,
},
NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => match &token.kind {
NonterminalKind::Pat(pat_kind) => match &token.kind {
// box, ref, mut, and other identifiers (can stricten)
token::Ident(..) | token::NtIdent(..) |
token::OpenDelim(Delimiter::Parenthesis) | // tuple pattern
@ -89,7 +91,7 @@ fn may_be_ident(nt: &token::Nonterminal) -> bool {
token::Lt | // path (UFCS constant)
token::BinOp(token::Shl) => true, // path (double UFCS)
// leading vert `|` or-pattern
token::BinOp(token::Or) => matches!(kind, NonterminalKind::PatWithOr),
token::BinOp(token::Or) => matches!(pat_kind, PatWithOr),
token::Interpolated(nt) => may_be_ident(nt),
_ => false,
},
@ -135,31 +137,25 @@ pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, ParseN
.create_err(UnexpectedNonterminal::Statement(self.token.span)));
}
},
NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => {
NtPat(self.collect_tokens_no_attrs(|this| match kind {
NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None, None),
NonterminalKind::PatWithOr => this.parse_pat_allow_top_alt(
NonterminalKind::Pat(pat_kind) => {
NtPat(self.collect_tokens_no_attrs(|this| match pat_kind {
PatParam { .. } => this.parse_pat_no_top_alt(None, None),
PatWithOr => this.parse_pat_allow_top_alt(
None,
RecoverComma::No,
RecoverColon::No,
CommaRecoveryMode::EitherTupleOrPipe,
),
_ => unreachable!(),
})?)
}
NonterminalKind::Expr | NonterminalKind::Expr2021 { inferred: _ } => {
NtExpr(self.parse_expr_force_collect()?)
}
NonterminalKind::Expr(_) => NtExpr(self.parse_expr_force_collect()?),
NonterminalKind::Literal => {
// The `:literal` matcher does not support attributes
NtLiteral(self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?)
}
NonterminalKind::Ty => {
NtTy(self.collect_tokens_no_attrs(|this| this.parse_ty_no_question_mark_recover())?)
}
// this could be handled like a token, since it is one
NonterminalKind::Ident => {
return if let Some((ident, is_raw)) = get_macro_ident(&self.token) {

View file

@ -1290,15 +1290,21 @@ fn needs_codegen_config(run: &RunConfig<'_>) -> bool {
pub(crate) const CODEGEN_BACKEND_PREFIX: &str = "rustc_codegen_";
fn is_codegen_cfg_needed(path: &TaskPath, run: &RunConfig<'_>) -> bool {
if path.path.to_str().unwrap().contains(CODEGEN_BACKEND_PREFIX) {
let path = path.path.to_str().unwrap();
let is_explicitly_called = |p| -> bool { run.builder.paths.contains(p) };
let should_enforce = run.builder.kind == Kind::Dist || run.builder.kind == Kind::Install;
if path.contains(CODEGEN_BACKEND_PREFIX) {
let mut needs_codegen_backend_config = true;
for backend in run.builder.config.codegen_backends(run.target) {
if path.path.to_str().unwrap().ends_with(&(CODEGEN_BACKEND_PREFIX.to_owned() + backend))
{
if path.ends_with(&(CODEGEN_BACKEND_PREFIX.to_owned() + backend)) {
needs_codegen_backend_config = false;
}
}
if needs_codegen_backend_config {
if (is_explicitly_called(&PathBuf::from(path)) || should_enforce)
&& needs_codegen_backend_config
{
run.builder.info(
"WARNING: no codegen-backends config matched the requested path to build a codegen backend. \
HELP: add backend to codegen-backends in config.toml.",

View file

@ -1018,7 +1018,7 @@ fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
// perhaps it should be removed in favor of making `dist` perform the `vendor` step?
// Ensure we have all submodules from src and other directories checked out.
for submodule in builder.get_all_submodules() {
for submodule in build_helper::util::parse_gitmodules(&builder.src) {
builder.update_submodule(Path::new(submodule));
}

View file

@ -1048,8 +1048,6 @@ impl Step for Tidy {
/// Once tidy passes, this step also runs `fmt --check` if tests are being run
/// for the `dev` or `nightly` channels.
fn run(self, builder: &Builder<'_>) {
builder.build.update_submodule(Path::new("src/tools/rustc-perf"));
let mut cmd = builder.tool_cmd(Tool::Tidy);
cmd.arg(&builder.src);
cmd.arg(&builder.initial_cargo);

View file

@ -4,13 +4,11 @@
use std::env;
use std::ffi::{OsStr, OsString};
use std::fmt::{Debug, Write};
use std::fs::{self, File};
use std::fs;
use std::hash::Hash;
use std::io::{BufRead, BufReader};
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::OnceLock;
use std::time::{Duration, Instant};
use crate::core::build_steps::tool::{self, SourceType};
@ -594,7 +592,7 @@ pub fn path(self, path: &str) -> Self {
///
/// [`path`]: ShouldRun::path
pub fn paths(mut self, paths: &[&str]) -> Self {
let submodules_paths = self.builder.get_all_submodules();
let submodules_paths = build_helper::util::parse_gitmodules(&self.builder.src);
self.paths.insert(PathSet::Set(
paths
@ -2243,28 +2241,6 @@ pub fn ensure<S: Step>(&'a self, step: S) -> S::Output {
out
}
/// Return paths of all submodules.
pub fn get_all_submodules(&self) -> &[String] {
static SUBMODULES_PATHS: OnceLock<Vec<String>> = OnceLock::new();
let init_submodules_paths = |src: &PathBuf| {
let file = File::open(src.join(".gitmodules")).unwrap();
let mut submodules_paths = vec![];
for line in BufReader::new(file).lines().map_while(Result::ok) {
let line = line.trim();
if line.starts_with("path") {
let actual_path = line.split(' ').last().expect("Couldn't get value of path");
submodules_paths.push(actual_path.to_owned());
}
}
submodules_paths
};
SUBMODULES_PATHS.get_or_init(|| init_submodules_paths(&self.src))
}
/// Ensure that a given step is built *only if it's supposed to be built by default*, returning
/// its output. This will cache the step, so it's safe (and good!) to call this as often as
/// needed to ensure that all dependencies are build.

View file

@ -135,7 +135,7 @@ pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result<
if config.dry_run() {
return Ok(());
}
let _ = fs::remove_dir(link);
let _ = fs::remove_dir_all(link);
return symlink_dir_inner(original, link);
#[cfg(not(windows))]

View file

@ -7,8 +7,6 @@
set -Euo pipefail
# https://github.com/rust-lang/rust/issues/77620#issuecomment-705144570
unset GIT_DIR
ROOT_DIR="$(git rev-parse --show-toplevel)"
echo "Running pre-push script $ROOT_DIR/x test tidy"

View file

@ -4,7 +4,7 @@ This tool generates the manifests uploaded to static.rust-lang.org and used by r
You can see a full list of all manifests at <https://static.rust-lang.org/manifests.txt>.
This listing is updated by <https://github.com/rust-lang/generate-manifest-list> every 7 days.
This gets called by `promote-release` <https://github.com/rust-lang/promote-release> via `x.py dist hash-and-sign`.
This gets called by `promote-release` <https://github.com/rust-lang/promote-release>. `promote-release` downloads a pre-built binary of `build-manifest` which is generated in the dist-x86_64-linux builder and uploaded to s3.
## Adding a new component

View file

@ -1,4 +1,8 @@
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::process::Command;
use std::sync::OnceLock;
/// Invokes `build_helper::util::detail_exit` with `cfg!(test)`
///
@ -45,3 +49,27 @@ pub fn try_run(cmd: &mut Command, print_cmd_on_fail: bool) -> Result<(), ()> {
Ok(())
}
}
/// Returns the submodule paths from the `.gitmodules` file in the given directory.
pub fn parse_gitmodules(target_dir: &Path) -> &[String] {
static SUBMODULES_PATHS: OnceLock<Vec<String>> = OnceLock::new();
let gitmodules = target_dir.join(".gitmodules");
assert!(gitmodules.exists(), "'{}' file is missing.", gitmodules.display());
let init_submodules_paths = || {
let file = File::open(gitmodules).unwrap();
let mut submodules_paths = vec![];
for line in BufReader::new(file).lines().map_while(Result::ok) {
let line = line.trim();
if line.starts_with("path") {
let actual_path = line.split(' ').last().expect("Couldn't get value of path");
submodules_paths.push(actual_path.to_owned());
}
}
submodules_paths
};
SUBMODULES_PATHS.get_or_init(|| init_submodules_paths())
}

View file

@ -877,6 +877,7 @@ pub fn line_directive<'line>(
"needs-sanitizer-shadow-call-stack",
"needs-sanitizer-support",
"needs-sanitizer-thread",
"needs-symlink",
"needs-threads",
"needs-unwind",
"needs-wasmtime",

View file

@ -144,6 +144,11 @@ pub(super) fn handle_needs(
condition: config.runner.as_ref().is_some_and(|r| r.contains("wasmtime")),
ignore_reason: "ignored when wasmtime runner is not available",
},
Need {
name: "needs-symlink",
condition: cache.symlinks,
ignore_reason: "ignored if symlinks are unavailable",
},
];
let (name, comment) = match ln.split_once([':', ' ']) {
@ -209,6 +214,7 @@ pub(super) struct CachedNeedsConditions {
xray: bool,
rust_lld: bool,
dlltool: bool,
symlinks: bool,
}
impl CachedNeedsConditions {
@ -253,6 +259,7 @@ pub(super) fn load(config: &Config) -> Self {
.exists(),
dlltool: find_dlltool(&config),
symlinks: has_symlinks(),
}
}
}
@ -279,3 +286,22 @@ fn find_dlltool(config: &Config) -> bool {
};
dlltool_found
}
#[cfg(windows)]
fn has_symlinks() -> bool {
if std::env::var_os("CI").is_some() {
return true;
}
let link = std::env::temp_dir().join("RUST_COMPILETEST_SYMLINK_CHECK");
if std::os::windows::fs::symlink_file("DOES NOT EXIST", &link).is_ok() {
std::fs::remove_file(&link).unwrap();
true
} else {
false
}
}
#[cfg(not(windows))]
fn has_symlinks() -> bool {
true
}

View file

@ -311,7 +311,9 @@ pub fn report_error<'tcx>(
ResourceExhaustion(_) => "resource exhaustion",
Unsupported(
// We list only the ones that can actually happen.
UnsupportedOpInfo::Unsupported(_) | UnsupportedOpInfo::UnsizedLocal,
UnsupportedOpInfo::Unsupported(_)
| UnsupportedOpInfo::UnsizedLocal
| UnsupportedOpInfo::ExternTypeField,
) => "unsupported operation",
InvalidProgram(
// We list only the ones that can actually happen.

View file

@ -1,8 +1,8 @@
error: unsupported operation: `extern type` does not have a known offset
error: unsupported operation: `extern type` field does not have a known offset
--> $DIR/extern-type-field-offset.rs:LL:CC
|
LL | let _field = &x.a;
| ^^^^ `extern type` does not have a known offset
| ^^^^ `extern type` field does not have a known offset
|
= help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
= note: BACKTRACE:

View file

@ -1,4 +1,4 @@
use rustc_ast::token::{Delimiter, NonterminalKind, TokenKind};
use rustc_ast::token::{Delimiter, NonterminalKind, NtExprKind::*, NtPatKind::*, TokenKind};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{ast, ptr};
use rustc_parse::parser::{ForceCollect, Parser, Recovery};
@ -48,7 +48,7 @@ macro_rules! parse_macro_arg {
parse_macro_arg!(
Expr,
NonterminalKind::Expr,
NonterminalKind::Expr(Expr),
|parser: &mut Parser<'b>| parser.parse_expr(),
|x: ptr::P<ast::Expr>| Some(x)
);
@ -60,7 +60,7 @@ macro_rules! parse_macro_arg {
);
parse_macro_arg!(
Pat,
NonterminalKind::PatParam { inferred: false },
NonterminalKind::Pat(PatParam { inferred: false }),
|parser: &mut Parser<'b>| parser.parse_pat_no_top_alt(None, None),
|x: ptr::P<ast::Pat>| Some(x)
);

View file

@ -5,6 +5,7 @@ edition = "2021"
autobins = false
[dependencies]
build_helper = { path = "../build_helper" }
cargo_metadata = "0.15"
regex = "1"
miropt-test-tools = { path = "../miropt-test-tools" }

View file

@ -1,7 +1,9 @@
//! Checks the licenses of third-party dependencies.
use build_helper::ci::CiEnv;
use cargo_metadata::{Metadata, Package, PackageId};
use std::collections::HashSet;
use std::fs::read_dir;
use std::path::Path;
/// These are licenses that are allowed for all crates, including the runtime,
@ -514,7 +516,19 @@
pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
let mut checked_runtime_licenses = false;
let submodules = build_helper::util::parse_gitmodules(root);
for &(workspace, exceptions, permitted_deps) in WORKSPACES {
// Skip if it's a submodule, not in a CI environment, and not initialized.
//
// This prevents enforcing developers to fetch submodules for tidy.
if submodules.contains(&workspace.into())
&& !CiEnv::is_ci()
// If the directory is empty, we can consider it as an uninitialized submodule.
&& read_dir(root.join(workspace)).unwrap().next().is_none()
{
continue;
}
if !root.join(workspace).join("Cargo.lock").exists() {
tidy_error!(bad, "the `{workspace}` workspace doesn't have a Cargo.lock");
continue;

View file

@ -1,6 +1,7 @@
//! Check for external package sources. Allow only vendorable packages.
use std::fs;
use build_helper::ci::CiEnv;
use std::fs::{self, read_dir};
use std::path::Path;
/// List of allowed sources for packages.
@ -13,7 +14,19 @@
/// Checks for external package sources. `root` is the path to the directory that contains the
/// workspace `Cargo.toml`.
pub fn check(root: &Path, bad: &mut bool) {
let submodules = build_helper::util::parse_gitmodules(root);
for &(workspace, _, _) in crate::deps::WORKSPACES {
// Skip if it's a submodule, not in a CI environment, and not initialized.
//
// This prevents enforcing developers to fetch submodules for tidy.
if submodules.contains(&workspace.into())
&& !CiEnv::is_ci()
// If the directory is empty, we can consider it as an uninitialized submodule.
&& read_dir(root.join(workspace)).unwrap().next().is_none()
{
continue;
}
// FIXME check other workspaces too
// `Cargo.lock` of rust.
let path = root.join(workspace).join("Cargo.lock");

View file

@ -9,6 +9,7 @@
// can result in successful compilation.
//@ ignore-cross-compile
//@ needs-symlink
use run_make_support::{create_symlink, cwd, fs_wrapper, rustc};

View file

@ -6,6 +6,7 @@
// See https://github.com/rust-lang/rust/issues/12459
//@ ignore-cross-compile
//@ needs-symlink
use run_make_support::{create_symlink, dynamic_lib_name, fs_wrapper, rustc};

View file

@ -6,6 +6,7 @@
// See https://github.com/rust-lang/rust/pull/32828
//@ ignore-cross-compile
//@ needs-symlink
use run_make_support::{create_symlink, cwd, rustc};

View file

@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed
--> $DIR/issue-91827-extern-types-field-offset.rs:38:17
|
LL | let field = &x.a;
| ^^^^ `extern type` does not have a known offset
| ^^^^ `extern type` field does not have a known offset
error: aborting due to 1 previous error

View file

@ -0,0 +1,15 @@
#![feature(extern_types)]
extern {
type Opaque;
}
struct ThinDst {
x: u8,
tail: Opaque,
}
const C1: &ThinDst = unsafe { std::mem::transmute(b"d".as_ptr()) };
//~^ERROR: evaluation of constant value failed
fn main() {}

View file

@ -0,0 +1,9 @@
error[E0080]: evaluation of constant value failed
--> $DIR/validation-ice-extern-type-field.rs:12:1
|
LL | const C1: &ThinDst = unsafe { std::mem::transmute(b"d".as_ptr()) };
| ^^^^^^^^^^^^^^^^^^ `extern type` field does not have a known offset
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0080`.

View file

@ -0,0 +1,11 @@
#![feature(fn_delegation)]
#![allow(incomplete_features)]
struct S;
impl S {
reuse <u8>::*; //~ ERROR qualified path without a trait in glob delegation
reuse <()>::*; //~ ERROR qualified path without a trait in glob delegation
}
fn main() {}

View file

@ -0,0 +1,14 @@
error: qualified path without a trait in glob delegation
--> $DIR/glob-traitless-qpath.rs:7:5
|
LL | reuse <u8>::*;
| ^^^^^^^^^^^^^^
error: qualified path without a trait in glob delegation
--> $DIR/glob-traitless-qpath.rs:8:5
|
LL | reuse <()>::*;
| ^^^^^^^^^^^^^^
error: aborting due to 2 previous errors

View file

@ -9,15 +9,11 @@ LL | const _: *const Foo = 0 as _;
| ^^^^^^^^^^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
error[E0080]: it is undefined behavior to use this value
error[E0080]: evaluation of constant value failed
--> $DIR/stack-overflow-trait-infer-98842.rs:15:1
|
LL | const _: *const Foo = 0 as _;
| ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation
|
= note: the raw bytes of the constant (size: 4, align: 4) {
00 00 00 00 │ ....
}
error: aborting due to 2 previous errors

View file

@ -9,15 +9,11 @@ LL | const _: *const Foo = 0 as _;
| ^^^^^^^^^^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
error[E0080]: it is undefined behavior to use this value
error[E0080]: evaluation of constant value failed
--> $DIR/stack-overflow-trait-infer-98842.rs:15:1
|
LL | const _: *const Foo = 0 as _;
| ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation
|
= note: the raw bytes of the constant (size: 8, align: 8) {
00 00 00 00 00 00 00 00 │ ........
}
error: aborting due to 2 previous errors

View file

@ -13,6 +13,6 @@
// and it will infinitely recurse somewhere trying to figure out the
// size of this pointer (is my guess):
const _: *const Foo = 0 as _;
//~^ ERROR it is undefined behavior to use this value
//~^ ERROR evaluation of constant value failed
pub fn main() {}

View file

@ -1,25 +0,0 @@
error[E0391]: cycle detected when computing layout of `Foo`
|
= note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`...
= note: ...which again requires computing layout of `Foo`, completing the cycle
note: cycle used when const-evaluating + checking `_`
--> $DIR/stack-overflow-trait-infer-98842.rs:13:1
|
LL | const _: *const Foo = 0 as _;
| ^^^^^^^^^^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
error[E0080]: it is undefined behavior to use this value
--> $DIR/stack-overflow-trait-infer-98842.rs:13:1
|
LL | const _: *const Foo = 0 as _;
| ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation
|
= note: the raw bytes of the constant (size: 8, align: 8) {
00 00 00 00 00 00 00 00 │ ........
}
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0080, E0391.
For more information about an error, try `rustc --explain E0080`.