Skip to content

Rollup of 7 pull requests #139996

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

Merged
merged 34 commits into from
Apr 18, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9eb6a54
sync::mpsc: add miri reproducer of double free
petrosagg Apr 10, 2025
b9e2ac5
sync::mpsc: prevent double free on `Drop`
petrosagg Apr 8, 2025
f35eae7
store the kind of pattern adjustments in `pat_adjustments`
dianne Mar 15, 2025
2907ab5
Move `is_builder_target`, `is_system_llvm` and `is_rust_llvm` from `B…
Kobzol Apr 15, 2025
3c01dfe
Rename `is_builder_target` to `is_host_target`
Kobzol Apr 15, 2025
8c50f95
rustdoc: Output target feature information
willglynn Apr 4, 2025
a870bba
Add a warning when combining LLD with external LLVM config
Kobzol Apr 16, 2025
cb6c499
lower implicit deref patterns to THIR
dianne Feb 24, 2025
e4b7b3d
pattern typing for immutable implicit deref patterns
dianne Mar 9, 2025
91d0b57
register `DerefMut` bounds for implicit mutable derefs
dianne Mar 9, 2025
d6df469
refactor path pattern checking to get info for peeling
dianne Apr 7, 2025
9196048
refactor struct pattern checking to get info for peeling
dianne Mar 4, 2025
1f40e9a
refactor tuple struct pattern checking to get info for peeling
dianne Mar 4, 2025
923d95c
don't peel ADTs the pattern could match
dianne Mar 9, 2025
977c9ab
respect the tcx's recursion limit when peeling
dianne Mar 10, 2025
ff0d4bc
upvar inference for implicit deref patterns
dianne Mar 14, 2025
4c4b61b
add a feature gate test
dianne Mar 14, 2025
93d13e9
add an unstable book chapter
dianne Mar 14, 2025
3b91b7a
Make cow_of_cow test a teeny bit more explicit
Nadrieril Apr 16, 2025
92ce44f
ignore aix for tests/ui/erros/pic-linker.rs
Apr 16, 2025
4be670f
Warnings-as-errors in `check-builtin-attr-ice.rs`.
nnethercote Apr 9, 2025
400e8e5
Fix attribute printing in an error.
nnethercote Apr 9, 2025
7e1f2f9
Augment some tests involving attributes.
nnethercote Apr 10, 2025
2fef0a3
Replace infallible `name_or_empty` methods with fallible `name` methods.
nnethercote Apr 10, 2025
846c10f
Avoid an `unwrap` in `RustcMirAttrs::set_field`.
nnethercote Apr 15, 2025
6214674
rustdoc/clean: Change terminology of items pertaining to (formal) fn …
fmease Apr 15, 2025
82ff0a0
rustdoc: Properly clean fn params in all contexts
fmease Apr 16, 2025
c8a9095
Rollup merge of #138528 - dianne:implicit-deref-patterns, r=Nadrieril
matthiaskrgr Apr 18, 2025
e15cf92
Rollup merge of #139393 - willglynn:rustdoc_output_target_feature_inf…
matthiaskrgr Apr 18, 2025
5e8bc7f
Rollup merge of #139553 - petrosagg:channel-double-free, r=RalfJung,t…
matthiaskrgr Apr 18, 2025
540fb22
Rollup merge of #139615 - nnethercote:rm-name_or_empty, r=jdonszelmann
matthiaskrgr Apr 18, 2025
ac00a37
Rollup merge of #139853 - Kobzol:lld-llvm-config, r=onur-ozkan
matthiaskrgr Apr 18, 2025
cbf2662
Rollup merge of #139913 - fmease:rustdoc-fix-fn-param-handling, r=Gui…
matthiaskrgr Apr 18, 2025
8cb57ed
Rollup merge of #139942 - dalvescb:master, r=jieyouxu
matthiaskrgr Apr 18, 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
Prev Previous commit
Next Next commit
pattern typing for immutable implicit deref patterns
  • Loading branch information
dianne committed Apr 16, 2025
commit e4b7b3d820b6db4f4a37a1a09b700bc9a39d80ba
116 changes: 89 additions & 27 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Mode for adjusting the expected type and binding mode.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum AdjustMode {
/// Peel off all immediate reference types.
Peel,
/// Peel off all immediate reference types. If the `deref_patterns` feature is enabled, this
/// also peels smart pointer ADTs.
Peel { kind: PeelKind },
/// Pass on the input binding mode and expected type.
Pass,
}

/// Restrictions on what types to peel when adjusting the expected type and binding mode.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum PeelKind {
/// Only peel reference types. This is used for explicit `deref!(_)` patterns, which dereference
/// any number of `&`/`&mut` references, plus a single smart pointer.
ExplicitDerefPat,
/// Implicitly peel any number of references, and if `deref_patterns` is enabled, smart pointer
/// ADTs. In order to peel only as much as necessary for the pattern to match, the `until_adt`
/// field contains the ADT def that the pattern is a constructor for, if applicable, so that we
/// don't peel it. See [`ResolvedPat`] for more information.
// TODO: add `ResolvedPat` and `until_adt`.
Implicit,
}

/// `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:
Expand Down Expand Up @@ -390,7 +405,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

