Skip to content

Commit 8348c7b

Browse files
committed
Auto merge of #124661 - RalfJung:only-structural-consts-in-patterns, r=<try>
Turn remaining non-structural-const-in-pattern lints into hard errors This completes the implementation of #120362 by turning out remaining future-compat lints into hard errors: indirect_structural_match and pointer_structural_match. They have been future-compat lints for a while (indirect_structural_match for many years, pointer_structural_match since Rust 1.75), and have shown up in dependency breakage reports since Rust 1.78 (released yesterday). I don't expect any code will still depend on them, but we will of course do a crater run. A lot of cleanup is now possible in const_to_pat, but that is deferred to a later PR. Fixes #70861
2 parents 561b5de + cbd682b commit 8348c7b

File tree

50 files changed

+185
-1209
lines changed

Some content is hidden

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

50 files changed

+185
-1209
lines changed

compiler/rustc_lint/src/lib.rs

+10
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,16 @@ fn register_builtins(store: &mut LintStore) {
539539
"converted into hard error, see RFC #3535 \
540540
<https://rust-lang.github.io/rfcs/3535-constants-in-patterns.html> for more information",
541541
);
542+
store.register_removed(
543+
"indirect_structural_match",
544+
"converted into hard error, see RFC #3535 \
545+
<https://rust-lang.github.io/rfcs/3535-constants-in-patterns.html> for more information",
546+
);
547+
store.register_removed(
548+
"pointer_structural_match",
549+
"converted into hard error, see RFC #3535 \
550+
<https://rust-lang.github.io/rfcs/3535-constants-in-patterns.html> for more information",
551+
);
542552
}
543553

