Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pattern Migration 2024: reword to make sense on all Editions #136475

Closed
59 changes: 36 additions & 23 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

// Determine the binding mode...
let bm = match user_bind_annot {
BindingMode(ByRef::No, Mutability::Mut) if matches!(def_br, ByRef::Yes(_)) => {
BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(def_br_mutbl) = def_br => {
// Only mention the experimental `mut_ref` feature if if we're in edition 2024 and
// using other experimental matching features compatible with it.
if pat.span.at_least_rust_2024()
Expand All @@ -826,22 +826,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// `mut` resets the binding mode on edition <= 2021
self.add_rust_2024_migration_desugared_pat(
pat_info.top_info.hir_id,
pat.span,
pat,
ident.span,
"requires binding by-value, but the implicit default is by-reference",
def_br_mutbl,
);
BindingMode(ByRef::No, Mutability::Mut)
}
}
BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
BindingMode(ByRef::Yes(_), _) => {
if matches!(def_br, ByRef::Yes(_)) {
if let ByRef::Yes(def_br_mutbl) = def_br {
// `ref`/`ref mut` overrides the binding mode on edition <= 2021
self.add_rust_2024_migration_desugared_pat(
pat_info.top_info.hir_id,
pat.span,
pat,
ident.span,
"cannot override to bind by-reference when that is the implicit default",
def_br_mutbl,
);
}
user_bind_annot
Expand Down Expand Up @@ -2378,9 +2378,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pat_info.binding_mode = ByRef::No;
self.add_rust_2024_migration_desugared_pat(
pat_info.top_info.hir_id,
pat.span,
pat,
inner.span,
"cannot implicitly match against multiple layers of reference",
inh_mut,
)
}
}
Expand Down Expand Up @@ -2770,33 +2770,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn add_rust_2024_migration_desugared_pat(
&self,
pat_id: HirId,
subpat_span: Span,
subpat: &'tcx Pat<'tcx>,
cutoff_span: Span,
detailed_label: &str,
def_br_mutbl: Mutability,
) {
// Try to trim the span we're labeling to just the `&` or binding mode that's an issue.
// If the subpattern's span is is from an expansion, the emitted label will not be trimmed.
let source_map = self.tcx.sess.source_map();
let cutoff_span = source_map
.span_extend_prev_while(cutoff_span, char::is_whitespace)
.span_extend_prev_while(cutoff_span, |c| c.is_whitespace() || c == '(')
.unwrap_or(cutoff_span);
// Ensure we use the syntax context and thus edition of `subpat_span`; this will be a hard
// Ensure we use the syntax context and thus edition of `subpat.span`; this will be a hard
// error if the subpattern is of edition >= 2024.
let trimmed_span = subpat_span.until(cutoff_span).with_ctxt(subpat_span.ctxt());
let trimmed_span = subpat.span.until(cutoff_span).with_ctxt(subpat.span.ctxt());

let mut typeck_results = self.typeck_results.borrow_mut();
let mut table = typeck_results.rust_2024_migration_desugared_pats_mut();
let info = table.entry(pat_id).or_default();

// Only provide a detailed label if the problematic subpattern isn't from an expansion.
// In the case that it's from a macro, we'll add a more detailed note in the emitter.
let desc = if subpat_span.from_expansion() {
"default binding mode is reset within expansion"
let from_expansion = subpat.span.from_expansion();
let primary_label = if from_expansion {
// NB: This wording assumes the only expansions that can produce problematic reference
// patterns and bindings are macros. If a desugaring or AST pass is added that can do
// so, we may want to inspect the span's source callee or macro backtrace.
"occurs within macro expansion".to_owned()
} else {
detailed_label
let pat_kind = if matches!(subpat.kind, PatKind::Binding(_, _, _, _)) {
info.bad_modifiers |= true;
"binding modifier"
} else {
info.bad_ref_pats |= true;
"reference pattern"
};
let dbm_str = match def_br_mutbl {
Mutability::Not => "ref",
Mutability::Mut => "ref mut",
};
format!("{pat_kind} not allowed under `{dbm_str}` default binding mode")
};

self.typeck_results
.borrow_mut()
.rust_2024_migration_desugared_pats_mut()
.entry(pat_id)
.or_default()
.push((trimmed_span, desc.to_owned()));
info.primary_labels.push((trimmed_span, primary_label));
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ pub use self::sty::{
pub use self::trait_def::TraitDef;
pub use self::typeck_results::{
CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, IsIdentity,
TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind,
Rust2024IncompatiblePatInfo, TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind,
};
pub use self::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason};
Expand Down
22 changes: 17 additions & 5 deletions compiler/rustc_middle/src/ty/typeck_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ pub struct TypeckResults<'tcx> {
/// Stores the actual binding mode for all instances of [`BindingMode`].
pat_binding_modes: ItemLocalMap<BindingMode>,

/// Top-level patterns whose match ergonomics need to be desugared by the Rust 2021 -> 2024
/// migration lint. Problematic subpatterns are stored in the `Vec` for the lint to highlight.
rust_2024_migration_desugared_pats: ItemLocalMap<Vec<(Span, String)>>,
/// Top-level patterns incompatible with Rust 2024's match ergonomics. These will be translated
/// to a form valid in all Editions, either as a lint diagnostic or hard error.
rust_2024_migration_desugared_pats: ItemLocalMap<Rust2024IncompatiblePatInfo>,

/// Stores the types which were implicitly dereferenced in pattern binding modes
/// for later usage in THIR lowering. For example,
Expand Down Expand Up @@ -420,7 +420,7 @@ impl<'tcx> TypeckResults<'tcx> {

pub fn rust_2024_migration_desugared_pats(
&self,
) -> LocalTableInContext<'_, Vec<(Span, String)>> {
) -> LocalTableInContext<'_, Rust2024IncompatiblePatInfo> {
LocalTableInContext {
hir_owner: self.hir_owner,
data: &self.rust_2024_migration_desugared_pats,
Expand All @@ -429,7 +429,7 @@ impl<'tcx> TypeckResults<'tcx> {

pub fn rust_2024_migration_desugared_pats_mut(
&mut self,
) -> LocalTableInContextMut<'_, Vec<(Span, String)>> {
) -> LocalTableInContextMut<'_, Rust2024IncompatiblePatInfo> {
LocalTableInContextMut {
hir_owner: self.hir_owner,
data: &mut self.rust_2024_migration_desugared_pats,
Expand Down Expand Up @@ -811,3 +811,15 @@ impl<'tcx> std::fmt::Display for UserTypeKind<'tcx> {
}
}
}

/// Information on a pattern incompatible with Rust 2024, for use by the error/migration diagnostic
/// emitted during THIR construction.
#[derive(TyEncodable, TyDecodable, Debug, HashStable, Default)]
pub struct Rust2024IncompatiblePatInfo {
/// Labeled spans for `&`s, `&mut`s, and binding modifiers incompatible with Rust 2024.
pub primary_labels: Vec<(Span, String)>,
/// Whether any binding modifiers occur under a non-`move` default binding mode.
pub bad_modifiers: bool,
/// Whether any `&` or `&mut` patterns occur under a non-`move` default binding mode.
pub bad_ref_pats: bool,
}
11 changes: 10 additions & 1 deletion compiler/rustc_mir_build/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,16 @@ mir_build_pointer_pattern = function pointers and raw pointers not derived from
mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
mir_build_rust_2024_incompatible_pat = this pattern relies on behavior which may change in edition 2024
mir_build_rust_2024_incompatible_pat = {$bad_modifiers ->
*[true] binding modifiers{$bad_ref_pats ->
*[true] {" "}and reference patterns
[false] {""}
}
[false] reference patterns
} may only be written when the default binding mode is `move`{$is_hard_error ->
*[true] {""}
[false] {" "}in Rust 2024
}
mir_build_static_in_pattern = statics cannot be referenced in patterns
.label = can't be used in patterns
Expand Down
34 changes: 28 additions & 6 deletions compiler/rustc_mir_build/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
Expand Down Expand Up @@ -1097,25 +1098,46 @@ pub(crate) enum MiscPatternSuggestion {

#[derive(LintDiagnostic)]
#[diag(mir_build_rust_2024_incompatible_pat)]
pub(crate) struct Rust2024IncompatiblePat<'a> {
pub(crate) struct Rust2024IncompatiblePat {
#[subdiagnostic]
pub(crate) sugg: Rust2024IncompatiblePatSugg<'a>,
pub(crate) sugg: Rust2024IncompatiblePatSugg,
pub(crate) bad_modifiers: bool,
pub(crate) bad_ref_pats: bool,
pub(crate) is_hard_error: bool,
}

pub(crate) struct Rust2024IncompatiblePatSugg<'a> {
pub(crate) struct Rust2024IncompatiblePatSugg {
pub(crate) suggestion: Vec<(Span, String)>,
pub(crate) ref_pattern_count: usize,
pub(crate) binding_mode_count: usize,
/// Labeled spans for subpatterns invalid in Rust 2024.
pub(crate) labels: &'a [(Span, String)],
/// Internal state: the ref-mutability of the default binding mode at the subpattern being
/// lowered, with the span where it was introduced. `None` for a by-value default mode.
pub(crate) default_mode_span: Option<(Span, ty::Mutability)>,
/// Labels for where incompatibility-causing by-ref default binding modes were introduced.
pub(crate) default_mode_labels: FxIndexMap<Span, ty::Mutability>,
}

impl<'a> Subdiagnostic for Rust2024IncompatiblePatSugg<'a> {
impl Subdiagnostic for Rust2024IncompatiblePatSugg {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self,
diag: &mut Diag<'_, G>,
_f: &F,
) {
// Format and emit explanatory notes about default binding modes. Reversing the spans' order
// means if we have nested spans, the innermost ones will be visited first.
for (span, def_br_mutbl) in self.default_mode_labels.into_iter().rev() {
// Don't point to a macro call site.
if !span.from_expansion() {
let note_msg = "matching on a reference type with a non-reference pattern changes the default binding mode";
let label_msg =
format!("this matches on type `{}_`", def_br_mutbl.ref_prefix_str());
let mut label = MultiSpan::from(span);
label.push_span_label(span, label_msg);
diag.span_note(label, note_msg);
}
}

// Format and emit the suggestion.
let applicability =
if self.suggestion.iter().all(|(span, _)| span.can_be_used_for_suggestions()) {
Applicability::MachineApplicable
Expand Down
Loading
Loading