Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions compiler/rustc_const_eval/src/interpret/discriminant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,23 +111,30 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
.try_to_scalar_int()
.map_err(|dbg_val| err_ub!(InvalidTag(dbg_val)))?
.to_bits(tag_layout.size);
// Ensure the tag is in its layout range. Codegen adds range metadata on the
// discriminant load so we really have to make this UB.
if !tag_scalar_layout.valid_range(self).contains(tag_bits) {
throw_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size)))
}
// Cast bits from tag layout to discriminant layout.
// After the checks we did above, this cannot fail, as
// discriminants are int-like.
let discr_val = self.int_to_int_or_float(&tag_val, discr_layout).unwrap();
let discr_bits = discr_val.to_scalar().to_bits(discr_layout.size)?;
// Convert discriminant to variant index, and catch invalid discriminants.
// Convert discriminant to variant index. Since we validated the tag against the
// layout range above, this cannot fail.
let index = match *ty.kind() {
ty::Adt(adt, _) => {
adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits)
adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits).unwrap()
}
ty::Coroutine(def_id, args) => {
let args = args.as_coroutine();
args.discriminants(def_id, *self.tcx).find(|(_, var)| var.val == discr_bits)
args.discriminants(def_id, *self.tcx)
.find(|(_, var)| var.val == discr_bits)
.unwrap()
}
_ => span_bug!(self.cur_span(), "tagged layout for non-adt non-coroutine"),
}
.ok_or_else(|| err_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))))?;
};
// Return the cast value, and the index.
index.0
}
Expand Down Expand Up @@ -174,13 +181,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let variants =
ty.ty_adt_def().expect("tagged layout for non adt").variants();
assert!(variant_index < variants.next_index());
// This should imply that the tag is in its layout range.
assert!(tag_scalar_layout.valid_range(self).contains(tag_bits));

if variant_index == untagged_variant {
// The untagged variant can be in the niche range, but even then it
// is not a valid encoding.
// is not a valid encoding. Codegen inserts an `assume` here
// so we really have to make this UB.
throw_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size)))
}
variant_index
} else {
// Ensure the tag is in its layout range. Codegen adds range metadata on
// the discriminant load so we really have to make this UB.
if !tag_scalar_layout.valid_range(self).contains(tag_bits) {
throw_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size)))
}
untagged_variant
}
}
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_error_codes/src/error_codes/E0636.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#### Note: this error code is no longer emitted by the compiler.

The same feature is enabled multiple times with `#![feature]` attributes

Erroneous code example:

```compile_fail,E0636
```compile_fail
#![allow(stable_features)]
#![feature(rust1)]
#![feature(rust1)] // error: the feature `rust1` has already been enabled
Expand Down
28 changes: 28 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ declare_lint_pass! {
DEPRECATED_IN_FUTURE,
DEPRECATED_SAFE_2024,
DEPRECATED_WHERE_CLAUSE_LOCATION,
DUPLICATE_FEATURES,
DUPLICATE_MACRO_ATTRIBUTES,
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
ELIDED_LIFETIMES_IN_PATHS,
Expand Down Expand Up @@ -1093,6 +1094,33 @@ declare_lint! {
"unused features found in crate-level `#[feature]` directives"
}

declare_lint! {
/// The `duplicate_features` lint detects duplicate features found in
/// crate-level [`feature` attributes].
///
/// Note: This lint used to be a hard error (E0636).
///
/// [`feature` attributes]: https://doc.rust-lang.org/nightly/unstable-book/
///
/// ### Example
///
/// ```rust,compile_fail
/// # #![allow(internal_features)]
/// #![feature(rustc_attrs)]
/// #![feature(rustc_attrs)]
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Enabling a feature more than once is a no-op.
/// To avoid this warning, remove the second `feature()` attribute.
pub DUPLICATE_FEATURES,
Deny,
"duplicate features found in crate-level `#[feature]` directives"
}

declare_lint! {
/// The `stable_features` lint detects a [`feature` attribute] that
/// has since been made stable.
Expand Down
40 changes: 16 additions & 24 deletions compiler/rustc_middle/src/query/job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,22 +77,15 @@ pub struct QueryWaiter<'tcx> {
pub cycle: Mutex<Option<CycleError<QueryStackDeferred<'tcx>>>>,
}

#[derive(Debug)]
pub struct QueryLatchInfo<'tcx> {
pub complete: bool,
pub waiters: Vec<Arc<QueryWaiter<'tcx>>>,
}

#[derive(Clone, Debug)]
pub struct QueryLatch<'tcx> {
pub info: Arc<Mutex<QueryLatchInfo<'tcx>>>,
/// The `Option` is `Some(..)` when the job is active, and `None` once completed.
pub waiters: Arc<Mutex<Option<Vec<Arc<QueryWaiter<'tcx>>>>>>,
}

impl<'tcx> QueryLatch<'tcx> {
fn new() -> Self {
QueryLatch {
info: Arc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })),
}
QueryLatch { waiters: Arc::new(Mutex::new(Some(Vec::new()))) }
}

