Skip to content

Commit 3b1b0ef

Browse files
committed
Auto merge of #153704 - JonathanBrouwer:rollup-f0S4ki3, r=JonathanBrouwer
Rollup of 4 pull requests Successful merges: - #153072 (Allow merging all libcore/alloc doctests into a single binary) - #153408 (miri: make read_discriminant UB when the tag is not in the validity range of the tag field) - #153674 (Detect inherent method behind deref being shadowed by trait method) - #153689 (Eliminate `QueryLatchInfo`.)
2 parents a63150b + 53e67f2 commit 3b1b0ef

42 files changed

Lines changed: 336 additions & 71 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

compiler/rustc_const_eval/src/interpret/discriminant.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,23 +111,30 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
111111
.try_to_scalar_int()
112112
.map_err(|dbg_val| err_ub!(InvalidTag(dbg_val)))?
113113
.to_bits(tag_layout.size);
114+
// Ensure the tag is in its layout range. Codegen adds range metadata on the
115+
// discriminant load so we really have to make this UB.
116+
if !tag_scalar_layout.valid_range(self).contains(tag_bits) {
117+
throw_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size)))
118+
}
114119
// Cast bits from tag layout to discriminant layout.
115120
// After the checks we did above, this cannot fail, as
116121
// discriminants are int-like.
117122
let discr_val = self.int_to_int_or_float(&tag_val, discr_layout).unwrap();
118123
let discr_bits = discr_val.to_scalar().to_bits(discr_layout.size)?;
119-
// Convert discriminant to variant index, and catch invalid discriminants.
124+
// Convert discriminant to variant index. Since we validated the tag against the
125+
// layout range above, this cannot fail.
120126
let index = match *ty.kind() {
121127
ty::Adt(adt, _) => {
122-
adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits)
128+
adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits).unwrap()
123129
}
124130
ty::Coroutine(def_id, args) => {
125131
let args = args.as_coroutine();
126-
args.discriminants(def_id, *self.tcx).find(|(_, var)| var.val == discr_bits)
132+
args.discriminants(def_id, *self.tcx)
133+
.find(|(_, var)| var.val == discr_bits)
134+
.unwrap()
127135
}
128136
_ => span_bug!(self.cur_span(), "tagged layout for non-adt non-coroutine"),
129-
}
130-
.ok_or_else(|| err_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))))?;
137+
};
131138
// Return the cast value, and the index.
132139
index.0
133140
}
@@ -174,13 +181,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
174181
let variants =
175182
ty.ty_adt_def().expect("tagged layout for non adt").variants();
176183
assert!(variant_index < variants.next_index());
184+
// This should imply that the tag is in its layout range.
185+
assert!(tag_scalar_layout.valid_range(self).contains(tag_bits));
186+
177187
if variant_index == untagged_variant {
178188
// The untagged variant can be in the niche range, but even then it
179-
// is not a valid encoding.
189+
// is not a valid encoding. Codegen inserts an `assume` here
190+
// so we really have to make this UB.
180191
throw_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size)))
181192
}
182193
variant_index
183194
} else {
195+
// Ensure the tag is in its layout range. Codegen adds range metadata on
196+
// the discriminant load so we really have to make this UB.
197+
if !tag_scalar_layout.valid_range(self).contains(tag_bits) {
198+
throw_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size)))
199+
}
184200
untagged_variant
185201
}
186202
}

compiler/rustc_error_codes/src/error_codes/E0636.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
#### Note: this error code is no longer emitted by the compiler.
2+
13
The same feature is enabled multiple times with `#![feature]` attributes
24

35
Erroneous code example:
46

