Merge pull request #4500 from RalfJung/rustup

Rustup
This commit is contained in:
Ralf Jung 2025-07-28 07:11:11 +00:00 committed by GitHub
commit fb464108ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
573 changed files with 7380 additions and 3030 deletions

View file

@ -441,20 +441,6 @@ dependencies = [
"thiserror 1.0.69",
]
[[package]]
name = "cargo_metadata"
version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba"
dependencies = [
"camino",
"cargo-platform 0.1.9",
"semver",
"serde",
"serde_json",
"thiserror 2.0.12",
]
[[package]]
name = "cargo_metadata"
version = "0.21.0"
@ -1364,7 +1350,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"askama",
"cargo_metadata 0.18.1",
"cargo_metadata 0.21.0",
"serde",
"serde_json",
"thiserror 1.0.69",
@ -4255,6 +4241,7 @@ dependencies = [
"rustc-literal-escaper",
"rustc_ast",
"rustc_ast_pretty",
"rustc_attr_parsing",
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
@ -5369,7 +5356,7 @@ name = "tidy"
version = "0.1.0"
dependencies = [
"build_helper",
"cargo_metadata 0.19.2",
"cargo_metadata 0.21.0",
"fluent-syntax",
"ignore",
"miropt-test-tools",

View file

@ -2849,7 +2849,7 @@ impl InlineAsmOperand {
}
}
#[derive(Clone, Copy, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
#[derive(Clone, Copy, Encodable, Decodable, Debug, HashStable_Generic, Walkable, PartialEq, Eq)]
pub enum AsmMacro {
/// The `asm!` macro
Asm,

View file

@ -416,12 +416,24 @@ pub enum AttributeKind {
/// Represents `#[pointee]`
Pointee(Span),
/// Represents `#[proc_macro]`
ProcMacro(Span),
/// Represents `#[proc_macro_attribute]`
ProcMacroAttribute(Span),
/// Represents `#[proc_macro_derive]`
ProcMacroDerive { trait_name: Symbol, helper_attrs: ThinVec<Symbol>, span: Span },
/// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
PubTransparent(Span),
/// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
Repr { reprs: ThinVec<(ReprAttr, Span)>, first_span: Span },
/// Represents `#[rustc_builtin_macro]`.
RustcBuiltinMacro { builtin_name: Option<Symbol>, helper_attrs: ThinVec<Symbol>, span: Span },
/// Represents `#[rustc_layout_scalar_valid_range_end]`.
RustcLayoutScalarValidRangeEnd(Box<u128>, Span),

View file

@ -61,8 +61,12 @@ impl AttributeKind {
PassByValue(..) => Yes,
Path(..) => No,
Pointee(..) => No,
ProcMacro(..) => No,
ProcMacroAttribute(..) => No,
ProcMacroDerive { .. } => No,
PubTransparent(..) => Yes,
Repr { .. } => No,
RustcBuiltinMacro { .. } => Yes,
RustcLayoutScalarValidRangeEnd(..) => Yes,
RustcLayoutScalarValidRangeStart(..) => Yes,
RustcObjectLifetimeDefault => No,

View file

@ -41,6 +41,7 @@ pub(crate) mod must_use;
pub(crate) mod no_implicit_prelude;
pub(crate) mod non_exhaustive;
pub(crate) mod path;
pub(crate) mod proc_macro_attrs;
pub(crate) mod repr;
pub(crate) mod rustc_internal;
pub(crate) mod semantics;

View file

@ -0,0 +1,139 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Span, Symbol, sym};
use thin_vec::ThinVec;
use crate::attributes::{
AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
};
use crate::context::{AcceptContext, Stage};
use crate::parser::ArgParser;
pub(crate) struct ProcMacroParser;
impl<S: Stage> NoArgsAttributeParser<S> for ProcMacroParser {
const PATH: &[Symbol] = &[sym::proc_macro];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ProcMacro;
}
pub(crate) struct ProcMacroAttributeParser;
impl<S: Stage> NoArgsAttributeParser<S> for ProcMacroAttributeParser {
const PATH: &[Symbol] = &[sym::proc_macro_attribute];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ProcMacroAttribute;
}
pub(crate) struct ProcMacroDeriveParser;
impl<S: Stage> SingleAttributeParser<S> for ProcMacroDeriveParser {
const PATH: &[Symbol] = &[sym::proc_macro_derive];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const TEMPLATE: AttributeTemplate =
template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let (trait_name, helper_attrs) = parse_derive_like(cx, args, true)?;
Some(AttributeKind::ProcMacroDerive {
trait_name: trait_name.expect("Trait name is mandatory, so it is present"),
helper_attrs,
span: cx.attr_span,
})
}
}
pub(crate) struct RustcBuiltinMacroParser;
impl<S: Stage> SingleAttributeParser<S> for RustcBuiltinMacroParser {
const PATH: &[Symbol] = &[sym::rustc_builtin_macro];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const TEMPLATE: AttributeTemplate =
template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let (builtin_name, helper_attrs) = parse_derive_like(cx, args, false)?;
Some(AttributeKind::RustcBuiltinMacro { builtin_name, helper_attrs, span: cx.attr_span })
}
}
fn parse_derive_like<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser<'_>,
trait_name_mandatory: bool,
) -> Option<(Option<Symbol>, ThinVec<Symbol>)> {
let Some(list) = args.list() else {
// For #[rustc_builtin_macro], it is permitted to leave out the trait name
if args.no_args().is_ok() && !trait_name_mandatory {
return Some((None, ThinVec::new()));
}
cx.expected_list(cx.attr_span);
return None;
};
let mut items = list.mixed();
// Parse the name of the trait that is derived.
let Some(trait_attr) = items.next() else {
cx.expected_at_least_one_argument(list.span);
return None;
};
let Some(trait_attr) = trait_attr.meta_item() else {
cx.unexpected_literal(trait_attr.span());
return None;
};
let Some(trait_ident) = trait_attr.path().word() else {
cx.expected_identifier(trait_attr.path().span());
return None;
};
if !trait_ident.name.can_be_raw() {
cx.expected_identifier(trait_ident.span);
return None;
}
if let Err(e) = trait_attr.args().no_args() {
cx.expected_no_args(e);
return None;
};
// Parse optional attributes
let mut attributes = ThinVec::new();
if let Some(attrs) = items.next() {
let Some(attr_list) = attrs.meta_item() else {
cx.expected_list(attrs.span());
return None;
};
if !attr_list.path().word_is(sym::attributes) {
cx.expected_specific_argument(attrs.span(), vec!["attributes"]);
return None;
}
let Some(attr_list) = attr_list.args().list() else {
cx.expected_list(attrs.span());
return None;
};
// Parse item in `attributes(...)` argument
for attr in attr_list.mixed() {
let Some(attr) = attr.meta_item() else {
cx.expected_identifier(attr.span());
return None;
};
if let Err(e) = attr.args().no_args() {
cx.expected_no_args(e);
return None;
};
let Some(ident) = attr.path().word() else {
cx.expected_identifier(attr.path().span());
return None;
};
if !ident.name.can_be_raw() {
cx.expected_identifier(ident.span);
return None;
}
attributes.push(ident.name);
}
}
// If anything else is specified, we should reject it
if let Some(next) = items.next() {
cx.expected_no_args(next.span());
}
Some((Some(trait_ident.name), attributes))
}

View file

@ -38,6 +38,9 @@ use crate::attributes::must_use::MustUseParser;
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
use crate::attributes::non_exhaustive::NonExhaustiveParser;
use crate::attributes::path::PathParser as PathAttributeParser;
use crate::attributes::proc_macro_attrs::{
ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser,
};
use crate::attributes::repr::{AlignParser, ReprParser};
use crate::attributes::rustc_internal::{
RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart,
@ -154,6 +157,8 @@ attribute_parsers!(
Single<MustUseParser>,
Single<OptimizeParser>,
Single<PathAttributeParser>,
Single<ProcMacroDeriveParser>,
Single<RustcBuiltinMacroParser>,
Single<RustcForceInlineParser>,
Single<RustcLayoutScalarValidRangeEnd>,
Single<RustcLayoutScalarValidRangeStart>,
@ -186,6 +191,8 @@ attribute_parsers!(
Single<WithoutArgs<ParenSugarParser>>,
Single<WithoutArgs<PassByValueParser>>,
Single<WithoutArgs<PointeeParser>>,
Single<WithoutArgs<ProcMacroAttributeParser>>,
Single<WithoutArgs<ProcMacroParser>>,
Single<WithoutArgs<PubTransparentParser>>,
Single<WithoutArgs<SpecializationTraitParser>>,
Single<WithoutArgs<StdInternalSymbolParser>>,

View file

@ -1290,6 +1290,58 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
span,
format!("if `{ty}` implemented `Clone`, you could clone the value"),
);
} else if let ty::Adt(_, _) = ty.kind()
&& let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
{
// For cases like `Option<NonClone>`, where `Option<T>: Clone` if `T: Clone`, we point
// at the types that should be `Clone`.
let ocx = ObligationCtxt::new_with_diagnostics(self.infcx);
let cause = ObligationCause::misc(expr.span, self.mir_def_id());
ocx.register_bound(cause, self.infcx.param_env, ty, clone_trait);
let errors = ocx.select_all_or_error();
if errors.iter().all(|error| {
match error.obligation.predicate.as_clause().and_then(|c| c.as_trait_clause()) {
Some(clause) => match clause.self_ty().skip_binder().kind() {
ty::Adt(def, _) => def.did().is_local() && clause.def_id() == clone_trait,
_ => false,
},
None => false,
}
}) {
let mut type_spans = vec![];
let mut types = FxIndexSet::default();
for clause in errors
.iter()
.filter_map(|e| e.obligation.predicate.as_clause())
.filter_map(|c| c.as_trait_clause())
{
let ty::Adt(def, _) = clause.self_ty().skip_binder().kind() else { continue };
type_spans.push(self.infcx.tcx.def_span(def.did()));
types.insert(
self.infcx
.tcx
.short_string(clause.self_ty().skip_binder(), &mut err.long_ty_path()),
);
}
let mut span: MultiSpan = type_spans.clone().into();
for sp in type_spans {
span.push_span_label(sp, "consider implementing `Clone` for this type");
}
span.push_span_label(expr.span, "you could clone this value");
let types: Vec<_> = types.into_iter().collect();
let msg = match &types[..] {
[only] => format!("`{only}`"),
[head @ .., last] => format!(
"{} and `{last}`",
head.iter().map(|t| format!("`{t}`")).collect::<Vec<_>>().join(", ")
),
[] => unreachable!(),
};
err.span_note(
span,
format!("if {msg} implemented `Clone`, you could clone the value"),
);
}
}
}

View file

