Skip to content

Commit

Permalink
Auto merge of #124639 - Jules-Bertholet:match-ergonomics-2024-migrati…
Browse files Browse the repository at this point in the history
…on-lint, r=Nadrieril

Match ergonomics 2024: migration lint

Depends on #124567

r? `@Nadrieril`

cc #123076

`@rustbot` label A-edition-2024 A-patterns
  • Loading branch information
bors committed May 12, 2024
2 parents 852a78e + 9d92a7f commit ef00278
Show file tree
Hide file tree
Showing 17 changed files with 409 additions and 116 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4369,6 +4369,7 @@ dependencies = [
"rustc_hir",
"rustc_index",
"rustc_infer",
"rustc_lint",
"rustc_macros",
"rustc_middle",
"rustc_pattern_analysis",
Expand Down
4 changes: 0 additions & 4 deletions compiler/rustc_hir_typeck/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,6 @@ hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private
hir_typeck_deref_is_empty = this expression `Deref`s to `{$deref_ty}` which implements `is_empty`
hir_typeck_dereferencing_mut_binding = dereferencing `mut` binding
.label = `mut` dereferences the type of this binding
.help = this will change in edition 2024
hir_typeck_expected_default_return_type = expected `()` because of default return type
hir_typeck_expected_return_type = expected `{$expected}` because of return type
Expand Down
7 changes: 0 additions & 7 deletions compiler/rustc_hir_typeck/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -651,10 +651,3 @@ pub enum SuggestBoxingForReturnImplTrait {
ends: Vec<Span>,
},
}
#[derive(LintDiagnostic)]
#[diag(hir_typeck_dereferencing_mut_binding)]
pub struct DereferencingMutBinding {
#[label]
#[help]
pub span: Span,
}
49 changes: 29 additions & 20 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,23 @@ struct TopInfo<'tcx> {
/// found type `std::result::Result<_, _>`
/// ```
span: Option<Span>,
/// The [`HirId`] of the top-level pattern.
hir_id: HirId,
}

#[derive(Copy, Clone)]
struct PatInfo<'tcx, 'a> {
binding_mode: ByRef,
max_ref_mutbl: MutblCap,
top_info: TopInfo<'tcx>,
decl_origin: Option<DeclOrigin<'a>>,
top_info: &'a TopInfo<'tcx>,
decl_origin: Option<DeclOrigin<'tcx>>,

/// The depth of current pattern
current_depth: u32,
}