5-
```compile_fail,E0636
7+
```compile_fail
68
#![allow(stable_features)]
79
#![feature(rust1)]
810
#![feature(rust1)] // error: the feature `rust1` has already been enabled

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ declare_lint_pass! {
3939
DEPRECATED_IN_FUTURE,
4040
DEPRECATED_SAFE_2024,
4141
DEPRECATED_WHERE_CLAUSE_LOCATION,
42+
DUPLICATE_FEATURES,
4243
DUPLICATE_MACRO_ATTRIBUTES,
4344
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
4445
ELIDED_LIFETIMES_IN_PATHS,
@@ -1093,6 +1094,33 @@ declare_lint! {
10931094
"unused features found in crate-level `#[feature]` directives"
10941095
}
10951096

1097+
declare_lint! {
1098+
/// The `duplicate_features` lint detects duplicate features found in
1099+
/// crate-level [`feature` attributes].
1100+
///
1101+
/// Note: This lint used to be a hard error (E0636).
1102+
///
1103+
/// [`feature` attributes]: https://doc.rust-lang.org/nightly/unstable-book/
1104+
///
1105+
/// ### Example
1106+
///
1107+
/// ```rust,compile_fail
1108+
/// # #![allow(internal_features)]
1109+
/// #![feature(rustc_attrs)]
1110+
/// #![feature(rustc_attrs)]
1111+
/// ```
1112+
///
1113+
/// {{produces}}
1114+
///
1115+
/// ### Explanation
1116+
///
1117+
/// Enabling a feature more than once is a no-op.
1118+
/// To avoid this warning, remove the second `feature()` attribute.
1119+
pub DUPLICATE_FEATURES,
1120+
Deny,
1121+
"duplicate features found in crate-level `#[feature]` directives"
1122+
}
1123+
10961124
declare_lint! {
10971125
/// The `stable_features` lint detects a [`feature` attribute] that
10981126
/// has since been made stable.

compiler/rustc_middle/src/query/job.rs

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -77,22 +77,15 @@ pub struct QueryWaiter<'tcx> {
7777
pub cycle: Mutex<Option<CycleError<QueryStackDeferred<'tcx>>>>,
7878
}
7979

80-
#[derive(Debug)]
81-
pub struct QueryLatchInfo<'tcx> {
82-
pub complete: bool,
83-
pub waiters: Vec<Arc<QueryWaiter<'tcx>>>,
84-
}
85-
8680
#[derive(Clone, Debug)]
8781
pub struct QueryLatch<'tcx> {
88-
pub info: Arc<Mutex<QueryLatchInfo<'tcx>>>,
82+
/// The `Option` is `Some(..)` when the job is active, and `None` once completed.
83+
pub waiters: Arc<Mutex<Option<Vec<Arc<QueryWaiter<'tcx>>>>>>,
8984
}
9085

9186
impl<'tcx> QueryLatch<'tcx> {
9287
fn new() -> Self {
93-
QueryLatch {
94-
info: Arc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })),
95-
}
88+
QueryLatch { waiters: Arc::new(Mutex::new(Some(Vec::new()))) }
9689
}
9790

9891
/// Awaits for the query job to complete.
@@ -102,10 +95,10 @@ impl<'tcx> QueryLatch<'tcx> {
10295
query: Option<QueryJobId>,
10396
span: Span,
10497
) -> Result<(), CycleError<QueryStackDeferred<'tcx>>> {
105-
let mut info = self.info.lock();
106-
if info.complete {
107-
return Ok(());
108-
}
98+
let mut waiters_guard = self.waiters.lock();
99+
let Some(waiters) = &mut *waiters_guard else {
100+
return Ok(()); // already complete
101+
};
109102

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

119112
// Awaits the caller on this latch by blocking the current thread.
120113
// If this detects a deadlock and the deadlock handler wants to resume this thread
121114
// we have to be in the `wait` call. This is ensured by the deadlock handler
122115
// getting the self.info lock.
123116
rustc_thread_pool::mark_blocked();
124117
tcx.jobserver_proxy.release_thread();
125-
waiter.condvar.wait(&mut info);
118+
waiter.condvar.wait(&mut waiters_guard);
126119
// Release the lock before we potentially block in `acquire_thread`
127-
drop(info);
120+
drop(waiters_guard);
128121
tcx.jobserver_proxy.acquire_thread();
129122

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

