From a3b84ad197fb209d903857df747af3ca0a1f2c25 Mon Sep 17 00:00:00 2001 From: Lamb Date: Mon, 10 Jan 2022 21:01:23 +0000 Subject: [PATCH 1/2] Check if extern crate enum has non exhaustive variant when cast --- compiler/rustc_typeck/src/check/cast.rs | 23 ++++++++++++++++++- .../rfc-2008-non-exhaustive/enum-as-cast.rs | 8 +------ .../enum-as-cast.stderr | 11 +++++++++ 3 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 src/test/ui/rfc-2008-non-exhaustive/enum-as-cast.stderr diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 7aaddc2bd7aab..88bce06297bdb 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -32,6 +32,7 @@ use super::FnCtxt; use crate::hir::def_id::DefId; use crate::type_error_struct; +use hir::def_id::LOCAL_CRATE; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; @@ -40,7 +41,7 @@ use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable}; +use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable, VariantDef}; use rustc_session::lint; use rustc_session::Session; use rustc_span::symbol::sym; @@ -173,6 +174,7 @@ pub enum CastError { /// or "a length". If this argument is None, then the metadata is unknown, for example, /// when we're typechecking a type parameter with a ?Sized bound. IntToFatCast(Option<&'static str>), + ForeignNonExhaustiveAdt, } impl From for CastError { @@ -591,6 +593,17 @@ impl<'a, 'tcx> CastCheck<'tcx> { } err.emit(); } + CastError::ForeignNonExhaustiveAdt => { + make_invalid_casting_error( + fcx.tcx.sess, + self.span, + self.expr_ty, + self.cast_ty, + fcx, + ) + .note("cannot cast a non-exhaustive enum defined in another crate") + .emit(); + } } } @@ -789,6 +802,14 @@ impl<'a, 'tcx> CastCheck<'tcx> { _ => return Err(CastError::NonScalar), }; + if let ty::Adt(adt_def, _) = *self.expr_ty.kind() { + if adt_def.did().krate != LOCAL_CRATE { + if adt_def.variants().iter().any(VariantDef::is_field_list_non_exhaustive) { + return Err(CastError::ForeignNonExhaustiveAdt); + } + } + } + match (t_from, t_cast) { // These types have invariants! can't cast into them. (_, Int(CEnum) | FnPtr) => Err(CastError::NonScalar), diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum-as-cast.rs b/src/test/ui/rfc-2008-non-exhaustive/enum-as-cast.rs index d9657bac77685..5dce8180f5920 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/enum-as-cast.rs +++ b/src/test/ui/rfc-2008-non-exhaustive/enum-as-cast.rs @@ -1,5 +1,4 @@ // aux-build:enums.rs -// run-pass extern crate enums; @@ -7,11 +6,6 @@ use enums::FieldLessWithNonExhaustiveVariant; fn main() { let e = FieldLessWithNonExhaustiveVariant::default(); - // FIXME: https://github.com/rust-lang/rust/issues/91161 - // This `as` cast *should* be an error, since it would fail - // if the non-exhaustive variant got fields. But today it - // doesn't. The fix for that will update this test to - // show an error (and not be run-pass any more). - let d = e as u8; + let d = e as u8; //~ ERROR casting `FieldLessWithNonExhaustiveVariant` as `u8` is invalid [E0606] assert_eq!(d, 0); } diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum-as-cast.stderr b/src/test/ui/rfc-2008-non-exhaustive/enum-as-cast.stderr new file mode 100644 index 0000000000000..c5f0105296fff --- /dev/null +++ b/src/test/ui/rfc-2008-non-exhaustive/enum-as-cast.stderr @@ -0,0 +1,11 @@ +error[E0606]: casting `FieldLessWithNonExhaustiveVariant` as `u8` is invalid + --> $DIR/enum-as-cast.rs:9:13 + | +LL | let d = e as u8; + | ^^^^^^^ + | + = note: cannot cast a non-exhaustive enum defined in another crate + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0606`. From dfb3713cdb7303a250d8b23792fe801319edf36c Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Wed, 10 Aug 2022 10:02:03 -0700 Subject: [PATCH 2/2] Update error message to clarify that it's not the enum itself that's non_exhaustive --- compiler/rustc_typeck/src/check/cast.rs | 2 +- src/test/ui/rfc-2008-non-exhaustive/enum-as-cast.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 88bce06297bdb..6c7b2a2889fa4 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -601,7 +601,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { self.cast_ty, fcx, ) - .note("cannot cast a non-exhaustive enum defined in another crate") + .note("cannot cast an enum with a non-exhaustive variant when it's defined in another crate") .emit(); } } diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum-as-cast.stderr b/src/test/ui/rfc-2008-non-exhaustive/enum-as-cast.stderr index c5f0105296fff..a61dcf8399f17 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/enum-as-cast.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/enum-as-cast.stderr @@ -4,7 +4,7 @@ error[E0606]: casting `FieldLessWithNonExhaustiveVariant` as `u8` is invalid LL | let d = e as u8; | ^^^^^^^ | - = note: cannot cast a non-exhaustive enum defined in another crate + = note: cannot cast an enum with a non-exhaustive variant when it's defined in another crate error: aborting due to previous error