// Resolve type if needed.
let expected = if let AdjustMode::Peel = adjust_mode
let expected = if let AdjustMode::Peel { .. } = adjust_mode
&& pat.default_binding_modes
{
self.try_structurally_resolve_type(pat.span, expected)
Expand All @@ -403,7 +418,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match pat.kind {
// Peel off a `&` or `&mut` from the scrutinee type. See the examples in
// `tests/ui/rfcs/rfc-2005-default-binding-mode`.
_ if let AdjustMode::Peel = adjust_mode
_ if let AdjustMode::Peel { .. } = adjust_mode
&& pat.default_binding_modes
&& let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() =>
{
Expand Down Expand Up @@ -443,6 +458,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Recurse with the new expected type.
self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info)
}
// If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the
// examples in `tests/ui/pattern/deref_patterns/`.
_ if self.tcx.features().deref_patterns()
&& let AdjustMode::Peel { kind: PeelKind::Implicit } = adjust_mode
&& pat.default_binding_modes
// For simplicity, only apply overloaded derefs if `expected` is a known ADT.
// FIXME(deref_patterns): we'll get better diagnostics for users trying to
// implicitly deref generics if we allow them here, but primitives, tuples, and
// inference vars definitely should be stopped. Figure out what makes most sense.
// TODO: stop peeling if the pattern is a constructor for the scrutinee type
&& expected.is_adt()
// At this point, the pattern isn't able to match `expected` without peeling. Check
// that it implements `Deref` before assuming it's a smart pointer, to get a normal
// type error instead of a missing impl error if not. This only checks for `Deref`,
// not `DerefPure`: we require that too, but we want a trait error if it's missing.
&& let Some(deref_trait) = self.tcx.lang_items().deref_trait()
&& self
.type_implements_trait(deref_trait, [expected], self.param_env)
.may_apply() =>
{
debug!("scrutinee ty {expected:?} is a smart pointer, inserting overloaded deref");
// The scrutinee is a smart pointer; implicitly dereference it. This adds a
// requirement that `expected: DerefPure`.
let inner_ty = self.deref_pat_target(pat.span, expected);
// Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any
// `ref mut` bindings. TODO: implement that, then reference here.

// Preserve the smart pointer type for THIR lowering and upvar analysis.
self.typeck_results
.borrow_mut()
.pat_adjustments_mut()
.entry(pat.hir_id)
.or_default()
.push(PatAdjustment { kind: PatAdjust::OverloadedDeref, source: expected });

// Recurse, using the old pat info to keep `current_depth` to its old value.
// Peeling smart pointers does not update the default binding mode.
self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, old_pat_info)
}
PatKind::Missing | PatKind::Wild | PatKind::Err(_) => expected,
// We allow any type here; we ensure that the type is uninhabited during match checking.
PatKind::Never => expected,
Expand Down Expand Up @@ -505,12 +559,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
PatKind::Struct(..)
| PatKind::TupleStruct(..)
| PatKind::Tuple(..)
| PatKind::Box(_)
| PatKind::Deref(_)
| PatKind::Range(..)
| PatKind::Slice(..) => AdjustMode::Peel,
| PatKind::Slice(..) => AdjustMode::Peel { kind: PeelKind::Implicit },
// When checking an explicit deref pattern, only peel reference types.
// FIXME(deref_patterns): If box patterns and deref patterns need to coexist, box
// patterns may want `PeelKind::Implicit`, stopping on encountering a box.
| PatKind::Box(_)
| PatKind::Deref(_) => AdjustMode::Peel { kind: PeelKind::ExplicitDerefPat },
// A never pattern behaves somewhat like a literal or unit variant.
PatKind::Never => AdjustMode::Peel,
PatKind::Never => AdjustMode::Peel { kind: PeelKind::Implicit },
PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => match opt_path_res.unwrap() {
// These constants can be of a reference type, e.g. `const X: &u8 = &0;`.
// Peeling the reference types too early will cause type checking failures.
Expand All @@ -520,7 +577,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// could successfully compile. The former being `Self` requires a unit struct.
// In either case, and unlike constants, the pattern itself cannot be
// a reference type wherefore peeling doesn't give up any expressiveness.
_ => AdjustMode::Peel,
_ => AdjustMode::Peel { kind: PeelKind::Implicit },
},