140133
/// Sets the latch and resumes all waiters on it
141134
fn set(&self) {
142-
let mut info = self.info.lock();
143-
debug_assert!(!info.complete);
144-
info.complete = true;
135+
let mut waiters_guard = self.waiters.lock();
136+
let waiters = waiters_guard.take().unwrap(); // mark the latch as complete
145137
let registry = rustc_thread_pool::Registry::current();
146-
for waiter in info.waiters.drain(..) {
138+
for waiter in waiters {
147139
rustc_thread_pool::mark_unblocked(&registry);
148140
waiter.condvar.notify_one();
149141
}
@@ -152,9 +144,9 @@ impl<'tcx> QueryLatch<'tcx> {
152144
/// Removes a single waiter from the list of waiters.
153145
/// This is used to break query cycles.
154146
pub fn extract_waiter(&self, waiter: usize) -> Arc<QueryWaiter<'tcx>> {
155-
let mut info = self.info.lock();
156-
debug_assert!(!info.complete);
147+
let mut waiters_guard = self.waiters.lock();
148+
let waiters = waiters_guard.as_mut().expect("non-empty waiters vec");
157149
// Remove the waiter from the list of waiters
158-
info.waiters.remove(waiter)
150+
waiters.remove(waiter)
159151
}
160152
}

compiler/rustc_passes/src/errors.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -987,14 +987,6 @@ pub(crate) struct ImpliedFeatureNotExist {
987987
pub implied_by: Symbol,
988988
}
989989

990-
#[derive(Diagnostic)]
991-
#[diag("the feature `{$feature}` has already been enabled", code = E0636)]
992-
pub(crate) struct DuplicateFeatureErr {
993-
#[primary_span]
994-
pub span: Span,
995-
pub feature: Symbol,
996-
}
997-
998990
#[derive(Diagnostic)]
999991
#[diag(
1000992
"attributes `#[rustc_const_unstable]`, `#[rustc_const_stable]` and `#[rustc_const_stable_indirect]` require the function or method to be `const`"
@@ -1144,6 +1136,12 @@ pub(crate) struct ProcMacroBadSig {
11441136
pub kind: ProcMacroKind,
11451137
}
11461138

1139+
#[derive(Diagnostic)]
1140+
#[diag("the feature `{$feature}` has already been enabled")]
1141+
pub(crate) struct DuplicateFeature {
1142+
pub feature: Symbol,
1143+
}
1144+
11471145
#[derive(Diagnostic)]
11481146
#[diag(
11491147
"the feature `{$feature}` has been stable since {$since} and no longer requires an attribute to enable"

compiler/rustc_passes/src/stability.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -972,7 +972,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
972972
}
973973
if !lang_features.insert(gate_name) {
974974
// Warn if the user enables a lang feature multiple times.
975-
tcx.dcx().emit_err(errors::DuplicateFeatureErr { span: *attr_sp, feature: *gate_name });
975+
duplicate_feature_lint(tcx, *attr_sp, *gate_name);
976976
}
977977
}
978978

@@ -981,7 +981,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
981981
for EnabledLibFeature { gate_name, attr_sp } in enabled_lib_features {
982982
if remaining_lib_features.contains_key(gate_name) {
983983
// Warn if the user enables a lib feature multiple times.
984-
tcx.dcx().emit_err(errors::DuplicateFeatureErr { span: *attr_sp, feature: *gate_name });
984+
duplicate_feature_lint(tcx, *attr_sp, *gate_name);
985985
}
986986
remaining_lib_features.insert(*gate_name, *attr_sp);
987987
}
@@ -1166,3 +1166,12 @@ fn unnecessary_stable_feature_lint(
11661166
errors::UnnecessaryStableFeature { feature, since },
11671167
);
11681168
}
1169+
1170+
fn duplicate_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol) {
1171+
tcx.emit_node_span_lint(
1172+
lint::builtin::DUPLICATE_FEATURES,
1173+
hir::CRATE_HIR_ID,
1174+
span,
1175+
errors::DuplicateFeature { feature },
1176+
);
1177+
}

compiler/rustc_query_impl/src/job.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ fn visit_waiters<'tcx>(
136136

