Auto merge of #155077 - jhpratt:rollup-6HqPjEt, r=jhpratt

Rollup of 10 pull requests

Successful merges:

 - rust-lang/rust#154973 (Break a single query cycle in the deadlock handler)
 - rust-lang/rust#155034 (Implement `GenericTypeVisitable` for some types)
 - rust-lang/rust#151377 (Fix linker error by resolving regions for main return type obligations)
 - rust-lang/rust#154677 (hwaddress: automatically add `-Ctarget-feature=+tagged-globals`)
 - rust-lang/rust#154774 (Add myself as co-maintainer for hexagon-unknown-linux-musl)
 - rust-lang/rust#155015 (Add tests for three fixed issues (an LLVM crash, an ICE and poor codegen))
 - rust-lang/rust#155039 (minor follow up to removing soft mode `#[unstable]`)
 - rust-lang/rust#155043 (Fix if branch in op.rs)
 - rust-lang/rust#155071 (Deny `#[global_allocator]` + `#[thread_local]`)
 - rust-lang/rust#155072 (Set the Fuchsia ABI to the documented minimum)
This commit is contained in:
bors
2026-04-10 06:26:18 +00:00
28 changed files with 266 additions and 196 deletions
@@ -158,6 +158,16 @@ pub(crate) struct AllocMustStatics {
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag("allocators cannot be `#[thread_local]`")]
pub(crate) struct AllocCannotThreadLocal {
#[primary_span]
pub(crate) span: Span,
#[label("marked `#[thread_local]` here")]
#[suggestion("remove this attribute", code = "", applicability = "maybe-incorrect")]
pub(crate) attr: Span,
}
pub(crate) use autodiff::*;
mod autodiff {
@@ -38,6 +38,12 @@ pub(crate) fn expand(
return vec![orig_item];
};
// Forbid `#[thread_local]` attributes on the item
if let Some(attr) = item.attrs.iter().find(|x| x.has_name(sym::thread_local)) {
ecx.dcx().emit_err(errors::AllocCannotThreadLocal { span: item.span, attr: attr.span });
return vec![orig_item];
}
// Generate a bunch of new items using the AllocFnFactory
let span = ecx.with_def_site_ctxt(item.span);
let f = AllocFnFactory { span, ty_span, global: ident, cx: ecx };
@@ -630,6 +630,7 @@ fn llvm_features_by_flags(sess: &Session, features: &mut Vec<String>) {
}
target_features::retpoline_features_by_flags(sess, features);
target_features::sanitizer_features_by_flags(sess, features);
// -Zfixed-x18
if sess.opts.unstable_opts.fixed_x18 {
@@ -10,7 +10,7 @@
use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON;
use rustc_session::parse::feature_err;
use rustc_span::{Span, Symbol, edit_distance, sym};
use rustc_target::spec::Arch;
use rustc_target::spec::{Arch, SanitizerSet};
use rustc_target::target_features::{RUSTC_SPECIFIC_FEATURES, Stability};
use smallvec::SmallVec;
@@ -460,6 +460,15 @@ pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec<String>) {
}
}
/// Computes the backend target features to be added to account for sanitizer flags.
pub fn sanitizer_features_by_flags(sess: &Session, features: &mut Vec<String>) {
// It's intentional that this is done only for non-kernel version of hwaddress. This matches
// clang behavior.
if sess.sanitizers().contains(SanitizerSet::HWADDRESS) {
features.push("+tagged-globals".into());
}
}
pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers {
rust_target_features: |tcx, cnum| {
+33 -34
View File
@@ -7,22 +7,23 @@
use rustc_middle::span_bug;
use rustc_middle::ty::{self, TyCtxt, TypingMode};
use rustc_session::config::EntryFnType;
use rustc_span::Span;
use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::regions::InferCtxtRegionExt;
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
use super::check_function_signature;
use crate::errors;
pub(crate) fn check_for_entry_fn(tcx: TyCtxt<'_>) {
pub(crate) fn check_for_entry_fn(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
match tcx.entry_fn(()) {
Some((def_id, EntryFnType::Main { .. })) => check_main_fn_ty(tcx, def_id),
_ => {}
_ => Ok(()),
}
}
fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) -> Result<(), ErrorGuaranteed> {
let main_fnsig = tcx.fn_sig(main_def_id).instantiate_identity();
let main_span = tcx.def_span(main_def_id);
@@ -87,20 +88,20 @@ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
}
}
let mut error = false;
let main_diagnostics_def_id = main_fn_diagnostics_def_id(tcx, main_def_id, main_span);
let main_asyncness = tcx.asyncness(main_def_id);
if main_asyncness.is_async() {
let asyncness_span = main_fn_asyncness_span(tcx, main_def_id);
tcx.dcx()
.emit_err(errors::MainFunctionAsync { span: main_span, asyncness: asyncness_span });
error = true;
return Err(tcx
.dcx()
.emit_err(errors::MainFunctionAsync { span: main_span, asyncness: asyncness_span }));
}
if let Some(attr_span) = find_attr!(tcx, main_def_id, TrackCaller(span) => *span) {
tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr_span, annotated: main_span });
error = true;
return Err(tcx
.dcx()
.emit_err(errors::TrackCallerOnMain { span: attr_span, annotated: main_span }));
}
if !tcx.codegen_fn_attrs(main_def_id).target_features.is_empty()
@@ -108,12 +109,7 @@ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
&& !tcx.sess.target.is_like_wasm
&& !tcx.sess.opts.actually_rustdoc
{
tcx.dcx().emit_err(errors::TargetFeatureOnMain { main: main_span });
error = true;
}
if error {
return;
return Err(tcx.dcx().emit_err(errors::TargetFeatureOnMain { main: main_span }));
}
// Main should have no WC, so empty param env is OK here.
@@ -123,8 +119,9 @@ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
let return_ty = main_fnsig.output();
let return_ty_span = main_fn_return_type_span(tcx, main_def_id).unwrap_or(main_span);
let Some(return_ty) = return_ty.no_bound_vars() else {
tcx.dcx().emit_err(errors::MainFunctionReturnTypeGeneric { span: return_ty_span });
return;
return Err(tcx
.dcx()
.emit_err(errors::MainFunctionReturnTypeGeneric { span: return_ty_span }));
};
let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
let cause = traits::ObligationCause::new(
@@ -137,8 +134,16 @@ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
ocx.register_bound(cause, param_env, norm_return_ty, term_did);
let errors = ocx.evaluate_obligations_error_on_ambiguity();
if !errors.is_empty() {
infcx.err_ctxt().report_fulfillment_errors(errors);
error = true;
return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
}
let region_errors =
infcx.resolve_regions(main_diagnostics_def_id, param_env, ty::List::empty());
if !region_errors.is_empty() {
return Err(infcx
.err_ctxt()
.report_region_errors(main_diagnostics_def_id, &region_errors));
}
// now we can take the return type of the given main function
expected_return_type = norm_return_ty;
@@ -147,10 +152,6 @@ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
expected_return_type = tcx.types.unit;
}
if error {
return;
}
let expected_sig = ty::Binder::dummy(tcx.mk_fn_sig(
[],
expected_return_type,
@@ -159,7 +160,7 @@ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
ExternAbi::Rust,
));
if check_function_signature(
check_function_signature(
tcx,
ObligationCause::new(
main_span,
@@ -168,26 +169,24 @@ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
),
main_def_id,
expected_sig,
)
.is_err()
{
return;
}
)?;
let main_fn_generics = tcx.generics_of(main_def_id);
let main_fn_predicates = tcx.predicates_of(main_def_id);
if main_fn_generics.count() != 0 || !main_fnsig.bound_vars().is_empty() {
let generics_param_span = main_fn_generics_params_span(tcx, main_def_id);
tcx.dcx().emit_err(errors::MainFunctionGenericParameters {
return Err(tcx.dcx().emit_err(errors::MainFunctionGenericParameters {
span: generics_param_span.unwrap_or(main_span),
label_span: generics_param_span,
});
}));
} else if !main_fn_predicates.predicates.is_empty() {
// generics may bring in implicit predicates, so we skip this check if generics is present.
let generics_where_clauses_span = main_fn_where_clauses_span(tcx, main_def_id);
tcx.dcx().emit_err(errors::WhereClauseOnMain {
return Err(tcx.dcx().emit_err(errors::WhereClauseOnMain {
span: generics_where_clauses_span.unwrap_or(main_span),
generics_span: generics_where_clauses_span,
});
}));
}
Ok(())
}
@@ -2372,7 +2372,8 @@ pub(super) fn check_type_wf(tcx: TyCtxt<'_>, (): ()) -> Result<(), ErrorGuarante
}))
.and(items.par_nested_bodies(|item| tcx.ensure_result().check_well_formed(item)))
.and(items.par_opaques(|item| tcx.ensure_result().check_well_formed(item)));
super::entry::check_for_entry_fn(tcx);
super::entry::check_for_entry_fn(tcx)?;
res
}
+20 -18
View File
@@ -294,24 +294,26 @@ fn overloaded_binop_ret_ty(
self.apply_adjustments(lhs_expr, vec![autoref]);
}
if let ty::Ref(_, _, mutbl) = method.sig.inputs()[1].kind() {
// Allow two-phase borrows for binops in initial deployment
// since they desugar to methods
let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
let autoref = Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
target: method.sig.inputs()[1],
};
// HACK(eddyb) Bypass checks due to reborrows being in
// some cases applied on the RHS, on top of which we need
// to autoref, which is not allowed by apply_adjustments.
// self.apply_adjustments(rhs_expr, vec![autoref]);
self.typeck_results
.borrow_mut()
.adjustments_mut()
.entry(rhs_expr.hir_id)
.or_default()
.push(autoref);
if by_ref_binop {
if let ty::Ref(_, _, mutbl) = method.sig.inputs()[1].kind() {
// Allow two-phase borrows for binops in initial deployment
// since they desugar to methods
let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
let autoref = Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
target: method.sig.inputs()[1],
};
// HACK(eddyb) Bypass checks due to reborrows being in
// some cases applied on the RHS, on top of which we need
// to autoref, which is not allowed by apply_adjustments.
// self.apply_adjustments(rhs_expr, vec![autoref]);
self.typeck_results
.borrow_mut()
.adjustments_mut()
.entry(rhs_expr.hir_id)
.or_default()
.push(autoref);
}
}
}
+2 -2
View File
@@ -184,7 +184,7 @@ pub(crate) fn run_in_thread_pool_with_globals<
use rustc_data_structures::defer;
use rustc_middle::ty::tls;
use rustc_query_impl::break_query_cycles;
use rustc_query_impl::break_query_cycle;
let thread_stack_size = init_stack_size(thread_builder_diag);
@@ -260,7 +260,7 @@ pub(crate) fn run_in_thread_pool_with_globals<
)
},
);
break_query_cycles(job_map, &registry);
break_query_cycle(job_map, &registry);
})
})
});
+22 -65
View File
@@ -303,29 +303,17 @@ struct EntryPoint {
}
}
/// Looks for a query cycle using the last query in `jobs`.
/// If a cycle is found, all queries in the cycle is removed from `jobs` and
/// the function return true.
/// If a cycle was not found, the starting query is removed from `jobs` and
/// the function returns false.
fn remove_cycle<'tcx>(
/// Looks for a query cycle starting at `query`.
/// Returns a waiter to resume if a cycle is found.
fn find_and_process_cycle<'tcx>(
job_map: &QueryJobMap<'tcx>,
jobs: &mut Vec<QueryJobId>,
wakelist: &mut Vec<Arc<QueryWaiter<'tcx>>>,
) -> bool {
query: QueryJobId,
) -> Option<Arc<QueryWaiter<'tcx>>> {
let mut visited = FxHashSet::default();
let mut stack = Vec::new();
// Look for a cycle starting with the last query in `jobs`
if let ControlFlow::Break(resumable) =
find_cycle(job_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited)
find_cycle(job_map, query, DUMMY_SP, &mut stack, &mut visited)
{
// Remove the queries in our cycle from the list of jobs to look at
for r in &stack {
if let Some(pos) = jobs.iter().position(|j| j == &r.1) {
jobs.remove(pos);
}
}
// Create the cycle error
let error = process_cycle(job_map, stack);
@@ -340,62 +328,31 @@ fn remove_cycle<'tcx>(
*waiter.cycle.lock() = Some(error);
// Put the waiter on the list of things to resume
wakelist.push(waiter);
true
Some(waiter)
} else {
false
None
}
}
/// Detects query cycles by using depth first search over all active query jobs.
/// If a query cycle is found it will break the cycle by finding an edge which
/// uses a query latch and then resuming that waiter.
/// There may be multiple cycles involved in a deadlock, so this searches
/// all active queries for cycles before finally resuming all the waiters at once.
pub fn break_query_cycles<'tcx>(
job_map: QueryJobMap<'tcx>,
registry: &rustc_thread_pool::Registry,
) {
let mut wakelist = Vec::new();
// It is OK per the comments:
// - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798854932
// - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798866392
#[allow(rustc::potential_query_instability)]
let mut jobs: Vec<QueryJobId> = job_map.map.keys().copied().collect();
///
/// There may be multiple cycles involved in a deadlock, but this only breaks one at a time so
/// there will be multiple rounds through the deadlock handler if multiple cycles are present.
#[allow(rustc::potential_query_instability)]
pub fn break_query_cycle<'tcx>(job_map: QueryJobMap<'tcx>, registry: &rustc_thread_pool::Registry) {
// Look for a cycle starting at each query job
let waiter = job_map
.map
.keys()
.find_map(|query| find_and_process_cycle(&job_map, *query))
.expect("unable to find a query cycle");
let mut found_cycle = false;
// Mark the thread we're about to wake up as unblocked.
rustc_thread_pool::mark_unblocked(registry);
while jobs.len() > 0 {
if remove_cycle(&job_map, &mut jobs, &mut wakelist) {
found_cycle = true;
}
}
// Check that a cycle was found. It is possible for a deadlock to occur without
// a query cycle if a query which can be waited on uses Rayon to do multithreading
// internally. Such a query (X) may be executing on 2 threads (A and B) and A may
// wait using Rayon on B. Rayon may then switch to executing another query (Y)
// which in turn will wait on X causing a deadlock. We have a false dependency from
// X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here
// only considers the true dependency and won't detect a cycle.
if !found_cycle {
panic!(
"deadlock detected as we're unable to find a query cycle to break\n\
current query map:\n{job_map:#?}",
);
}
// Mark all the thread we're about to wake up as unblocked. This needs to be done before
// we wake the threads up as otherwise Rayon could detect a deadlock if a thread we
// resumed fell asleep and this thread had yet to mark the remaining threads as unblocked.
for _ in 0..wakelist.len() {
rustc_thread_pool::mark_unblocked(registry);
}
for waiter in wakelist.into_iter() {
waiter.condvar.notify_one();
}
assert!(waiter.condvar.notify_one(), "unable to wake the waiter");
}
pub fn print_query_stack<'tcx>(
+1 -1
View File
@@ -17,7 +17,7 @@
pub use crate::dep_kind_vtables::make_dep_kind_vtables;
pub use crate::execution::{CollectActiveJobsKind, collect_active_query_jobs};
pub use crate::job::{QueryJobMap, break_query_cycles, print_query_stack};
pub use crate::job::{QueryJobMap, break_query_cycle, print_query_stack};
mod dep_kind_vtables;
mod error;
@@ -6,7 +6,7 @@ pub(crate) fn target() -> Target {
let mut base = base::fuchsia::opts();
base.code_model = Some(CodeModel::Medium);
base.cpu = "generic-rv64".into();
base.features = "+m,+a,+f,+d,+c,+zicsr,+zifencei".into();
base.features = "+m,+a,+f,+d,+c,+v,+zicsr,+zifencei".into();
base.llvm_abiname = LlvmAbi::Lp64d;
base.max_atomic_width = Some(64);
base.stack_probes = StackProbeType::Inline;
+7 -4
View File
@@ -956,7 +956,7 @@ pub enum BoundVarIndexKind {
/// identified by both a universe, as well as a name residing within that universe. Distinct bound
/// regions/types/consts within the same universe simply have an unknown relationship to one
#[derive_where(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash; I: Interner, T)]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic, GenericTypeVisitable)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
@@ -995,7 +995,7 @@ fn lift_to_interner(self, cx: U) -> Option<Self::Lifted> {
}
#[derive_where(Clone, Copy, PartialEq, Eq, Hash; I: Interner)]
#[derive(Lift_Generic)]
#[derive(Lift_Generic, GenericTypeVisitable)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
@@ -1058,7 +1058,7 @@ pub fn get_id(&self) -> Option<I::DefId> {
}
#[derive_where(Clone, Copy, PartialEq, Eq, Debug, Hash; I: Interner)]
#[derive(Lift_Generic)]
#[derive(Lift_Generic, GenericTypeVisitable)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
@@ -1069,7 +1069,7 @@ pub enum BoundTyKind<I: Interner> {
}
#[derive_where(Clone, Copy, PartialEq, Eq, Debug, Hash; I: Interner)]
#[derive(Lift_Generic)]
#[derive(Lift_Generic, GenericTypeVisitable)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
@@ -1104,6 +1104,7 @@ pub fn expect_const(self) {
}
#[derive_where(Clone, Copy, PartialEq, Eq, Hash; I: Interner)]
#[derive(GenericTypeVisitable)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, HashStable_NoContext, Decodable_NoContext)
@@ -1164,6 +1165,7 @@ pub fn new_anon(ui: UniverseIndex, var: ty::BoundVar) -> Self {
}
#[derive_where(Clone, Copy, PartialEq, Eq, Hash; I: Interner)]
#[derive(GenericTypeVisitable)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
@@ -1229,6 +1231,7 @@ pub fn new_anon(ui: UniverseIndex, var: ty::BoundVar) -> Self {
}
#[derive_where(Clone, Copy, PartialEq, Debug, Eq, Hash; I: Interner)]
#[derive(GenericTypeVisitable)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
+1 -1
View File
@@ -141,7 +141,7 @@ fn hash_stable(&self, hcx: &mut Hcx, hasher: &mut StableHasher) {
/// `ValTree` does not have this problem with representation, as it only contains integers or
/// lists of (nested) `ty::Const`s (which may indirectly contain more `ValTree`s).
#[derive_where(Clone, Copy, Debug, Hash, Eq, PartialEq; I: Interner)]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic, GenericTypeVisitable)]
#[cfg_attr(
feature = "nightly",
derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext)
+12 -50
View File
@@ -1,45 +1,13 @@
//! A visiting traversal mechanism for complex data structures that contain type
//! information.
//! Special visiting used by rust-analyzer only.
//!
//! This is a read-only traversal of the data structure.
//! It is different from `TypeVisitable` in two ways:
//!
//! This traversal has limited flexibility. Only a small number of "types of
//! interest" within the complex data structures can receive custom
//! visitation. These are the ones containing the most important type-related
//! information, such as `Ty`, `Predicate`, `Region`, and `Const`.
//!
//! There are three traits involved in each traversal.
//! - `GenericTypeVisitable`. This is implemented once for many types, including:
//! - Types of interest, for which the methods delegate to the visitor.
//! - All other types, including generic containers like `Vec` and `Option`.
//! It defines a "skeleton" of how they should be visited.
//! - `TypeSuperVisitable`. This is implemented only for recursive types of
//! interest, and defines the visiting "skeleton" for these types. (This
//! excludes `Region` because it is non-recursive, i.e. it never contains
//! other types of interest.)
//! - `CustomizableTypeVisitor`. This is implemented for each visitor. This defines how
//! types of interest are visited.
//!
//! This means each visit is a mixture of (a) generic visiting operations, and (b)
//! custom visit operations that are specific to the visitor.
//! - The `GenericTypeVisitable` impls handle most of the traversal, and call into
//! `CustomizableTypeVisitor` when they encounter a type of interest.
//! - A `CustomizableTypeVisitor` may call into another `GenericTypeVisitable` impl, because some of
//! the types of interest are recursive and can contain other types of interest.
//! - A `CustomizableTypeVisitor` may also call into a `TypeSuperVisitable` impl, because each
//! visitor might provide custom handling only for some types of interest, or
//! only for some variants of each type of interest, and then use default
//! traversal for the remaining cases.
//!
//! For example, if you have `struct S(Ty, U)` where `S: GenericTypeVisitable` and `U:
//! GenericTypeVisitable`, and an instance `s = S(ty, u)`, it would be visited like so:
//! ```text
//! s.generic_visit_with(visitor) calls
//! - ty.generic_visit_with(visitor) calls
//! - visitor.visit_ty(ty) may call
//! - ty.super_generic_visit_with(visitor)
//! - u.generic_visit_with(visitor)
//! ```
//! - The visitor is a generic of the trait and not the method, allowing types to attach
//! special behavior to visitors (as long as they know it; we don't use this capability
//! in rustc crates, but rust-analyzer needs it).
//! - It **must visit** every field. This is why we don't have an attribute like `#[type_visitable(ignore)]`
//! for this visit. The reason for this is soundness: rust-analyzer uses this visit to
//! garbage collect types, so a missing field can mean a use after free
use std::sync::Arc;
@@ -53,16 +21,6 @@
/// To implement this conveniently, use the derive macro located in
/// `rustc_macros`.
pub trait GenericTypeVisitable<V> {
/// The entry point for visiting. To visit a value `t` with a visitor `v`
/// call: `t.generic_visit_with(v)`.
///
/// For most types, this just traverses the value, calling `generic_visit_with` on
/// each field/element.
///
/// For types of interest (such as `Ty`), the implementation of this method
/// that calls a visitor method specifically for that type (such as
/// `V::visit_ty`). This is where control transfers from `GenericTypeVisitable` to
/// `CustomizableTypeVisitor`.
fn generic_visit_with(&self, visitor: &mut V);
}
@@ -216,6 +174,10 @@ fn generic_visit_with(&self, _visitor: &mut V) {}
};
}
impl<T: ?Sized, V> GenericTypeVisitable<V> for std::marker::PhantomData<T> {
fn generic_visit_with(&self, _visitor: &mut V) {}
}
trivial_impls!(
(),
rustc_ast_ir::Mutability,
@@ -12,6 +12,7 @@ DSP architecture.
## Target maintainers
[@androm3da](https://github.com/androm3da)
[@quic-mliebel](https://github.com/quic-mliebel)
## Requirements
The target is cross-compiled. This target supports `std`. By default, code
@@ -552,10 +552,6 @@ HWAddressSanitizer is supported on the following targets:
* `aarch64-linux-android`
* `aarch64-unknown-linux-gnu`
HWAddressSanitizer requires `tagged-globals` target feature to instrument
globals. To enable this target feature compile with `-C
target-feature=+tagged-globals`
See the [Clang HWAddressSanitizer documentation][clang-hwasan] for more details.
## Example
@@ -570,9 +566,8 @@ fn main() {
```
```shell
$ rustc main.rs -Zsanitizer=hwaddress -C target-feature=+tagged-globals -C
linker=aarch64-linux-gnu-gcc -C link-arg=-fuse-ld=lld --target
aarch64-unknown-linux-gnu
$ rustc main.rs -Zsanitizer=hwaddress -Clinker=aarch64-linux-gnu-gcc
-Clink-arg=-fuse-ld=lld --target aarch64-unknown-linux-gnu
```
```shell
-1
View File
@@ -332,7 +332,6 @@ fn generate_output_example(&self, lint: &mut Lint) -> Result<(), Box<dyn Error>>
if matches!(
lint.name.as_str(),
"unused_features" // broken lint
| "soft_unstable" // cannot have a stable example
) {
return Ok(());
}
@@ -0,0 +1,28 @@
//! Regression test for https://github.com/rust-lang/rust/issues/113899.
//! When indexing into an array of an enum type with spare niches, the compiler
//! used to emit a superfluous branch checking whether the loaded value was
//! a niche value. Every element in the array is a valid variant, so this check
//! is unnecessary and should be optimised away.
//@ compile-flags: -Copt-level=3
#![crate_type = "lib"]
#[derive(Clone, Copy)]
pub enum Outer {
A([u8; 8]),
B([u8; 8]),
}
pub struct Error(u8);
// CHECK-LABEL: @test
#[no_mangle]
pub fn test(x: usize) -> Result<Outer, Error> {
// There should be exactly one comparison: the bounds check on `x`.
// There must be no second comparison checking the discriminant
// against the niche value used by `Option<Outer>` (from `get()`).
// CHECK: icmp ult
// CHECK-NOT: icmp
// CHECK: ret void
[Outer::A([10; 8]), Outer::B([20; 8])].get(x).copied().ok_or(Error(5))
}
@@ -18,6 +18,7 @@
// hwasan: @__hwasan_tls
// hwasan: call void @llvm.hwasan.check.memaccess.shortgranules
// hwasan: declare void @__hwasan_init()
// hwasan: attributes #0 {{.*"target-features"=".*\+tagged-globals.*"}}
// The `__hwasan_tls` symbol is unconditionally declared by LLVM's `HWAddressSanitizer` pass.
// However, in kernel mode KHWASAN does not actually use it (because shadow mapping is fixed
@@ -33,6 +34,7 @@
//
// khwasan-NOT: @__hwasan_init
// khwasan: call void @llvm.hwasan.check.memaccess.shortgranules
// khwasan-NOT: attributes #0 {{.*"target-features"=".*\+tagged-globals.*"}}
#[no_mangle]
pub fn test(b: &mut u8) -> u8 {
*b
+10
View File
@@ -0,0 +1,10 @@
#![feature(thread_local)]
use std::alloc::System;
#[global_allocator]
#[thread_local]
static A: System = System;
//~^ ERROR: allocators cannot be `#[thread_local]`
fn main() {}
+13
View File
@@ -0,0 +1,13 @@
error: allocators cannot be `#[thread_local]`
--> $DIR/no-thread-local.rs:7:1
|
LL | #[thread_local]
| ---------------
| |
| marked `#[thread_local]` here
| help: remove this attribute
LL | static A: System = System;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error
@@ -0,0 +1,17 @@
//! Regression test for https://github.com/rust-lang/rust/issues/104037.
//! LLVM used to hit an assertion "Vector elements must have same size"
//! when compiling derived Clone with MIR optimisation level of 3.
//@ build-pass
//@ compile-flags: -Zmir-opt-level=3 -Copt-level=3
#[derive(Clone)]
pub struct Foo(Bar, u32);
#[derive(Clone, Copy)]
pub struct Bar(u8, u8, u8);
fn main() {
let foo: Vec<Foo> = Vec::new();
let _ = foo.clone();
}
+1 -3
View File
@@ -1,7 +1,7 @@
//@ needs-sanitizer-support
//@ needs-sanitizer-hwaddress
//
//@ compile-flags: -Z sanitizer=hwaddress -O -g -C target-feature=+tagged-globals -C unsafe-allow-abi-mismatch=sanitizer
//@ compile-flags: -Z sanitizer=hwaddress -O -g -C unsafe-allow-abi-mismatch=sanitizer
//
//@ run-fail
//@ error-pattern: HWAddressSanitizer: tag-mismatch
@@ -15,5 +15,3 @@ fn main() {
let code = unsafe { *xs.offset(4) };
std::process::exit(code);
}
//~? WARN unknown and unstable feature specified for `-Ctarget-feature`: `tagged-globals`
-7
View File
@@ -1,7 +0,0 @@
warning: unknown and unstable feature specified for `-Ctarget-feature`: `tagged-globals`
|
= note: it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future
= help: consider filing a feature request
warning: 1 warning emitted
@@ -0,0 +1,24 @@
//! Regression test for https://github.com/rust-lang/rust/issues/112623.
//! The const evaluator used to ICE with an assertion failure on a size mismatch
//! when a trait impl changed the `self` receiver type from by-value to by-reference.
#![feature(const_trait_impl)]
const trait Func {
fn trigger(self) -> usize;
}
struct Cls;
impl const Func for Cls {
fn trigger(&self, a: usize) -> usize {
//~^ ERROR method `trigger` has 2 parameters but the declaration in trait `Func::trigger` has 1
0
}
}
enum Bug<T = [(); Cls.trigger()]> {
V(T),
}
fn main() {}
@@ -0,0 +1,12 @@
error[E0050]: method `trigger` has 2 parameters but the declaration in trait `Func::trigger` has 1
--> $DIR/self-receiver-type-mismatch.rs:14:16
|
LL | fn trigger(self) -> usize;
| ---- trait requires 1 parameter
...
LL | fn trigger(&self, a: usize) -> usize {
| ^^^^^^^^^^^^^^^ expected 1 parameter, found 2
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0050`.
@@ -0,0 +1,17 @@
// This test checks that the compiler correctly handles lifetime requirements
// on the `Termination` trait for the `main` function return type.
// See https://github.com/rust-lang/rust/issues/148421
use std::process::ExitCode;
use std::process::Termination;
trait IsStatic {}
impl<'a: 'static> IsStatic for &'a () {}
struct Thing;
impl Termination for Thing where for<'a> &'a (): IsStatic {
fn report(self) -> ExitCode { panic!() }
}
fn main() -> Thing { Thing } //~ ERROR implementation of `IsStatic` is not general enough
@@ -0,0 +1,11 @@
error: implementation of `IsStatic` is not general enough
--> $DIR/main-termination-lifetime-issue-148421.rs:17:14
|
LL | fn main() -> Thing { Thing }
| ^^^^^ implementation of `IsStatic` is not general enough
|
= note: `IsStatic` would have to be implemented for the type `&'0 ()`, for any lifetime `'0`...
= note: ...but `IsStatic` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1`
error: aborting due to 1 previous error