/// Awaits for the query job to complete.
Expand All @@ -102,10 +95,10 @@ impl<'tcx> QueryLatch<'tcx> {
query: Option<QueryJobId>,
span: Span,
) -> Result<(), CycleError<QueryStackDeferred<'tcx>>> {
let mut info = self.info.lock();
if info.complete {
return Ok(());
}
let mut waiters_guard = self.waiters.lock();
let Some(waiters) = &mut *waiters_guard else {
return Ok(()); // already complete
};

let waiter =
Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() });
Expand All @@ -114,17 +107,17 @@ impl<'tcx> QueryLatch<'tcx> {
// the `wait` call below, by 1) the `set` method or 2) by deadlock detection.
// Both of these will remove it from the `waiters` list before resuming
// this thread.
info.waiters.push(Arc::clone(&waiter));
waiters.push(Arc::clone(&waiter));

// Awaits the caller on this latch by blocking the current thread.
// If this detects a deadlock and the deadlock handler wants to resume this thread
// we have to be in the `wait` call. This is ensured by the deadlock handler
// getting the self.info lock.
rustc_thread_pool::mark_blocked();
tcx.jobserver_proxy.release_thread();
waiter.condvar.wait(&mut info);
waiter.condvar.wait(&mut waiters_guard);
// Release the lock before we potentially block in `acquire_thread`
drop(info);
drop(waiters_guard);
tcx.jobserver_proxy.acquire_thread();

// FIXME: Get rid of this lock. We have ownership of the QueryWaiter
Expand All @@ -139,11 +132,10 @@ impl<'tcx> QueryLatch<'tcx> {

/// Sets the latch and resumes all waiters on it
fn set(&self) {
let mut info = self.info.lock();
debug_assert!(!info.complete);
info.complete = true;
let mut waiters_guard = self.waiters.lock();
let waiters = waiters_guard.take().unwrap(); // mark the latch as complete
let registry = rustc_thread_pool::Registry::current();
for waiter in info.waiters.drain(..) {
for waiter in waiters {
rustc_thread_pool::mark_unblocked(&registry);
waiter.condvar.notify_one();
}
Expand All @@ -152,9 +144,9 @@ impl<'tcx> QueryLatch<'tcx> {
/// Removes a single waiter from the list of waiters.
/// This is used to break query cycles.
pub fn extract_waiter(&self, waiter: usize) -> Arc<QueryWaiter<'tcx>> {
let mut info = self.info.lock();
debug_assert!(!info.complete);
let mut waiters_guard = self.waiters.lock();
let waiters = waiters_guard.as_mut().expect("non-empty waiters vec");
// Remove the waiter from the list of waiters
info.waiters.remove(waiter)
waiters.remove(waiter)
}
}
14 changes: 6 additions & 8 deletions compiler/rustc_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -987,14 +987,6 @@ pub(crate) struct ImpliedFeatureNotExist {
pub implied_by: Symbol,
}

#[derive(Diagnostic)]
#[diag("the feature `{$feature}` has already been enabled", code = E0636)]
pub(crate) struct DuplicateFeatureErr {
#[primary_span]
pub span: Span,
pub feature: Symbol,
}

#[derive(Diagnostic)]
#[diag(
"attributes `#[rustc_const_unstable]`, `#[rustc_const_stable]` and `#[rustc_const_stable_indirect]` require the function or method to be `const`"
Expand Down Expand Up @@ -1144,6 +1136,12 @@ pub(crate) struct ProcMacroBadSig {
pub kind: ProcMacroKind,
}

#[derive(Diagnostic)]
#[diag("the feature `{$feature}` has already been enabled")]
pub(crate) struct DuplicateFeature {
pub feature: Symbol,
}

#[derive(Diagnostic)]
#[diag(
"the feature `{$feature}` has been stable since {$since} and no longer requires an attribute to enable"
Expand Down
13 changes: 11 additions & 2 deletions compiler/rustc_passes/src/stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
}
if !lang_features.insert(gate_name) {
// Warn if the user enables a lang feature multiple times.
tcx.dcx().emit_err(errors::DuplicateFeatureErr { span: *attr_sp, feature: *gate_name });
duplicate_feature_lint(tcx, *attr_sp, *gate_name);
}
}

Expand All @@ -981,7 +981,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
for EnabledLibFeature { gate_name, attr_sp } in enabled_lib_features {
if remaining_lib_features.contains_key(gate_name) {
// Warn if the user enables a lib feature multiple times.
tcx.dcx().emit_err(errors::DuplicateFeatureErr { span: *attr_sp, feature: *gate_name });
duplicate_feature_lint(tcx, *attr_sp, *gate_name);
}
remaining_lib_features.insert(*gate_name, *attr_sp);
}
Expand Down Expand Up @@ -1166,3 +1166,12 @@ fn unnecessary_stable_feature_lint(
errors::UnnecessaryStableFeature { feature, since },
);
}