// String and byte-string literals result in types `&str` and `&[u8]` respectively.
Expand All @@ -530,7 +587,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Call `resolve_vars_if_possible` here for inline const blocks.
PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() {
ty::Ref(..) => AdjustMode::Pass,
_ => AdjustMode::Peel,
_ => AdjustMode::Peel { kind: PeelKind::Implicit },
},

// Ref patterns are complicated, we handle them in `check_pat_ref`.
Expand Down Expand Up @@ -2256,38 +2313,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected: Ty<'tcx>,
pat_info: PatInfo<'tcx>,
) -> Ty<'tcx> {
let tcx = self.tcx;
// Register a `DerefPure` bound, which is required by all `deref!()` pats.
self.register_bound(
expected,
tcx.require_lang_item(hir::LangItem::DerefPure, Some(span)),
self.misc(span),
);
// <expected as Deref>::Target
let ty = Ty::new_projection(
tcx,
tcx.require_lang_item(hir::LangItem::DerefTarget, Some(span)),
[expected],
);
let ty = self.normalize(span, ty);
let ty = self.try_structurally_resolve_type(span, ty);
self.check_pat(inner, ty, pat_info);
let target_ty = self.deref_pat_target(span, expected);
self.check_pat(inner, target_ty, pat_info);

// Check if the pattern has any `ref mut` bindings, which would require
// `DerefMut` to be emitted in MIR building instead of just `Deref`.
// We do this *after* checking the inner pattern, since we want to make
// sure to apply any match-ergonomics adjustments.
// TODO: move this to a separate definition to share it with implicit deref pats
if self.typeck_results.borrow().pat_has_ref_mut_binding(inner) {
self.register_bound(
expected,
tcx.require_lang_item(hir::LangItem::DerefMut, Some(span)),
self.tcx.require_lang_item(hir::LangItem::DerefMut, Some(span)),
self.misc(span),
);
}

expected
}

