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

Rollup of 5 pull requests #135755

Merged
merged 21 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
550b0ad
make experimental pattern typing features mutually exclusive
dianne Dec 23, 2024
ff165d5
pattern typing cleanup: remove a redundant assignment
dianne Jan 1, 2025
e2f3ce9
always track patterns' `MutblCap`
dianne Jan 1, 2025
c0e8bad
cleanup: de-tangle experimental pattern typing rules some
dianne Dec 23, 2024
5dfb972
move the experimental match ergonomics tests to be with the other rfc…
dianne Dec 23, 2024
b350d94
reorganize and comment some of the experimental pattern typing tests
dianne Dec 24, 2024
93a1950
Always force non-trimming of path in `unreachable_patterns` lint
estebank Jan 9, 2025
d8f975c
rename and comment the test for "Rule 5"-related mutability errors
dianne Jan 4, 2025
562c522
remove `tests/ui/pattern/no_ref_mut_behind_and.rs`
dianne Jan 13, 2025
3a0554a
further improve panic_immediate_abort by removing rtprintpanic messages
klensy Jan 13, 2025
c2ed284
remove unnecessary rustc_allowed_through_unstable_modules
RalfJung Jan 14, 2025
f3cf39f
wasi/io: remove dead files
RalfJung Jan 14, 2025
5bd20b0
fully de-stabilize all custom inner attributes
RalfJung Dec 13, 2024
48e023a
Add comments explaining inherited references
dianne Jan 19, 2025
bfa0e1e
Add debug-assertions for assumptions about enabled typing rules
dianne Jan 19, 2025
0263772
elaborate/clarify the comments on `InheritedRefMatchRule`'s variants
dianne Jan 20, 2025
be8f1f9
Rollup merge of #134276 - RalfJung:destabilize-custom-inner-attr, r=S…
jieyouxu Jan 20, 2025
1419f79
Rollup merge of #135237 - dianne:match-2024-cleanup, r=Nadrieril
jieyouxu Jan 20, 2025
3f2f695
Rollup merge of #135310 - estebank:issue-135289, r=Nadrieril
jieyouxu Jan 20, 2025
e5b8503
Rollup merge of #135446 - klensy:panic_immediate_abort_ext, r=Mark-Si…
jieyouxu Jan 20, 2025
e1e26f3
Rollup merge of #135491 - RalfJung:remove-dead-rustc_allowed_through_…
jieyouxu Jan 20, 2025
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
9 changes: 5 additions & 4 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,8 @@ impl Features {

/// Some features are not allowed to be used together at the same time, if
/// the two are present, produce an error.
///
/// Currently empty, but we will probably need this again in the future,
/// so let's keep it in for now.
pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[];
pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[
// Experimental match ergonomics rulesets are incompatible with each other, to simplify the
// boolean logic required to tell which typing rules to use.
(sym::ref_pat_eat_one_layer_2024, sym::ref_pat_eat_one_layer_2024_structural),
];
211 changes: 141 additions & 70 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use rustc_middle::{bug, span_bug};
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_session::parse::feature_err;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::edition::Edition;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::source_map::Spanned;
use rustc_span::{BytePos, DUMMY_SP, Ident, Span, kw, sym};
Expand Down Expand Up @@ -169,15 +170,16 @@ enum AdjustMode {
Pass,
}

/// `ref mut` patterns (explicit or match-ergonomics)
/// are not allowed behind an `&` reference.
/// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference.
/// Normally, the borrow checker enforces this, but for (currently experimental) match ergonomics,
/// we track this when typing patterns for two purposes:
///
/// This includes explicit `ref mut` behind `&` patterns
/// that match against `&mut` references,
/// where the code would have compiled
/// had the pattern been written as `&mut`.
/// However, the borrow checker will not catch
/// this last case, so we need to throw an error ourselves.
/// - For RFC 3627's Rule 3, when this would prevent us from binding with `ref mut`, we limit the
/// default binding mode to be by shared `ref` when it would otherwise be `ref mut`.
///
/// - For RFC 3627's Rule 5, we allow `&` patterns to match against `&mut` references, treating them
/// as if they were shared references. Since the scrutinee is mutable in this case, the borrow
/// checker won't catch if we bind with `ref mut`, so we need to throw an error ourselves.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum MutblCap {
/// Mutability restricted to immutable.
Expand Down Expand Up @@ -213,7 +215,67 @@ impl MutblCap {
}
}