impl<'tcx> FnCtxt<'_, 'tcx> {
fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> {
fn pattern_cause(&self, ti: &TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> {
let code = ObligationCauseCode::Pattern {
span: ti.span,
root_ty: ti.expected,
Expand All @@ -101,7 +103,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
cause_span: Span,
expected: Ty<'tcx>,
actual: Ty<'tcx>,
ti: TopInfo<'tcx>,
ti: &TopInfo<'tcx>,
) -> Option<Diag<'tcx>> {
let mut diag =
self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)?;
Expand All @@ -118,7 +120,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
cause_span: Span,
expected: Ty<'tcx>,
actual: Ty<'tcx>,
ti: TopInfo<'tcx>,
ti: &TopInfo<'tcx>,
) {
if let Some(err) = self.demand_eqtype_pat_diag(cause_span, expected, actual, ti) {
err.emit();
Expand Down Expand Up @@ -199,11 +201,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
origin_expr: Option<&'tcx hir::Expr<'tcx>>,
decl_origin: Option<DeclOrigin<'tcx>>,
) {
let info = TopInfo { expected, origin_expr, span };
let info = TopInfo { expected, origin_expr, span, hir_id: pat.hir_id };
let pat_info = PatInfo {
binding_mode: ByRef::No,
max_ref_mutbl: MutblCap::Mut,
top_info: info,
top_info: &info,
decl_origin,
current_depth: 0,
};
Expand Down Expand Up @@ -463,7 +465,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: Span,
lt: &hir::Expr<'tcx>,
expected: Ty<'tcx>,
ti: TopInfo<'tcx>,
ti: &TopInfo<'tcx>,
) -> Ty<'tcx> {
// We've already computed the type above (when checking for a non-ref pat),
// so avoid computing it again.
Expand Down Expand Up @@ -533,7 +535,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
lhs: Option<&'tcx hir::Expr<'tcx>>,
rhs: Option<&'tcx hir::Expr<'tcx>>,
expected: Ty<'tcx>,
ti: TopInfo<'tcx>,
ti: &TopInfo<'tcx>,
) -> Ty<'tcx> {
let calc_side = |opt_expr: Option<&'tcx hir::Expr<'tcx>>| match opt_expr {
None => None,
Expand Down Expand Up @@ -671,18 +673,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

// Determine the binding mode...
let bm = match user_bind_annot {
// `mut` resets binding mode on edition <= 2021
BindingMode(ByRef::No, Mutability::Mut)
if !(pat.span.at_least_rust_2024()
&& self.tcx.features().mut_preserve_binding_mode_2024)
&& matches!(def_br, ByRef::Yes(_)) =>
{
// `mut x` resets the binding mode in edition <= 2021.
self.tcx.emit_node_span_lint(
rustc_lint::builtin::DEREFERENCING_MUT_BINDING,
pat.hir_id,
pat.span,
errors::DereferencingMutBinding { span: pat.span },
);
self.typeck_results
.borrow_mut()
.rust_2024_migration_desugared_pats_mut()
.insert(pat_info.top_info.hir_id);
BindingMode(ByRef::No, Mutability::Mut)
}
BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
Expand Down Expand Up @@ -754,7 +754,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: Span,
var_id: HirId,
ty: Ty<'tcx>,
ti: TopInfo<'tcx>,
ti: &TopInfo<'tcx>,
) {
let var_ty = self.local_ty(span, var_id);
if let Some(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) {
Expand Down Expand Up @@ -996,7 +996,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
qpath: &hir::QPath<'_>,
path_resolution: (Res, Option<LoweredTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]),
expected: Ty<'tcx>,
ti: TopInfo<'tcx>,
ti: &TopInfo<'tcx>,
) -> Ty<'tcx> {
let tcx = self.tcx;

Expand Down Expand Up @@ -2178,8 +2178,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
} else {
// Reset binding mode on old editions
pat_info.binding_mode = ByRef::No;
pat_info.max_ref_mutbl = MutblCap::Mut

if pat_info.binding_mode != ByRef::No {
pat_info.binding_mode = ByRef::No;

self.typeck_results
.borrow_mut()
.rust_2024_migration_desugared_pats_mut()
.insert(pat_info.top_info.hir_id);
}

pat_info.max_ref_mutbl = MutblCap::Mut;
}

let tcx = self.tcx;
Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_hir_typeck/src/writeback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
_ => {}
};

self.visit_rust_2024_migration_desugared_pats(p.hir_id);
self.visit_skipped_ref_pats(p.hir_id);
self.visit_pat_adjustments(p.span, p.hir_id);

Expand Down Expand Up @@ -655,6 +656,22 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
}
}

#[instrument(skip(self), level = "debug")]
fn visit_rust_2024_migration_desugared_pats(&mut self, hir_id: hir::HirId) {
if self
.fcx
.typeck_results
.borrow_mut()
.rust_2024_migration_desugared_pats_mut()
.remove(hir_id)
{
debug!(
"node is a pat whose match ergonomics are desugared by the Rust 2024 migration lint"
);
self.typeck_results.rust_2024_migration_desugared_pats_mut().insert(hir_id);
}
}

#[instrument(skip(self, span), level = "debug")]
fn visit_pat_adjustments(&mut self, span: Span, hir_id: HirId) {
let adjustment = self.fcx.typeck_results.borrow_mut().pat_adjustments_mut().remove(hir_id);
Expand Down
38 changes: 19 additions & 19 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ declare_lint_pass! {
DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
DEPRECATED_IN_FUTURE,
DEPRECATED_WHERE_CLAUSE_LOCATION,
DEREFERENCING_MUT_BINDING,
DUPLICATE_MACRO_ATTRIBUTES,
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
ELIDED_LIFETIMES_IN_PATHS,
Expand Down Expand Up @@ -90,6 +89,7 @@ declare_lint_pass! {
RUST_2021_INCOMPATIBLE_OR_PATTERNS,
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
RUST_2021_PRELUDE_COLLISIONS,
RUST_2024_INCOMPATIBLE_PAT,
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
SINGLE_USE_LIFETIMES,
SOFT_UNSTABLE,
Expand Down Expand Up @@ -1630,34 +1630,34 @@ declare_lint! {
}

declare_lint! {
/// The `dereferencing_mut_binding` lint detects a `mut x` pattern that resets the binding mode,
/// as this behavior will change in rust 2024.
/// The `rust_2024_incompatible_pat` lint
/// detects patterns whose meaning will change in the Rust 2024 edition.
///
/// ### Example
///
/// ```rust
/// # #![warn(dereferencing_mut_binding)]
/// let x = Some(123u32);
/// let _y = match &x {
/// Some(mut x) => {
/// x += 1;
/// x
/// }
/// None => 0,
/// };
/// ```rust,edition2021
/// #![feature(ref_pat_eat_one_layer_2024)]
/// #![warn(rust_2024_incompatible_pat)]
///
/// if let Some(&a) = &Some(&0u8) {
/// let _: u8 = a;
/// }
/// if let Some(mut _a) = &mut Some(0u8) {
/// _a = 7u8;
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Without the `mut`, `x` would have type `&u32`. Pre-2024, adding `mut` makes `x` have type
/// `u32`, which was deemed surprising. After edition 2024, adding `mut` will not change the
/// type of `x`. This lint warns users of editions before 2024 to update their code.
pub DEREFERENCING_MUT_BINDING,
/// In Rust 2024 and above, the `mut` keyword does not reset the pattern binding mode,
/// and nor do `&` or `&mut` patterns. The lint will suggest code that
/// has the same meaning in all editions.
pub RUST_2024_INCOMPATIBLE_PAT,
Allow,
"detects `mut x` bindings that change the type of `x`",
@feature_gate = sym::mut_preserve_binding_mode_2024;
"detects patterns whose meaning will change in Rust 2024",
@feature_gate = sym::ref_pat_eat_one_layer_2024;
// FIXME uncomment below upon stabilization
/*@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_middle/src/ty/typeck_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ 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.
rust_2024_migration_desugared_pats: ItemLocalSet,

/// Stores the types which were implicitly dereferenced in pattern binding modes
/// for later usage in THIR lowering. For example,
///
Expand Down Expand Up @@ -229,6 +233,7 @@ impl<'tcx> TypeckResults<'tcx> {
adjustments: Default::default(),
pat_binding_modes: Default::default(),
pat_adjustments: Default::default(),
rust_2024_migration_desugared_pats: Default::default(),
skipped_ref_pats: Default::default(),
closure_kind_origins: Default::default(),
liberated_fn_sigs: Default::default(),
Expand Down Expand Up @@ -432,6 +437,20 @@ impl<'tcx> TypeckResults<'tcx> {
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments }
}

pub fn rust_2024_migration_desugared_pats(&self) -> LocalSetInContext<'_> {
LocalSetInContext {
hir_owner: self.hir_owner,
data: &self.rust_2024_migration_desugared_pats,
}
}

pub fn rust_2024_migration_desugared_pats_mut(&mut self) -> LocalSetInContextMut<'_> {
LocalSetInContextMut {
hir_owner: self.hir_owner,
data: &mut self.rust_2024_migration_desugared_pats,
}
}

pub fn skipped_ref_pats(&self) -> LocalSetInContext<'_> {
LocalSetInContext { hir_owner: self.hir_owner, data: &self.skipped_ref_pats }
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_infer = { path = "../rustc_infer" }
rustc_lint = { path = "../rustc_lint" }
rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" }
rustc_pattern_analysis = { path = "../rustc_pattern_analysis" }
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_mir_build/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ 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 = the semantics of this pattern will change in edition 2024
mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly
.attributes = no other attributes may be applied
.not_box = `#[rustc_box]` may only be applied to a `Box::new()` call
Expand Down
27 changes: 27 additions & 0 deletions compiler/rustc_mir_build/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -950,3 +950,30 @@ pub enum RustcBoxAttrReason {
#[note(mir_build_missing_box)]
MissingBox,
}

#[derive(LintDiagnostic)]
#[diag(mir_build_rust_2024_incompatible_pat)]
pub struct Rust2024IncompatiblePat {
#[subdiagnostic]
pub sugg: Rust2024IncompatiblePatSugg,
}

pub struct Rust2024IncompatiblePatSugg {
pub suggestion: Vec<(Span, String)>,
}

impl Subdiagnostic for Rust2024IncompatiblePatSugg {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self,
diag: &mut Diag<'_, G>,
_f: &F,
) {
let applicability =
if self.suggestion.iter().all(|(span, _)| span.can_be_used_for_suggestions()) {
Applicability::MachineApplicable
} else {
Applicability::MaybeIncorrect
};
diag.multipart_suggestion("desugar the match ergonomics", self.suggestion, applicability);
}
}

0 comments on commit ef00278

Please sign in to comment.