Auto merge of #13167 - iDawer:exhaustive_patterns, r=Veykril
feat: Implement `feature(exhaustive_patterns)` from unstable Rust Closes #12753 Recognize Rust's unstable `#![feature(exhaustive_patterns)]` (RFC 1872). Allow omitting visibly uninhabited variants from `match` expressions when the feature is on. This adjusts match checking to the current implementation of the postponed RFC 1872 in rustc.
This commit is contained in:
commit
4f8153e4a5
@ -64,7 +64,7 @@
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use la_arena::Arena;
|
use la_arena::Arena;
|
||||||
use profile::Count;
|
use profile::Count;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use syntax::{ast, SmolStr};
|
use syntax::{ast, SmolStr};
|
||||||
|
|
||||||
@ -114,6 +114,8 @@ pub struct DefMap {
|
|||||||
registered_attrs: Vec<SmolStr>,
|
registered_attrs: Vec<SmolStr>,
|
||||||
/// Custom tool modules registered with `#![register_tool]`.
|
/// Custom tool modules registered with `#![register_tool]`.
|
||||||
registered_tools: Vec<SmolStr>,
|
registered_tools: Vec<SmolStr>,
|
||||||
|
/// Unstable features of Rust enabled with `#![feature(A, B)]`.
|
||||||
|
unstable_features: FxHashSet<SmolStr>,
|
||||||
|
|
||||||
edition: Edition,
|
edition: Edition,
|
||||||
recursion_limit: Option<u32>,
|
recursion_limit: Option<u32>,
|
||||||
@ -284,6 +286,7 @@ fn empty(krate: CrateId, edition: Edition, module_data: ModuleData) -> DefMap {
|
|||||||
modules,
|
modules,
|
||||||
registered_attrs: Vec::new(),
|
registered_attrs: Vec::new(),
|
||||||
registered_tools: Vec::new(),
|
registered_tools: Vec::new(),
|
||||||
|
unstable_features: FxHashSet::default(),
|
||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -314,6 +317,10 @@ pub fn registered_attrs(&self) -> &[SmolStr] {
|
|||||||
&self.registered_attrs
|
&self.registered_attrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_unstable_feature_enabled(&self, feature: &str) -> bool {
|
||||||
|
self.unstable_features.contains(feature)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn root(&self) -> LocalModuleId {
|
pub fn root(&self) -> LocalModuleId {
|
||||||
self.root
|
self.root
|
||||||
}
|
}
|
||||||
@ -483,6 +490,7 @@ fn shrink_to_fit(&mut self) {
|
|||||||
registered_tools,
|
registered_tools,
|
||||||
fn_proc_macro_mapping,
|
fn_proc_macro_mapping,
|
||||||
derive_helpers_in_scope,
|
derive_helpers_in_scope,
|
||||||
|
unstable_features,
|
||||||
proc_macro_loading_error: _,
|
proc_macro_loading_error: _,
|
||||||
block: _,
|
block: _,
|
||||||
edition: _,
|
edition: _,
|
||||||
@ -500,6 +508,7 @@ fn shrink_to_fit(&mut self) {
|
|||||||
registered_tools.shrink_to_fit();
|
registered_tools.shrink_to_fit();
|
||||||
fn_proc_macro_mapping.shrink_to_fit();
|
fn_proc_macro_mapping.shrink_to_fit();
|
||||||
derive_helpers_in_scope.shrink_to_fit();
|
derive_helpers_in_scope.shrink_to_fit();
|
||||||
|
unstable_features.shrink_to_fit();
|
||||||
for (_, module) in modules.iter_mut() {
|
for (_, module) in modules.iter_mut() {
|
||||||
module.children.shrink_to_fit();
|
module.children.shrink_to_fit();
|
||||||
module.scope.shrink_to_fit();
|
module.scope.shrink_to_fit();
|
||||||
|
@ -294,6 +294,17 @@ fn seed_with_top_level(&mut self) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *attr_name == hir_expand::name![feature] {
|
||||||
|
let features =
|
||||||
|
attr.parse_path_comma_token_tree().into_iter().flatten().filter_map(
|
||||||
|
|feat| match feat.segments() {
|
||||||
|
[name] => Some(name.to_smol_str()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.def_map.unstable_features.extend(features);
|
||||||
|
}
|
||||||
|
|
||||||
let attr_is_register_like = *attr_name == hir_expand::name![register_attr]
|
let attr_is_register_like = *attr_name == hir_expand::name![register_attr]
|
||||||
|| *attr_name == hir_expand::name![register_tool];
|
|| *attr_name == hir_expand::name![register_tool];
|
||||||
if !attr_is_register_like {
|
if !attr_is_register_like {
|
||||||
|
@ -336,6 +336,7 @@ macro_rules! known_names {
|
|||||||
test,
|
test,
|
||||||
test_case,
|
test_case,
|
||||||
recursion_limit,
|
recursion_limit,
|
||||||
|
feature,
|
||||||
// Safe intrinsics
|
// Safe intrinsics
|
||||||
abort,
|
abort,
|
||||||
add_with_overflow,
|
add_with_overflow,
|
||||||
|
@ -159,12 +159,7 @@ fn validate_match(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let pattern_arena = Arena::new();
|
let pattern_arena = Arena::new();
|
||||||
let cx = MatchCheckCtx {
|
let cx = MatchCheckCtx::new(self.owner.module(db.upcast()), self.owner, db, &pattern_arena);
|
||||||
module: self.owner.module(db.upcast()),
|
|
||||||
body: self.owner,
|
|
||||||
db,
|
|
||||||
pattern_arena: &pattern_arena,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut m_arms = Vec::with_capacity(arms.len());
|
let mut m_arms = Vec::with_capacity(arms.len());
|
||||||
let mut has_lowering_errors = false;
|
let mut has_lowering_errors = false;
|
||||||
|
@ -52,7 +52,10 @@
|
|||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
|
|
||||||
use crate::{infer::normalize, AdtId, Interner, Scalar, Ty, TyExt, TyKind};
|
use crate::{
|
||||||
|
infer::normalize, inhabitedness::is_enum_variant_uninhabited_from, AdtId, Interner, Scalar, Ty,
|
||||||
|
TyExt, TyKind,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
is_box,
|
is_box,
|
||||||
@ -557,8 +560,8 @@ pub(super) fn new(pcx: PatCtxt<'_, '_>) -> Self {
|
|||||||
TyKind::Scalar(Scalar::Bool) => smallvec![make_range(0, 1, Scalar::Bool)],
|
TyKind::Scalar(Scalar::Bool) => smallvec![make_range(0, 1, Scalar::Bool)],
|
||||||
// TyKind::Array(..) if ... => unhandled(),
|
// TyKind::Array(..) if ... => unhandled(),
|
||||||
TyKind::Array(..) | TyKind::Slice(..) => unhandled(),
|
TyKind::Array(..) | TyKind::Slice(..) => unhandled(),
|
||||||
&TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), ..) => {
|
TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), subst) => {
|
||||||
let enum_data = cx.db.enum_data(enum_id);
|
let enum_data = cx.db.enum_data(*enum_id);
|
||||||
|
|
||||||
// If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
|
// If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
|
||||||
// additional "unknown" constructor.
|
// additional "unknown" constructor.
|
||||||
@ -591,14 +594,15 @@ pub(super) fn new(pcx: PatCtxt<'_, '_>) -> Self {
|
|||||||
let mut ctors: SmallVec<[_; 1]> = enum_data
|
let mut ctors: SmallVec<[_; 1]> = enum_data
|
||||||
.variants
|
.variants
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|&(_, _v)| {
|
.map(|(local_id, _)| EnumVariantId { parent: *enum_id, local_id })
|
||||||
|
.filter(|&variant| {
|
||||||
// If `exhaustive_patterns` is enabled, we exclude variants known to be
|
// If `exhaustive_patterns` is enabled, we exclude variants known to be
|
||||||
// uninhabited.
|
// uninhabited.
|
||||||
let is_uninhabited = is_exhaustive_pat_feature
|
let is_uninhabited = is_exhaustive_pat_feature
|
||||||
&& unimplemented!("after MatchCheckCtx.feature_exhaustive_patterns()");
|
&& is_enum_variant_uninhabited_from(variant, subst, cx.module, cx.db);
|
||||||
!is_uninhabited
|
!is_uninhabited
|
||||||
})
|
})
|
||||||
.map(|(local_id, _)| Variant(EnumVariantId { parent: enum_id, local_id }))
|
.map(Variant)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if is_secretly_empty || is_declared_nonexhaustive {
|
if is_secretly_empty || is_declared_nonexhaustive {
|
||||||
|
@ -277,7 +277,7 @@
|
|||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use typed_arena::Arena;
|
use typed_arena::Arena;
|
||||||
|
|
||||||
use crate::{db::HirDatabase, Ty, TyExt};
|
use crate::{db::HirDatabase, inhabitedness::is_ty_uninhabited_from, Ty, TyExt};
|
||||||
|
|
||||||
use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard};
|
use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard};
|
||||||
|
|
||||||
@ -289,13 +289,27 @@ pub(crate) struct MatchCheckCtx<'a, 'p> {
|
|||||||
pub(crate) db: &'a dyn HirDatabase,
|
pub(crate) db: &'a dyn HirDatabase,
|
||||||
/// Lowered patterns from arms plus generated by the check.
|
/// Lowered patterns from arms plus generated by the check.
|
||||||
pub(crate) pattern_arena: &'p Arena<DeconstructedPat<'p>>,
|
pub(crate) pattern_arena: &'p Arena<DeconstructedPat<'p>>,
|
||||||
|
exhaustive_patterns: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'p> MatchCheckCtx<'a, 'p> {
|
impl<'a, 'p> MatchCheckCtx<'a, 'p> {
|
||||||
pub(super) fn is_uninhabited(&self, _ty: &Ty) -> bool {
|
pub(crate) fn new(
|
||||||
// FIXME(iDawer) implement exhaustive_patterns feature. More info in:
|
module: ModuleId,
|
||||||
// Tracking issue for RFC 1872: exhaustive_patterns feature https://github.com/rust-lang/rust/issues/51085
|
body: DefWithBodyId,
|
||||||
false
|
db: &'a dyn HirDatabase,
|
||||||
|
pattern_arena: &'p Arena<DeconstructedPat<'p>>,
|
||||||
|
) -> Self {
|
||||||
|
let def_map = db.crate_def_map(module.krate());
|
||||||
|
let exhaustive_patterns = def_map.is_unstable_feature_enabled("exhaustive_patterns");
|
||||||
|
Self { module, body, db, pattern_arena, exhaustive_patterns }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn is_uninhabited(&self, ty: &Ty) -> bool {
|
||||||
|
if self.feature_exhaustive_patterns() {
|
||||||
|
is_ty_uninhabited_from(ty, self.module, self.db)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
|
/// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
|
||||||
@ -311,10 +325,9 @@ pub(super) fn is_foreign_non_exhaustive_enum(&self, ty: &Ty) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rust feature described as "Allows exhaustive pattern matching on types that contain uninhabited types."
|
// Rust's unstable feature described as "Allows exhaustive pattern matching on types that contain uninhabited types."
|
||||||
pub(super) fn feature_exhaustive_patterns(&self) -> bool {
|
pub(super) fn feature_exhaustive_patterns(&self) -> bool {
|
||||||
// FIXME see MatchCheckCtx::is_uninhabited
|
self.exhaustive_patterns
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
173
crates/hir-ty/src/inhabitedness.rs
Normal file
173
crates/hir-ty/src/inhabitedness.rs
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
//! Type inhabitedness logic.
|
||||||
|
use std::ops::ControlFlow::{self, Break, Continue};
|
||||||
|
|
||||||
|
use chalk_ir::{
|
||||||
|
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
|
||||||
|
DebruijnIndex,
|
||||||
|
};
|
||||||
|
use hir_def::{
|
||||||
|
adt::VariantData, attr::Attrs, type_ref::ConstScalar, visibility::Visibility, AdtId,
|
||||||
|
EnumVariantId, HasModule, Lookup, ModuleId, VariantId,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
db::HirDatabase, Binders, ConcreteConst, Const, ConstValue, Interner, Substitution, Ty, TyKind,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Checks whether a type is visibly uninhabited from a particular module.
|
||||||
|
pub(crate) fn is_ty_uninhabited_from(ty: &Ty, target_mod: ModuleId, db: &dyn HirDatabase) -> bool {
|
||||||
|
let mut uninhabited_from = UninhabitedFrom { target_mod, db };
|
||||||
|
let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
|
||||||
|
inhabitedness == BREAK_VISIBLY_UNINHABITED
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks whether a variant is visibly uninhabited from a particular module.
|
||||||
|
pub(crate) fn is_enum_variant_uninhabited_from(
|
||||||
|
variant: EnumVariantId,
|
||||||
|
subst: &Substitution,
|
||||||
|
target_mod: ModuleId,
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
) -> bool {
|
||||||
|
let enum_data = db.enum_data(variant.parent);
|
||||||
|
let vars_attrs = db.variants_attrs(variant.parent);
|
||||||
|
let is_local = variant.parent.lookup(db.upcast()).container.krate() == target_mod.krate();
|
||||||
|
|
||||||
|
let mut uninhabited_from = UninhabitedFrom { target_mod, db };
|
||||||
|
let inhabitedness = uninhabited_from.visit_variant(
|
||||||
|
variant.into(),
|
||||||
|
&enum_data.variants[variant.local_id].variant_data,
|
||||||
|
subst,
|
||||||
|
&vars_attrs[variant.local_id],
|
||||||
|
is_local,
|
||||||
|
);
|
||||||
|
inhabitedness == BREAK_VISIBLY_UNINHABITED
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UninhabitedFrom<'a> {
|
||||||
|
target_mod: ModuleId,
|
||||||
|
db: &'a dyn HirDatabase,
|
||||||
|
}
|
||||||
|
|
||||||
|
const CONTINUE_OPAQUELY_INHABITED: ControlFlow<VisiblyUninhabited> = Continue(());
|
||||||
|
const BREAK_VISIBLY_UNINHABITED: ControlFlow<VisiblyUninhabited> = Break(VisiblyUninhabited);
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
struct VisiblyUninhabited;
|
||||||
|
|
||||||
|
impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
|
||||||
|
type BreakTy = VisiblyUninhabited;
|
||||||
|
|
||||||
|
fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = VisiblyUninhabited> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_ty(
|
||||||
|
&mut self,
|
||||||
|
ty: &Ty,
|
||||||
|
outer_binder: DebruijnIndex,
|
||||||
|
) -> ControlFlow<VisiblyUninhabited> {
|
||||||
|
match ty.kind(Interner) {
|
||||||
|
TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst),
|
||||||
|
TyKind::Never => BREAK_VISIBLY_UNINHABITED,
|
||||||
|
TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder),
|
||||||
|
TyKind::Array(item_ty, len) => match try_usize_const(len) {
|
||||||
|
Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
|
||||||
|
Some(1..) => item_ty.super_visit_with(self, outer_binder),
|
||||||
|
},
|
||||||
|
|
||||||
|
TyKind::Ref(..) | _ => CONTINUE_OPAQUELY_INHABITED,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interner(&self) -> Interner {
|
||||||
|
Interner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UninhabitedFrom<'_> {
|
||||||
|
fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow<VisiblyUninhabited> {
|
||||||
|
let attrs = self.db.attrs(adt.into());
|
||||||
|
let adt_non_exhaustive = attrs.by_key("non_exhaustive").exists();
|
||||||
|
let is_local = adt.module(self.db.upcast()).krate() == self.target_mod.krate();
|
||||||
|
if adt_non_exhaustive && !is_local {
|
||||||
|
return CONTINUE_OPAQUELY_INHABITED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// An ADT is uninhabited iff all its variants uninhabited.
|
||||||
|
match adt {
|
||||||
|
// rustc: For now, `union`s are never considered uninhabited.
|
||||||
|
AdtId::UnionId(_) => CONTINUE_OPAQUELY_INHABITED,
|
||||||
|
AdtId::StructId(s) => {
|
||||||
|
let struct_data = self.db.struct_data(s);
|
||||||
|
self.visit_variant(s.into(), &struct_data.variant_data, subst, &attrs, is_local)
|
||||||
|
}
|
||||||
|
AdtId::EnumId(e) => {
|
||||||
|
let vars_attrs = self.db.variants_attrs(e);
|
||||||
|
let enum_data = self.db.enum_data(e);
|
||||||
|
|
||||||
|
for (local_id, enum_var) in enum_data.variants.iter() {
|
||||||
|
let variant_inhabitedness = self.visit_variant(
|
||||||
|
EnumVariantId { parent: e, local_id }.into(),
|
||||||
|
&enum_var.variant_data,
|
||||||
|
subst,
|
||||||
|
&vars_attrs[local_id],
|
||||||
|
is_local,
|
||||||
|
);
|
||||||
|
match variant_inhabitedness {
|
||||||
|
Break(VisiblyUninhabited) => continue,
|
||||||
|
Continue(()) => return CONTINUE_OPAQUELY_INHABITED,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BREAK_VISIBLY_UNINHABITED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_variant(
|
||||||
|
&mut self,
|
||||||
|
variant: VariantId,
|
||||||
|
variant_data: &VariantData,
|
||||||
|
subst: &Substitution,
|
||||||
|
attrs: &Attrs,
|
||||||
|
is_local: bool,
|
||||||
|
) -> ControlFlow<VisiblyUninhabited> {
|
||||||
|
let non_exhaustive_field_list = attrs.by_key("non_exhaustive").exists();
|
||||||
|
if non_exhaustive_field_list && !is_local {
|
||||||
|
return CONTINUE_OPAQUELY_INHABITED;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_enum = matches!(variant, VariantId::EnumVariantId(..));
|
||||||
|
let field_tys = self.db.field_types(variant);
|
||||||
|
let field_vis = self.db.field_visibilities(variant);
|
||||||
|
|
||||||
|
for (fid, _) in variant_data.fields().iter() {
|
||||||
|
self.visit_field(field_vis[fid], &field_tys[fid], subst, is_enum)?;
|
||||||
|
}
|
||||||
|
CONTINUE_OPAQUELY_INHABITED
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_field(
|
||||||
|
&mut self,
|
||||||
|
vis: Visibility,
|
||||||
|
ty: &Binders<Ty>,
|
||||||
|
subst: &Substitution,
|
||||||
|
is_enum: bool,
|
||||||
|
) -> ControlFlow<VisiblyUninhabited> {
|
||||||
|
if is_enum || vis.is_visible_from(self.db.upcast(), self.target_mod) {
|
||||||
|
let ty = ty.clone().substitute(Interner, subst);
|
||||||
|
ty.visit_with(self, DebruijnIndex::INNERMOST)
|
||||||
|
} else {
|
||||||
|
CONTINUE_OPAQUELY_INHABITED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_usize_const(c: &Const) -> Option<u128> {
|
||||||
|
let data = &c.data(Interner);
|
||||||
|
if data.ty.kind(Interner) != &TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
match data.value {
|
||||||
|
ConstValue::Concrete(ConcreteConst { interned: ConstScalar::UInt(value) }) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ macro_rules! eprintln {
|
|||||||
mod chalk_ext;
|
mod chalk_ext;
|
||||||
pub mod consteval;
|
pub mod consteval;
|
||||||
mod infer;
|
mod infer;
|
||||||
|
mod inhabitedness;
|
||||||
mod interner;
|
mod interner;
|
||||||
mod lower;
|
mod lower;
|
||||||
mod mapping;
|
mod mapping;
|
||||||
|
@ -947,6 +947,50 @@ fn f() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod rust_unstable {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rfc_1872_exhaustive_patterns() {
|
||||||
|
check_diagnostics_no_bails(
|
||||||
|
r"
|
||||||
|
//- minicore: option, result
|
||||||
|
#![feature(exhaustive_patterns)]
|
||||||
|
enum Void {}
|
||||||
|
fn test() {
|
||||||
|
match None::<!> { None => () }
|
||||||
|
match Result::<u8, !>::Ok(2) { Ok(_) => () }
|
||||||
|
match Result::<u8, Void>::Ok(2) { Ok(_) => () }
|
||||||
|
match (2, loop {}) {}
|
||||||
|
match Result::<!, !>::Ok(loop {}) {}
|
||||||
|
match (&loop {}) {} // https://github.com/rust-lang/rust/issues/50642#issuecomment-388234919
|
||||||
|
// ^^^^^^^^^^ error: missing match arm: type `&!` is non-empty
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rfc_1872_private_uninhabitedness() {
|
||||||
|
check_diagnostics_no_bails(
|
||||||
|
r"
|
||||||
|
//- minicore: option
|
||||||
|
//- /lib.rs crate:lib
|
||||||
|
#![feature(exhaustive_patterns)]
|
||||||
|
pub struct PrivatelyUninhabited { private_field: Void }
|
||||||
|
enum Void {}
|
||||||
|
fn test_local(x: Option<PrivatelyUninhabited>) {
|
||||||
|
match x {}
|
||||||
|
} // ^ error: missing match arm: `None` not covered
|
||||||
|
//- /main.rs crate:main deps:lib
|
||||||
|
#![feature(exhaustive_patterns)]
|
||||||
|
fn test(x: Option<lib::PrivatelyUninhabited>) {
|
||||||
|
match x {}
|
||||||
|
// ^ error: missing match arm: `None` and `Some(_)` not covered
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod false_negatives {
|
mod false_negatives {
|
||||||
//! The implementation of match checking here is a work in progress. As we roll this out, we
|
//! The implementation of match checking here is a work in progress. As we roll this out, we
|
||||||
//! prefer false negatives to false positives (ideally there would be no false positives). This
|
//! prefer false negatives to false positives (ideally there would be no false positives). This
|
||||||
|
Loading…
Reference in New Issue
Block a user