/// Variations on RFC 3627's Rule 4: when do reference patterns match against inherited references?
///
/// "Inherited reference" designates the `&`/`&mut` types that arise from using match ergonomics, i.e.
/// from matching a reference type with a non-reference pattern. E.g. when `Some(x)` matches on
/// `&mut Option<&T>`, `x` gets type `&mut &T` and the outer `&mut` is considered "inherited".
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum InheritedRefMatchRule {
/// Reference patterns consume only the inherited reference if possible, regardless of whether
/// the underlying type being matched against is a reference type. If there is no inherited
/// reference, a reference will be consumed from the underlying type.
EatOuter,
/// Reference patterns consume only a reference from the underlying type if possible. If the
/// underlying type is not a reference type, the inherited reference will be consumed.
EatInner,
/// When the underlying type is a reference type, reference patterns consume both layers of
/// reference, i.e. they both reset the binding mode and consume the reference type. Reference
/// patterns are not permitted when there is no underlying reference type, i.e. they can't eat
/// only an inherited reference. This is the current stable Rust behavior.
EatBoth,
}

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Experimental pattern feature: after matching against a shared reference, do we limit the
/// default binding mode in subpatterns to be `ref` when it would otherwise be `ref mut`?
/// This corresponds to Rule 3 of RFC 3627.
fn downgrade_mut_inside_shared(&self) -> bool {
// NB: RFC 3627 proposes stabilizing Rule 3 in all editions. If we adopt the same behavior
// across all editions, this may be removed.
self.tcx.features().ref_pat_eat_one_layer_2024()
|| self.tcx.features().ref_pat_eat_one_layer_2024_structural()
}

/// Experimental pattern feature: when do reference patterns match against inherited references?
/// This corresponds to variations on Rule 4 of RFC 3627.
fn ref_pat_matches_inherited_ref(&self, edition: Edition) -> InheritedRefMatchRule {
// NB: The particular rule used here is likely to differ across editions, so calls to this
// may need to become edition checks after match ergonomics stabilize.
if edition.at_least_rust_2024() {
if self.tcx.features().ref_pat_eat_one_layer_2024() {
InheritedRefMatchRule::EatOuter
} else if self.tcx.features().ref_pat_eat_one_layer_2024_structural() {
InheritedRefMatchRule::EatInner
} else {
// Currently, matching against an inherited ref on edition 2024 is an error.
// Use `EatBoth` as a fallback to be similar to stable Rust.
InheritedRefMatchRule::EatBoth
}
} else {
InheritedRefMatchRule::EatBoth
}
}

/// Experimental pattern feature: do `&` patterns match against `&mut` references, treating them
/// as if they were shared references? This corresponds to Rule 5 of RFC 3627.
fn ref_pat_matches_mut_ref(&self) -> bool {
// NB: RFC 3627 proposes stabilizing Rule 5 in all editions. If we adopt the same behavior
// across all editions, this may be removed.
self.tcx.features().ref_pat_eat_one_layer_2024()
|| self.tcx.features().ref_pat_eat_one_layer_2024_structural()
}

/// Type check the given top level pattern against the `expected` type.
///
/// If a `Some(span)` is provided and `origin_expr` holds,
Expand Down Expand Up @@ -474,13 +536,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
}

let features = self.tcx.features();
if features.ref_pat_eat_one_layer_2024() || features.ref_pat_eat_one_layer_2024_structural()
{
if self.downgrade_mut_inside_shared() {
def_br = def_br.cap_ref_mutability(max_ref_mutbl.as_mutbl());
if def_br == ByRef::Yes(Mutability::Not) {
max_ref_mutbl = MutblCap::Not;
}
}
if def_br == ByRef::Yes(Mutability::Not) {
max_ref_mutbl = MutblCap::Not;
}

if !pat_adjustments.is_empty() {
Expand Down Expand Up @@ -731,6 +791,8 @@ 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(_)) => {
// 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()
&& (self.tcx.features().ref_pat_eat_one_layer_2024()
|| self.tcx.features().ref_pat_eat_one_layer_2024_structural())
Expand Down Expand Up @@ -2228,55 +2290,70 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
mut pat_info: PatInfo<'_, 'tcx>,
) -> Ty<'tcx> {
let tcx = self.tcx;
let features = tcx.features();
let ref_pat_eat_one_layer_2024 = features.ref_pat_eat_one_layer_2024();
let ref_pat_eat_one_layer_2024_structural =
features.ref_pat_eat_one_layer_2024_structural();

let no_ref_mut_behind_and =
ref_pat_eat_one_layer_2024 || ref_pat_eat_one_layer_2024_structural;
let new_match_ergonomics = pat.span.at_least_rust_2024() && no_ref_mut_behind_and;

let pat_prefix_span =
inner.span.find_ancestor_inside(pat.span).map(|end| pat.span.until(end));

if no_ref_mut_behind_and {
if pat_mutbl == Mutability::Not {
// Prevent the inner pattern from binding with `ref mut`.
pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not(pat_prefix_span);
}
} else {
pat_info.max_ref_mutbl = MutblCap::Mut;
let ref_pat_matches_mut_ref = self.ref_pat_matches_mut_ref();
if ref_pat_matches_mut_ref && pat_mutbl == Mutability::Not {
// If `&` patterns can match against mutable reference types (RFC 3627, Rule 5), we need
// to prevent subpatterns from binding with `ref mut`. Subpatterns of a shared reference
// pattern should have read-only access to the scrutinee, and the borrow checker won't
// catch it in this case.
pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not(pat_prefix_span);
}

expected = self.try_structurally_resolve_type(pat.span, expected);
if new_match_ergonomics {
if let ByRef::Yes(inh_mut) = pat_info.binding_mode {
if !ref_pat_eat_one_layer_2024 && let ty::Ref(_, _, r_mutbl) = *expected.kind() {
// Don't attempt to consume inherited reference
pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(r_mutbl);
} else {
// Determine whether we're consuming an inherited reference and resetting the default
// binding mode, based on edition and enabled experimental features.
if let ByRef::Yes(inh_mut) = pat_info.binding_mode {
match self.ref_pat_matches_inherited_ref(pat.span.edition()) {
InheritedRefMatchRule::EatOuter => {
// ref pattern attempts to consume inherited reference
if pat_mutbl > inh_mut {
// Tried to match inherited `ref` with `&mut`
if !ref_pat_eat_one_layer_2024_structural {
let err_msg = "mismatched types";
let err = if let Some(span) = pat_prefix_span {
let mut err = self.dcx().struct_span_err(span, err_msg);
err.code(E0308);
err.note("cannot match inherited `&` with `&mut` pattern");
err.span_suggestion_verbose(
span,
"replace this `&mut` pattern with `&`",
"&",
Applicability::MachineApplicable,
);
err
} else {
self.dcx().struct_span_err(pat.span, err_msg)
};
err.emit();
// NB: This assumes that `&` patterns can match against mutable references
// (RFC 3627, Rule 5). If we implement a pattern typing ruleset with Rule 4E
// but not Rule 5, we'll need to check that here.
debug_assert!(ref_pat_matches_mut_ref);
let err_msg = "mismatched types";
let err = if let Some(span) = pat_prefix_span {
let mut err = self.dcx().struct_span_err(span, err_msg);
err.code(E0308);
err.note("cannot match inherited `&` with `&mut` pattern");
err.span_suggestion_verbose(
span,
"replace this `&mut` pattern with `&`",
"&",
Applicability::MachineApplicable,
);
err
} else {
self.dcx().struct_span_err(pat.span, err_msg)
};
err.emit();
}

pat_info.binding_mode = ByRef::No;
self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
self.check_pat(inner, expected, pat_info);
return expected;
}
InheritedRefMatchRule::EatInner => {
if let ty::Ref(_, _, r_mutbl) = *expected.kind() {
// Match against the reference type; don't consume the inherited ref.
pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(r_mutbl);
} else {
// The expected type isn't a reference, so match against the inherited ref.
if pat_mutbl > inh_mut {
// We can't match an inherited shared reference with `&mut`. This will
// be a type error later, since we're matching a reference pattern
// against a non-reference type.
// NB: This assumes that `&` patterns can match against mutable
// references (RFC 3627, Rule 5). If we implement a pattern typing
// ruleset with Rule 4 but not Rule 5, we'll need to check that here.
debug_assert!(ref_pat_matches_mut_ref);
} else {
pat_info.binding_mode = ByRef::No;
self.typeck_results
.borrow_mut()
Expand All @@ -2285,24 +2362,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_pat(inner, expected, pat_info);
return expected;
}
} else {
pat_info.binding_mode = ByRef::No;
self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
self.check_pat(inner, expected, pat_info);
return expected;
}
}
}
} else {
// Reset binding mode on old editions
if pat_info.binding_mode != ByRef::No {
pat_info.binding_mode = ByRef::No;
self.add_rust_2024_migration_desugared_pat(
pat_info.top_info.hir_id,
pat.span,
inner.span,
"cannot implicitly match against multiple layers of reference",
)
InheritedRefMatchRule::EatBoth => {
// Reset binding mode on old editions
pat_info.binding_mode = ByRef::No;
self.add_rust_2024_migration_desugared_pat(
pat_info.top_info.hir_id,
pat.span,
inner.span,
"cannot implicitly match against multiple layers of reference",
)
}
}
}

Expand All @@ -2317,10 +2388,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("check_pat_ref: expected={:?}", expected);
match *expected.kind() {
ty::Ref(_, r_ty, r_mutbl)
if (no_ref_mut_behind_and && r_mutbl >= pat_mutbl)
if (ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl)
|| r_mutbl == pat_mutbl =>
{
if no_ref_mut_behind_and && r_mutbl == Mutability::Not {
if r_mutbl == Mutability::Not {
pat_info.max_ref_mutbl = MutblCap::Not;
}

Expand Down
9 changes: 1 addition & 8 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1086,14 +1086,7 @@ fn find_fallback_pattern_typo<'tcx>(
let vis = cx.tcx.visibility(item.owner_id);
if vis.is_accessible_from(parent, cx.tcx) {
accessible.push(item_name);
let path = if item_name == name {
// We know that the const wasn't in scope because it has the exact
// same name, so we suggest the full path.
with_no_trimmed_paths!(cx.tcx.def_path_str(item.owner_id))
} else {
// The const is likely just typoed, and nothing else.
cx.tcx.def_path_str(item.owner_id)
};
let path = with_no_trimmed_paths!(cx.tcx.def_path_str(item.owner_id));
accessible_path.push(path);
} else if name == item_name {
// The const exists somewhere in this crate, but it can't be imported
Expand Down
Loading
Loading