544554
fn register_internals(store: &mut LintStore) {

compiler/rustc_lint_defs/src/builtin.rs

-87
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ declare_lint_pass! {
5050
HIDDEN_GLOB_REEXPORTS,
5151
ILL_FORMED_ATTRIBUTE_INPUT,
5252
INCOMPLETE_INCLUDE,
53-
INDIRECT_STRUCTURAL_MATCH,
5453
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
5554
INLINE_NO_SANITIZE,
5655
INVALID_DOC_ATTRIBUTES,
@@ -75,7 +74,6 @@ declare_lint_pass! {
7574
ORDER_DEPENDENT_TRAIT_OBJECTS,
7675
OVERLAPPING_RANGE_ENDPOINTS,
7776
PATTERNS_IN_FNS_WITHOUT_BODY,
78-
POINTER_STRUCTURAL_MATCH,
7977
PRIVATE_BOUNDS,
8078
PRIVATE_INTERFACES,
8179
PROC_MACRO_BACK_COMPAT,
@@ -2355,52 +2353,6 @@ declare_lint! {
23552353
"outlives requirements can be inferred"
23562354
}
23572355

2358-
declare_lint! {
2359-
/// The `indirect_structural_match` lint detects a `const` in a pattern
2360-
/// that manually implements [`PartialEq`] and [`Eq`].
2361-
///
2362-
/// [`PartialEq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html
2363-
/// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
2364-
///
2365-
/// ### Example
2366-
///
2367-
/// ```rust,compile_fail
2368-
/// #![deny(indirect_structural_match)]
2369-
///
2370-
/// struct NoDerive(i32);
2371-
/// impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } }
2372-
/// impl Eq for NoDerive { }
2373-
/// #[derive(PartialEq, Eq)]
2374-
/// struct WrapParam<T>(T);
2375-
/// const WRAP_INDIRECT_PARAM: & &WrapParam<NoDerive> = & &WrapParam(NoDerive(0));
2376-
/// fn main() {
2377-
/// match WRAP_INDIRECT_PARAM {
2378-
/// WRAP_INDIRECT_PARAM => { }
2379-
/// _ => { }
2380-
/// }
2381-
/// }
2382-
/// ```
2383-
///
2384-
/// {{produces}}
2385-
///
2386-
/// ### Explanation
2387-
///
2388-
/// The compiler unintentionally accepted this form in the past. This is a
2389-
/// [future-incompatible] lint to transition this to a hard error in the
2390-
/// future. See [issue #62411] for a complete description of the problem,
2391-
/// and some possible solutions.
2392-
///
2393-
/// [issue #62411]: https://github.com/rust-lang/rust/issues/62411
2394-
/// [future-incompatible]: ../index.md#future-incompatible-lints
2395-
pub INDIRECT_STRUCTURAL_MATCH,
2396-
Warn,
2397-
"constant used in pattern contains value of non-structural-match type in a field or a variant",
2398-
@future_incompatible = FutureIncompatibleInfo {
2399-
reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
2400-
reference: "issue #120362 <https://github.com/rust-lang/rust/issues/120362>",
2401-
};
2402-
}
2403-
24042356
declare_lint! {
24052357
/// The `deprecated_in_future` lint is internal to rustc and should not be
24062358
/// used by user code.
@@ -2418,45 +2370,6 @@ declare_lint! {
24182370
report_in_external_macro
24192371
}
24202372

2421-
declare_lint! {
2422-
/// The `pointer_structural_match` lint detects pointers used in patterns whose behaviour
2423-
/// cannot be relied upon across compiler versions and optimization levels.
2424-
///
2425-
/// ### Example
2426-
///
2427-
/// ```rust,compile_fail
2428-
/// #![deny(pointer_structural_match)]
2429-
/// fn foo(a: usize, b: usize) -> usize { a + b }
2430-
/// const FOO: fn(usize, usize) -> usize = foo;
2431-
/// fn main() {
2432-
/// match FOO {
2433-
/// FOO => {},
2434-
/// _ => {},
2435-
/// }
2436-
/// }
2437-
/// ```
2438-
///
2439-
/// {{produces}}
2440-
///
2441-
/// ### Explanation
2442-
///
2443-
/// Previous versions of Rust allowed function pointers and all raw pointers in patterns.
2444-
/// While these work in many cases as expected by users, it is possible that due to
2445-
/// optimizations pointers are "not equal to themselves" or pointers to different functions
2446-
/// compare as equal during runtime. This is because LLVM optimizations can deduplicate
2447-
/// functions if their bodies are the same, thus also making pointers to these functions point
2448-
/// to the same location. Additionally functions may get duplicated if they are instantiated
2449-
/// in different crates and not deduplicated again via LTO. Pointer identity for memory
2450-
/// created by `const` is similarly unreliable.
2451-
pub POINTER_STRUCTURAL_MATCH,
2452-
Warn,
2453-
"pointers are not structural-match",
2454-
@future_incompatible = FutureIncompatibleInfo {
2455-
reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
2456-
reference: "issue #120362 <https://github.com/rust-lang/rust/issues/120362>",
2457-
};
2458-
}
2459-
24602373
declare_lint! {
24612374
/// The `ambiguous_associated_items` lint detects ambiguity between
24622375
/// [associated items] and [enum variants].

compiler/rustc_mir_build/messages.ftl

-6
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,6 @@ mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
109109
.note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
110110
.label = use of extern static
111111
112-
mir_build_indirect_structural_match =
113-
to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq)]`
114-
115112
mir_build_inform_irrefutable = `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
116113
117114
mir_build_initializing_type_with_requires_unsafe =
@@ -257,9 +254,6 @@ mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type
257254
mir_build_non_partial_eq_match =
258255
to use a constant of type `{$non_peq_ty}` in a pattern, the type must implement `PartialEq`
259256
260-
mir_build_nontrivial_structural_match =
261-
to use a constant of type `{$non_sm_ty}` in a pattern, the constant's initializer must be trivial or `{$non_sm_ty}` must be annotated with `#[derive(PartialEq)]`
262-
263257
mir_build_pattern_not_covered = refutable pattern in {$origin}
264258
.pattern_ty = the matched value is of type `{$pattern_ty}`
265259

compiler/rustc_mir_build/src/errors.rs

+5-18
Original file line numberDiff line numberDiff line change
@@ -788,9 +788,12 @@ pub struct NaNPattern {
788788
pub span: Span,
789789
}
790790

791-
#[derive(LintDiagnostic)]
791+
#[derive(Diagnostic)]
792792
#[diag(mir_build_pointer_pattern)]
793-
pub struct PointerPattern;
793+
pub struct PointerPattern {
794+
#[primary_span]
795+
pub span: Span,
796+
}
794797

795798
#[derive(Diagnostic)]
796799
#[diag(mir_build_non_empty_never_pattern)]
@@ -802,22 +805,6 @@ pub struct NonEmptyNeverPattern<'tcx> {
802805
pub ty: Ty<'tcx>,
803806
}
804807

805-
#[derive(LintDiagnostic)]
806-
#[diag(mir_build_indirect_structural_match)]
807-
#[note(mir_build_type_not_structural_tip)]
808-
#[note(mir_build_type_not_structural_more_info)]
809-
pub struct IndirectStructuralMatch<'tcx> {
810-
pub non_sm_ty: Ty<'tcx>,
811-
}
812-
813-
#[derive(LintDiagnostic)]
814-
#[diag(mir_build_nontrivial_structural_match)]
815-
#[note(mir_build_type_not_structural_tip)]
816-
#[note(mir_build_type_not_structural_more_info)]
817-
pub struct NontrivialStructuralMatch<'tcx> {
818-
pub non_sm_ty: Ty<'tcx>,
819-
}
820-
821808
#[derive(Diagnostic)]
822809
#[diag(mir_build_exceeds_mcdc_condition_num_limit)]
823810
pub(crate) struct MCDCExceedsConditionNumLimit {

compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs

+6-80
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use rustc_infer::traits::Obligation;
66
use rustc_middle::mir;
77
use rustc_middle::thir::{FieldPat, Pat, PatKind};
88
use rustc_middle::ty::{self, Ty, TyCtxt, ValTree};
9-
use rustc_session::lint;
109
use rustc_span::{ErrorGuaranteed, Span};
1110
use rustc_target::abi::{FieldIdx, VariantIdx};
1211
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
@@ -16,8 +15,8 @@ use std::cell::Cell;
1615

1716
use super::PatCtxt;
1817
use crate::errors::{
19-
IndirectStructuralMatch, InvalidPattern, NaNPattern, PointerPattern, TypeNotPartialEq,
20-
TypeNotStructural, UnionPattern, UnsizedPattern,
18+
InvalidPattern, NaNPattern, PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern,
19+
UnsizedPattern,
2120
};
2221

2322
impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
@@ -49,15 +48,6 @@ struct ConstToPat<'tcx> {
4948
// value.
5049
saw_const_match_error: Cell<Option<ErrorGuaranteed>>,
5150

52-
// This tracks if we emitted some diagnostic for a given const value, so that
53-
// we will not subsequently issue an irrelevant lint for the same const
54-
// value.
55-
saw_const_match_lint: Cell<bool>,
56-
57-
// For backcompat we need to keep allowing non-structurally-eq types behind references.
58-
// See also all the `cant-hide-behind` tests.
59-
behind_reference: Cell<bool>,
60-
6151
// inference context used for checking `T: Structural` bounds.
6252
infcx: InferCtxt<'tcx>,
6353

@@ -84,8 +74,6 @@ impl<'tcx> ConstToPat<'tcx> {
8474
infcx,
8575
param_env: pat_ctxt.param_env,
8676
saw_const_match_error: Cell::new(None),
87-
saw_const_match_lint: Cell::new(false),
88-
behind_reference: Cell::new(false),
8977
treat_byte_string_as_slice: pat_ctxt
9078
.typeck_results
9179
.treat_byte_string_as_slice
@@ -197,15 +185,12 @@ impl<'tcx> ConstToPat<'tcx> {
197185
// complained about structural match violations there, so we don't
198186
// have to check anything any more.
199187
}
200-
} else if !have_valtree && !self.saw_const_match_lint.get() {
188+
} else if !have_valtree {
201189
// The only way valtree construction can fail without the structural match
202190
// checker finding a violation is if there is a pointer somewhere.
203-
self.tcx().emit_node_span_lint(
204-
lint::builtin::POINTER_STRUCTURAL_MATCH,
205-
self.id,
206-
self.span,
207-
PointerPattern,
208-
);
191+
let e = self.tcx().dcx().emit_err(PointerPattern { span: self.span });
192+
let kind = PatKind::Error(e);
193+
return Box::new(Pat { span: self.span, ty: cv.ty(), kind });
209194
}
210195

211196
// Always check for `PartialEq` if we had no other errors yet.
@@ -274,36 +259,11 @@ impl<'tcx> ConstToPat<'tcx> {
274259
cv: ValTree<'tcx>,
275260
ty: Ty<'tcx>,
276261
) -> Result<Box<Pat<'tcx>>, FallbackToOpaqueConst> {
277-
let id = self.id;
278262
let span = self.span;
279263
let tcx = self.tcx();
280264
let param_env = self.param_env;
281265

282266
let kind = match ty.kind() {
283-
// If the type is not structurally comparable, just emit the constant directly,
284-
// causing the pattern match code to treat it opaquely.
285-
// FIXME: This code doesn't emit errors itself, the caller emits the errors.
286-
// So instead of specific errors, you just get blanket errors about the whole
287-
// const type. See
288-
// https://github.com/rust-lang/rust/pull/70743#discussion_r404701963 for
289-
// details.
290-
// Backwards compatibility hack because we can't cause hard errors on these
291-
// types, so we compare them via `PartialEq::eq` at runtime.
292-
ty::Adt(..) if !self.type_marked_structural(ty) && self.behind_reference.get() => {
293-
if self.saw_const_match_error.get().is_none() && !self.saw_const_match_lint.get() {
294-
self.saw_const_match_lint.set(true);
295-
tcx.emit_node_span_lint(
296-
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
297-
id,
298-
span,
299-
IndirectStructuralMatch { non_sm_ty: ty },
300-
);
301-
}
302-
// Since we are behind a reference, we can just bubble the error up so we get a
303-
// constant at reference type, making it easy to let the fallback call
304-
// `PartialEq::eq` on it.
305-
return Err(FallbackToOpaqueConst);
306-
}
307267
ty::FnDef(..) => {
308268
let e = tcx.dcx().emit_err(InvalidPattern { span, non_sm_ty: ty });
309269
self.saw_const_match_error.set(Some(e));
@@ -377,38 +337,6 @@ impl<'tcx> ConstToPat<'tcx> {
377337
ty::Str => {
378338
PatKind::Constant { value: mir::Const::Ty(ty::Const::new_value(tcx, cv, ty)) }
379339
}
380-
// Backwards compatibility hack: support references to non-structural types,
381-
// but hard error if we aren't behind a double reference. We could just use
382-
// the fallback code path below, but that would allow *more* of this fishy
383-
// code to compile, as then it only goes through the future incompat lint
384-
// instead of a hard error.
385-
ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => {
386-
if self.behind_reference.get() {
387-
if self.saw_const_match_error.get().is_none()
388-
&& !self.saw_const_match_lint.get()
389-
{
390-
self.saw_const_match_lint.set(true);
391-
tcx.emit_node_span_lint(
392-
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
393-
self.id,
394-
span,
395-
IndirectStructuralMatch { non_sm_ty: *pointee_ty },
396-
);
397-
}
398-
return Err(FallbackToOpaqueConst);
399-
} else {
400-
if let Some(e) = self.saw_const_match_error.get() {
401-
// We already errored. Signal that in the pattern, so that follow up errors can be silenced.
402-
PatKind::Error(e)
403-
} else {
404-
let err = TypeNotStructural { span, non_sm_ty: *pointee_ty };
405-
let e = tcx.dcx().emit_err(err);
406-
self.saw_const_match_error.set(Some(e));
407-
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
408-
PatKind::Error(e)
409-
}
410-
}
411-
}
412340
// All other references are converted into deref patterns and then recursively
413341
// convert the dereferenced constant to a pattern that is the sub-pattern of the
414342
// deref pattern.
@@ -419,7 +347,6 @@ impl<'tcx> ConstToPat<'tcx> {
419347
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
420348
PatKind::Error(e)
421349
} else {
422-
let old = self.behind_reference.replace(true);
423350
// `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
424351
// matching against references, you can only use byte string literals.
425352
// The typechecker has a special case for byte string literals, by treating them
@@ -434,7 +361,6 @@ impl<'tcx> ConstToPat<'tcx> {
434361
};
435362
// References have the same valtree representation as their pointee.
436363
let subpattern = self.recur(cv, pointee_ty)?;
437-
self.behind_reference.set(old);
438364
PatKind::Deref { subpattern }
439365
}
440366
}

tests/ui/closures/2229_closure_analysis/match/match-edge-cases_1.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
//@ edition:2021
33

44
const PATTERN_REF: &str = "Hello World";
5-
const NUMBER: i32 = 30;
6-
const NUMBER_POINTER: *const i32 = &NUMBER;
5+
const NUMBER_POINTER: *const i32 = 30 as *const i32;
76

87
pub fn edge_case_ref(event: &str) {
98
let _ = || {
@@ -26,8 +25,7 @@ pub fn edge_case_str(event: String) {
2625
pub fn edge_case_raw_ptr(event: *const i32) {
2726
let _ = || {
2827
match event {
29-
NUMBER_POINTER => (), //~WARN behave unpredictably
30-
//~| previously accepted
28+
NUMBER_POINTER => (),
3129
_ => (),
3230
};
3331
};

tests/ui/closures/2229_closure_analysis/match/match-edge-cases_1.stderr

-23
This file was deleted.

tests/ui/consts/const_in_pattern/accept_structural.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
//@ run-pass
22

33
#![allow(non_local_definitions)]
4-
#![warn(indirect_structural_match)]
54

65
// This test is checking our logic for structural match checking by enumerating
76
// the different kinds of const expressions. This test is collecting cases where

0 commit comments

Comments
 (0)