Reveal opaque types in exhaustiveness checking
This commit is contained in:
parent
7e4924b55d
commit
2a87bae48d
7 changed files with 76 additions and 42 deletions
|
@ -62,7 +62,8 @@ pub trait TypeCx: Sized + Clone + fmt::Debug {
|
|||
/// patterns during analysis.
|
||||
type PatData: Clone + Default;
|
||||
|
||||
fn is_opaque_ty(ty: Self::Ty) -> bool;
|
||||
/// FIXME(Nadrieril): `Cx` should only give us revealed types.
|
||||
fn reveal_opaque_ty(&self, ty: Self::Ty) -> Self::Ty;
|
||||
fn is_exhaustive_patterns_feature_on(&self) -> bool;
|
||||
|
||||
/// The number of fields for this constructor.
|
||||
|
|
|
@ -48,22 +48,14 @@ impl<'a, 'p, 'tcx> PatternColumn<'a, 'p, 'tcx> {
|
|||
fn is_empty(&self) -> bool {
|
||||
self.patterns.is_empty()
|
||||
}
|
||||
fn head_ty(&self) -> Option<Ty<'tcx>> {
|
||||
fn head_ty(&self, cx: MatchCtxt<'a, 'p, 'tcx>) -> Option<Ty<'tcx>> {
|
||||
if self.patterns.len() == 0 {
|
||||
return None;
|
||||
}
|
||||
// If the type is opaque and it is revealed anywhere in the column, we take the revealed
|
||||
// version. Otherwise we could encounter constructors for the revealed type and crash.
|
||||
let first_ty = self.patterns[0].ty();
|
||||
if RustcMatchCheckCtxt::is_opaque_ty(first_ty) {
|
||||
for pat in &self.patterns {
|
||||
let ty = pat.ty();
|
||||
if !RustcMatchCheckCtxt::is_opaque_ty(ty) {
|
||||
return Some(ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(first_ty)
|
||||
|
||||
let ty = self.patterns[0].ty();
|
||||
// FIXME(Nadrieril): `Cx` should only give us revealed types.
|
||||
Some(cx.tycx.reveal_opaque_ty(ty))
|
||||
}
|
||||
|
||||
/// Do constructor splitting on the constructors of the column.
|
||||
|
@ -125,7 +117,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
|
|||
cx: MatchCtxt<'a, 'p, 'tcx>,
|
||||
column: &PatternColumn<'a, 'p, 'tcx>,
|
||||
) -> Vec<WitnessPat<'p, 'tcx>> {
|
||||
let Some(ty) = column.head_ty() else {
|
||||
let Some(ty) = column.head_ty(cx) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let pcx = &PlaceCtxt::new_dummy(cx, ty);
|
||||
|
@ -226,7 +218,7 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
|
|||
cx: MatchCtxt<'a, 'p, 'tcx>,
|
||||
column: &PatternColumn<'a, 'p, 'tcx>,
|
||||
) {
|
||||
let Some(ty) = column.head_ty() else {
|
||||
let Some(ty) = column.head_ty(cx) else {
|
||||
return;
|
||||
};
|
||||
let pcx = &PlaceCtxt::new_dummy(cx, ty);
|
||||
|
|
|
@ -44,6 +44,7 @@ pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat<RustcMatchCheckCtxt<'p, '
|
|||
#[derive(Clone)]
|
||||
pub struct RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
pub tcx: TyCtxt<'tcx>,
|
||||
pub typeck_results: &'tcx ty::TypeckResults<'tcx>,
|
||||
/// The module in which the match occurs. This is necessary for
|
||||
/// checking inhabited-ness of types because whether a type is (visibly)
|
||||
/// inhabited can depend on whether it was defined in the current module or
|
||||
|
@ -101,6 +102,21 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Type inference occasionally gives us opaque types in places where corresponding patterns
|
||||
/// have more specific types. To avoid inconsistencies as well as detect opaque uninhabited
|
||||
/// types, we use the corresponding concrete type if possible.
|
||||
fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() {
|
||||
if let Some(local_def_id) = alias_ty.def_id.as_local() {
|
||||
let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args };
|
||||
if let Some(real_ty) = self.typeck_results.concrete_opaque_types.get(&key) {
|
||||
return real_ty.ty;
|
||||
}
|
||||
}
|
||||
}
|
||||
ty
|
||||
}
|
||||
|
||||
// In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide
|
||||
// uninhabited fields in order not to reveal the uninhabitedness of the whole variant.
|
||||
// This lists the fields we keep along with their types.
|
||||
|
@ -873,8 +889,9 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
|
|||
fn is_exhaustive_patterns_feature_on(&self) -> bool {
|
||||
self.tcx.features().exhaustive_patterns
|
||||
}
|
||||
fn is_opaque_ty(ty: Self::Ty) -> bool {
|
||||
matches!(ty.kind(), ty::Alias(ty::Opaque, ..))
|
||||
|
||||
fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
self.reveal_opaque_ty(ty)
|
||||
}
|
||||
|
||||
fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: Self::Ty) -> usize {
|
||||
|
|
|
@ -865,24 +865,14 @@ impl<'a, 'p, Cx: TypeCx> Matrix<'a, 'p, Cx> {
|
|||
matrix
|
||||
}
|
||||
|
||||
fn head_ty(&self) -> Option<Cx::Ty> {
|
||||
fn head_ty(&self, mcx: MatchCtxt<'a, 'p, Cx>) -> Option<Cx::Ty> {
|
||||
if self.column_count() == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut ty = self.wildcard_row.head().ty();
|
||||
// If the type is opaque and it is revealed anywhere in the column, we take the revealed
|
||||
// version. Otherwise we could encounter constructors for the revealed type and crash.
|
||||
if Cx::is_opaque_ty(ty) {
|
||||
for pat in self.heads() {
|
||||
let pat_ty = pat.ty();
|
||||
if !Cx::is_opaque_ty(pat_ty) {
|
||||
ty = pat_ty;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(ty)
|
||||
let ty = self.wildcard_row.head().ty();
|
||||
// FIXME(Nadrieril): `Cx` should only give us revealed types.
|
||||
Some(mcx.tycx.reveal_opaque_ty(ty))
|
||||
}
|
||||
fn column_count(&self) -> usize {
|
||||
self.wildcard_row.len()
|
||||
|
@ -1181,7 +1171,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
|
|||
) -> WitnessMatrix<Cx> {
|
||||
debug_assert!(matrix.rows().all(|r| r.len() == matrix.column_count()));
|
||||
|
||||
let Some(ty) = matrix.head_ty() else {
|
||||
let Some(ty) = matrix.head_ty(mcx) else {
|
||||
// The base case: there are no columns in the matrix. We are morally pattern-matching on ().
|
||||
// A row is useful iff it has no (unguarded) rows above it.
|
||||
for row in matrix.rows_mut() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue