Skip to content

Commit ecda83b

Browse files
committed
Auto merge of rust-lang#135755 - jieyouxu:rollup-rdwbhod, r=jieyouxu
Rollup of 5 pull requests Successful merges: - rust-lang#134276 (fully de-stabilize all custom inner attributes) - rust-lang#135237 (Match Ergonomics 2024: document and reorganize the currently-implemented feature gates) - rust-lang#135310 (Always force non-trimming of path in `unreachable_patterns` lint) - rust-lang#135446 (further improve panic_immediate_abort by removing rtprintpanic! messages) - rust-lang#135491 (Remove dead rustc_allowed_through_unstable_modules for std::os::fd contents) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 9a1d156 + e1e26f3 commit ecda83b

File tree

45 files changed

+822
-901
lines changed

Some content is hidden

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

45 files changed

+822
-901
lines changed

compiler/rustc_feature/src/unstable.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,8 @@ impl Features {
722722

723723
/// Some features are not allowed to be used together at the same time, if
724724
/// the two are present, produce an error.
725-
///
726-
/// Currently empty, but we will probably need this again in the future,
727-
/// so let's keep it in for now.
728-
pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[];
725+
pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[
726+
// Experimental match ergonomics rulesets are incompatible with each other, to simplify the
727+
// boolean logic required to tell which typing rules to use.
728+
(sym::ref_pat_eat_one_layer_2024, sym::ref_pat_eat_one_layer_2024_structural),
729+
];

compiler/rustc_hir_typeck/src/pat.rs

+141-70
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use rustc_middle::{bug, span_bug};
2121
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
2222
use rustc_session::parse::feature_err;
2323
use rustc_span::edit_distance::find_best_match_for_name;
24+
use rustc_span::edition::Edition;
2425
use rustc_span::hygiene::DesugaringKind;
2526
use rustc_span::source_map::Spanned;
2627
use rustc_span::{BytePos, DUMMY_SP, Ident, Span, kw, sym};
@@ -169,15 +170,16 @@ enum AdjustMode {
169170
Pass,
170171
}
171172

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

218+
/// Variations on RFC 3627's Rule 4: when do reference patterns match against inherited references?
219+
///
220+
/// "Inherited reference" designates the `&`/`&mut` types that arise from using match ergonomics, i.e.
221+
/// from matching a reference type with a non-reference pattern. E.g. when `Some(x)` matches on
222+
/// `&mut Option<&T>`, `x` gets type `&mut &T` and the outer `&mut` is considered "inherited".
223+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
224+
enum InheritedRefMatchRule {
225+
/// Reference patterns consume only the inherited reference if possible, regardless of whether
226+
/// the underlying type being matched against is a reference type. If there is no inherited
227+
/// reference, a reference will be consumed from the underlying type.
228+
EatOuter,
229+
/// Reference patterns consume only a reference from the underlying type if possible. If the
230+
/// underlying type is not a reference type, the inherited reference will be consumed.
231+
EatInner,
232+
/// When the underlying type is a reference type, reference patterns consume both layers of
233+
/// reference, i.e. they both reset the binding mode and consume the reference type. Reference
234+
/// patterns are not permitted when there is no underlying reference type, i.e. they can't eat
235+
/// only an inherited reference. This is the current stable Rust behavior.
236+
EatBoth,
237+
}
238+
216239
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
240+
/// Experimental pattern feature: after matching against a shared reference, do we limit the
241+
/// default binding mode in subpatterns to be `ref` when it would otherwise be `ref mut`?
242+
/// This corresponds to Rule 3 of RFC 3627.
243+
fn downgrade_mut_inside_shared(&self) -> bool {
244+
// NB: RFC 3627 proposes stabilizing Rule 3 in all editions. If we adopt the same behavior
245+
// across all editions, this may be removed.
246+
self.tcx.features().ref_pat_eat_one_layer_2024()
247+
|| self.tcx.features().ref_pat_eat_one_layer_2024_structural()
248+
}
249+
250+
/// Experimental pattern feature: when do reference patterns match against inherited references?
251+
/// This corresponds to variations on Rule 4 of RFC 3627.
252+
fn ref_pat_matches_inherited_ref(&self, edition: Edition) -> InheritedRefMatchRule {
253+
// NB: The particular rule used here is likely to differ across editions, so calls to this
254+
// may need to become edition checks after match ergonomics stabilize.
255+
if edition.at_least_rust_2024() {
256+
if self.tcx.features().ref_pat_eat_one_layer_2024() {
257+
InheritedRefMatchRule::EatOuter
258+
} else if self.tcx.features().ref_pat_eat_one_layer_2024_structural() {
259+
InheritedRefMatchRule::EatInner
260+
} else {
261+
// Currently, matching against an inherited ref on edition 2024 is an error.
262+
// Use `EatBoth` as a fallback to be similar to stable Rust.
263+
InheritedRefMatchRule::EatBoth
264+
}
265+
} else {
266+
InheritedRefMatchRule::EatBoth
267+
}
268+
}
269+
270+
/// Experimental pattern feature: do `&` patterns match against `&mut` references, treating them
271+
/// as if they were shared references? This corresponds to Rule 5 of RFC 3627.
272+
fn ref_pat_matches_mut_ref(&self) -> bool {
273+
// NB: RFC 3627 proposes stabilizing Rule 5 in all editions. If we adopt the same behavior
274+
// across all editions, this may be removed.
275+
self.tcx.features().ref_pat_eat_one_layer_2024()
276+
|| self.tcx.features().ref_pat_eat_one_layer_2024_structural()
277+
}
278+
217279
/// Type check the given top level pattern against the `expected` type.
218280
///
219281
/// If a `Some(span)` is provided and `origin_expr` holds,
@@ -474,13 +536,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
474536
});
475537
}
476538

477-
let features = self.tcx.features();
478-
if features.ref_pat_eat_one_layer_2024() || features.ref_pat_eat_one_layer_2024_structural()
479-
{
539+
if self.downgrade_mut_inside_shared() {
480540
def_br = def_br.cap_ref_mutability(max_ref_mutbl.as_mutbl());
481-
if def_br == ByRef::Yes(Mutability::Not) {
482-
max_ref_mutbl = MutblCap::Not;
483-
}
541+
}
542+
if def_br == ByRef::Yes(Mutability::Not) {
543+
max_ref_mutbl = MutblCap::Not;
484544
}
485545

486546
if !pat_adjustments.is_empty() {
@@ -731,6 +791,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
731791
// Determine the binding mode...
732792
let bm = match user_bind_annot {
733793
BindingMode(ByRef::No, Mutability::Mut) if matches!(def_br, ByRef::Yes(_)) => {
794+
// Only mention the experimental `mut_ref` feature if if we're in edition 2024 and
795+
// using other experimental matching features compatible with it.
734796
if pat.span.at_least_rust_2024()
735797
&& (self.tcx.features().ref_pat_eat_one_layer_2024()
736798
|| self.tcx.features().ref_pat_eat_one_layer_2024_structural())
@@ -2228,55 +2290,70 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22282290
mut pat_info: PatInfo<'_, 'tcx>,
22292291
) -> Ty<'tcx> {
22302292
let tcx = self.tcx;
2231-
let features = tcx.features();
2232-
let ref_pat_eat_one_layer_2024 = features.ref_pat_eat_one_layer_2024();
2233-
let ref_pat_eat_one_layer_2024_structural =
2234-
features.ref_pat_eat_one_layer_2024_structural();
2235-
2236-
let no_ref_mut_behind_and =
2237-
ref_pat_eat_one_layer_2024 || ref_pat_eat_one_layer_2024_structural;
2238-
let new_match_ergonomics = pat.span.at_least_rust_2024() && no_ref_mut_behind_and;
22392293

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

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

22522306
expected = self.try_structurally_resolve_type(pat.span, expected);
2253-
if new_match_ergonomics {
2254-
if let ByRef::Yes(inh_mut) = pat_info.binding_mode {
2255-
if !ref_pat_eat_one_layer_2024 && let ty::Ref(_, _, r_mutbl) = *expected.kind() {
2256-
// Don't attempt to consume inherited reference
2257-
pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(r_mutbl);
2258-
} else {
2307+
// Determine whether we're consuming an inherited reference and resetting the default
2308+
// binding mode, based on edition and enabled experimental features.
2309+
if let ByRef::Yes(inh_mut) = pat_info.binding_mode {
2310+
match self.ref_pat_matches_inherited_ref(pat.span.edition()) {
2311+
InheritedRefMatchRule::EatOuter => {
22592312
// ref pattern attempts to consume inherited reference
22602313
if pat_mutbl > inh_mut {
22612314
// Tried to match inherited `ref` with `&mut`
2262-
if !ref_pat_eat_one_layer_2024_structural {
2263-
let err_msg = "mismatched types";
2264-
let err = if let Some(span) = pat_prefix_span {
2265-
let mut err = self.dcx().struct_span_err(span, err_msg);
2266-
err.code(E0308);
2267-
err.note("cannot match inherited `&` with `&mut` pattern");
2268-
err.span_suggestion_verbose(
2269-
span,
2270-
"replace this `&mut` pattern with `&`",
2271-
"&",
2272-
Applicability::MachineApplicable,
2273-
);
2274-
err
2275-
} else {
2276-
self.dcx().struct_span_err(pat.span, err_msg)
2277-
};
2278-
err.emit();
2315+
// NB: This assumes that `&` patterns can match against mutable references
2316+
// (RFC 3627, Rule 5). If we implement a pattern typing ruleset with Rule 4E
2317+
// but not Rule 5, we'll need to check that here.
2318+
debug_assert!(ref_pat_matches_mut_ref);
2319+
let err_msg = "mismatched types";
2320+
let err = if let Some(span) = pat_prefix_span {
2321+
let mut err = self.dcx().struct_span_err(span, err_msg);
2322+
err.code(E0308);
2323+
err.note("cannot match inherited `&` with `&mut` pattern");
2324+
err.span_suggestion_verbose(
2325+
span,
2326+
"replace this `&mut` pattern with `&`",
2327+
"&",
2328+
Applicability::MachineApplicable,
2329+
);
2330+
err
2331+
} else {
2332+
self.dcx().struct_span_err(pat.span, err_msg)
2333+
};
2334+
err.emit();
2335+
}
22792336

2337+
pat_info.binding_mode = ByRef::No;
2338+
self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
2339+
self.check_pat(inner, expected, pat_info);
2340+
return expected;
2341+
}
2342+
InheritedRefMatchRule::EatInner => {
2343+
if let ty::Ref(_, _, r_mutbl) = *expected.kind() {
2344+
// Match against the reference type; don't consume the inherited ref.
2345+
pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(r_mutbl);
2346+
} else {
2347+
// The expected type isn't a reference, so match against the inherited ref.
2348+
if pat_mutbl > inh_mut {
2349+
// We can't match an inherited shared reference with `&mut`. This will
2350+
// be a type error later, since we're matching a reference pattern
2351+
// against a non-reference type.
2352+
// NB: This assumes that `&` patterns can match against mutable
2353+
// references (RFC 3627, Rule 5). If we implement a pattern typing
2354+
// ruleset with Rule 4 but not Rule 5, we'll need to check that here.
2355+
debug_assert!(ref_pat_matches_mut_ref);
2356+
} else {
22802357
pat_info.binding_mode = ByRef::No;
22812358
self.typeck_results
22822359
.borrow_mut()
@@ -2285,24 +2362,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22852362
self.check_pat(inner, expected, pat_info);
22862363
return expected;
22872364
}
2288-
} else {
2289-
pat_info.binding_mode = ByRef::No;
2290-
self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
2291-
self.check_pat(inner, expected, pat_info);
2292-
return expected;
22932365
}
22942366
}
2295-
}
2296-
} else {
2297-
// Reset binding mode on old editions
2298-
if pat_info.binding_mode != ByRef::No {
2299-
pat_info.binding_mode = ByRef::No;
2300-
self.add_rust_2024_migration_desugared_pat(
2301-
pat_info.top_info.hir_id,
2302-
pat.span,
2303-
inner.span,
2304-
"cannot implicitly match against multiple layers of reference",
2305-
)
2367+
InheritedRefMatchRule::EatBoth => {
2368+
// Reset binding mode on old editions
2369+
pat_info.binding_mode = ByRef::No;
2370+
self.add_rust_2024_migration_desugared_pat(
2371+
pat_info.top_info.hir_id,
2372+
pat.span,
2373+
inner.span,
2374+
"cannot implicitly match against multiple layers of reference",
2375+
)
2376+
}
23062377
}
23072378
}
23082379

@@ -2317,10 +2388,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23172388
debug!("check_pat_ref: expected={:?}", expected);
23182389
match *expected.kind() {
23192390
ty::Ref(_, r_ty, r_mutbl)
2320-
if (no_ref_mut_behind_and && r_mutbl >= pat_mutbl)
2391+
if (ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl)
23212392
|| r_mutbl == pat_mutbl =>
23222393
{
2323-
if no_ref_mut_behind_and && r_mutbl == Mutability::Not {
2394+
if r_mutbl == Mutability::Not {
23242395
pat_info.max_ref_mutbl = MutblCap::Not;
23252396
}
23262397

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

+1-8
Original file line numberDiff line numberDiff line change
@@ -1086,14 +1086,7 @@ fn find_fallback_pattern_typo<'tcx>(
10861086
let vis = cx.tcx.visibility(item.owner_id);
10871087
if vis.is_accessible_from(parent, cx.tcx) {
10881088
accessible.push(item_name);
1089-
let path = if item_name == name {
1090-
// We know that the const wasn't in scope because it has the exact
1091-
// same name, so we suggest the full path.
1092-
with_no_trimmed_paths!(cx.tcx.def_path_str(item.owner_id))
1093-
} else {
1094-
// The const is likely just typoed, and nothing else.
1095-
cx.tcx.def_path_str(item.owner_id)
1096-
};
1089+
let path = with_no_trimmed_paths!(cx.tcx.def_path_str(item.owner_id));
10971090
accessible_path.push(path);
10981091
} else if name == item_name {
10991092
// The const exists somewhere in this crate, but it can't be imported

0 commit comments

Comments
 (0)