mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-26 13:01:27 +03:00
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:
@@ -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| {
|
||||
|
||||
@@ -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, ®ion_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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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, ®istry);
|
||||
break_query_cycle(job_map, ®istry);
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {}
|
||||
@@ -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,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`
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user