-
Notifications
You must be signed in to change notification settings - Fork 13.4k
allow deref patterns to participate in exhaustiveness analysis #140106
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
Changes from all commits
b41d8bd
cf43bba
fb261a1
09fed2d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -269,6 +269,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { | |
} | ||
_ => bug!("bad slice pattern {:?} {:?}", ctor, ty), | ||
}, | ||
DerefPattern(pointee_ty) => reveal_and_alloc(cx, once(pointee_ty.inner())), | ||
Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..) | ||
| F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing | ||
| PrivateUninhabited | Wildcard => &[], | ||
|
@@ -296,7 +297,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { | |
} | ||
_ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"), | ||
}, | ||
Ref => 1, | ||
Ref | DerefPattern(_) => 1, | ||
Slice(slice) => slice.arity(), | ||
Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..) | ||
| F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing | ||
|
@@ -493,11 +494,15 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { | |
), | ||
}; | ||
} | ||
PatKind::DerefPattern { .. } => { | ||
// FIXME(deref_patterns): At least detect that `box _` is irrefutable. | ||
fields = vec![]; | ||
arity = 0; | ||
ctor = Opaque(OpaqueId::new()); | ||
PatKind::DerefPattern { subpattern, .. } => { | ||
// NB(deref_patterns): This assumes the deref pattern is matching on a trusted | ||
// `DerefPure` type. If the `Deref` impl isn't trusted, exhaustiveness must take | ||
// into account that multiple calls to deref may return different results. Hence | ||
// multiple deref! patterns cannot be exhaustive together unless each is exhaustive | ||
// by itself. | ||
fields = vec![self.lower_pat(subpattern).at_index(0)]; | ||
arity = 1; | ||
ctor = DerefPattern(cx.reveal_opaque_ty(subpattern.ty)); | ||
} | ||
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { | ||
match ty.kind() { | ||
|
@@ -874,6 +879,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { | |
print::write_ref_like(&mut s, pat.ty().inner(), &print(&pat.fields[0])).unwrap(); | ||
s | ||
} | ||
DerefPattern(_) => format!("deref!({})", print(&pat.fields[0])), | ||
Slice(slice) => { | ||
let (prefix_len, has_dot_dot) = match slice.kind { | ||
SliceKind::FixedLen(len) => (len, false), | ||
|
@@ -1100,6 +1106,14 @@ pub fn analyze_match<'p, 'tcx>( | |
scrut_ty: Ty<'tcx>, | ||
) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> { | ||
let scrut_ty = tycx.reveal_opaque_ty(scrut_ty); | ||
|
||
// The analysis doesn't support deref patterns mixed with normal constructors; error if present. | ||
// FIXME(deref_patterns): This only needs to run when a deref pattern was found during lowering. | ||
if tycx.tcx.features().deref_patterns() { | ||
let pat_column = PatternColumn::new(arms); | ||
detect_mixed_deref_pat_ctors(tycx, &pat_column)?; | ||
} | ||
|
||
let scrut_validity = PlaceValidity::from_bool(tycx.known_valid_scrutinee); | ||
let report = compute_match_usefulness( | ||
tycx, | ||
|
@@ -1119,6 +1133,51 @@ pub fn analyze_match<'p, 'tcx>( | |
Ok(report) | ||
} | ||
|
||
// FIXME(deref_patterns): Currently it's the responsibility of the frontend (rustc or rust-analyzer) | ||
// to ensure that deref patterns don't appear in the same column as normal constructors. Deref | ||
// patterns aren't currently implemented in rust-analyzer, but should they be, the columnwise check | ||
// here could be made generic and shared between frontends. | ||
fn detect_mixed_deref_pat_ctors<'p, 'tcx>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a better home for this anywhere? The other use of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's looking like maybe this should work for generic There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I looked into rehousing this and making it work for generic There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, perf experiment seems the only way to know There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As for where to house it, we could rename |
||
cx: &RustcPatCtxt<'p, 'tcx>, | ||
column: &PatternColumn<'p, RustcPatCtxt<'p, 'tcx>>, | ||
) -> Result<(), ErrorGuaranteed> { | ||
let Some(&ty) = column.head_ty() else { | ||
return Ok(()); | ||
}; | ||
|
||
// Check for a mix of deref patterns and normal constructors. | ||
let mut normal_ctor_span = None; | ||
let mut deref_pat_span = None; | ||
for pat in column.iter() { | ||
match pat.ctor() { | ||
// The analysis can handle mixing deref patterns with wildcards and opaque patterns. | ||
Wildcard | Opaque(_) => {} | ||
DerefPattern(_) => deref_pat_span = Some(pat.data().span), | ||
// Nothing else can be compared to deref patterns in `Constructor::is_covered_by`. | ||
_ => normal_ctor_span = Some(pat.data().span), | ||
} | ||
} | ||
if let Some(normal_constructor_label) = normal_ctor_span | ||
&& let Some(deref_pattern_label) = deref_pat_span | ||
{ | ||
return Err(cx.tcx.dcx().emit_err(errors::MixedDerefPatternConstructors { | ||
spans: vec![deref_pattern_label, normal_constructor_label], | ||
smart_pointer_ty: ty.inner(), | ||
deref_pattern_label, | ||
normal_constructor_label, | ||
})); | ||
} | ||
|
||
// Specialize and recurse into the patterns' fields. | ||
let set = column.analyze_ctors(cx, &ty)?; | ||
for ctor in set.present { | ||
for specialized_column in column.specialize(cx, &ty, &ctor).iter() { | ||
detect_mixed_deref_pat_ctors(cx, specialized_column)?; | ||
} | ||
} | ||
Ok(()) | ||
} | ||
|
||
struct RecursiveOpaque { | ||
def_id: DefId, | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.