fn deref_pat_target(&self, span: Span, source_ty: Ty<'tcx>) -> Ty<'tcx> {
// Register a `DerefPure` bound, which is required by all `deref!()` pats.
let tcx = self.tcx;
self.register_bound(
source_ty,
tcx.require_lang_item(hir::LangItem::DerefPure, Some(span)),
self.misc(span),
);
// The expected type for the deref pat's inner pattern is `<expected as Deref>::Target`.
let target_ty = Ty::new_projection(
tcx,
tcx.require_lang_item(hir::LangItem::DerefTarget, Some(span)),
[source_ty],
);
let target_ty = self.normalize(span, target_ty);
self.try_structurally_resolve_type(span, target_ty)
}

// Precondition: Pat is Ref(inner)
fn check_pat_ref(
&self,
Expand Down
27 changes: 27 additions & 0 deletions tests/ui/pattern/deref-patterns/bindings.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//@ revisions: explicit implicit
//@ run-pass
#![feature(deref_patterns)]
#![allow(incomplete_features)]

#[cfg(explicit)]
fn simple_vec(vec: Vec<u32>) -> u32 {
match vec {
deref!([]) => 100,
Expand All @@ -13,6 +15,19 @@ fn simple_vec(vec: Vec<u32>) -> u32 {
}
}

#[cfg(implicit)]
fn simple_vec(vec: Vec<u32>) -> u32 {
match vec {
[] => 100,
[x] if x == 4 => x + 4,
[x] => x,
[1, x] => x + 200,
deref!(ref slice) => slice.iter().sum(),
_ => 2000,
}
}

#[cfg(explicit)]
fn nested_vec(vecvec: Vec<Vec<u32>>) -> u32 {
match vecvec {
deref!([]) => 0,
Expand All @@ -24,6 +39,18 @@ fn nested_vec(vecvec: Vec<Vec<u32>>) -> u32 {
}
}

#[cfg(implicit)]
fn nested_vec(vecvec: Vec<Vec<u32>>) -> u32 {
match vecvec {
[] => 0,
[[x]] => x,
[[0, x] | [1, x]] => x,
[ref x] => x.iter().sum(),
[[], [1, x, y]] => y - x,
_ => 2000,
}
}

fn ref_mut(val: u32) -> u32 {
let mut b = Box::new(0u32);
match &mut b {
Expand Down
22 changes: 22 additions & 0 deletions tests/ui/pattern/deref-patterns/branch.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//@ revisions: explicit implicit
//@ run-pass
// Test the execution of deref patterns.
#![feature(deref_patterns)]
#![allow(incomplete_features)]

#[cfg(explicit)]
fn branch(vec: Vec<u32>) -> u32 {
match vec {
deref!([]) => 0,
Expand All @@ -12,6 +14,17 @@ fn branch(vec: Vec<u32>) -> u32 {
}
}

#[cfg(implicit)]
fn branch(vec: Vec<u32>) -> u32 {
match vec {
[] => 0,
[1, _, 3] => 1,
[2, ..] => 2,
_ => 1000,
}
}

#[cfg(explicit)]
fn nested(vec: Vec<Vec<u32>>) -> u32 {
match vec {
deref!([deref!([]), ..]) => 1,
Expand All @@ -20,6 +33,15 @@ fn nested(vec: Vec<Vec<u32>>) -> u32 {
}
}

#[cfg(implicit)]
fn nested(vec: Vec<Vec<u32>>) -> u32 {
match vec {
[[], ..] => 1,
[[0, ..], [1, ..]] => 2,
_ => 1000,
}
}

fn main() {
assert!(matches!(Vec::<u32>::new(), deref!([])));
assert!(matches!(vec![1], deref!([1])));
Expand Down
18 changes: 18 additions & 0 deletions tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,22 @@ fn cant_move_out_rc(rc: Rc<Struct>) -> Struct {
}
}

struct Container(Struct);

fn cant_move_out_box_implicit(b: Box<Container>) -> Struct {
match b {
//~^ ERROR: cannot move out of a shared reference
Container(x) => x,
_ => unreachable!(),
}
}

fn cant_move_out_rc_implicit(rc: Rc<Container>) -> Struct {
match rc {
//~^ ERROR: cannot move out of a shared reference
Container(x) => x,
_ => unreachable!(),
}
}

fn main() {}
36 changes: 35 additions & 1 deletion tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,40 @@ help: consider borrowing the pattern binding
LL | deref!(ref x) => x,
| +++

error: aborting due to 2 previous errors
error[E0507]: cannot move out of a shared reference
--> $DIR/cant_move_out_of_pattern.rs:27:11
|
LL | match b {
| ^
LL |
LL | Container(x) => x,
| -
| |
| data moved here
| move occurs because `x` has type `Struct`, which does not implement the `Copy` trait
|
help: consider borrowing the pattern binding
|
LL | Container(ref x) => x,
| +++

error[E0507]: cannot move out of a shared reference
--> $DIR/cant_move_out_of_pattern.rs:35:11
|
LL | match rc {
| ^^
LL |
LL | Container(x) => x,
| -
| |
| data moved here
| move occurs because `x` has type `Struct`, which does not implement the `Copy` trait
|
help: consider borrowing the pattern binding
|
LL | Container(ref x) => x,
| +++

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0507`.
6 changes: 6 additions & 0 deletions tests/ui/pattern/deref-patterns/typeck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,32 @@ fn main() {
let vec: Vec<u32> = Vec::new();
match vec {
deref!([..]) => {}
[..] => {}
_ => {}
}
match Box::new(true) {
deref!(true) => {}
true => {}
_ => {}
}
match &Box::new(true) {
deref!(true) => {}
true => {}
_ => {}
}
match &Rc::new(0) {
deref!(1..) => {}
1.. => {}
_ => {}
}
let _: &Struct = match &Rc::new(Struct) {
deref!(x) => x,
Struct => &Struct,
_ => unreachable!(),
};
let _: &[Struct] = match &Rc::new(vec![Struct]) {
deref!(deref!(x)) => x,
[Struct] => &[Struct],
_ => unreachable!(),
};
}
Loading