fn duplicate_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol) {
tcx.emit_node_span_lint(
lint::builtin::DUPLICATE_FEATURES,
hir::CRATE_HIR_ID,
span,
errors::DuplicateFeature { feature },
);
}
2 changes: 1 addition & 1 deletion compiler/rustc_query_impl/src/job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ fn visit_waiters<'tcx>(

// Visit the explicit waiters which use condvars and are resumable
if let Some(latch) = job_map.latch_of(query) {
for (i, waiter) in latch.info.lock().waiters.iter().enumerate() {
for (i, waiter) in latch.waiters.lock().as_ref().unwrap().iter().enumerate() {
if let Some(waiter_query) = waiter.query {
// Return a value which indicates that this waiter can be resumed
visit(waiter.span, waiter_query).map_break(|_| Some((query, i)))?;
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,7 @@ symbols! {
include_bytes,
include_str,
inclusive_range_syntax,
incomplete_features,
index,
index_mut,
infer_outlives_requirements,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3178,6 +3178,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {

self.suggest_tuple_wrapping(err, root_obligation, obligation);
}
self.suggest_shadowed_inherent_method(err, obligation, trait_predicate);
}

fn add_help_message_for_fn_trait(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4916,6 +4916,79 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}

pub(super) fn suggest_shadowed_inherent_method(
&self,
err: &mut Diag<'_>,
obligation: &PredicateObligation<'tcx>,
trait_predicate: ty::PolyTraitPredicate<'tcx>,
) {
let ObligationCauseCode::FunctionArg { call_hir_id, .. } = obligation.cause.code() else {
return;
};
let Node::Expr(call) = self.tcx.hir_node(*call_hir_id) else { return };
let hir::ExprKind::MethodCall(segment, rcvr, args, ..) = call.kind else { return };
let Some(typeck) = &self.typeck_results else { return };
let Some(rcvr_ty) = typeck.expr_ty_adjusted_opt(rcvr) else { return };
let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty);
let autoderef = (self.autoderef_steps)(rcvr_ty);
for (ty, def_id) in autoderef.iter().filter_map(|(ty, obligations)| {
if let ty::Adt(def, _) = ty.kind()
&& *ty != rcvr_ty.peel_refs()
&& obligations.iter().all(|obligation| self.predicate_may_hold(obligation))
{
Some((ty, def.did()))
} else {
None
}
}) {
for impl_def_id in self.tcx.inherent_impls(def_id) {
if *impl_def_id == trait_predicate.def_id() {
continue;
}
for m in self
.tcx
.provided_trait_methods(*impl_def_id)
.filter(|m| m.name() == segment.ident.name)
{
let fn_sig = self.tcx.fn_sig(m.def_id);
if fn_sig.skip_binder().inputs().skip_binder().len() != args.len() + 1 {
continue;
}
let rcvr_ty = fn_sig.skip_binder().input(0).skip_binder();
let (mutability, _ty) = match rcvr_ty.kind() {
ty::Ref(_, ty, hir::Mutability::Mut) => ("&mut ", ty),
ty::Ref(_, ty, _) => ("&", ty),
_ => ("", &rcvr_ty),
};
let path = self.tcx.def_path_str(def_id);
err.note(format!(
"there's an inherent method on `{ty}` of the same name, which can be \
auto-dereferenced from `{rcvr_ty}`"
));
err.multipart_suggestion(
format!(
"to access the inherent method on `{ty}`, use the fully-qualified path",
),
vec![
(
call.span.until(rcvr.span),
format!("{path}::{}({}", m.name(), mutability),
),
match &args {
[] => (
rcvr.span.shrink_to_hi().with_hi(call.span.hi()),
")".to_string(),
),
[first, ..] => (rcvr.span.between(first.span), ", ".to_string()),
},
],
Applicability::MaybeIncorrect,
);
}
}
}
}

pub(super) fn explain_hrtb_projection(
&self,
diag: &mut Diag<'_>,
Expand Down
2 changes: 1 addition & 1 deletion library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
#![doc(
html_playground_url = "https://play.rust-lang.org/",
issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/",
test(no_crate_inject, attr(allow(unused_variables), deny(warnings)))
test(no_crate_inject, attr(allow(unused_variables, duplicate_features), deny(warnings)))
)]
#![doc(auto_cfg(hide(no_global_oom_handling, no_rc, no_sync, target_has_atomic = "ptr")))]
#![doc(rust_logo)]
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/alloc/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{cmp, ptr};
///
/// # Example
///
/// ```
/// ```standalone_crate
/// use std::alloc::{GlobalAlloc, Layout};
/// use std::cell::UnsafeCell;
/// use std::ptr::null_mut;
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/hint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ pub const fn black_box<T>(dummy: T) -> T {
/// macro_rules! make_error {
/// ($($args:expr),*) => {
/// core::hint::must_use({
/// let error = $crate::make_error(core::format_args!($($args),*));
/// let error = make_error(core::format_args!($($args),*));
/// error
/// })
/// };
Expand Down
Loading
Loading