Skip to content

Commit 2ab69b8

Browse files
authored
Rollup merge of rust-lang#138001 - meithecatte:privately-uninhabited, r=Nadrieril
mir_build: consider privacy when checking for irrefutable patterns This PR fixes rust-lang#137999. Note that, since this makes the compiler reject code that was previously accepted, it will probably need a crater run. I include a commit that factors out a common code pattern into a helper function, purely because the fact that this was repeated all over the place was bothering me. Let me know if I should split that into a separate PR instead.
2 parents ce76292 + 044deec commit 2ab69b8

File tree

17 files changed

+121
-50
lines changed

17 files changed

+121
-50
lines changed

compiler/rustc_hir_typeck/src/cast.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ use rustc_middle::ty::error::TypeError;
4343
use rustc_middle::ty::{self, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, VariantDef, elaborate};
4444
use rustc_middle::{bug, span_bug};
4545
use rustc_session::lint;
46-
use rustc_span::def_id::LOCAL_CRATE;
4746
use rustc_span::{DUMMY_SP, Span, sym};
4847
use rustc_trait_selection::infer::InferCtxtExt;
4948
use tracing::{debug, instrument};
@@ -805,7 +804,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
805804
_ => return Err(CastError::NonScalar),
806805
};
807806
if let ty::Adt(adt_def, _) = *self.expr_ty.kind()
808-
&& adt_def.did().krate != LOCAL_CRATE
807+
&& !adt_def.did().is_local()
809808
&& adt_def.variants().iter().any(VariantDef::is_field_list_non_exhaustive)
810809
{
811810
return Err(CastError::ForeignNonExhaustiveAdt);

compiler/rustc_hir_typeck/src/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1977,7 +1977,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19771977

19781978
// Prohibit struct expressions when non-exhaustive flag is set.
19791979
let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type");
1980-
if !adt.did().is_local() && variant.is_field_list_non_exhaustive() {
1980+
if variant.field_list_has_applicable_non_exhaustive() {
19811981
self.dcx()
19821982
.emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() });
19831983
}

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

+2-9
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_middle::hir::place::ProjectionKind;
2020
pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection};
2121
use rustc_middle::mir::FakeReadCause;
2222
use rustc_middle::ty::{
23-
self, AdtKind, BorrowKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, adjustment,
23+
self, BorrowKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, adjustment,
2424
};
2525
use rustc_middle::{bug, span_bug};
2626
use rustc_span::{ErrorGuaranteed, Span};
@@ -1899,14 +1899,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
18991899
// to assume that more cases will be added to the variant in the future. This mean
19001900
// that we should handle non-exhaustive SingleVariant the same way we would handle
19011901
// a MultiVariant.
1902-
// If the variant is not local it must be defined in another crate.
1903-
let is_non_exhaustive = match def.adt_kind() {
1904-
AdtKind::Struct | AdtKind::Union => {
1905-
def.non_enum_variant().is_field_list_non_exhaustive()
1906-
}
1907-
AdtKind::Enum => def.is_variant_list_non_exhaustive(),
1908-
};
1909-
def.variants().len() > 1 || (!def.did().is_local() && is_non_exhaustive)
1902+
def.variants().len() > 1 || def.variant_list_has_applicable_non_exhaustive()
19101903
} else {
19111904
false
19121905
}

compiler/rustc_hir_typeck/src/pat.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1724,7 +1724,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17241724
};
17251725

17261726
// Require `..` if struct has non_exhaustive attribute.
1727-
let non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did().is_local();
1727+
let non_exhaustive = variant.field_list_has_applicable_non_exhaustive();
17281728
if non_exhaustive && !has_rest_pat {
17291729
self.error_foreign_non_exhaustive_spat(pat, adt.variant_descr(), fields.is_empty());
17301730
}

compiler/rustc_lint/src/types.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -1193,9 +1193,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11931193
};
11941194
}
11951195