137137
// Visit the explicit waiters which use condvars and are resumable
138138
if let Some(latch) = job_map.latch_of(query) {
139-
for (i, waiter) in latch.info.lock().waiters.iter().enumerate() {
139+
for (i, waiter) in latch.waiters.lock().as_ref().unwrap().iter().enumerate() {
140140
if let Some(waiter_query) = waiter.query {
141141
// Return a value which indicates that this waiter can be resumed
142142
visit(waiter.span, waiter_query).map_break(|_| Some((query, i)))?;

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,7 @@ symbols! {
10731073
include_bytes,
10741074
include_str,
10751075
inclusive_range_syntax,
1076+
incomplete_features,
10761077
index,
10771078
index_mut,
10781079
infer_outlives_requirements,

compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3178,6 +3178,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
31783178

31793179
self.suggest_tuple_wrapping(err, root_obligation, obligation);
31803180
}
3181+
self.suggest_shadowed_inherent_method(err, obligation, trait_predicate);
31813182
}
31823183

31833184
fn add_help_message_for_fn_trait(

compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4916,6 +4916,79 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
49164916
}
49174917
}
49184918

4919+
pub(super) fn suggest_shadowed_inherent_method(
4920+
&self,
4921+
err: &mut Diag<'_>,
4922+
obligation: &PredicateObligation<'tcx>,
4923+
trait_predicate: ty::PolyTraitPredicate<'tcx>,
4924+
) {
4925+
let ObligationCauseCode::FunctionArg { call_hir_id, .. } = obligation.cause.code() else {
4926+
return;
4927+
};
4928+
let Node::Expr(call) = self.tcx.hir_node(*call_hir_id) else { return };
4929+
let hir::ExprKind::MethodCall(segment, rcvr, args, ..) = call.kind else { return };
4930+
let Some(typeck) = &self.typeck_results else { return };
4931+
let Some(rcvr_ty) = typeck.expr_ty_adjusted_opt(rcvr) else { return };
4932+
let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty);
4933+
let autoderef = (self.autoderef_steps)(rcvr_ty);
4934+
for (ty, def_id) in autoderef.iter().filter_map(|(ty, obligations)| {
4935+
if let ty::Adt(def, _) = ty.kind()
4936+
&& *ty != rcvr_ty.peel_refs()
4937+
&& obligations.iter().all(|obligation| self.predicate_may_hold(obligation))
4938+
{
4939+
Some((ty, def.did()))
4940+
} else {
4941+
None
4942+
}
4943+
}) {
4944+
for impl_def_id in self.tcx.inherent_impls(def_id) {
4945+
if *impl_def_id == trait_predicate.def_id() {
4946+
continue;
4947+
}
4948+
for m in self
4949+
.tcx
4950+
.provided_trait_methods(*impl_def_id)
4951+
.filter(|m| m.name() == segment.ident.name)
4952+
{
4953+
let fn_sig = self.tcx.fn_sig(m.def_id);
4954+
if fn_sig.skip_binder().inputs().skip_binder().len() != args.len() + 1 {
4955+
continue;
4956+
}
4957+
let rcvr_ty = fn_sig.skip_binder().input(0).skip_binder();
4958+
let (mutability, _ty) = match rcvr_ty.kind() {
4959+
ty::Ref(_, ty, hir::Mutability::Mut) => ("&mut ", ty),
4960+
ty::Ref(_, ty, _) => ("&", ty),
4961+
_ => ("", &rcvr_ty),
4962+
};
4963+
let path = self.tcx.def_path_str(def_id);
4964+
err.note(format!(
4965+
"there's an inherent method on `{ty}` of the same name, which can be \
4966+
auto-dereferenced from `{rcvr_ty}`"
4967+
));
4968+
err.multipart_suggestion(
4969+
format!(
4970+
"to access the inherent method on `{ty}`, use the fully-qualified path",
4971+
),
4972+
vec![
4973+
(
4974+
call.span.until(rcvr.span),
4975+
format!("{path}::{}({}", m.name(), mutability),
4976+
),
4977+
match &args {
4978+
[] => (
4979+
rcvr.span.shrink_to_hi().with_hi(call.span.hi()),
4980+
")".to_string(),
4981+
),
4982+
[first, ..] => (rcvr.span.between(first.span), ", ".to_string()),
4983+
},
4984+
],
4985+
Applicability::MaybeIncorrect,
4986+
);
4987+
}
4988+
}
4989+
}
4990+
}
4991+
49194992
pub(super) fn explain_hrtb_projection(
49204993
&self,
49214994
diag: &mut Diag<'_>,

0 commit comments

Comments
 (0)