@ -1,11 +1,13 @@
use std::mem;
use std::{mem, slice};
use rustc_ast::ptr::P;
use rustc_ast::visit::{self, Visitor};
use rustc_ast::{self as ast, NodeId, attr};
use rustc_ast::{self as ast, HasNodeId, NodeId, attr};
use rustc_ast_pretty::pprust;
use rustc_attr_data_structures::AttributeKind;
use rustc_attr_parsing::AttributeParser;
use rustc_errors::DiagCtxtHandle;
use rustc_expand::base::{ExtCtxt, ResolverExpand, parse_macro_name_and_helper_attrs};
use rustc_expand::base::{ExtCtxt, ResolverExpand};
use rustc_expand::expand::{AstFragment, ExpansionConfig};
use rustc_feature::Features;
use rustc_session::Session;
@ -22,7 +24,7 @@ struct ProcMacroDerive {
trait_name: Symbol,
function_ident: Ident,
span: Span,
attrs: Vec<Symbol>,
attrs: ThinVec<Symbol>,
}
struct ProcMacroDef {
@ -41,6 +43,7 @@ struct CollectProcMacros<'a> {
macros: Vec<ProcMacro>,
in_root: bool,
dcx: DiagCtxtHandle<'a>,
session: &'a Session,
source_map: &'a SourceMap,
is_proc_macro_crate: bool,
is_test_crate: bool,
@ -63,6 +66,7 @@ pub fn inject(
macros: Vec::new(),
in_root: true,
dcx,
session: sess,
source_map: sess.source_map(),
is_proc_macro_crate,
is_test_crate,
@ -98,8 +102,18 @@ impl<'a> CollectProcMacros<'a> {
function_ident: Ident,
attr: &'a ast::Attribute,
) {
let Some((trait_name, proc_attrs)) =
parse_macro_name_and_helper_attrs(self.dcx, attr, "derive")
let Some(rustc_hir::Attribute::Parsed(AttributeKind::ProcMacroDerive {
trait_name,
helper_attrs,
..
})) = AttributeParser::parse_limited(
self.session,
slice::from_ref(attr),
sym::proc_macro_derive,
item.span,
item.node_id(),
None,
)
else {
return;
};
@ -110,7 +124,7 @@ impl<'a> CollectProcMacros<'a> {
span: item.span,
trait_name,
function_ident,
attrs: proc_attrs,
attrs: helper_attrs,
}));
} else {
let msg = if !self.in_root {

View file

@ -39,10 +39,7 @@ impl Coords {
/// or other expansions), and if it does happen then skipping a span or function is
/// better than an ICE or `llvm-cov` failure that the user might have no way to avoid.
pub(crate) fn make_coords(source_map: &SourceMap, file: &SourceFile, span: Span) -> Option<Coords> {
if span.is_empty() {
debug_assert!(false, "can't make coords from empty span: {span:?}");
return None;
}
let span = ensure_non_empty_span(source_map, span)?;
let lo = span.lo();
let hi = span.hi();
@ -73,6 +70,29 @@ pub(crate) fn make_coords(source_map: &SourceMap, file: &SourceFile, span: Span)
})
}
fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option<Span> {
if !span.is_empty() {
return Some(span);
}
// The span is empty, so try to enlarge it to cover an adjacent '{' or '}'.
source_map
.span_to_source(span, |src, start, end| try {
// Adjusting span endpoints by `BytePos(1)` is normally a bug,
// but in this case we have specifically checked that the character
// we're skipping over is one of two specific ASCII characters, so
// adjusting by exactly 1 byte is correct.
if src.as_bytes().get(end).copied() == Some(b'{') {
Some(span.with_hi(span.hi() + BytePos(1)))
} else if start > 0 && src.as_bytes()[start - 1] == b'}' {
Some(span.with_lo(span.lo() - BytePos(1)))
} else {
None
}
})
.ok()?
}
/// If `llvm-cov` sees a source region that is improperly ordered (end < start),
/// it will immediately exit with a fatal error. To prevent that from happening,
/// discard regions that are improperly ordered, or might be interpreted in a

View file

@ -382,26 +382,16 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
let width = size.bits();
let llty = self.type_ix(width);
match name {
sym::ctlz | sym::cttz => {
let y = self.const_bool(false);
let ret = self.call_intrinsic(
format!("llvm.{name}"),
&[llty],
&[args[0].immediate(), y],
);
self.intcast(ret, result.layout.llvm_type(self), false)
}
sym::ctlz_nonzero => {
let y = self.const_bool(true);
sym::ctlz | sym::ctlz_nonzero | sym::cttz | sym::cttz_nonzero => {
let y =
self.const_bool(name == sym::ctlz_nonzero || name == sym::cttz_nonzero);
let llvm_name = if name == sym::ctlz || name == sym::ctlz_nonzero {
"llvm.ctlz"
} else {
"llvm.cttz"
};
let ret =
self.call_intrinsic("llvm.ctlz", &[llty], &[args[0].immediate(), y]);
self.intcast(ret, result.layout.llvm_type(self), false)
}
sym::cttz_nonzero => {
let y = self.const_bool(true);
let ret =
self.call_intrinsic("llvm.cttz", &[llty], &[args[0].immediate(), y]);
self.call_intrinsic(llvm_name, &[llty], &[args[0].immediate(), y]);
self.intcast(ret, result.layout.llvm_type(self), false)
}
sym::ctpop => {

View file

@ -405,6 +405,8 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) {
("mips64" | "mips64r6", _) => false,
// Selection bug <https://github.com/llvm/llvm-project/issues/95471>
("nvptx64", _) => false,
// Unsupported https://github.com/llvm/llvm-project/issues/121122
("amdgpu", _) => false,
// ABI bugs <https://github.com/rust-lang/rust/issues/125109> et al. (full
// list at <https://github.com/rust-lang/rust/issues/116909>)
("powerpc" | "powerpc64", _) => false,
@ -433,6 +435,9 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) {
// This rules out anything that doesn't have `long double` = `binary128`; <= 32 bits
// (ld is `f64`), anything other than Linux (Windows and MacOS use `f64`), and `x86`
// (ld is 80-bit extended precision).
//
// musl does not implement the symbols required for f128 math at all.
_ if target_env == "musl" => false,
("x86_64", _) => false,
(_, "linux") if target_pointer_width == 64 => true,
_ => false,

View file

@ -511,15 +511,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
err.emit();
}
// Any linkage to LLVM intrinsics for now forcibly marks them all as never
// unwinds since LLVM sometimes can't handle codegen which `invoke`s
// intrinsic functions.
if let Some(name) = &codegen_fn_attrs.link_name
&& name.as_str().starts_with("llvm.")
{
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
}
if let Some(features) = check_tied_features(
tcx.sess,
&codegen_fn_attrs

View file

@ -1,12 +1,13 @@
//! An analysis to determine which locals require allocas and
//! which do not.
use rustc_abi as abi;
use rustc_data_structures::graph::dominators::Dominators;
use rustc_index::bit_set::DenseBitSet;
use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{self, DefLocation, Location, TerminatorKind, traversal};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::{bug, span_bug};
use tracing::debug;
@ -99,63 +100,75 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> LocalAnalyzer<'a, 'b, 'tcx, Bx>
context: PlaceContext,
location: Location,
) {
let cx = self.fx.cx;
if !place_ref.projection.is_empty() {
const COPY_CONTEXT: PlaceContext =
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
if let Some((place_base, elem)) = place_ref.last_projection() {
let mut base_context = if context.is_mutating_use() {
PlaceContext::MutatingUse(MutatingUseContext::Projection)
} else {
PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
};
// Allow uses of projections that are ZSTs or from scalar fields.
let is_consume = matches!(
context,
PlaceContext::NonMutatingUse(
NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
)
);
if is_consume {
let base_ty = place_base.ty(self.fx.mir, cx.tcx());
let base_ty = self.fx.monomorphize(base_ty);
// ZSTs don't require any actual memory access.
let elem_ty = base_ty.projection_ty(cx.tcx(), self.fx.monomorphize(elem)).ty;
let span = self.fx.mir.local_decls[place_ref.local].source_info.span;
if cx.spanned_layout_of(elem_ty, span).is_zst() {
return;
// `PlaceElem::Index` is the only variant that can mention other `Local`s,
// so check for those up-front before any potential short-circuits.
for elem in place_ref.projection {
if let mir::PlaceElem::Index(index_local) = *elem {
self.visit_local(index_local, COPY_CONTEXT, location);
}
}
if let mir::ProjectionElem::Field(..) = elem {
let layout = cx.spanned_layout_of(base_ty.ty, span);
if cx.is_backend_immediate(layout) || cx.is_backend_scalar_pair(layout) {
// Recurse with the same context, instead of `Projection`,
// potentially stopping at non-operand projections,
// which would trigger `not_ssa` on locals.
base_context = context;
// If our local is already memory, nothing can make it *more* memory
// so we don't need to bother checking the projections further.
if self.locals[place_ref.local] == LocalKind::Memory {
return;
}
if place_ref.is_indirect_first_projection() {
// If this starts with a `Deref`, we only need to record a read of the
// pointer being dereferenced, as all the subsequent projections are
// working on a place which is always supported. (And because we're
// looking at codegen MIR, it can only happen as the first projection.)
self.visit_local(place_ref.local, COPY_CONTEXT, location);
return;
}
if context.is_mutating_use() {
// If it's a mutating use it doesn't matter what the projections are,
// if there are *any* then we need a place to write. (For example,
// `_1 = Foo()` works in SSA but `_2.0 = Foo()` does not.)
let mut_projection = PlaceContext::MutatingUse(MutatingUseContext::Projection);
self.visit_local(place_ref.local, mut_projection, location);
return;
}
// Scan through to ensure the only projections are those which
// `FunctionCx::maybe_codegen_consume_direct` can handle.
let base_ty = self.fx.monomorphized_place_ty(mir::PlaceRef::from(place_ref.local));
let mut layout = self.fx.cx.layout_of(base_ty);
for elem in place_ref.projection {
layout = match *elem {
mir::PlaceElem::Field(fidx, ..) => layout.field(self.fx.cx, fidx.as_usize()),
mir::PlaceElem::Downcast(_, vidx)
if let abi::Variants::Single { index: single_variant } =
layout.variants
&& vidx == single_variant =>
{
layout.for_variant(self.fx.cx, vidx)
}
mir::PlaceElem::Subtype(subtype_ty) => {
let subtype_ty = self.fx.monomorphize(subtype_ty);
self.fx.cx.layout_of(subtype_ty)
}
_ => {
self.locals[place_ref.local] = LocalKind::Memory;
return;
}
}
}
if let mir::ProjectionElem::Deref = elem {
// Deref projections typically only read the pointer.
base_context = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
}
self.process_place(&place_base, base_context, location);
// HACK(eddyb) this emulates the old `visit_projection_elem`, this
// entire `visit_place`-like `process_place` method should be rewritten,
// now that we have moved to the "slice of projections" representation.
if let mir::ProjectionElem::Index(local) = elem {
self.visit_local(
local,
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
location,
);
}
} else {
self.visit_local(place_ref.local, context, location);
debug_assert!(
!self.fx.cx.is_backend_ref(layout),
"Post-projection {place_ref:?} layout should be non-Ref, but it's {layout:?}",
);
}
// Even with supported projections, we still need to have `visit_local`
// check for things that can't be done in SSA (like `SharedBorrow`).
self.visit_local(place_ref.local, context, location);
}
}
@ -170,11 +183,6 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer
if let Some(local) = place.as_local() {
self.define(local, DefLocation::Assignment(location));
if self.locals[local] != LocalKind::Memory {
if !self.fx.rvalue_creates_operand(rvalue) {
self.locals[local] = LocalKind::Memory;
}
}
} else {
self.visit_place(place, PlaceContext::MutatingUse(MutatingUseContext::Store), location);
}

View file

@ -335,13 +335,6 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
let val = if field.is_zst() {
OperandValue::ZeroSized
} else if let BackendRepr::SimdVector { .. } = self.layout.backend_repr {
// codegen_transmute_operand doesn't support SIMD, but since the previous
// check handled ZSTs, the only possible field access into something SIMD
// is to the `non_1zst_field` that's the same SIMD. (Other things, even
// just padding, would change the wrapper's representation type.)
assert_eq!(field.size, self.layout.size);
self.val
} else if field.size == self.layout.size {
assert_eq!(offset.bytes(), 0);
fx.codegen_transmute_operand(bx, *self, field)
@ -928,9 +921,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
match self.locals[place_ref.local] {
LocalRef::Operand(mut o) => {
// Moves out of scalar and scalar pair fields are trivial.
for elem in place_ref.projection.iter() {
match elem {
// We only need to handle the projections that
// `LocalAnalyzer::process_place` let make it here.
for elem in place_ref.projection {
match *elem {
mir::ProjectionElem::Field(f, _) => {
assert!(
!o.layout.ty.is_any_ptr(),
@ -939,17 +933,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
);
o = o.extract_field(self, bx, f.index());
}
mir::ProjectionElem::Index(_)
| mir::ProjectionElem::ConstantIndex { .. } => {
// ZSTs don't require any actual memory access.
// FIXME(eddyb) deduplicate this with the identical
// checks in `codegen_consume` and `extract_field`.
let elem = o.layout.field(bx.cx(), 0);
if elem.is_zst() {
o = OperandRef::zero_sized(elem);
} else {
return None;
}
mir::PlaceElem::Downcast(_, vidx) => {
debug_assert_eq!(
o.layout.variants,
abi::Variants::Single { index: vidx },
);
let layout = o.layout.for_variant(bx.cx(), vidx);
o = OperandRef { val: o.val, layout }
}
mir::PlaceElem::Subtype(subtype_ty) => {
let subtype_ty = self.monomorphize(subtype_ty);
let layout = self.cx.layout_of(subtype_ty);
o = OperandRef { val: o.val, layout }
}
_ => return None,
}

View file

@ -2,12 +2,12 @@ use rustc_abi::{self as abi, FIRST_VARIANT};
use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use rustc_middle::{bug, mir};
use rustc_middle::{bug, mir, span_bug};
use rustc_session::config::OptLevel;
use tracing::{debug, instrument};
use super::operand::{OperandRef, OperandRefBuilder, OperandValue};
use super::place::{PlaceRef, codegen_tag_value};
use super::place::{PlaceRef, PlaceValue, codegen_tag_value};
use super::{FunctionCx, LocalRef};
use crate::common::{IntPredicate, TypeKind};
use crate::traits::*;
@ -180,7 +180,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
_ => {
assert!(self.rvalue_creates_operand(rvalue));
let temp = self.codegen_rvalue_operand(bx, rvalue);
temp.val.store(bx, dest);
}
@ -218,17 +217,26 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
/// Transmutes an `OperandValue` to another `OperandValue`.
///
/// This is supported only for cases where [`Self::rvalue_creates_operand`]
/// returns `true`, and will ICE otherwise. (In particular, anything that
/// would need to `alloca` in order to return a `PlaceValue` will ICE,
/// expecting those to go via [`Self::codegen_transmute`] instead where
/// the destination place is already allocated.)
/// This is supported for all cases where the `cast` type is SSA,
/// but for non-ZSTs with [`abi::BackendRepr::Memory`] it ICEs.
pub(crate) fn codegen_transmute_operand(
&mut self,
bx: &mut Bx,
operand: OperandRef<'tcx, Bx::Value>,
cast: TyAndLayout<'tcx>,
) -> OperandValue<Bx::Value> {
if let abi::BackendRepr::Memory { .. } = cast.backend_repr
&& !cast.is_zst()
{
span_bug!(self.mir.span, "Use `codegen_transmute` to transmute to {cast:?}");
}
// `Layout` is interned, so we can do a cheap check for things that are
// exactly the same and thus don't need any handling.
if abi::Layout::eq(&operand.layout.layout, &cast.layout) {
return operand.val;
}
// Check for transmutes that are always UB.
if operand.layout.size != cast.size
|| operand.layout.is_uninhabited()
@ -241,11 +249,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
return OperandValue::poison(bx, cast);
}
// To or from pointers takes different methods, so we use this to restrict
// the SimdVector case to types which can be `bitcast` between each other.
#[inline]
fn vector_can_bitcast(x: abi::Scalar) -> bool {
matches!(
x,
abi::Scalar::Initialized {
value: abi::Primitive::Int(..) | abi::Primitive::Float(..),
..
}
)
}
let cx = bx.cx();
match (operand.val, operand.layout.backend_repr, cast.backend_repr) {
_ if cast.is_zst() => OperandValue::ZeroSized,
(_, _, abi::BackendRepr::Memory { .. }) => {
bug!("Cannot `codegen_transmute_operand` to non-ZST memory-ABI output {cast:?}");
}
(OperandValue::Ref(source_place_val), abi::BackendRepr::Memory { .. }, _) => {
assert_eq!(source_place_val.llextra, None);
// The existing alignment is part of `source_place_val`,
@ -256,16 +275,46 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
OperandValue::Immediate(imm),
abi::BackendRepr::Scalar(from_scalar),
abi::BackendRepr::Scalar(to_scalar),
) => OperandValue::Immediate(transmute_scalar(bx, imm, from_scalar, to_scalar)),
) if from_scalar.size(cx) == to_scalar.size(cx) => {
OperandValue::Immediate(transmute_scalar(bx, imm, from_scalar, to_scalar))
}
(
OperandValue::Immediate(imm),
abi::BackendRepr::SimdVector { element: from_scalar, .. },
abi::BackendRepr::SimdVector { element: to_scalar, .. },
) if vector_can_bitcast(from_scalar) && vector_can_bitcast(to_scalar) => {
let to_backend_ty = bx.cx().immediate_backend_type(cast);
OperandValue::Immediate(bx.bitcast(imm, to_backend_ty))
}
(
OperandValue::Pair(imm_a, imm_b),
abi::BackendRepr::ScalarPair(in_a, in_b),
abi::BackendRepr::ScalarPair(out_a, out_b),
) => OperandValue::Pair(
transmute_scalar(bx, imm_a, in_a, out_a),
transmute_scalar(bx, imm_b, in_b, out_b),
),
_ => bug!("Cannot `codegen_transmute_operand` {operand:?} to {cast:?}"),
) if in_a.size(cx) == out_a.size(cx) && in_b.size(cx) == out_b.size(cx) => {
OperandValue::Pair(
transmute_scalar(bx, imm_a, in_a, out_a),
transmute_scalar(bx, imm_b, in_b, out_b),
)
}
_ => {
// For any other potentially-tricky cases, make a temporary instead.
// If anything else wants the target local to be in memory this won't
// be hit, as `codegen_transmute` will get called directly. Thus this
// is only for places where everything else wants the operand form,
// and thus it's not worth making those places get it from memory.
//
// Notably, Scalar ⇌ ScalarPair cases go here to avoid padding
// and endianness issues, as do SimdVector ones to avoid worrying
// about things like f32x8 ⇌ ptrx4 that would need multiple steps.
let align = Ord::max(operand.layout.align.abi, cast.align.abi);
let size = Ord::max(operand.layout.size, cast.size);
let temp = PlaceValue::alloca(bx, size, align);
bx.lifetime_start(temp.llval, size);
operand.val.store(bx, temp.with_type(operand.layout));
let val = bx.load_operand(temp.with_type(cast)).val;
bx.lifetime_end(temp.llval, size);
val
}
}
}
@ -326,8 +375,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx: &mut Bx,
rvalue: &mir::Rvalue<'tcx>,
) -> OperandRef<'tcx, Bx::Value> {
assert!(self.rvalue_creates_operand(rvalue), "cannot codegen {rvalue:?} to operand",);
match *rvalue {
mir::Rvalue::Cast(ref kind, ref source, mir_cast_ty) => {
let operand = self.codegen_operand(bx, source);
@ -653,8 +700,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let ty = self.monomorphize(ty);
let layout = self.cx.layout_of(ty);
// `rvalue_creates_operand` has arranged that we only get here if
// we can build the aggregate immediate from the field immediates.
let mut builder = OperandRefBuilder::new(layout);
for (field_idx, field) in fields.iter_enumerated() {
let op = self.codegen_operand(bx, field);
@ -955,69 +1000,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
OperandValue::Pair(val, of)
}
/// Returns `true` if the `rvalue` can be computed into an [`OperandRef`],
/// rather than needing a full `PlaceRef` for the assignment destination.
///
/// This is used by the [`super::analyze`] code to decide which MIR locals
/// can stay as SSA values (as opposed to generating `alloca` slots for them).
/// As such, some paths here return `true` even where the specific rvalue
/// will not actually take the operand path because the result type is such
/// that it always gets an `alloca`, but where it's not worth re-checking the
/// layout in this code when the right thing will happen anyway.
pub(crate) fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>) -> bool {
match *rvalue {
mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, cast_ty) => {
let operand_ty = operand.ty(self.mir, self.cx.tcx());
let cast_layout = self.cx.layout_of(self.monomorphize(cast_ty));
let operand_layout = self.cx.layout_of(self.monomorphize(operand_ty));
match (operand_layout.backend_repr, cast_layout.backend_repr) {
// When the output will be in memory anyway, just use its place
// (instead of the operand path) unless it's the trivial ZST case.
(_, abi::BackendRepr::Memory { .. }) => cast_layout.is_zst(),
// Otherwise (for a non-memory output) if the input is memory
// then we can just read the value from the place.
(abi::BackendRepr::Memory { .. }, _) => true,
// When we have scalar immediates, we can only convert things
// where the sizes match, to avoid endianness questions.
(abi::BackendRepr::Scalar(a), abi::BackendRepr::Scalar(b)) =>
a.size(self.cx) == b.size(self.cx),
(abi::BackendRepr::ScalarPair(a0, a1), abi::BackendRepr::ScalarPair(b0, b1)) =>
a0.size(self.cx) == b0.size(self.cx) && a1.size(self.cx) == b1.size(self.cx),
// Mixing Scalars and ScalarPairs can get quite complicated when
// padding and undef get involved, so leave that to the memory path.
(abi::BackendRepr::Scalar(_), abi::BackendRepr::ScalarPair(_, _)) |
(abi::BackendRepr::ScalarPair(_, _), abi::BackendRepr::Scalar(_)) => false,
// SIMD vectors aren't worth the trouble of dealing with complex
// cases like from vectors of f32 to vectors of pointers or
// from fat pointers to vectors of u16. (See #143194 #110021 ...)
(abi::BackendRepr::SimdVector { .. }, _) | (_, abi::BackendRepr::SimdVector { .. }) => false,
}
}
mir::Rvalue::Ref(..) |
mir::Rvalue::CopyForDeref(..) |
mir::Rvalue::RawPtr(..) |
mir::Rvalue::Len(..) |
mir::Rvalue::Cast(..) | // (*)
mir::Rvalue::ShallowInitBox(..) | // (*)
mir::Rvalue::BinaryOp(..) |
mir::Rvalue::UnaryOp(..) |
mir::Rvalue::Discriminant(..) |
mir::Rvalue::NullaryOp(..) |
mir::Rvalue::ThreadLocalRef(_) |
mir::Rvalue::Use(..) |
mir::Rvalue::Repeat(..) | // (*)
mir::Rvalue::Aggregate(..) | // (*)
mir::Rvalue::WrapUnsafeBinder(..) => // (*)
true,
}
// (*) this is only true if the type is suitable
}
}
/// Transmutes a single scalar value `imm` from `from_scalar` to `to_scalar`.

View file

@ -128,15 +128,15 @@ const_eval_frame_note_inner = inside {$where_ ->
const_eval_frame_note_last = the failure occurred here
const_eval_incompatible_arg_types =
calling a function whose parameter #{$arg_idx} has type {$callee_ty} passing argument of type {$caller_ty}
const_eval_incompatible_calling_conventions =
calling a function with calling convention "{$callee_conv}" using calling convention "{$caller_conv}"
const_eval_incompatible_return_types =
calling a function with return type {$callee_ty} passing return place of type {$caller_ty}
const_eval_incompatible_types =
calling a function with argument of type {$callee_ty} passing data of type {$caller_ty}
const_eval_interior_mutable_borrow_escaping =
interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
.label = this borrow of an interior mutable value refers to such a temporary

View file

@ -500,7 +500,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
InvalidNichedEnumVariantWritten { .. } => {
const_eval_invalid_niched_enum_variant_written
}
AbiMismatchArgument { .. } => const_eval_incompatible_types,
AbiMismatchArgument { .. } => const_eval_incompatible_arg_types,
AbiMismatchReturn { .. } => const_eval_incompatible_return_types,
}
}
@ -625,12 +625,16 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
diag.arg("data_size", info.data_size);
}
InvalidNichedEnumVariantWritten { enum_ty } => {
diag.arg("ty", enum_ty.to_string());
diag.arg("ty", enum_ty);
}
AbiMismatchArgument { caller_ty, callee_ty }
| AbiMismatchReturn { caller_ty, callee_ty } => {
diag.arg("caller_ty", caller_ty.to_string());
diag.arg("callee_ty", callee_ty.to_string());
AbiMismatchArgument { arg_idx, caller_ty, callee_ty } => {
diag.arg("arg_idx", arg_idx + 1); // adjust for 1-indexed lists in output
diag.arg("caller_ty", caller_ty);
diag.arg("callee_ty", callee_ty);
}
AbiMismatchReturn { caller_ty, callee_ty } => {
diag.arg("caller_ty", caller_ty);
diag.arg("callee_ty", callee_ty);
}
}
}

View file

@ -270,6 +270,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
Item = (&'x FnArg<'tcx, M::Provenance>, &'y ArgAbi<'tcx, Ty<'tcx>>),
>,
callee_abi: &ArgAbi<'tcx, Ty<'tcx>>,
callee_arg_idx: usize,
callee_arg: &mir::Place<'tcx>,
callee_ty: Ty<'tcx>,
already_live: bool,
@ -298,6 +299,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// Check compatibility
if !self.check_argument_compat(caller_abi, callee_abi)? {
throw_ub!(AbiMismatchArgument {
arg_idx: callee_arg_idx,
caller_ty: caller_abi.layout.ty,
callee_ty: callee_abi.layout.ty
});
@ -424,7 +426,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// this is a single iterator (that handles `spread_arg`), then
// `pass_argument` would be the loop body. It takes care to
// not advance `caller_iter` for ignored arguments.
let mut callee_args_abis = callee_fn_abi.args.iter();
let mut callee_args_abis = callee_fn_abi.args.iter().enumerate();
for local in body.args_iter() {
// Construct the destination place for this argument. At this point all
// locals are still dead, so we cannot construct a `PlaceTy`.
@ -445,10 +447,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
&[mir::ProjectionElem::Field(FieldIdx::from_usize(i), field_ty)],
*self.tcx,
);
let callee_abi = callee_args_abis.next().unwrap();
let (idx, callee_abi) = callee_args_abis.next().unwrap();
self.pass_argument(
&mut caller_args,
callee_abi,
idx,
&dest,
field_ty,
/* already_live */ true,
@ -456,10 +459,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
} else {
// Normal argument. Cannot mark it as live yet, it might be unsized!
let callee_abi = callee_args_abis.next().unwrap();
let (idx, callee_abi) = callee_args_abis.next().unwrap();
self.pass_argument(
&mut caller_args,
callee_abi,
idx,
&dest,
ty,
/* already_live */ false,

View file

@ -551,6 +551,11 @@ impl SelfProfilerRef {
pub fn get_self_profiler(&self) -> Option<Arc<SelfProfiler>> {
self.profiler.clone()
}
/// Is expensive recording of query keys and/or function arguments enabled?
pub fn is_args_recording_enabled(&self) -> bool {
self.enabled() && self.event_filter_mask.intersects(EventFilter::ARGS)
}
}
/// A helper for recording costly arguments to self-profiling events. Used with

View file

@ -18,7 +18,7 @@ thread_local! {
pub(crate) fn entrypoint(stream: &MdStream<'_>, buf: &mut Buffer) -> io::Result<()> {
#[cfg(not(test))]
if let Some((w, _)) = termize::dimensions() {
WIDTH.with(|c| c.set(std::cmp::min(w, DEFAULT_COLUMN_WIDTH)));
WIDTH.set(std::cmp::min(w, DEFAULT_COLUMN_WIDTH));
}
write_stream(stream, buf, None, 0)?;
buf.write_all(b"\n")
@ -84,7 +84,7 @@ fn write_tt(tt: &MdTree<'_>, buf: &mut Buffer, indent: usize) -> io::Result<()>
reset_cursor();
}
MdTree::HorizontalRule => {
(0..WIDTH.with(Cell::get)).for_each(|_| buf.write_all(b"-").unwrap());
(0..WIDTH.get()).for_each(|_| buf.write_all(b"-").unwrap());
reset_cursor();
}
MdTree::Heading(n, stream) => {
@ -121,7 +121,7 @@ fn write_tt(tt: &MdTree<'_>, buf: &mut Buffer, indent: usize) -> io::Result<()>
/// End of that block, just wrap the line
fn reset_cursor() {
CURSOR.with(|cur| cur.set(0));
CURSOR.set(0);
}
/// Change to be generic on Write for testing. If we have a link URL, we don't
@ -144,7 +144,7 @@ fn write_wrapping<B: io::Write>(
buf.write_all(ind_ws)?;
cur.set(indent);
}
let ch_count = WIDTH.with(Cell::get) - cur.get();
let ch_count = WIDTH.get() - cur.get();
let mut iter = to_write.char_indices();
let Some((end_idx, _ch)) = iter.nth(ch_count) else {
// Write entire line

View file

@ -1,26 +1,8 @@
expand_arg_not_attributes =
second argument must be `attributes`
expand_attr_no_arguments =
attribute must have either one or two arguments
expand_attribute_meta_item =
attribute must be a meta item, not a literal
expand_attribute_single_word =
attribute must only be a single word
expand_attributes_on_expressions_experimental =
attributes on expressions are experimental
.help_outer_doc = `///` is used for outer documentation comments; for a plain comment, use `//`
.help_inner_doc = `//!` is used for inner documentation comments; for a plain comment, use `//` by removing the `!` or inserting a space in between them: `// !`
expand_attributes_wrong_form =
attribute must be of form: `attributes(foo, bar)`
expand_cannot_be_name_of_macro =
`{$trait_ident}` cannot be a name of {$macro_type} macro
expand_collapse_debuginfo_illegal =
illegal value for attribute #[collapse_debuginfo(no|external|yes)]
@ -71,9 +53,6 @@ expand_glob_delegation_outside_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
expand_incomplete_parse =
macro expansion ignores {$descr} and any tokens following
.label = caused by the macro expansion here
@ -165,12 +144,6 @@ expand_mve_unrecognized_var =
expand_non_inline_modules_in_proc_macro_input_are_unstable =
non-inline modules in proc macro input are unstable
expand_not_a_meta_item =
not a meta item
expand_only_one_word =
must only be one word
expand_proc_macro_back_compat = using an old version of `{$crate_name}`
.note = older versions of the `{$crate_name}` crate no longer compile; please update to `{$crate_name}` v{$fixed_version}, or switch to one of the `{$crate_name}` alternatives

View file

@ -861,7 +861,7 @@ impl SyntaxExtension {
/// | (unspecified) | no | if-ext | if-ext | yes |
/// | external | no | if-ext | if-ext | yes |
/// | yes | yes | yes | yes | yes |
fn get_collapse_debuginfo(sess: &Session, attrs: &[impl AttributeExt], ext: bool) -> bool {
fn get_collapse_debuginfo(sess: &Session, attrs: &[hir::Attribute], ext: bool) -> bool {
let flag = sess.opts.cg.collapse_macro_debuginfo;
let attr = ast::attr::find_by_name(attrs, sym::collapse_debuginfo)
.and_then(|attr| {
@ -872,7 +872,7 @@ impl SyntaxExtension {
.ok()
})
.unwrap_or_else(|| {
if ast::attr::contains_name(attrs, sym::rustc_builtin_macro) {
if find_attr!(attrs, AttributeKind::RustcBuiltinMacro { .. }) {
CollapseMacroDebuginfo::Yes
} else {
CollapseMacroDebuginfo::Unspecified
@ -915,16 +915,18 @@ impl SyntaxExtension {
let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, !is_local);
tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe);
let (builtin_name, helper_attrs) = ast::attr::find_by_name(attrs, sym::rustc_builtin_macro)
.map(|attr| {
// Override `helper_attrs` passed above if it's a built-in macro,
// marking `proc_macro_derive` macros as built-in is not a realistic use case.
parse_macro_name_and_helper_attrs(sess.dcx(), attr, "built-in").map_or_else(
|| (Some(name), Vec::new()),
|(name, helper_attrs)| (Some(name), helper_attrs),
)
})
.unwrap_or_else(|| (None, helper_attrs));
let (builtin_name, helper_attrs) = match find_attr!(attrs, AttributeKind::RustcBuiltinMacro { builtin_name, helper_attrs, .. } => (builtin_name, helper_attrs))
{
// Override `helper_attrs` passed above if it's a built-in macro,
// marking `proc_macro_derive` macros as built-in is not a realistic use case.
Some((Some(name), helper_attrs)) => {
(Some(*name), helper_attrs.iter().copied().collect())
}
Some((None, _)) => (Some(name), Vec::new()),
// Not a built-in macro
None => (None, helper_attrs),
};
let stability = find_attr!(attrs, AttributeKind::Stability { stability, .. } => *stability);
@ -1141,7 +1143,7 @@ pub trait ResolverExpand {
/// Names of specific methods to which glob delegation expands.
fn glob_delegation_suffixes(
&mut self,
&self,
trait_def_id: DefId,
impl_def_id: LocalDefId,
) -> Result<Vec<(Ident, Option<Ident>)>, Indeterminate>;
@ -1224,6 +1226,7 @@ pub struct ExtCtxt<'a> {
pub(super) expanded_inert_attrs: MarkedAttrs,
/// `-Zmacro-stats` data.
pub macro_stats: FxHashMap<(Symbol, MacroKind), MacroStat>,
pub nb_macro_errors: usize,
}
impl<'a> ExtCtxt<'a> {
@ -1254,6 +1257,7 @@ impl<'a> ExtCtxt<'a> {
expanded_inert_attrs: MarkedAttrs::new(),
buffered_early_lint: vec![],
macro_stats: Default::default(),
nb_macro_errors: 0,
}
}
@ -1315,6 +1319,12 @@ impl<'a> ExtCtxt<'a> {
self.current_expansion.id.expansion_cause()
}
/// This method increases the internal macro errors count and then call `trace_macros_diag`.
pub fn macro_error_and_trace_macros_diag(&mut self) {
self.nb_macro_errors += 1;
self.trace_macros_diag();
}
pub fn trace_macros_diag(&mut self) {
for (span, notes) in self.expansions.iter() {
let mut db = self.dcx().create_note(errors::TraceMacro { span: *span });
@ -1382,80 +1392,6 @@ pub fn resolve_path(sess: &Session, path: impl Into<PathBuf>, span: Span) -> PRe
}
}
pub fn parse_macro_name_and_helper_attrs(
dcx: DiagCtxtHandle<'_>,
attr: &impl AttributeExt,
macro_type: &str,
) -> Option<(Symbol, Vec<Symbol>)> {
// Once we've located the `#[proc_macro_derive]` attribute, verify
// that it's of the form `#[proc_macro_derive(Foo)]` or
// `#[proc_macro_derive(Foo, attributes(A, ..))]`
let list = attr.meta_item_list()?;
let ([trait_attr] | [trait_attr, _]) = list.as_slice() else {
dcx.emit_err(errors::AttrNoArguments { span: attr.span() });
return None;
};
let Some(trait_attr) = trait_attr.meta_item() else {
dcx.emit_err(errors::NotAMetaItem { span: trait_attr.span() });
return None;
};
let trait_ident = match trait_attr.ident() {
Some(trait_ident) if trait_attr.is_word() => trait_ident,
_ => {
dcx.emit_err(errors::OnlyOneWord { span: trait_attr.span });
return None;
}
};
if !trait_ident.name.can_be_raw() {
dcx.emit_err(errors::CannotBeNameOfMacro {
span: trait_attr.span,
trait_ident,
macro_type,
});
}
let attributes_attr = list.get(1);
let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
if !attr.has_name(sym::attributes) {
dcx.emit_err(errors::ArgumentNotAttributes { span: attr.span() });
}
attr.meta_item_list()
.unwrap_or_else(|| {
dcx.emit_err(errors::AttributesWrongForm { span: attr.span() });
&[]
})
.iter()
.filter_map(|attr| {
let Some(attr) = attr.meta_item() else {
dcx.emit_err(errors::AttributeMetaItem { span: attr.span() });
return None;
};
let ident = match attr.ident() {
Some(ident) if attr.is_word() => ident,
_ => {
dcx.emit_err(errors::AttributeSingleWord { span: attr.span });
return None;
}
};
if !ident.name.can_be_raw() {
dcx.emit_err(errors::HelperAttributeNameInvalid {
span: attr.span,
name: ident,
});
}
Some(ident.name)
})
.collect()
} else {
Vec::new()
};
Some((trait_ident.name, proc_attrs))
}
/// If this item looks like a specific enums from `rental`, emit a fatal error.
/// See #73345 and #83125 for more details.
/// FIXME(#73933): Remove this eventually.

View file

@ -78,72 +78,6 @@ pub(crate) struct MacroBodyStability {
pub head_span: Span,
}
#[derive(Diagnostic)]
#[diag(expand_attr_no_arguments)]
pub(crate) struct AttrNoArguments {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(expand_not_a_meta_item)]
pub(crate) struct NotAMetaItem {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(expand_only_one_word)]
pub(crate) struct OnlyOneWord {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(expand_cannot_be_name_of_macro)]
pub(crate) struct CannotBeNameOfMacro<'a> {
#[primary_span]
pub span: Span,
pub trait_ident: Ident,
pub macro_type: &'a str,
}
#[derive(Diagnostic)]
#[diag(expand_arg_not_attributes)]
pub(crate) struct ArgumentNotAttributes {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(expand_attributes_wrong_form)]
pub(crate) struct AttributesWrongForm {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(expand_attribute_meta_item)]
pub(crate) struct AttributeMetaItem {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(expand_attribute_single_word)]
pub(crate) struct AttributeSingleWord {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(expand_helper_attribute_name_invalid)]
pub(crate) struct HelperAttributeNameInvalid {
#[primary_span]
pub span: Span,
pub name: Ident,
}
#[derive(Diagnostic)]
#[diag(expand_feature_removed, code = E0557)]
#[note]

View file

@ -693,7 +693,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
crate_name: self.cx.ecfg.crate_name,
});
self.cx.trace_macros_diag();
self.cx.macro_error_and_trace_macros_diag();
guar
}
@ -707,7 +707,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
) -> ErrorGuaranteed {
let guar =
self.cx.dcx().emit_err(WrongFragmentKind { span, kind: kind.name(), name: &mac.path });
self.cx.trace_macros_diag();
self.cx.macro_error_and_trace_macros_diag();
guar
}
@ -1048,7 +1048,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
}
annotate_err_with_kind(&mut err, kind, span);
let guar = err.emit();
self.cx.trace_macros_diag();
self.cx.macro_error_and_trace_macros_diag();
kind.dummy(span, guar)
}
}

View file

@ -299,6 +299,7 @@ enum EofMatcherPositions {
}
/// Represents the possible results of an attempted parse.
#[derive(Debug)]
pub(crate) enum ParseResult<T, F> {
/// Parsed successfully.
Success(T),

View file

@ -280,7 +280,7 @@ fn expand_macro<'cx>(
// Retry and emit a better error.
let (span, guar) =
diagnostics::failed_to_match_macro(cx.psess(), sp, def_span, name, arg, rules);
cx.trace_macros_diag();
cx.macro_error_and_trace_macros_diag();
DummyResult::any(span, guar)
}
}

View file

@ -1363,6 +1363,17 @@ impl AttributeExt for Attribute {
_ => None,
}
}
fn is_proc_macro_attr(&self) -> bool {
matches!(
self,
Attribute::Parsed(
AttributeKind::ProcMacro(..)
| AttributeKind::ProcMacroAttribute(..)
| AttributeKind::ProcMacroDerive { .. }
)
)
}
}
// FIXME(fn_delegation): use function delegation instead of manually forwarding

View file

@ -767,7 +767,10 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
DefKind::Static { .. } => {
check_static_inhabited(tcx, def_id);
check_static_linkage(tcx, def_id);
res = res.and(wfcheck::check_static_item(tcx, def_id));
let ty = tcx.type_of(def_id).instantiate_identity();
res = res.and(wfcheck::check_static_item(
tcx, def_id, ty, /* should_check_for_sync */ true,
));
}
DefKind::Const => res = res.and(wfcheck::check_const_item(tcx, def_id)),
_ => unreachable!(),

View file

@ -1180,12 +1180,13 @@ fn check_item_fn(
}
#[instrument(level = "debug", skip(tcx))]
pub(super) fn check_static_item(
tcx: TyCtxt<'_>,
pub(crate) fn check_static_item<'tcx>(
tcx: TyCtxt<'tcx>,
item_id: LocalDefId,
ty: Ty<'tcx>,
should_check_for_sync: bool,
) -> Result<(), ErrorGuaranteed> {
enter_wf_checking_ctxt(tcx, item_id, |wfcx| {
let ty = tcx.type_of(item_id).instantiate_identity();
let span = tcx.ty_span(item_id);
let item_ty = wfcx.deeply_normalize(span, Some(WellFormedLoc::Ty(item_id)), ty);
@ -1212,9 +1213,9 @@ pub(super) fn check_static_item(
}
// Ensure that the end result is `Sync` in a non-thread local `static`.
let should_check_for_sync = tcx.static_mutability(item_id.to_def_id())
== Some(hir::Mutability::Not)
let should_check_for_sync = should_check_for_sync
&& !is_foreign_item
&& tcx.static_mutability(item_id.to_def_id()) == Some(hir::Mutability::Not)
&& !tcx.is_thread_local_static(item_id.to_def_id());
if should_check_for_sync {

View file

@ -14,6 +14,7 @@ use rustc_middle::{bug, span_bug};
use rustc_span::{DUMMY_SP, Ident, Span};
use super::{HirPlaceholderCollector, ItemCtxt, bad_placeholder};
use crate::check::wfcheck::check_static_item;
use crate::errors::TypeofReservedKeywordUsed;
use crate::hir_ty_lowering::HirTyLowerer;
@ -217,7 +218,15 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
"static variable",
)
} else {
icx.lower_ty(ty)
let ty = icx.lower_ty(ty);
// MIR relies on references to statics being scalars.
// Verify that here to avoid ill-formed MIR.
// We skip the `Sync` check to avoid cycles for type-alias-impl-trait,
// relying on the fact that non-Sync statics don't ICE the rest of the compiler.
match check_static_item(tcx, def_id, ty, /* should_check_for_sync */ false) {
Ok(()) => ty,
Err(guar) => Ty::new_error(tcx, guar),
}
}
}
ItemKind::Const(ident, _, ty, body_id) => {
@ -275,7 +284,17 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
let args = ty::GenericArgs::identity_for_item(tcx, def_id);
Ty::new_fn_def(tcx, def_id.to_def_id(), args)
}
ForeignItemKind::Static(t, _, _) => icx.lower_ty(t),
ForeignItemKind::Static(ty, _, _) => {
let ty = icx.lower_ty(ty);
// MIR relies on references to statics being scalars.
// Verify that here to avoid ill-formed MIR.
// We skip the `Sync` check to avoid cycles for type-alias-impl-trait,
// relying on the fact that non-Sync statics don't ICE the rest of the compiler.
match check_static_item(tcx, def_id, ty, /* should_check_for_sync */ false) {
Ok(()) => ty,
Err(guar) => Ty::new_error(tcx, guar),
}
}
ForeignItemKind::Type => Ty::new_foreign(tcx, def_id.to_def_id()),
},

View file

@ -264,6 +264,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.span_label(within_macro_span, "due to this macro variable");
}
self.suggest_valid_traits(&mut err, item_name, out_of_scope_traits, true);
self.suggest_unwrapping_inner_self(&mut err, source, rcvr_ty, item_name);
err.emit()
}

View file

@ -208,6 +208,10 @@ fn configure_and_expand(
// Expand macros now!
let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate));
if ecx.nb_macro_errors > 0 {
sess.dcx().abort_if_errors();
}
// The rest is error reporting and stats
sess.psess.buffered_lints.with_lock(|buffered_lints: &mut Vec<BufferedEarlyLint>| {

View file

@ -2870,7 +2870,7 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels {
if let hir::Expr {
kind:
hir::ExprKind::InlineAsm(hir::InlineAsm {
asm_macro: AsmMacro::Asm | AsmMacro::NakedAsm,
asm_macro: asm_macro @ (AsmMacro::Asm | AsmMacro::NakedAsm),
template_strs,
options,
..
@ -2878,6 +2878,15 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels {
..
} = expr
{
// Non-generic naked functions are allowed to define arbitrary
// labels.
if *asm_macro == AsmMacro::NakedAsm {
let def_id = expr.hir_id.owner.def_id;
if !cx.tcx.generics_of(def_id).requires_monomorphization(cx.tcx) {
return;
}
}
// asm with `options(raw)` does not do replacement with `{` and `}`.
let raw = options.contains(InlineAsmOptions::RAW);

View file

@ -356,7 +356,16 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
let store = unerased_lint_store(tcx.sess);
if store.late_module_passes.is_empty() {
late_lint_mod_inner(tcx, module_def_id, context, builtin_lints);
// If all builtin lints can be skipped, there is no point in running `late_lint_mod_inner`
// at all. This happens often for dependencies built with `--cap-lints=allow`.
let dont_need_to_run = tcx.lints_that_dont_need_to_run(());
let can_skip_lints = builtin_lints
.get_lints()
.iter()
.all(|lint| dont_need_to_run.contains(&LintId::of(lint)));
if !can_skip_lints {
late_lint_mod_inner(tcx, module_def_id, context, builtin_lints);
}
} else {
let builtin_lints = Box::new(builtin_lints) as Box<dyn LateLintPass<'tcx>>;
let mut binding = store

View file

@ -933,6 +933,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool {
let feature = if let Some(feature) = lint_id.lint.feature_gate
&& !self.features.enabled(feature)
&& !span.allows_unstable(feature)
{
// Lint is behind a feature that is not enabled; eventually return false.
feature

View file

@ -92,7 +92,7 @@ macro_rules! expand_combined_late_lint_pass_methods {
/// Combines multiple lints passes into a single lint pass, at compile time,
/// for maximum speed. Each `check_foo` method in `$methods` within this pass
/// simply calls `check_foo` once per `$pass`. Compare with
/// `LateLintPassObjects`, which is similar, but combines lint passes at
/// `RuntimeCombinedLateLintPass`, which is similar, but combines lint passes at
/// runtime.
#[macro_export]
macro_rules! declare_combined_late_lint_pass {
@ -123,10 +123,10 @@ macro_rules! declare_combined_late_lint_pass {
#[allow(rustc::lint_pass_impl_without_macro)]
impl $crate::LintPass for $name {
fn name(&self) -> &'static str {
panic!()
stringify!($name)
}
fn get_lints(&self) -> LintVec {
panic!()
$name::get_lints()
}
}
)

View file

@ -1340,7 +1340,15 @@ impl EarlyLintPass for UnusedParens {
self.with_self_ty_parens = false;
}
ast::TyKind::Ref(_, mut_ty) | ast::TyKind::Ptr(mut_ty) => {
self.in_no_bounds_pos.insert(mut_ty.ty.id, NoBoundsException::OneBound);
// If this type itself appears in no-bounds position, we propagate its
// potentially tighter constraint or risk a false posive (issue 143653).
let own_constraint = self.in_no_bounds_pos.get(&ty.id);
let constraint = match own_constraint {
Some(NoBoundsException::None) => NoBoundsException::None,
Some(NoBoundsException::OneBound) => NoBoundsException::OneBound,
None => NoBoundsException::OneBound,
};
self.in_no_bounds_pos.insert(mut_ty.ty.id, constraint);
}
ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
for i in 0..bounds.len() {

View file

@ -1610,7 +1610,7 @@ extern "C" void LLVMRustPositionBefore(LLVMBuilderRef B, LLVMValueRef Instr) {
extern "C" void LLVMRustPositionAfter(LLVMBuilderRef B, LLVMValueRef Instr) {
if (auto I = dyn_cast<Instruction>(unwrap<Value>(Instr))) {
auto J = I->getNextNonDebugInstruction();
auto J = I->getNextNode();
unwrap(B)->SetInsertPoint(J);
}
}

View file

@ -5,7 +5,7 @@ use std::io::{Read, Seek, Write};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use rustc_attr_data_structures::EncodeCrossCrate;
use rustc_attr_data_structures::{AttributeKind, EncodeCrossCrate, find_attr};
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::memmap::{Mmap, MmapMut};
use rustc_data_structures::sync::{join, par_for_each_in};
@ -1965,18 +1965,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
// Proc-macros may have attributes like `#[allow_internal_unstable]`,
// so downstream crates need access to them.
let attrs = tcx.hir_attrs(proc_macro);
let macro_kind = if ast::attr::contains_name(attrs, sym::proc_macro) {
let macro_kind = if find_attr!(attrs, AttributeKind::ProcMacro(..)) {
MacroKind::Bang
} else if ast::attr::contains_name(attrs, sym::proc_macro_attribute) {
} else if find_attr!(attrs, AttributeKind::ProcMacroAttribute(..)) {
MacroKind::Attr
} else if let Some(attr) = ast::attr::find_by_name(attrs, sym::proc_macro_derive) {
// This unwrap chain should have been checked by the proc-macro harness.
name = attr.meta_item_list().unwrap()[0]
.meta_item()
.unwrap()
.ident()
.unwrap()
.name;
} else if let Some(trait_name) = find_attr!(attrs, AttributeKind::ProcMacroDerive { trait_name, ..} => trait_name)
{
name = *trait_name;
MacroKind::Derive
} else {
bug!("Unknown proc-macro type for item {:?}", id);
@ -2124,11 +2119,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
};
let def_id = id.owner_id.to_def_id();
self.tables.defaultness.set_some(def_id.index, tcx.defaultness(def_id));
if of_trait && let Some(header) = tcx.impl_trait_header(def_id) {
record!(self.tables.impl_trait_header[def_id] <- header);
self.tables.defaultness.set_some(def_id.index, tcx.defaultness(def_id));
let trait_ref = header.trait_ref.instantiate_identity();
let simplified_self_ty = fast_reject::simplify_type(
self.tcx,

View file

@ -426,7 +426,12 @@ pub enum UndefinedBehaviorInfo<'tcx> {
/// Trying to set discriminant to the niched variant, but the value does not match.
InvalidNichedEnumVariantWritten { enum_ty: Ty<'tcx> },
/// ABI-incompatible argument types.
AbiMismatchArgument { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
AbiMismatchArgument {
/// The index of the argument whose type is wrong.
arg_idx: usize,
caller_ty: Ty<'tcx>,
callee_ty: Ty<'tcx>,
},
/// ABI-incompatible return types.
AbiMismatchReturn { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
}

View file

@ -1041,11 +1041,13 @@ const NUM_PREINTERNED_TY_VARS: u32 = 100;
const NUM_PREINTERNED_FRESH_TYS: u32 = 20;
const NUM_PREINTERNED_FRESH_INT_TYS: u32 = 3;
const NUM_PREINTERNED_FRESH_FLOAT_TYS: u32 = 3;
const NUM_PREINTERNED_ANON_BOUND_TYS_I: u32 = 3;
const NUM_PREINTERNED_ANON_BOUND_TYS_V: u32 = 20;
// This number may seem high, but it is reached in all but the smallest crates.
const NUM_PREINTERNED_RE_VARS: u32 = 500;
const NUM_PREINTERNED_RE_LATE_BOUNDS_I: u32 = 2;
const NUM_PREINTERNED_RE_LATE_BOUNDS_V: u32 = 20;
const NUM_PREINTERNED_ANON_RE_BOUNDS_I: u32 = 3;
const NUM_PREINTERNED_ANON_RE_BOUNDS_V: u32 = 20;
pub struct CommonTypes<'tcx> {
pub unit: Ty<'tcx>,
@ -1088,6 +1090,11 @@ pub struct CommonTypes<'tcx> {
/// Pre-interned `Infer(ty::FreshFloatTy(n))` for small values of `n`.
pub fresh_float_tys: Vec<Ty<'tcx>>,
/// Pre-interned values of the form:
/// `Bound(DebruijnIndex(i), BoundTy { var: v, kind: BoundTyKind::Anon})`
/// for small values of `i` and `v`.
pub anon_bound_tys: Vec<Vec<Ty<'tcx>>>,
}
pub struct CommonLifetimes<'tcx> {
@ -1101,9 +1108,9 @@ pub struct CommonLifetimes<'tcx> {
pub re_vars: Vec<Region<'tcx>>,
/// Pre-interned values of the form:
/// `ReBound(DebruijnIndex(i), BoundRegion { var: v, kind: BrAnon })`
/// `ReBound(DebruijnIndex(i), BoundRegion { var: v, kind: BoundRegionKind::Anon })`
/// for small values of `i` and `v`.
pub re_late_bounds: Vec<Vec<Region<'tcx>>>,
pub anon_re_bounds: Vec<Vec<Region<'tcx>>>,
}
pub struct CommonConsts<'tcx> {
@ -1131,6 +1138,19 @@ impl<'tcx> CommonTypes<'tcx> {
let fresh_float_tys: Vec<_> =
(0..NUM_PREINTERNED_FRESH_FLOAT_TYS).map(|n| mk(Infer(ty::FreshFloatTy(n)))).collect();
let anon_bound_tys = (0..NUM_PREINTERNED_ANON_BOUND_TYS_I)
.map(|i| {
(0..NUM_PREINTERNED_ANON_BOUND_TYS_V)
.map(|v| {
mk(ty::Bound(
ty::DebruijnIndex::from(i),
ty::BoundTy { var: ty::BoundVar::from(v), kind: ty::BoundTyKind::Anon },
))
})
.collect()
})
.collect();
CommonTypes {
unit: mk(Tuple(List::empty())),
bool: mk(Bool),
@ -1161,6 +1181,7 @@ impl<'tcx> CommonTypes<'tcx> {
fresh_tys,
fresh_int_tys,
fresh_float_tys,
anon_bound_tys,
}
}
}
@ -1176,9 +1197,9 @@ impl<'tcx> CommonLifetimes<'tcx> {
let re_vars =
(0..NUM_PREINTERNED_RE_VARS).map(|n| mk(ty::ReVar(ty::RegionVid::from(n)))).collect();
let re_late_bounds = (0..NUM_PREINTERNED_RE_LATE_BOUNDS_I)
let anon_re_bounds = (0..NUM_PREINTERNED_ANON_RE_BOUNDS_I)
.map(|i| {
(0..NUM_PREINTERNED_RE_LATE_BOUNDS_V)
(0..NUM_PREINTERNED_ANON_RE_BOUNDS_V)
.map(|v| {
mk(ty::ReBound(
ty::DebruijnIndex::from(i),
@ -1196,7 +1217,7 @@ impl<'tcx> CommonLifetimes<'tcx> {
re_static: mk(ty::ReStatic),
re_erased: mk(ty::ReErased),
re_vars,
re_late_bounds,
anon_re_bounds,
}
}
}

View file

@ -2024,7 +2024,10 @@ impl<'tcx> TyCtxt<'tcx> {
&& let Some(def_id) = def_id.as_local()
&& let outer = self.def_span(def_id).ctxt().outer_expn_data()
&& matches!(outer.kind, ExpnKind::Macro(MacroKind::Derive, _))
&& self.has_attr(outer.macro_def_id.unwrap(), sym::rustc_builtin_macro)
&& find_attr!(
self.get_all_attrs(outer.macro_def_id.unwrap()),
AttributeKind::RustcBuiltinMacro { .. }
)
{
true
} else {

View file

@ -86,7 +86,7 @@ macro_rules! define_helper {
impl $helper {
pub fn new() -> $helper {
$helper($tl.with(|c| c.replace(true)))
$helper($tl.replace(true))
}
}
@ -100,12 +100,12 @@ macro_rules! define_helper {
impl Drop for $helper {
fn drop(&mut self) {
$tl.with(|c| c.set(self.0))
$tl.set(self.0)
}
}
pub fn $name() -> bool {
$tl.with(|c| c.get())
$tl.get()
}
)+
}

View file

@ -54,7 +54,7 @@ impl<'tcx> Region<'tcx> {
) -> Region<'tcx> {
// Use a pre-interned one when possible.
if let ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon } = bound_region
&& let Some(inner) = tcx.lifetimes.re_late_bounds.get(debruijn.as_usize())
&& let Some(inner) = tcx.lifetimes.anon_re_bounds.get(debruijn.as_usize())
&& let Some(re) = inner.get(var.as_usize()).copied()
{
re

View file

@ -485,7 +485,15 @@ impl<'tcx> Ty<'tcx> {
index: ty::DebruijnIndex,
bound_ty: ty::BoundTy,
) -> Ty<'tcx> {
Ty::new(tcx, Bound(index, bound_ty))
// Use a pre-interned one when possible.
if let ty::BoundTy { var, kind: ty::BoundTyKind::Anon } = bound_ty
&& let Some(inner) = tcx.types.anon_bound_tys.get(index.as_usize())
&& let Some(ty) = inner.get(var.as_usize()).copied()
{
ty
} else {
Ty::new(tcx, Bound(index, bound_ty))
}
}
#[inline]

View file

@ -86,10 +86,16 @@ mir_build_confused = missing patterns are not covered because `{$variable}` is i
mir_build_const_continue_bad_const = could not determine the target branch for this `#[const_continue]`
.label = this value is too generic
.note = the value must be a literal or a monomorphic const
mir_build_const_continue_missing_value = a `#[const_continue]` must break to a label with a value
mir_build_const_continue_not_const = could not determine the target branch for this `#[const_continue]`
.help = try extracting the expression into a `const` item
mir_build_const_continue_not_const_const_block = `const` blocks may use generics, and are not evaluated early enough
mir_build_const_continue_not_const_const_other = this value must be a literal or a monomorphic const
mir_build_const_continue_not_const_constant_parameter = constant parameters may use generics, and are not evaluated early enough
mir_build_const_continue_unknown_jump_target = the target of this `#[const_continue]` is not statically known
.label = this value must be a literal or a monomorphic const

View file

@ -100,7 +100,9 @@ use tracing::{debug, instrument};
use super::matches::BuiltMatchTree;
use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
use crate::errors::{ConstContinueBadConst, ConstContinueUnknownJumpTarget};
use crate::errors::{
ConstContinueBadConst, ConstContinueNotMonomorphicConst, ConstContinueUnknownJumpTarget,
};
#[derive(Debug)]
pub(crate) struct Scopes<'tcx> {
@ -867,7 +869,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
span_bug!(span, "break value must be a scope")
};
let constant = match &self.thir[value].kind {
let expr = &self.thir[value];
let constant = match &expr.kind {
ExprKind::Adt(box AdtExpr { variant_index, fields, base, .. }) => {
assert!(matches!(base, AdtExprBase::None));
assert!(fields.is_empty());
@ -887,7 +890,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
),
}
}
_ => self.as_constant(&self.thir[value]),
ExprKind::Literal { .. }
| ExprKind::NonHirLiteral { .. }
| ExprKind::ZstLiteral { .. }
| ExprKind::NamedConst { .. } => self.as_constant(&self.thir[value]),
other => {
use crate::errors::ConstContinueNotMonomorphicConstReason as Reason;
let span = expr.span;
let reason = match other {
ExprKind::ConstParam { .. } => Reason::ConstantParameter { span },
ExprKind::ConstBlock { .. } => Reason::ConstBlock { span },
_ => Reason::Other { span },
};
self.tcx
.dcx()
.emit_err(ConstContinueNotMonomorphicConst { span: expr.span, reason });
return block.unit();
}
};
let break_index = self

View file

@ -1213,6 +1213,38 @@ pub(crate) struct LoopMatchArmWithGuard {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_const_continue_not_const)]
#[help]
pub(crate) struct ConstContinueNotMonomorphicConst {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub reason: ConstContinueNotMonomorphicConstReason,
}
#[derive(Subdiagnostic)]
pub(crate) enum ConstContinueNotMonomorphicConstReason {
#[label(mir_build_const_continue_not_const_constant_parameter)]
ConstantParameter {
#[primary_span]
span: Span,
},
#[label(mir_build_const_continue_not_const_const_block)]
ConstBlock {
#[primary_span]
span: Span,
},
#[label(mir_build_const_continue_not_const_const_other)]
Other {
#[primary_span]
span: Span,
},
}
#[derive(Diagnostic)]
#[diag(mir_build_const_continue_bad_const)]
pub(crate) struct ConstContinueBadConst {

View file

@ -1,8 +1,7 @@
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::mir;
use rustc_middle::ty::TyCtxt;
use rustc_span::source_map::SourceMap;
use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span};
use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span};
use tracing::instrument;
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
@ -84,18 +83,8 @@ pub(super) fn extract_refined_covspans<'tcx>(
// Discard any span that overlaps with a hole.
discard_spans_overlapping_holes(&mut covspans, &holes);
// Discard spans that overlap in unwanted ways.
// Perform more refinement steps after holes have been dealt with.
let mut covspans = remove_unwanted_overlapping_spans(covspans);
// For all empty spans, either enlarge them to be non-empty, or discard them.
let source_map = tcx.sess.source_map();
covspans.retain_mut(|covspan| {
let Some(span) = ensure_non_empty_span(source_map, covspan.span) else { return false };
covspan.span = span;
true
});
// Merge covspans that can be merged.
covspans.dedup_by(|b, a| a.merge_if_eligible(b));
code_mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| {
@ -241,26 +230,3 @@ fn compare_spans(a: Span, b: Span) -> std::cmp::Ordering {
// - Both have the same start and span A extends further right
.then_with(|| Ord::cmp(&a.hi(), &b.hi()).reverse())
}
fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option<Span> {
if !span.is_empty() {
return Some(span);
}
// The span is empty, so try to enlarge it to cover an adjacent '{' or '}'.
source_map
.span_to_source(span, |src, start, end| try {
// Adjusting span endpoints by `BytePos(1)` is normally a bug,
// but in this case we have specifically checked that the character
// we're skipping over is one of two specific ASCII characters, so
// adjusting by exactly 1 byte is correct.
if src.as_bytes().get(end).copied() == Some(b'{') {
Some(span.with_hi(span.hi() + BytePos(1)))
} else if start > 0 && src.as_bytes()[start - 1] == b'}' {
Some(span.with_lo(span.lo() - BytePos(1)))
} else {
None
}
})
.ok()?
}

View file

@ -10,8 +10,8 @@ use rustc_type_ir::inherent::*;
use rustc_type_ir::lang_items::TraitSolverLangItem;
use rustc_type_ir::solve::SizedTraitKind;
use rustc_type_ir::{
self as ty, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt as _,
TypeVisitor, TypingMode, Upcast as _, elaborate,
self as ty, Interner, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable,
TypeVisitableExt as _, TypeVisitor, TypingMode, Upcast as _, elaborate,
};
use tracing::{debug, instrument};
@ -132,6 +132,7 @@ where
})
.enter(|ecx| {
Self::match_assumption(ecx, goal, assumption, |ecx| {
ecx.try_evaluate_added_goals()?;
source.set(ecx.characterize_param_env_assumption(goal.param_env, assumption)?);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
@ -1069,8 +1070,10 @@ where
} else {
ControlFlow::Continue(())
}
} else {
} else if ty.has_type_flags(TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_RE_INFER) {
ty.super_visit_with(self)
} else {
ControlFlow::Continue(())
}
}
@ -1086,8 +1089,10 @@ where
} else {
ControlFlow::Continue(())
}
} else {
} else if ct.has_type_flags(TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_RE_INFER) {
ct.super_visit_with(self)
} else {
ControlFlow::Continue(())
}
}

View file

@ -9,6 +9,7 @@ bitflags = "2.4.1"
rustc-literal-escaper = "0.0.5"
rustc_ast = { path = "../rustc_ast" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" }

View file

@ -1,11 +1,14 @@
//! Meta-syntax validation logic of attributes for post-expansion.
use std::slice;
use rustc_ast::token::Delimiter;
use rustc_ast::tokenstream::DelimSpan;
use rustc_ast::{
self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, NodeId,
Path, Safety,
};
use rustc_attr_parsing::{AttributeParser, Late};
use rustc_errors::{Applicability, DiagCtxtHandle, FatalError, PResult};
use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
use rustc_session::errors::report_lit_error;
@ -266,67 +269,7 @@ pub fn check_builtin_meta_item(
) {
if !is_attr_template_compatible(&template, &meta.kind) {
// attrs with new parsers are locally validated so excluded here
if matches!(
name,
sym::inline
| sym::export_stable
| sym::ffi_const
| sym::ffi_pure
| sym::rustc_std_internal_symbol
| sym::may_dangle
| sym::rustc_as_ptr
| sym::rustc_pub_transparent
| sym::rustc_const_stable_indirect
| sym::rustc_force_inline
| sym::rustc_confusables
| sym::rustc_skip_during_method_dispatch
| sym::rustc_pass_by_value
| sym::rustc_deny_explicit_impl
| sym::rustc_do_not_implement_via_object
| sym::rustc_coinductive
| sym::const_trait
| sym::stable
| sym::unstable
| sym::rustc_allowed_through_unstable_modules
| sym::rustc_specialization_trait
| sym::rustc_unsafe_specialization_marker
| sym::rustc_allow_incoherent_impl
| sym::rustc_coherence_is_core
| sym::marker
| sym::fundamental
| sym::rustc_paren_sugar
| sym::type_const
| sym::repr
// FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres
// ambiguity
| sym::rustc_align
| sym::deprecated
| sym::optimize
| sym::pointee
| sym::cold
| sym::target_feature
| sym::rustc_allow_const_fn_unstable
| sym::macro_use
| sym::macro_escape
| sym::naked
| sym::no_mangle
| sym::non_exhaustive
| sym::omit_gdb_pretty_printer_section
| sym::path
| sym::ignore
| sym::must_use
| sym::track_caller
| sym::link_name
| sym::link_ordinal
| sym::export_name
| sym::rustc_macro_transparency
| sym::link_section
| sym::rustc_layout_scalar_valid_range_start
| sym::rustc_layout_scalar_valid_range_end
| sym::no_implicit_prelude
| sym::automatically_derived
| sym::coverage
) {
if AttributeParser::<Late>::is_parsed_attribute(slice::from_ref(&name)) {
return;
}
emit_malformed_attribute(psess, style, meta.span, name, template);

View file

@ -130,6 +130,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
for attr in attrs {
let mut style = None;
match attr {
Attribute::Parsed(AttributeKind::ProcMacro(_)) => {
self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike)
}
Attribute::Parsed(AttributeKind::ProcMacroAttribute(_)) => {
self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute);
}
Attribute::Parsed(AttributeKind::ProcMacroDerive { span: attr_span, .. }) => {
self.check_generic_attr(
hir_id,
sym::proc_macro_derive,
*attr_span,
target,
Target::Fn,
);
self.check_proc_macro(hir_id, target, ProcMacroKind::Derive)
}
Attribute::Parsed(
AttributeKind::SkipDuringMethodDispatch { span: attr_span, .. }
| AttributeKind::Coinductive(attr_span)
@ -275,6 +291,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::MacroTransparency(_)
| AttributeKind::Pointee(..)
| AttributeKind::Dummy
| AttributeKind::RustcBuiltinMacro { .. }
| AttributeKind::OmitGdbPrettyPrinterSection,
) => { /* do nothing */ }
Attribute::Parsed(AttributeKind::AsPtr(attr_span)) => {
@ -373,16 +390,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
[sym::should_panic, ..] => {
self.check_generic_attr_unparsed(hir_id, attr, target, Target::Fn)
}
[sym::proc_macro, ..] => {
self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike)
}
[sym::proc_macro_attribute, ..] => {
self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute);
}
[sym::proc_macro_derive, ..] => {
self.check_generic_attr_unparsed(hir_id, attr, target, Target::Fn);
self.check_proc_macro(hir_id, target, ProcMacroKind::Derive)
}
[sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => {
self.check_autodiff(hir_id, attr, span, target)
}

View file

@ -950,9 +950,7 @@ impl<Cx: PatCx> Constructor<Cx> {
}
}
Never => write!(f, "!")?,
Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => {
write!(f, "_ : {:?}", ty)?
}
Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => write!(f, "_")?,
}
Ok(())
}

View file

@ -57,6 +57,13 @@ pub trait PatCx: Sized + fmt::Debug {
fn is_exhaustive_patterns_feature_on(&self) -> bool;
/// Whether to ensure the non-exhaustiveness witnesses we report for a complete set. This is
/// `false` by default to avoid some exponential blowup cases such as
/// <https://github.com/rust-lang/rust/issues/118437>.
fn exhaustive_witnesses(&self) -> bool {
false
}
/// The number of fields for this constructor.
fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize;

View file

@ -994,7 +994,8 @@ impl<Cx: PatCx> PlaceInfo<Cx> {
if !missing_ctors.is_empty() && !report_individual_missing_ctors {
// Report `_` as missing.
missing_ctors = vec![Constructor::Wildcard];
} else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) {
} else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) && !cx.exhaustive_witnesses()
{
// We need to report a `_` anyway, so listing other constructors would be redundant.
// `NonExhaustive` is displayed as `_` just like `Wildcard`, but it will be picked
// up by diagnostics to add a note about why `_` is required here.
@ -1747,7 +1748,9 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>(
// `ctor` is *irrelevant* if there's another constructor in `split_ctors` that matches
// strictly fewer rows. In that case we can sometimes skip it. See the top of the file for
// details.
let ctor_is_relevant = matches!(ctor, Constructor::Missing) || missing_ctors.is_empty();
let ctor_is_relevant = matches!(ctor, Constructor::Missing)
|| missing_ctors.is_empty()
|| mcx.tycx.exhaustive_witnesses();
let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor, ctor_is_relevant)?;
let mut witnesses = ensure_sufficient_stack(|| {
compute_exhaustiveness_and_usefulness(mcx, &mut spec_matrix)

View file

@ -1,3 +1,4 @@
#![allow(dead_code, unreachable_pub)]
use rustc_pattern_analysis::constructor::{
Constructor, ConstructorSet, IntRange, MaybeInfiniteInt, RangeEnd, VariantVisibility,
};
@ -22,8 +23,10 @@ fn init_tracing() {
.try_init();
}
pub(super) const UNIT: Ty = Ty::Tuple(&[]);
pub(super) const NEVER: Ty = Ty::Enum(&[]);
/// A simple set of types.
#[allow(dead_code)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(super) enum Ty {
/// Booleans
@ -38,6 +41,8 @@ pub(super) enum Ty {
BigStruct { arity: usize, ty: &'static Ty },
/// A enum with `arity` variants of type `ty`.
BigEnum { arity: usize, ty: &'static Ty },
/// Like `Enum` but non-exhaustive.
NonExhaustiveEnum(&'static [Ty]),
}
/// The important logic.
@ -47,7 +52,7 @@ impl Ty {
match (ctor, *self) {
(Struct, Ty::Tuple(tys)) => tys.iter().copied().collect(),
(Struct, Ty::BigStruct { arity, ty }) => (0..arity).map(|_| *ty).collect(),
(Variant(i), Ty::Enum(tys)) => vec![tys[*i]],
(Variant(i), Ty::Enum(tys) | Ty::NonExhaustiveEnum(tys)) => vec![tys[*i]],
(Variant(_), Ty::BigEnum { ty, .. }) => vec![*ty],
(Bool(..) | IntRange(..) | NonExhaustive | Missing | Wildcard, _) => vec![],
_ => panic!("Unexpected ctor {ctor:?} for type {self:?}"),
@ -61,6 +66,7 @@ impl Ty {
Ty::Enum(tys) => tys.iter().all(|ty| ty.is_empty()),
Ty::BigStruct { arity, ty } => arity != 0 && ty.is_empty(),
Ty::BigEnum { arity, ty } => arity == 0 || ty.is_empty(),
Ty::NonExhaustiveEnum(..) => false,
}
}
@ -90,6 +96,19 @@ impl Ty {
.collect(),
non_exhaustive: false,
},
Ty::NonExhaustiveEnum(tys) => ConstructorSet::Variants {
variants: tys
.iter()
.map(|ty| {
if ty.is_empty() {
VariantVisibility::Empty
} else {
VariantVisibility::Visible
}
})
.collect(),
non_exhaustive: true,
},
Ty::BigEnum { arity: 0, .. } => ConstructorSet::NoConstructors,
Ty::BigEnum { arity, ty } => {
let vis = if ty.is_empty() {
@ -113,7 +132,9 @@ impl Ty {
match (*self, ctor) {
(Ty::Tuple(..), _) => Ok(()),
(Ty::BigStruct { .. }, _) => write!(f, "BigStruct"),
(Ty::Enum(..), Constructor::Variant(i)) => write!(f, "Enum::Variant{i}"),
(Ty::Enum(..) | Ty::NonExhaustiveEnum(..), Constructor::Variant(i)) => {
write!(f, "Enum::Variant{i}")
}
(Ty::BigEnum { .. }, Constructor::Variant(i)) => write!(f, "BigEnum::Variant{i}"),
_ => write!(f, "{:?}::{:?}", self, ctor),
}
@ -126,10 +147,11 @@ pub(super) fn compute_match_usefulness<'p>(
ty: Ty,
scrut_validity: PlaceValidity,
complexity_limit: usize,
exhaustive_witnesses: bool,
) -> Result<UsefulnessReport<'p, Cx>, ()> {
init_tracing();
rustc_pattern_analysis::usefulness::compute_match_usefulness(
&Cx,
&Cx { exhaustive_witnesses },
arms,
ty,
scrut_validity,
@ -138,7 +160,9 @@ pub(super) fn compute_match_usefulness<'p>(
}
#[derive(Debug)]
pub(super) struct Cx;
pub(super) struct Cx {
exhaustive_witnesses: bool,
}
/// The context for pattern analysis. Forwards anything interesting to `Ty` methods.
impl PatCx for Cx {
@ -153,6 +177,10 @@ impl PatCx for Cx {
false
}
fn exhaustive_witnesses(&self) -> bool {
self.exhaustive_witnesses
}
fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize {
ty.sub_tys(ctor).len()
}
@ -219,16 +247,18 @@ macro_rules! pats {
// Entrypoint
// Parse `type; ..`
($ty:expr; $($rest:tt)*) => {{
#[allow(unused_imports)]
#[allow(unused)]
use rustc_pattern_analysis::{
constructor::{Constructor, IntRange, MaybeInfiniteInt, RangeEnd},
pat::DeconstructedPat,
pat::{DeconstructedPat, IndexedPat},
};
let ty = $ty;
// The heart of the macro is designed to push `IndexedPat`s into a `Vec`, so we work around
// that.
#[allow(unused)]
let sub_tys = ::std::iter::repeat(&ty);
let mut vec = Vec::new();
#[allow(unused)]
let mut vec: Vec<IndexedPat<_>> = Vec::new();
pats!(@ctor(vec:vec, sub_tys:sub_tys, idx:0) $($rest)*);
vec.into_iter().map(|ipat| ipat.pat).collect::<Vec<_>>()
}};
@ -263,6 +293,8 @@ macro_rules! pats {
let ctor = Constructor::Wildcard;
pats!(@pat($($args)*, ctor:ctor) $($rest)*)
}};
// Nothing
(@ctor($($args:tt)*)) => {};
// Integers and int ranges
(@ctor($($args:tt)*) $($start:literal)?..$end:literal $($rest:tt)*) => {{

View file

@ -16,7 +16,7 @@ fn check(patterns: &[DeconstructedPat<Cx>], complexity_limit: usize) -> Result<(
let ty = *patterns[0].ty();
let arms: Vec<_> =
patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect();
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, complexity_limit)
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, complexity_limit, false)
.map(|_report| ())
}

View file

@ -11,16 +11,30 @@ use rustc_pattern_analysis::usefulness::PlaceValidity;
mod common;
/// Analyze a match made of these patterns.
fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<WitnessPat<Cx>> {
let ty = *patterns[0].ty();
fn run(
ty: Ty,
patterns: Vec<DeconstructedPat<Cx>>,
exhaustive_witnesses: bool,
) -> Vec<WitnessPat<Cx>> {
let arms: Vec<_> =
patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect();
let report =
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, usize::MAX)
.unwrap();
let report = compute_match_usefulness(
arms.as_slice(),
ty,
PlaceValidity::ValidOnly,
usize::MAX,
exhaustive_witnesses,
)
.unwrap();
report.non_exhaustiveness_witnesses
}
/// Analyze a match made of these patterns. Panics if there are no patterns
fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<WitnessPat<Cx>> {
let ty = *patterns[0].ty();
run(ty, patterns, true)
}
#[track_caller]
fn assert_exhaustive(patterns: Vec<DeconstructedPat<Cx>>) {
let witnesses = check(patterns);
@ -35,6 +49,26 @@ fn assert_non_exhaustive(patterns: Vec<DeconstructedPat<Cx>>) {
assert!(!witnesses.is_empty())
}
use WhichWitnesses::*;
enum WhichWitnesses {
AllOfThem,
OnlySome,
}
#[track_caller]
/// We take the type as input to support empty matches.
fn assert_witnesses(
which: WhichWitnesses,
ty: Ty,
patterns: Vec<DeconstructedPat<Cx>>,
expected: Vec<&str>,
) {
let exhaustive_wit = matches!(which, AllOfThem);
let witnesses = run(ty, patterns, exhaustive_wit);
let witnesses: Vec<_> = witnesses.iter().map(|w| format!("{w:?}")).collect();
assert_eq!(witnesses, expected)
}
#[test]
fn test_int_ranges() {
let ty = Ty::U8;
@ -59,6 +93,8 @@ fn test_int_ranges() {
#[test]
fn test_nested() {
// enum E { A(bool), B(bool) }
// ty = (E, E)
let ty = Ty::BigStruct { arity: 2, ty: &Ty::BigEnum { arity: 2, ty: &Ty::Bool } };
assert_non_exhaustive(pats!(ty;
Struct(Variant.0, _),
@ -78,10 +114,74 @@ fn test_nested() {
));
}
#[test]
fn test_witnesses() {
// TY = Option<bool>
const TY: Ty = Ty::Enum(&[Ty::Bool, UNIT]);
// ty = (Option<bool>, Option<bool>)
let ty = Ty::Tuple(&[TY, TY]);
assert_witnesses(AllOfThem, ty, vec![], vec!["(_, _)"]);
assert_witnesses(
OnlySome,
ty,
pats!(ty;
(Variant.0(false), Variant.0(false)),
),
vec!["(Enum::Variant1(_), _)"],
);
assert_witnesses(
AllOfThem,
ty,
pats!(ty;
(Variant.0(false), Variant.0(false)),
),
vec![
"(Enum::Variant0(false), Enum::Variant0(true))",
"(Enum::Variant0(false), Enum::Variant1(_))",
"(Enum::Variant0(true), _)",
"(Enum::Variant1(_), _)",
],
);
assert_witnesses(
OnlySome,
ty,
pats!(ty;
(_, Variant.0(false)),
),
vec!["(_, Enum::Variant1(_))"],
);
assert_witnesses(
AllOfThem,
ty,
pats!(ty;
(_, Variant.0(false)),
),
vec!["(_, Enum::Variant0(true))", "(_, Enum::Variant1(_))"],
);
let ty = Ty::NonExhaustiveEnum(&[UNIT, UNIT, UNIT]);
assert_witnesses(
OnlySome,
ty,
pats!(ty;
Variant.0,
),
vec!["_"],
);
assert_witnesses(
AllOfThem,
ty,
pats!(ty;
Variant.0,
),
vec!["Enum::Variant1(_)", "Enum::Variant2(_)", "_"],
);
}
#[test]
fn test_empty() {
// `TY = Result<bool, !>`
const TY: Ty = Ty::Enum(&[Ty::Bool, Ty::Enum(&[])]);
const TY: Ty = Ty::Enum(&[Ty::Bool, NEVER]);
assert_exhaustive(pats!(TY;
Variant.0,
));

View file

@ -16,7 +16,7 @@ fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<Vec<usize>> {
let arms: Vec<_> =
patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect();
let report =
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, usize::MAX)
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, usize::MAX, false)
.unwrap();
report.arm_intersections.into_iter().map(|bitset| bitset.iter().collect()).collect()
}

View file

@ -720,7 +720,7 @@ fn incremental_verify_ich_failed<Tcx>(
static INSIDE_VERIFY_PANIC: Cell<bool> = const { Cell::new(false) };
};
let old_in_panic = INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.replace(true));
let old_in_panic = INSIDE_VERIFY_PANIC.replace(true);
if old_in_panic {
tcx.sess().dcx().emit_err(crate::error::Reentrant);
@ -739,7 +739,7 @@ fn incremental_verify_ich_failed<Tcx>(
panic!("Found unstable fingerprints for {dep_node:?}: {}", result());
}
INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.set(old_in_panic));
INSIDE_VERIFY_PANIC.set(old_in_panic);
}
/// Ensure that either this query has all green inputs or been executed.

View file

@ -23,9 +23,9 @@ use rustc_hir::def::{self, *};
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
use rustc_index::bit_set::DenseBitSet;
use rustc_metadata::creader::LoadedMacro;
use rustc_middle::bug;
use rustc_middle::metadata::ModChild;
use rustc_middle::ty::{Feed, Visibility};
use rustc_middle::{bug, span_bug};
use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind};
use rustc_span::{Ident, Span, Symbol, kw, sym};
use thin_vec::ThinVec;
@ -46,30 +46,59 @@ type Res = def::Res<NodeId>;
impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
/// Defines `name` in namespace `ns` of module `parent` to be `def` if it is not yet defined;
/// otherwise, reports an error.
pub(crate) fn define_binding(
pub(crate) fn define_binding_local(
&mut self,
parent: Module<'ra>,
ident: Ident,
ns: Namespace,
binding: NameBinding<'ra>,
) {
if let Err(old_binding) = self.try_define(parent, ident, ns, binding, false) {
if let Err(old_binding) = self.try_define_local(parent, ident, ns, binding, false) {
self.report_conflict(parent, ident, ns, old_binding, binding);
}
}
fn define(
fn define_local(
&mut self,
parent: Module<'ra>,
ident: Ident,
ns: Namespace,
res: Res,
vis: Visibility<impl Into<DefId>>,
vis: Visibility,
span: Span,
expn_id: LocalExpnId,
) {
let binding = self.arenas.new_res_binding(res, vis.to_def_id(), span, expn_id);
self.define_binding(parent, ident, ns, binding)
self.define_binding_local(parent, ident, ns, binding);
}
fn define_extern(
&self,
parent: Module<'ra>,
ident: Ident,
ns: Namespace,
res: Res,
vis: Visibility<DefId>,
span: Span,
expn_id: LocalExpnId,
) {
let binding = self.arenas.new_res_binding(res, vis, span, expn_id);
// Even if underscore names cannot be looked up, we still need to add them to modules,
// because they can be fetched by glob imports from those modules, and bring traits
// into scope both directly and through glob imports.
let key = BindingKey::new_disambiguated(ident, ns, || {
parent.underscore_disambiguator.update(|d| d + 1);
parent.underscore_disambiguator.get()
});
if self
.resolution_or_default(parent, key)
.borrow_mut()
.non_glob_binding
.replace(binding)
.is_some()
{
span_bug!(span, "an external binding was already defined");
}
}
/// Walks up the tree of definitions starting at `def_id`,
@ -192,7 +221,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
visitor.parent_scope.macro_rules
}
pub(crate) fn build_reduced_graph_external(&mut self, module: Module<'ra>) {
pub(crate) fn build_reduced_graph_external(&self, module: Module<'ra>) {
for child in self.tcx.module_children(module.def_id()) {
let parent_scope = ParentScope::module(module, self);
self.build_reduced_graph_for_external_crate_res(child, parent_scope)
@ -201,7 +230,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
/// Builds the reduced graph for a single item in an external crate.
fn build_reduced_graph_for_external_crate_res(
&mut self,
&self,
child: &ModChild,
parent_scope: ParentScope<'ra>,
) {
@ -232,7 +261,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
_,
)
| Res::PrimTy(..)
| Res::ToolMod => self.define(parent, ident, TypeNS, res, vis, span, expansion),
| Res::ToolMod => self.define_extern(parent, ident, TypeNS, res, vis, span, expansion),
Res::Def(
DefKind::Fn
| DefKind::AssocFn
@ -241,9 +270,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
| DefKind::AssocConst
| DefKind::Ctor(..),
_,
) => self.define(parent, ident, ValueNS, res, vis, span, expansion),
) => self.define_extern(parent, ident, ValueNS, res, vis, span, expansion),
Res::Def(DefKind::Macro(..), _) | Res::NonMacroAttr(..) => {
self.define(parent, ident, MacroNS, res, vis, span, expansion)
self.define_extern(parent, ident, MacroNS, res, vis, span, expansion)
}
Res::Def(
DefKind::TyParam
@ -452,8 +481,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
self.r.per_ns(|this, ns| {
if !type_ns_only || ns == TypeNS {
let key = BindingKey::new(target, ns);
let mut resolution = this.resolution(current_module, key).borrow_mut();
resolution.single_imports.insert(import);
this.resolution_or_default(current_module, key)
.borrow_mut()
.single_imports
.insert(import);
}
});
}
@ -709,7 +740,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
let expansion = parent_scope.expansion;
// Define a name in the type namespace if it is not anonymous.
self.r.define(parent, ident, TypeNS, adt_res, adt_vis, adt_span, expansion);
self.r.define_local(parent, ident, TypeNS, adt_res, adt_vis, adt_span, expansion);
self.r.feed_visibility(feed, adt_vis);
let def_id = feed.key();
@ -761,7 +792,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
}
ItemKind::Mod(_, ident, ref mod_kind) => {
self.r.define(parent, ident, TypeNS, res, vis, sp, expansion);
self.r.define_local(parent, ident, TypeNS, res, vis, sp, expansion);
if let ast::ModKind::Loaded(_, _, _, Err(_)) = mod_kind {
self.r.mods_with_parse_errors.insert(def_id);
@ -780,10 +811,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
ItemKind::Const(box ConstItem { ident, .. })
| ItemKind::Delegation(box Delegation { ident, .. })
| ItemKind::Static(box StaticItem { ident, .. }) => {
self.r.define(parent, ident, ValueNS, res, vis, sp, expansion);
self.r.define_local(parent, ident, ValueNS, res, vis, sp, expansion);
}
ItemKind::Fn(box Fn { ident, .. }) => {
self.r.define(parent, ident, ValueNS, res, vis, sp, expansion);
self.r.define_local(parent, ident, ValueNS, res, vis, sp, expansion);
// Functions introducing procedural macros reserve a slot
// in the macro namespace as well (see #52225).
@ -792,11 +823,11 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
// These items live in the type namespace.
ItemKind::TyAlias(box TyAlias { ident, .. }) | ItemKind::TraitAlias(ident, ..) => {
self.r.define(parent, ident, TypeNS, res, vis, sp, expansion);
self.r.define_local(parent, ident, TypeNS, res, vis, sp, expansion);
}
ItemKind::Enum(ident, _, _) | ItemKind::Trait(box ast::Trait { ident, .. }) => {
self.r.define(parent, ident, TypeNS, res, vis, sp, expansion);
self.r.define_local(parent, ident, TypeNS, res, vis, sp, expansion);
self.parent_scope.module = self.r.new_local_module(
Some(parent),
@ -848,7 +879,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
let feed = self.r.feed(ctor_node_id);
let ctor_def_id = feed.key();
let ctor_res = self.res(ctor_def_id);
self.r.define(parent, ident, ValueNS, ctor_res, ctor_vis, sp, expansion);
self.r.define_local(parent, ident, ValueNS, ctor_res, ctor_vis, sp, expansion);
self.r.feed_visibility(feed, ctor_vis);
// We need the field visibility spans also for the constructor for E0603.
self.insert_field_visibilities_local(ctor_def_id.to_def_id(), vdata.fields());
@ -972,7 +1003,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
);
}
}
self.r.define_binding(parent, ident, TypeNS, imported_binding);
self.r.define_binding_local(parent, ident, TypeNS, imported_binding);
}
/// Constructs the reduced graph for one foreign item.
@ -989,7 +1020,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
let parent = self.parent_scope.module;
let expansion = self.parent_scope.expansion;
let vis = self.resolve_visibility(&item.vis);
self.r.define(parent, ident, ns, self.res(def_id), vis, item.span, expansion);
self.r.define_local(parent, ident, ns, self.res(def_id), vis, item.span, expansion);
self.r.feed_visibility(feed, vis);
}
@ -1072,7 +1103,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
if let Some(span) = import_all {
let import = macro_use_import(self, span, false);
self.r.potentially_unused_imports.push(import);
module.for_each_child(self, |this, ident, ns, binding| {
module.for_each_child_mut(self, |this, ident, ns, binding| {
if ns == MacroNS {
let import = if this.r.is_accessible_from(binding.vis, this.parent_scope.module)
{
@ -1237,7 +1268,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
});
self.r.import_use_map.insert(import, Used::Other);
let import_binding = self.r.import(binding, import);
self.r.define_binding(self.r.graph_root, ident, MacroNS, import_binding);
self.r.define_binding_local(self.r.graph_root, ident, MacroNS, import_binding);
} else {
self.r.check_reserved_macro_name(ident, res);
self.insert_unused_macro(ident, def_id, item.id);
@ -1265,7 +1296,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
if !vis.is_public() {
self.insert_unused_macro(ident, def_id, item.id);
}
self.r.define(module, ident, MacroNS, res, vis, span, expansion);
self.r.define_local(module, ident, MacroNS, res, vis, span, expansion);
self.r.feed_visibility(feed, vis);
self.parent_scope.macro_rules
}
@ -1401,7 +1432,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
if ctxt == AssocCtxt::Trait {
let parent = self.parent_scope.module;
let expansion = self.parent_scope.expansion;
self.r.define(parent, ident, ns, self.res(def_id), vis, item.span, expansion);
self.r.define_local(parent, ident, ns, self.res(def_id), vis, item.span, expansion);
} else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob)
&& ident.name != kw::Underscore
{
@ -1489,7 +1520,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
let feed = self.r.feed(variant.id);
let def_id = feed.key();
let vis = self.resolve_visibility(&variant.vis);
self.r.define(parent, ident, TypeNS, self.res(def_id), vis, variant.span, expn_id);
self.r.define_local(parent, ident, TypeNS, self.res(def_id), vis, variant.span, expn_id);
self.r.feed_visibility(feed, vis);
// If the variant is marked as non_exhaustive then lower the visibility to within the crate.
@ -1505,7 +1536,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
let feed = self.r.feed(ctor_node_id);
let ctor_def_id = feed.key();
let ctor_res = self.res(ctor_def_id);
self.r.define(parent, ident, ValueNS, ctor_res, ctor_vis, variant.span, expn_id);
self.r.define_local(parent, ident, ValueNS, ctor_res, ctor_vis, variant.span, expn_id);
self.r.feed_visibility(feed, ctor_vis);
}

View file

@ -509,9 +509,7 @@ impl Resolver<'_, '_> {
let mut check_redundant_imports = FxIndexSet::default();
for module in self.arenas.local_modules().iter() {
for (_key, resolution) in self.resolutions(*module).borrow().iter() {
let resolution = resolution.borrow();
if let Some(binding) = resolution.best_binding()
if let Some(binding) = resolution.borrow().best_binding()
&& let NameBindingKind::Import { import, .. } = binding.kind
&& let ImportKind::Single { id, .. } = import.kind
{

View file

@ -523,7 +523,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
pub(crate) fn add_module_candidates(
&mut self,
&self,
module: Module<'ra>,
names: &mut Vec<TypoSuggestion>,
filter_fn: &impl Fn(Res) -> bool,
@ -1150,7 +1150,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
fn lookup_import_candidates_from_module<FilterFn>(
&mut self,
&self,
lookup_ident: Ident,
namespace: Namespace,
parent_scope: &ParentScope<'ra>,
@ -2659,10 +2659,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
return None;
}
let resolutions = self.resolutions(crate_module).borrow();
let binding_key = BindingKey::new(ident, MacroNS);
let resolution = resolutions.get(&binding_key)?;
let binding = resolution.borrow().binding()?;
let binding = self.resolution(crate_module, binding_key)?.binding()?;
let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() else {
return None;
};

View file

@ -114,9 +114,7 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> {
/// including their whole reexport chains.
fn set_bindings_effective_visibilities(&mut self, module_id: LocalDefId) {
let module = self.r.expect_module(module_id.to_def_id());
let resolutions = self.r.resolutions(module);
for (_, name_resolution) in resolutions.borrow().iter() {
for (_, name_resolution) in self.r.resolutions(module).borrow().iter() {
let Some(mut binding) = name_resolution.borrow().binding() else {
continue;
};

View file

@ -848,8 +848,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
};
let key = BindingKey::new(ident, ns);
let resolution =
self.resolution(module, key).try_borrow_mut().map_err(|_| (Determined, Weak::No))?; // This happens when there is a cycle of imports.
// `try_borrow_mut` is required to ensure exclusive access, even if the resulting binding
// doesn't need to be mutable. It will fail when there is a cycle of imports, and without
// the exclusive access infinite recursion will crash the compiler with stack overflow.
let resolution = &*self
.resolution_or_default(module, key)
.try_borrow_mut()
.map_err(|_| (Determined, Weak::No))?;
// If the primary binding is unusable, search further and return the shadowed glob
// binding if it exists. What we really want here is having two separate scopes in

View file

@ -334,8 +334,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
/// Define the name or return the existing binding if there is a collision.
/// `update` indicates if the definition is a redefinition of an existing binding.
pub(crate) fn try_define(
pub(crate) fn try_define_local(
&mut self,
module: Module<'ra>,
ident: Ident,
@ -353,7 +352,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
module.underscore_disambiguator.update(|d| d + 1);
module.underscore_disambiguator.get()
});
self.update_resolution(module, key, warn_ambiguity, |this, resolution| {
self.update_local_resolution(module, key, warn_ambiguity, |this, resolution| {
if let Some(old_binding) = resolution.best_binding() {
if res == Res::Err && old_binding.res() != Res::Err {
// Do not override real bindings with `Res::Err`s from error recovery.
@ -456,7 +455,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// Use `f` to mutate the resolution of the name in the module.
// If the resolution becomes a success, define it in the module's glob importers.
fn update_resolution<T, F>(
fn update_local_resolution<T, F>(
&mut self,
module: Module<'ra>,
key: BindingKey,
@ -469,7 +468,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// Ensure that `resolution` isn't borrowed when defining in the module's glob importers,
// during which the resolution might end up getting re-defined via a glob cycle.
let (binding, t, warn_ambiguity) = {
let resolution = &mut *self.resolution(module, key).borrow_mut();
let resolution = &mut *self.resolution_or_default(module, key).borrow_mut();
let old_binding = resolution.binding();
let t = f(self, resolution);
@ -497,7 +496,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
};
if self.is_accessible_from(binding.vis, scope) {
let imported_binding = self.import(binding, *import);
let _ = self.try_define(
let _ = self.try_define_local(
import.parent_scope.module,
ident,
key.ns,
@ -523,11 +522,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let dummy_binding = self.import(dummy_binding, import);
self.per_ns(|this, ns| {
let module = import.parent_scope.module;
let _ = this.try_define(module, target, ns, dummy_binding, false);
let _ = this.try_define_local(module, target, ns, dummy_binding, false);
// Don't remove underscores from `single_imports`, they were never added.
if target.name != kw::Underscore {
let key = BindingKey::new(target, ns);
this.update_resolution(module, key, false, |_, resolution| {
this.update_local_resolution(module, key, false, |_, resolution| {
resolution.single_imports.swap_remove(&import);
})
}
@ -651,7 +650,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
for module in self.arenas.local_modules().iter() {
for (key, resolution) in self.resolutions(*module).borrow().iter() {
let resolution = resolution.borrow();
let Some(binding) = resolution.best_binding() else { continue };
if let NameBindingKind::Import { import, .. } = binding.kind
@ -903,14 +901,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
// We need the `target`, `source` can be extracted.
let imported_binding = this.import(binding, import);
this.define_binding(parent, target, ns, imported_binding);
this.define_binding_local(parent, target, ns, imported_binding);
PendingBinding::Ready(Some(imported_binding))
}
Err(Determinacy::Determined) => {
// Don't remove underscores from `single_imports`, they were never added.
if target.name != kw::Underscore {
let key = BindingKey::new(target, ns);
this.update_resolution(parent, key, false, |_, resolution| {
this.update_local_resolution(parent, key, false, |_, resolution| {
resolution.single_imports.swap_remove(&import);
});
}
@ -1202,41 +1200,39 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
});
return if all_ns_failed {
let resolutions = match module {
ModuleOrUniformRoot::Module(module) => Some(self.resolutions(module).borrow()),
_ => None,
};
let resolutions = resolutions.as_ref().into_iter().flat_map(|r| r.iter());
let names = resolutions
.filter_map(|(BindingKey { ident: i, .. }, resolution)| {
if i.name == ident.name {
return None;
} // Never suggest the same name
match *resolution.borrow() {
ref resolution
if let Some(name_binding) = resolution.best_binding() =>
{
match name_binding.kind {
NameBindingKind::Import { binding, .. } => {
match binding.kind {
// Never suggest the name that has binding error
// i.e., the name that cannot be previously resolved
NameBindingKind::Res(Res::Err) => None,
_ => Some(i.name),
let names = match module {
ModuleOrUniformRoot::Module(module) => {
self.resolutions(module)
.borrow()
.iter()
.filter_map(|(BindingKey { ident: i, .. }, resolution)| {
if i.name == ident.name {
return None;
} // Never suggest the same name
let resolution = resolution.borrow();
if let Some(name_binding) = resolution.best_binding() {
match name_binding.kind {
NameBindingKind::Import { binding, .. } => {
match binding.kind {
// Never suggest the name that has binding error
// i.e., the name that cannot be previously resolved
NameBindingKind::Res(Res::Err) => None,
_ => Some(i.name),
}
}
_ => Some(i.name),
}
_ => Some(i.name),
} else if resolution.single_imports.is_empty() {
None
} else {
Some(i.name)
}
}
NameResolution { ref single_imports, .. }
if single_imports.is_empty() =>
{
None
}
_ => Some(i.name),
}
})
.collect::<Vec<Symbol>>();
})
.collect()
}
_ => Vec::new(),
};
let lev_suggestion =
find_best_match_for_name(&names, ident.name, None).map(|suggestion| {
@ -1517,10 +1513,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let imported_binding = self.import(binding, import);
let warn_ambiguity = self
.resolution(import.parent_scope.module, key)
.borrow()
.binding()
.and_then(|r| r.binding())
.is_some_and(|binding| binding.warn_ambiguity_recursive());
let _ = self.try_define(
let _ = self.try_define_local(
import.parent_scope.module,
key.ident,
key.ns,

View file

@ -3449,8 +3449,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
};
ident.span.normalize_to_macros_2_0_and_adjust(module.expansion);
let key = BindingKey::new(ident, ns);
let mut binding =
self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.best_binding());
let mut binding = self.r.resolution(module, key).and_then(|r| r.best_binding());
debug!(?binding);
if binding.is_none() {
// We could not find the trait item in the correct namespace.
@ -3461,8 +3460,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
_ => ns,
};
let key = BindingKey::new(ident, ns);
binding =
self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.best_binding());
binding = self.r.resolution(module, key).and_then(|r| r.best_binding());
debug!(?binding);
}

View file

@ -1461,15 +1461,17 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
self.resolve_path(mod_path, None, None)
{
let resolutions = self.r.resolutions(module).borrow();
let targets: Vec<_> = resolutions
let targets: Vec<_> = self
.r
.resolutions(module)
.borrow()
.iter()
.filter_map(|(key, resolution)| {
resolution
.borrow()
.best_binding()
.map(|binding| binding.res())
.and_then(|res| if filter_fn(res) { Some((key, res)) } else { None })
.and_then(|res| if filter_fn(res) { Some((*key, res)) } else { None })
})
.collect();
if let [target] = targets.as_slice() {
@ -2300,8 +2302,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
return None;
}
let resolutions = self.r.resolutions(*module);
let targets = resolutions
let targets = self
.r
.resolutions(*module)
.borrow()
.iter()
.filter_map(|(key, res)| {
@ -2630,7 +2633,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
false
}
fn find_module(&mut self, def_id: DefId) -> Option<(Module<'ra>, ImportSuggestion)> {
fn find_module(&self, def_id: DefId) -> Option<(Module<'ra>, ImportSuggestion)> {
let mut result = None;
let mut seen_modules = FxHashSet::default();
let root_did = self.r.graph_root.def_id();
@ -2687,7 +2690,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
result
}
fn collect_enum_ctors(&mut self, def_id: DefId) -> Option<Vec<(Path, DefId, CtorKind)>> {
fn collect_enum_ctors(&self, def_id: DefId) -> Option<Vec<(Path, DefId, CtorKind)>> {
self.find_module(def_id).map(|(enum_module, enum_import_suggestion)| {
let mut variants = Vec::new();
enum_module.for_each_child(self.r, |_, ident, _, name_binding| {
@ -2704,7 +2707,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
/// Adds a suggestion for using an enum's variant when an enum is used instead.
fn suggest_using_enum_variant(
&mut self,
&self,
err: &mut Diag<'_>,
source: PathSource<'_, '_, '_>,
def_id: DefId,

View file

@ -21,7 +21,7 @@
#![recursion_limit = "256"]
// tidy-alphabetical-end
use std::cell::{Cell, RefCell};
use std::cell::{Cell, Ref, RefCell};
use std::collections::BTreeSet;
use std::fmt;
use std::sync::Arc;
@ -656,11 +656,23 @@ impl<'ra> ModuleData<'ra> {
}
impl<'ra> Module<'ra> {
fn for_each_child<'tcx, R, F>(self, resolver: &mut R, mut f: F)
where
R: AsMut<Resolver<'ra, 'tcx>>,
F: FnMut(&mut R, Ident, Namespace, NameBinding<'ra>),
{
fn for_each_child<'tcx, R: AsRef<Resolver<'ra, 'tcx>>>(
self,
resolver: &R,
mut f: impl FnMut(&R, Ident, Namespace, NameBinding<'ra>),
) {
for (key, name_resolution) in resolver.as_ref().resolutions(self).borrow().iter() {
if let Some(binding) = name_resolution.borrow().best_binding() {
f(resolver, key.ident, key.ns, binding);
}
}
}
fn for_each_child_mut<'tcx, R: AsMut<Resolver<'ra, 'tcx>>>(
self,
resolver: &mut R,
mut f: impl FnMut(&mut R, Ident, Namespace, NameBinding<'ra>),
) {
for (key, name_resolution) in resolver.as_mut().resolutions(self).borrow().iter() {
if let Some(binding) = name_resolution.borrow().best_binding() {
f(resolver, key.ident, key.ns, binding);
@ -669,10 +681,7 @@ impl<'ra> Module<'ra> {
}
/// This modifies `self` in place. The traits will be stored in `self.traits`.
fn ensure_traits<'tcx, R>(self, resolver: &mut R)
where
R: AsMut<Resolver<'ra, 'tcx>>,
{
fn ensure_traits<'tcx>(self, resolver: &impl AsRef<Resolver<'ra, 'tcx>>) {
let mut traits = self.traits.borrow_mut();
if traits.is_none() {
let mut collected_traits = Vec::new();
@ -681,7 +690,7 @@ impl<'ra> Module<'ra> {
return;
}
if let Res::Def(DefKind::Trait | DefKind::TraitAlias, def_id) = binding.res() {
collected_traits.push((name, binding, r.as_mut().get_module(def_id)))
collected_traits.push((name, binding, r.as_ref().get_module(def_id)))
}
});
*traits = Some(collected_traits.into_boxed_slice());
@ -1340,6 +1349,12 @@ impl<'ra, 'tcx> AsMut<Resolver<'ra, 'tcx>> for Resolver<'ra, 'tcx> {
}
}
impl<'ra, 'tcx> AsRef<Resolver<'ra, 'tcx>> for Resolver<'ra, 'tcx> {
fn as_ref(&self) -> &Resolver<'ra, 'tcx> {
self
}
}
impl<'tcx> Resolver<'_, 'tcx> {
fn opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> {
self.opt_feed(node).map(|f| f.key())
@ -1861,7 +1876,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// We don't reject trait aliases (`trait_module == None`) because we don't have access to their
// associated items.
fn trait_may_have_item(
&mut self,
&self,
trait_module: Option<Module<'ra>>,
assoc_item: Option<(Symbol, Namespace)>,
) -> bool {
@ -1893,7 +1908,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
import_ids
}
fn resolutions(&mut self, module: Module<'ra>) -> &'ra Resolutions<'ra> {
fn resolutions(&self, module: Module<'ra>) -> &'ra Resolutions<'ra> {
if module.populate_on_access.get() {
module.populate_on_access.set(false);
self.build_reduced_graph_external(module);
@ -1902,12 +1917,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
fn resolution(
&mut self,
&self,
module: Module<'ra>,
key: BindingKey,
) -> Option<Ref<'ra, NameResolution<'ra>>> {
self.resolutions(module).borrow().get(&key).map(|resolution| resolution.borrow())
}
fn resolution_or_default(
&self,
module: Module<'ra>,
key: BindingKey,
) -> &'ra RefCell<NameResolution<'ra>> {
*self
.resolutions(module)
self.resolutions(module)
.borrow_mut()
.entry(key)
.or_insert_with(|| self.arenas.alloc_name_resolution())

View file

@ -511,7 +511,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
}
fn glob_delegation_suffixes(
&mut self,
&self,
trait_def_id: DefId,
impl_def_id: LocalDefId,
) -> Result<Vec<(Ident, Option<Ident>)>, Indeterminate> {

View file

@ -313,6 +313,7 @@ impl Session {
|| self.opts.unstable_opts.query_dep_graph
|| self.opts.unstable_opts.dump_mir.is_some()
|| self.opts.unstable_opts.unpretty.is_some()
|| self.prof.is_args_recording_enabled()
|| self.opts.output_types.contains_key(&OutputType::Mir)
|| std::env::var_os("RUSTC_LOG").is_some()
{

View file

@ -6,7 +6,7 @@ pub(crate) fn target() -> Target {
let mut base = base::linux_musl::opts();
base.max_atomic_width = Some(128);
base.supports_xray = true;
base.features = "+v8a".into();
base.features = "+v8a,+outline-atomics".into();
base.stack_probes = StackProbeType::Inline;
base.supported_sanitizers = SanitizerSet::ADDRESS
| SanitizerSet::CFI

View file

@ -80,8 +80,11 @@ fn sizedness_constraint_for_ty<'tcx>(
fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
match tcx.hir_node_by_def_id(def_id) {
hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.defaultness,
hir::Node::ImplItem(hir::ImplItem { defaultness, .. })
hir::Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { defaultness, of_trait: Some(_), .. }),
..
})
| hir::Node::ImplItem(hir::ImplItem { defaultness, .. })
| hir::Node::TraitItem(hir::TraitItem { defaultness, .. }) => *defaultness,
node => {
bug!("`defaultness` called on {:?}", node);

View file

@ -400,8 +400,12 @@ pub fn structurally_relate_tys<I: Interner, R: TypeRelation<I>>(
(ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => Ok(a),
(ty::Adt(a_def, a_args), ty::Adt(b_def, b_args)) if a_def == b_def => {
let args = relation.relate_item_args(a_def.def_id(), a_args, b_args)?;
Ok(Ty::new_adt(cx, a_def, args))
Ok(if a_args.is_empty() {
a
} else {
let args = relation.relate_item_args(a_def.def_id(), a_args, b_args)?;
if args == a_args { a } else { Ty::new_adt(cx, a_def, args) }
})
}
(ty::Foreign(a_id), ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(cx, a_id)),
@ -515,8 +519,12 @@ pub fn structurally_relate_tys<I: Interner, R: TypeRelation<I>>(
}
(ty::FnDef(a_def_id, a_args), ty::FnDef(b_def_id, b_args)) if a_def_id == b_def_id => {
let args = relation.relate_item_args(a_def_id, a_args, b_args)?;
Ok(Ty::new_fn_def(cx, a_def_id, args))
Ok(if a_args.is_empty() {
a
} else {
let args = relation.relate_item_args(a_def_id, a_args, b_args)?;
if args == a_args { a } else { Ty::new_fn_def(cx, a_def_id, args) }
})
}
(ty::FnPtr(a_sig_tys, a_hdr), ty::FnPtr(b_sig_tys, b_hdr)) => {

View file

@ -78,9 +78,9 @@ dependencies = [
[[package]]
name = "dlmalloc"
version = "0.2.9"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d01597dde41c0b9da50d5f8c219023d63d8f27f39a27095070fd191fddc83891"
checksum = "fa3a2dbee57b69fbb5dbe852fa9c0925697fb0c7fbcb1593e90e5ffaedf13d51"
dependencies = [
"cfg-if",
"libc",
@ -90,11 +90,10 @@ dependencies = [
[[package]]
name = "fortanix-sgx-abi"
version = "0.5.0"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57cafc2274c10fab234f176b25903ce17e690fca7597090d50880e047a0389c5"
checksum = "5efc85edd5b83e8394f4371dd0da6859dff63dd387dab8568fece6af4cde6f84"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
]

View file

@ -426,8 +426,10 @@ pub macro debug_assert_matches($($arg:tt)*) {
#[macro_export]
#[stable(feature = "matches_macro", since = "1.42.0")]
#[rustc_diagnostic_item = "matches_macro"]
#[allow_internal_unstable(non_exhaustive_omitted_patterns_lint, stmt_expr_attributes)]
macro_rules! matches {
($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
#[allow(non_exhaustive_omitted_patterns)]
match $expression {
$pattern $(if $guard)? => true,
_ => false

View file

@ -76,6 +76,7 @@
#![feature(min_specialization)]
#![feature(never_type)]
#![feature(next_index)]
#![feature(non_exhaustive_omitted_patterns_lint)]
#![feature(numfmt)]
#![feature(pattern)]
#![feature(pointer_is_aligned_to)]

View file

@ -213,3 +213,9 @@ fn _expression() {
}
);
}
#[deny(non_exhaustive_omitted_patterns)]
fn _matches_does_not_trigger_non_exhaustive_omitted_patterns_lint(o: core::sync::atomic::Ordering) {
// Ordering is a #[non_exhaustive] enum from a separate crate
let _m = matches!(o, core::sync::atomic::Ordering::Relaxed);
}

View file

@ -63,10 +63,10 @@ rand = { version = "0.9.0", default-features = false, features = ["alloc"] }
rand_xorshift = "0.4.0"
[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies]
dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] }
dlmalloc = { version = "0.2.10", features = ['rustc-dep-of-std'] }
[target.x86_64-fortanix-unknown-sgx.dependencies]
fortanix-sgx-abi = { version = "0.5.0", features = [
fortanix-sgx-abi = { version = "0.6.1", features = [
'rustc-dep-of-std',
], public = true }

View file

@ -617,7 +617,7 @@ impl Error for JoinPathsError {
/// # Unix
///
/// - Returns the value of the 'HOME' environment variable if it is set
/// (including to an empty string).
/// (and not an empty string).
/// - Otherwise, it tries to determine the home directory by invoking the `getpwuid_r` function
/// using the UID of the current user. An empty home directory field returned from the
/// `getpwuid_r` function is considered to be a valid value.

View file

@ -267,7 +267,7 @@ pub fn send(event_set: u64, tcs: Option<Tcs>) -> IoResult<()> {
/// Usercall `insecure_time`. See the ABI documentation for more information.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub fn insecure_time() -> Duration {
let t = unsafe { raw::insecure_time() };
let t = unsafe { raw::insecure_time().0 };
Duration::new(t / 1_000_000_000, (t % 1_000_000_000) as _)
}

View file

@ -633,7 +633,10 @@ pub fn temp_dir() -> PathBuf {
}
pub fn home_dir() -> Option<PathBuf> {
return crate::env::var_os("HOME").or_else(|| unsafe { fallback() }).map(PathBuf::from);
return crate::env::var_os("HOME")
.filter(|s| !s.is_empty())
.or_else(|| unsafe { fallback() })
.map(PathBuf::from);
#[cfg(any(
target_os = "android",

View file

@ -1399,6 +1399,11 @@ where
}
/// The internal representation of a `Thread` handle
///
/// We explicitly set the alignment for our guarantee in Thread::into_raw. This
/// allows applications to stuff extra metadata bits into the alignment, which
/// can be rather useful when working with atomics.
#[repr(align(8))]
struct Inner {
name: Option<ThreadNameString>,
id: ThreadId,
@ -1582,7 +1587,8 @@ impl Thread {
/// Consumes the `Thread`, returning a raw pointer.
///
/// To avoid a memory leak the pointer must be converted
/// back into a `Thread` using [`Thread::from_raw`].
/// back into a `Thread` using [`Thread::from_raw`]. The pointer is
/// guaranteed to be aligned to at least 8 bytes.
///
/// # Examples
///

View file

@ -191,6 +191,16 @@ unsafe extern "C-unwind" {
// #[cfg_attr(test, assert_instr(throw, TAG = 0, ptr = core::ptr::null_mut()))]
#[inline]
#[unstable(feature = "wasm_exception_handling_intrinsics", issue = "122465")]
// FIXME: Since this instruction unwinds, `core` built with `-C panic=unwind`
// cannot be linked with `-C panic=abort` programs. But that's not
// entirely supported anyway, because runtimes without EH support won't
// be able to handle `try` blocks in `-C panic=unwind` crates either.
// We ship `-C panic=abort` `core`, so this doesn't affect users
// directly. Resolving this will likely require patching out both `try`
// and `throw` instructions, at which point we can look into whitelisting
// this function in the compiler to allow linking.
// See https://github.com/rust-lang/rust/issues/118168.
#[allow(ffi_unwind_calls)]
pub unsafe fn throw<const TAG: i32>(ptr: *mut u8) -> ! {
static_assert!(TAG == 0); // LLVM only supports tag 0 == C++ right now.
wasm_throw(TAG, ptr)

View file

@ -34,22 +34,12 @@ pub macro link_dylib {
#[cfg(feature = "windows_raw_dylib")]
pub macro link($($tt:tt)*) {
$crate::link_raw_dylib!($($tt)*)
$crate::link_raw_dylib!($($tt)*);
}
#[cfg(not(feature = "windows_raw_dylib"))]
pub macro link {
($library:literal $abi:literal $($link_name:literal)? $(#[$doc:meta])? fn $($function:tt)*) => (
// Note: the windows-targets crate uses a pre-built Windows.lib import library which we don't
// have in this repo. So instead we always link kernel32.lib and add the rest of the import
// libraries below by using an empty extern block. This works because extern blocks are not
// connected to the library given in the #[link] attribute.
#[link(name = "kernel32")]
unsafe extern $abi {
$(#[link_name=$link_name])?
pub fn $($function)*;
}
)
pub macro link($($tt:tt)*) {
$crate::link_dylib!($($tt)*);
}
#[cfg(not(feature = "windows_raw_dylib"))]

View file

@ -19,6 +19,7 @@ const IGNORED_RULES_FOR_STD_AND_RUSTC: &[&str] = &[
"too_many_arguments",
"needless_lifetimes", // people want to keep the lifetimes
"wrong_self_convention",
"approx_constant", // libcore is what defines those
];
fn lint_args(builder: &Builder<'_>, config: &LintConfig, ignored_rules: &[&str]) -> Vec<String> {

View file

@ -597,11 +597,6 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car
let mut features = String::new();
if stage != 0 && builder.config.default_codegen_backend(target).as_deref() == Some("cranelift")
{
features += "compiler-builtins-no-f16-f128 ";
}
if builder.no_std(target) == Some(true) {
features += " compiler-builtins-mem";
if !target.starts_with("bpf") {

View file

@ -3132,7 +3132,11 @@ impl Step for Bootstrap {
}
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/bootstrap")
// Bootstrap tests might not be perfectly self-contained and can depend on the external
// environment, submodules that are checked out, etc.
// Therefore we only run them by default on CI.
let runs_on_ci = run.builder.config.is_running_on_ci;
run.path("src/bootstrap").default_condition(runs_on_ci)
}
fn make_run(run: RunConfig<'_>) {

View file

@ -338,12 +338,6 @@ than building it.
// Make sure musl-root is valid.
if target.contains("musl") && !target.contains("unikraft") {
// If this is a native target (host is also musl) and no musl-root is given,
// fall back to the system toolchain in /usr before giving up
if build.musl_root(*target).is_none() && build.config.is_host_target(*target) {
let target = build.config.target_config.entry(*target).or_default();
target.musl_root = Some("/usr".into());
}
match build.musl_libdir(*target) {
Some(libdir) => {
if fs::metadata(libdir.join("libc.a")).is_err() {

View file

@ -1329,23 +1329,33 @@ impl Build {
}
}
/// Returns the "musl root" for this `target`, if defined
/// Returns the "musl root" for this `target`, if defined.
///
/// If this is a native target (host is also musl) and no musl-root is given,
/// it falls back to the system toolchain in /usr.
fn musl_root(&self, target: TargetSelection) -> Option<&Path> {
self.config
let configured_root = self
.config
.target_config
.get(&target)
.and_then(|t| t.musl_root.as_ref())
.or(self.config.musl_root.as_ref())
.map(|p| &**p)
.map(|p| &**p);
if self.config.is_host_target(target) && configured_root.is_none() {
Some(Path::new("/usr"))
} else {
configured_root
}
}
/// Returns the "musl libdir" for this `target`.
fn musl_libdir(&self, target: TargetSelection) -> Option<PathBuf> {
let t = self.config.target_config.get(&target)?;
if let libdir @ Some(_) = &t.musl_libdir {
return libdir.clone();
}
self.musl_root(target).map(|root| root.join("lib"))
self.config
.target_config
.get(&target)
.and_then(|t| t.musl_libdir.clone())
.or_else(|| self.musl_root(target).map(|root| root.join("lib")))
}
/// Returns the `lib` directory for the WASI target specified, if

View file

@ -439,7 +439,7 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
ChangeInfo {
change_id: 143255,
severity: ChangeSeverity::Warning,
summary: "`llvm.lld` is no longer enabled by default for the dist profile.",
summary: "`rust.lld` is no longer enabled by default for the dist profile.",
},
ChangeInfo {
change_id: 143251,

View file

@ -1,14 +1,18 @@
//! This module serves two purposes:
//! 1. It is part of the `utils` module and used in other parts of bootstrap.
//! 2. It is embedded inside bootstrap shims to avoid a dependency on the bootstrap library.
//! Therefore, this module should never use any other bootstrap module. This reduces binary
//! size and improves compilation time by minimizing linking time.
//!
//! 1. It is part of the `utils` module and used in other parts of bootstrap.
//! 2. It is embedded inside bootstrap shims to avoid a dependency on the bootstrap library.
//! Therefore, this module should never use any other bootstrap module. This reduces binary size
//! and improves compilation time by minimizing linking time.
// # Note on tests
//
// If we were to declare a tests submodule here, the shim binaries that include this module via
// `#[path]` would fail to find it, which breaks `./x check bootstrap`. So instead the unit tests
// for this module are in `super::tests::shared_helpers_tests`.
#![allow(dead_code)]
#[cfg(test)]
mod tests;
use std::env;
use std::ffi::OsString;
use std::fs::OpenOptions;
@ -16,10 +20,6 @@ use std::io::Write;
use std::process::Command;
use std::str::FromStr;
// If we were to declare a tests submodule here, the shim binaries that include this
// module via `#[path]` would fail to find it, which breaks `./x check bootstrap`.
// So instead the unit tests for this module are in `super::tests::shared_helpers_tests`.
/// Returns the environment variable which the dynamic library lookup path
/// resides in for this platform.
pub fn dylib_path_var() -> &'static str {

View file

@ -12,6 +12,10 @@ use crate::{Build, Config, Flags, t};
pub mod git;
// Note: tests for `shared_helpers` is separate here, as otherwise shim binaries that include the
// `shared_helpers` via `#[path]` would fail to find it, breaking `./x check bootstrap`.
mod shared_helpers_tests;
/// Holds temporary state of a bootstrap test.
/// Right now it is only used to redirect the build directory of the bootstrap
/// invocation, in the future it would be great if we could actually execute

View file

@ -1,3 +1,10 @@
//! The `shared_helpers` module can't have its own tests submodule, because that would cause
//! problems for the shim binaries that include it via `#[path]`, so instead those unit tests live
//! here.
//!
//! To prevent tidy from complaining about this file not being named `tests.rs`, it lives inside a
//! submodule directory named `tests`.
use crate::utils::shared_helpers::parse_value_from_args;
#[test]

View file

@ -40,6 +40,7 @@ COPY host-x86_64/pr-check-1/validate-toolstate.sh /scripts/
# We disable optimized compiler built-ins because that requires a C toolchain for the target.
# We also skip the x86_64-unknown-linux-gnu target as it is well-tested by other jobs.
ENV SCRIPT \
python3 ../x.py check bootstrap && \
/scripts/check-default-config-profiles.sh && \
python3 ../x.py build src/tools/build-manifest && \
python3 ../x.py test --stage 0 src/tools/compiletest && \

View file

@ -79,7 +79,6 @@ ENV MUSL_TARGETS=x86_64-unknown-linux-musl \
CXX_x86_64_unknown_linux_musl=x86_64-linux-musl-g++
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=aarch64-unknown-uefi,i686-unknown-uefi,x86_64-unknown-uefi \
CC_aarch64_unknown_uefi=clang-11 \
CXX_aarch64_unknown_uefi=clang++-11 \
@ -88,6 +87,8 @@ ENV UEFI_TARGETS=aarch64-unknown-uefi,i686-unknown-uefi,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
python3 /checkout/x.py --stage 2 test tests/run-make/uefi-qemu/rmake.rs --target aarch64-unknown-uefi && \
python3 /checkout/x.py --stage 2 test tests/run-make/uefi-qemu/rmake.rs --target i686-unknown-uefi && \
python3 /checkout/x.py --stage 2 test tests/run-make/uefi-qemu/rmake.rs --target x86_64-unknown-uefi
ENV SCRIPT $WASM_SCRIPT && $NVPTX_SCRIPT && $MUSL_SCRIPT && $UEFI_SCRIPT

View file

@ -1,140 +0,0 @@
#!/usr/bin/env python3
import os
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
TARGET_AARCH64 = "aarch64-unknown-uefi"
TARGET_I686 = "i686-unknown-uefi"
TARGET_X86_64 = "x86_64-unknown-uefi"
def run(*cmd, capture=False, check=True, env=None, timeout=None):
"""Print and run a command, optionally capturing the output."""
cmd = [str(p) for p in cmd]
print(" ".join(cmd))
return subprocess.run(
cmd, capture_output=capture, check=check, env=env, text=True, timeout=timeout
)
def build_and_run(tmp_dir, target):
if target == TARGET_AARCH64:
boot_file_name = "bootaa64.efi"
ovmf_dir = Path("/usr/share/AAVMF")
ovmf_code = "AAVMF_CODE.fd"
ovmf_vars = "AAVMF_VARS.fd"
qemu = "qemu-system-aarch64"
machine = "virt"
cpu = "cortex-a72"
elif target == TARGET_I686:
boot_file_name = "bootia32.efi"
ovmf_dir = Path("/usr/share/OVMF")
ovmf_code = "OVMF32_CODE_4M.secboot.fd"
ovmf_vars = "OVMF32_VARS_4M.fd"
# The i686 target intentionally uses 64-bit qemu; the important
# difference is that the OVMF code provides a 32-bit environment.
qemu = "qemu-system-x86_64"
machine = "q35"
cpu = "qemu64"
elif target == TARGET_X86_64:
boot_file_name = "bootx64.efi"
ovmf_dir = Path("/usr/share/OVMF")
ovmf_code = "OVMF_CODE.fd"
ovmf_vars = "OVMF_VARS.fd"
qemu = "qemu-system-x86_64"
machine = "q35"
cpu = "qemu64"
else:
raise KeyError("invalid target")
host_artifacts = Path("/checkout/obj/build/x86_64-unknown-linux-gnu")
stage0 = host_artifacts / "stage0/bin"
stage2 = host_artifacts / "stage2/bin"
env = dict(os.environ)
env["PATH"] = "{}:{}:{}".format(stage2, stage0, env["PATH"])
# Copy the test create into `tmp_dir`.
test_crate = Path(tmp_dir) / "uefi_qemu_test"
shutil.copytree("/uefi_qemu_test", test_crate)
# Build the UEFI executable.
run(
"cargo",
"build",
"--manifest-path",
test_crate / "Cargo.toml",
"--target",
target,
env=env,
)
# Create a mock EFI System Partition in a subdirectory.
esp = test_crate / "esp"
boot = esp / "efi/boot"
os.makedirs(boot, exist_ok=True)
# Copy the executable into the ESP.
src_exe_path = test_crate / "target" / target / "debug/uefi_qemu_test.efi"
shutil.copy(src_exe_path, boot / boot_file_name)
print(src_exe_path, boot / boot_file_name)
# Select the appropriate EDK2 build.
ovmf_code = ovmf_dir / ovmf_code
ovmf_vars = ovmf_dir / ovmf_vars
# Make a writable copy of the vars file. aarch64 doesn't boot
# correctly with read-only vars.
ovmf_rw_vars = Path(tmp_dir) / "vars.fd"
shutil.copy(ovmf_vars, ovmf_rw_vars)
# Run the executable in QEMU and capture the output.
output = run(
qemu,
"-machine",
machine,
"-cpu",
cpu,
"-display",
"none",
"-serial",
"stdio",
"-drive",
f"if=pflash,format=raw,readonly=on,file={ovmf_code}",
"-drive",
f"if=pflash,format=raw,readonly=off,file={ovmf_rw_vars}",
"-drive",
f"format=raw,file=fat:rw:{esp}",
capture=True,
check=True,
# Set a timeout to kill the VM in case something goes wrong.
timeout=60,
).stdout
if "Hello World!" in output:
print("VM produced expected output")
else:
print("unexpected VM output:")
print("---start---")
print(output)
print("---end---")
sys.exit(1)
def main():
targets = [TARGET_AARCH64, TARGET_I686, TARGET_X86_64]
for target in targets:
# Create a temporary directory so that we have a writeable
# workspace.
with tempfile.TemporaryDirectory() as tmp_dir:
build_and_run(tmp_dir, target)
if __name__ == "__main__":
main()

View file

@ -1 +1 @@
460259d14de0274b97b8801e08cb2fe5f16fdac5
efd420c770bb179537c01063e98cb6990c439654

Some files were not shown because too many files have changed in this diff Show more