1196-
let is_non_exhaustive =
1197-
def.non_enum_variant().is_field_list_non_exhaustive();
1198-
if is_non_exhaustive && !def.did().is_local() {
1196+
if def.non_enum_variant().field_list_has_applicable_non_exhaustive() {
11991197
return FfiUnsafe {
12001198
ty,
12011199
reason: if def.is_struct() {
@@ -1248,14 +1246,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12481246
};
12491247
}
12501248

1251-
use improper_ctypes::{
1252-
check_non_exhaustive_variant, non_local_and_non_exhaustive,
1253-
};
1249+
use improper_ctypes::check_non_exhaustive_variant;
12541250

1255-
let non_local_def = non_local_and_non_exhaustive(def);
1251+
let non_exhaustive = def.variant_list_has_applicable_non_exhaustive();
12561252
// Check the contained variants.
12571253
let ret = def.variants().iter().try_for_each(|variant| {
1258-
check_non_exhaustive_variant(non_local_def, variant)
1254+
check_non_exhaustive_variant(non_exhaustive, variant)
12591255
.map_break(|reason| FfiUnsafe { ty, reason, help: None })?;
12601256

12611257
match self.check_variant_for_ffi(acc, ty, def, variant, args) {

compiler/rustc_lint/src/types/improper_ctypes.rs

+3-11
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ use crate::fluent_generated as fluent;
1515
/// so we don't need the lint to account for it.
1616
/// e.g. going from enum Foo { A, B, C } to enum Foo { A, B, C, D(u32) }.
1717
pub(crate) fn check_non_exhaustive_variant(
18-
non_local_def: bool,
18+
non_exhaustive_variant_list: bool,
1919
variant: &ty::VariantDef,
2020
) -> ControlFlow<DiagMessage, ()> {
2121
// non_exhaustive suggests it is possible that someone might break ABI
2222
// see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344
2323
// so warn on complex enums being used outside their crate
24-
if non_local_def {
24+
if non_exhaustive_variant_list {
2525
// which is why we only warn about really_tagged_union reprs from https://rust.tf/rfc2195
2626
// with an enum like `#[repr(u8)] enum Enum { A(DataA), B(DataB), }`
2727
// but exempt enums with unit ctors like C's (e.g. from rust-bindgen)
@@ -30,8 +30,7 @@ pub(crate) fn check_non_exhaustive_variant(
3030
}
3131
}
3232

33-
let non_exhaustive_variant_fields = variant.is_field_list_non_exhaustive();
34-
if non_exhaustive_variant_fields && !variant.def_id.is_local() {
33+
if variant.field_list_has_applicable_non_exhaustive() {
3534
return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive_variant);
3635
}
3736

@@ -42,10 +41,3 @@ fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool {
4241
// CtorKind::Const means a "unit" ctor
4342
!matches!(variant.ctor_kind(), Some(CtorKind::Const))
4443
}
45-
46-
// non_exhaustive suggests it is possible that someone might break ABI
47-
// see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344
48-
// so warn on complex enums being used outside their crate
49-
pub(crate) fn non_local_and_non_exhaustive(def: ty::AdtDef<'_>) -> bool {
50-
def.is_variant_list_non_exhaustive() && !def.did().is_local()
51-
}

compiler/rustc_middle/src/ty/adt.rs

+11
Original file line numberDiff line numberDiff line change
@@ -327,11 +327,22 @@ impl<'tcx> AdtDef<'tcx> {
327327
}
328328

329329
/// Returns `true` if the variant list of this ADT is `#[non_exhaustive]`.
330+
///
331+
/// Note that this function will return `true` even if the ADT has been
332+
/// defined in the crate currently being compiled. If that's not what you
333+
/// want, see [`Self::variant_list_has_applicable_non_exhaustive`].
330334
#[inline]
331335
pub fn is_variant_list_non_exhaustive(self) -> bool {
332336
self.flags().contains(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE)
333337
}
334338

339+
/// Returns `true` if the variant list of this ADT is `#[non_exhaustive]`
340+
/// and has been defined in another crate.
341+
#[inline]
342+
pub fn variant_list_has_applicable_non_exhaustive(self) -> bool {
343+
self.is_variant_list_non_exhaustive() && !self.did().is_local()
344+
}
345+
335346
/// Returns the kind of the ADT.
336347
#[inline]
337348
pub fn adt_kind(self) -> AdtKind {

compiler/rustc_middle/src/ty/inhabitedness/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ impl<'tcx> Ty<'tcx> {
107107
// For now, unions are always considered inhabited
108108
Adt(adt, _) if adt.is_union() => InhabitedPredicate::True,
109109
// Non-exhaustive ADTs from other crates are always considered inhabited
110-
Adt(adt, _) if adt.is_variant_list_non_exhaustive() && !adt.did().is_local() => {
110+
Adt(adt, _) if adt.variant_list_has_applicable_non_exhaustive() => {
111111
InhabitedPredicate::True
112112
}
113113
Never => InhabitedPredicate::False,

compiler/rustc_middle/src/ty/mod.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -1208,12 +1208,23 @@ impl VariantDef {
12081208
}
12091209
}
12101210

1211-
/// Is this field list non-exhaustive?
1211+
/// Returns `true` if the field list of this variant is `#[non_exhaustive]`.
1212+
///
1213+
/// Note that this function will return `true` even if the type has been
1214+
/// defined in the crate currently being compiled. If that's not what you
1215+
/// want, see [`Self::field_list_has_applicable_non_exhaustive`].
12121216
#[inline]
12131217
pub fn is_field_list_non_exhaustive(&self) -> bool {
12141218
self.flags.intersects(VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE)
12151219
}
12161220

1221+
/// Returns `true` if the field list of this variant is `#[non_exhaustive]`
1222+
/// and the type has been defined in another crate.
1223+
#[inline]
1224+
pub fn field_list_has_applicable_non_exhaustive(&self) -> bool {
1225+
self.is_field_list_non_exhaustive() && !self.def_id.is_local()
1226+
}
1227+
12171228
/// Computes the `Ident` of this variant by looking up the `Span`
12181229
pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident {
12191230
Ident::new(self.name, tcx.def_ident_span(self.def_id).unwrap())

compiler/rustc_mir_build/src/builder/matches/match_pair.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -273,12 +273,12 @@ impl<'tcx> MatchPairTree<'tcx> {
273273

274274
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
275275
i == variant_index
276-
|| !v
277-
.inhabited_predicate(cx.tcx, adt_def)
278-
.instantiate(cx.tcx, args)
279-
.apply_ignore_module(cx.tcx, cx.infcx.typing_env(cx.param_env))
280-
}) && (adt_def.did().is_local()
281-
|| !adt_def.is_variant_list_non_exhaustive());
276+
|| !v.inhabited_predicate(cx.tcx, adt_def).instantiate(cx.tcx, args).apply(
277+
cx.tcx,
278+
cx.infcx.typing_env(cx.param_env),
279+
cx.def_id.into(),
280+
)
281+
}) && !adt_def.variant_list_has_applicable_non_exhaustive();
282282
if irrefutable { None } else { Some(TestCase::Variant { adt_def, variant_index }) }
283283
}
284284

compiler/rustc_mir_build/src/errors.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -613,9 +613,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo
613613
diag.span_note(span, fluent::mir_build_def_note);
614614
}
615615

616-
let is_variant_list_non_exhaustive = matches!(self.ty.kind(),
617-
ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local());
618-
if is_variant_list_non_exhaustive {
616+
let is_non_exhaustive = matches!(self.ty.kind(),
617+
ty::Adt(def, _) if def.variant_list_has_applicable_non_exhaustive());
618+
if is_non_exhaustive {
619619
diag.note(fluent::mir_build_non_exhaustive_type_note);
620620
} else {
621621
diag.note(fluent::mir_build_type_note);

compiler/rustc_pattern_analysis/src/rustc.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
150150
/// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
151151
pub fn is_foreign_non_exhaustive_enum(&self, ty: RevealedTy<'tcx>) -> bool {
152152
match ty.kind() {
153-
ty::Adt(def, ..) => {
154-
def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did().is_local()
155-
}
153+
ty::Adt(def, ..) => def.variant_list_has_applicable_non_exhaustive(),
156154
_ => false,
157155
}
158156
}

src/tools/clippy/clippy_lints/src/default.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
134134
&& let ty::Adt(adt, args) = *binding_type.kind()
135135
&& adt.is_struct()
136136
&& let variant = adt.non_enum_variant()
137-
&& (adt.did().is_local() || !variant.is_field_list_non_exhaustive())
137+
&& !variant.field_list_has_applicable_non_exhaustive()
138138
&& let module_did = cx.tcx.parent_module(stmt.hir_id)
139139
&& variant
140140
.fields

src/tools/clippy/clippy_lints/src/needless_update.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate {
5454
if let ExprKind::Struct(_, fields, StructTailExpr::Base(base)) = expr.kind {
5555
let ty = cx.typeck_results().expr_ty(expr);
5656
if let ty::Adt(def, _) = ty.kind() {
57-
if fields.len() == def.non_enum_variant().fields.len()
58-
&& !def.variant(0_usize.into()).is_field_list_non_exhaustive()
57+
let variant = def.non_enum_variant();
58+
if fields.len() == variant.fields.len()
59+
&& !variant.is_field_list_non_exhaustive()
5960
{
6061
span_lint(
6162
cx,

src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl LateLintPass<'_> for UnneededStructPattern {
5151
let variant = cx.tcx.adt_def(enum_did).variant_with_id(did);
5252

5353
let has_only_fields_brackets = variant.ctor.is_some() && variant.fields.is_empty();
54-
let non_exhaustive_activated = !variant.def_id.is_local() && variant.is_field_list_non_exhaustive();
54+
let non_exhaustive_activated = variant.field_list_has_applicable_non_exhaustive();
5555
if !has_only_fields_brackets || non_exhaustive_activated {
5656
return;
5757
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//@ edition:2024
2+
//@ check-fail
3+
4+
mod m {
5+
enum Void {}
6+
7+
pub struct Internal {
8+
_v: Void,
9+
}
10+
11+
pub enum Test {
12+
A(u32, u32),
13+
B(Internal),
14+
}
15+
}
16+
17+
use m::Test;
18+
19+
pub fn f1(x: &mut Test) {
20+
let r1: &mut u32 = match x {
21+
Test::A(a, _) => a,
22+
_ => todo!(),
23+
};
24+
25+
let r2: &mut u32 = match x { //~ ERROR cannot use `*x` because it was mutably borrowed
26+
Test::A(_, b) => b,
27+
_ => todo!(),
28+
};
29+
30+
let _ = *r1;
31+
let _ = *r2;
32+
}
33+
34+
pub fn f2(x: &mut Test) {
35+
let r = &mut *x;
36+
match x { //~ ERROR cannot use `*x` because it was mutably borrowed
37+
Test::A(_, _) => {}
38+
_ => {}
39+
}
40+
41+
let _ = r;
42+
}
43+
44+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error[E0503]: cannot use `*x` because it was mutably borrowed
2+
--> $DIR/privately-uninhabited-issue-137999.rs:25:30
3+
|
4+
LL | Test::A(a, _) => a,
5+
| - `x.0` is borrowed here
6+
...
7+
LL | let r2: &mut u32 = match x {
8+
| ^ use of borrowed `x.0`
9+
...
10+
LL | let _ = *r1;
11+
| --- borrow later used here
12+
13+
error[E0503]: cannot use `*x` because it was mutably borrowed
14+
--> $DIR/privately-uninhabited-issue-137999.rs:36:11
15+
|
16+
LL | let r = &mut *x;
17+
| ------- `*x` is borrowed here
18+
LL | match x {
19+
| ^ use of borrowed `*x`
20+
...
21+
LL | let _ = r;
22+
| - borrow later used here
23+
24+
error: aborting due to 2 previous errors
25+
26+
For more information about this error, try `rustc --explain E0503`.

0 commit comments

Comments
 (0)