diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 58823ea30ce57..f21de1609cb7f 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -448,13 +448,35 @@ impl<'a, 'tcx> CastCheck<'tcx> { ); } } - let msg = "an `as` expression can only be used to convert between primitive \ - types or to coerce to a specific trait object"; + + let (msg, note) = if let ty::Adt(adt, _) = self.expr_ty.kind() + && adt.is_enum() + && self.cast_ty.is_numeric() + { + ( + "an `as` expression can be used to convert enum types to numeric \ + types only if the enum type is unit-only or field-less", + Some( + "see https://doc.rust-lang.org/reference/items/enumerations.html#casting for more information", + ), + ) + } else { + ( + "an `as` expression can only be used to convert between primitive \ + types or to coerce to a specific trait object", + None, + ) + }; + if label { err.span_label(self.span, msg); } else { err.note(msg); } + + if let Some(note) = note { + err.note(note); + } } else { err.span_label(self.span, "invalid cast"); } diff --git a/tests/ui/cast/enum-to-numeric-cast.rs b/tests/ui/cast/enum-to-numeric-cast.rs new file mode 100644 index 0000000000000..d5ab2a6a1fed7 --- /dev/null +++ b/tests/ui/cast/enum-to-numeric-cast.rs @@ -0,0 +1,46 @@ +// Tests that `as` casts from enums to numeric types succeed +// only if the enum type is "unit-only" or "fieldless" as +// described here: https://doc.rust-lang.org/reference/items/enumerations.html#casting + +pub enum UnitOnly { + Foo, + Bar, + Baz, +} + +pub enum Fieldless { + Tuple(), + Struct{}, + Unit, +} + +pub enum NotUnitOnlyOrFieldless { + Foo, + Bar(u8), + Baz +} + +fn main() { + let unit_only = UnitOnly::Foo; + + let _ = unit_only as isize; + let _ = unit_only as i32; + let _ = unit_only as usize; + let _ = unit_only as u32; + + + let fieldless = Fieldless::Struct{}; + + let _ = fieldless as isize; + let _ = fieldless as i32; + let _ = fieldless as usize; + let _ = fieldless as u32; + + + let not_unit_only_or_fieldless = NotUnitOnlyOrFieldless::Foo; + + let _ = not_unit_only_or_fieldless as isize; //~ ERROR non-primitive cast: `NotUnitOnlyOrFieldless` as `isize` + let _ = not_unit_only_or_fieldless as i32; //~ ERROR non-primitive cast: `NotUnitOnlyOrFieldless` as `i32` + let _ = not_unit_only_or_fieldless as usize; //~ ERROR non-primitive cast: `NotUnitOnlyOrFieldless` as `usize` + let _ = not_unit_only_or_fieldless as u32; //~ ERROR non-primitive cast: `NotUnitOnlyOrFieldless` as `u32` +} diff --git a/tests/ui/cast/enum-to-numeric-cast.stderr b/tests/ui/cast/enum-to-numeric-cast.stderr new file mode 100644 index 0000000000000..1a49cb97451ce --- /dev/null +++ b/tests/ui/cast/enum-to-numeric-cast.stderr @@ -0,0 +1,35 @@ +error[E0605]: non-primitive cast: `NotUnitOnlyOrFieldless` as `isize` + --> $DIR/enum-to-numeric-cast.rs:42:13 + | +LL | let _ = not_unit_only_or_fieldless as isize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can be used to convert enum types to numeric types only if the enum type is unit-only or field-less + | + = note: see https://doc.rust-lang.org/reference/items/enumerations.html#casting for more information + +error[E0605]: non-primitive cast: `NotUnitOnlyOrFieldless` as `i32` + --> $DIR/enum-to-numeric-cast.rs:43:13 + | +LL | let _ = not_unit_only_or_fieldless as i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can be used to convert enum types to numeric types only if the enum type is unit-only or field-less + | + = note: see https://doc.rust-lang.org/reference/items/enumerations.html#casting for more information + +error[E0605]: non-primitive cast: `NotUnitOnlyOrFieldless` as `usize` + --> $DIR/enum-to-numeric-cast.rs:44:13 + | +LL | let _ = not_unit_only_or_fieldless as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can be used to convert enum types to numeric types only if the enum type is unit-only or field-less + | + = note: see https://doc.rust-lang.org/reference/items/enumerations.html#casting for more information + +error[E0605]: non-primitive cast: `NotUnitOnlyOrFieldless` as `u32` + --> $DIR/enum-to-numeric-cast.rs:45:13 + | +LL | let _ = not_unit_only_or_fieldless as u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can be used to convert enum types to numeric types only if the enum type is unit-only or field-less + | + = note: see https://doc.rust-lang.org/reference/items/enumerations.html#casting for more information + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0605`. diff --git a/tests/ui/cast/issue-88621.stderr b/tests/ui/cast/issue-88621.stderr index 0459ce5eabde5..201651a207a20 100644 --- a/tests/ui/cast/issue-88621.stderr +++ b/tests/ui/cast/issue-88621.stderr @@ -2,7 +2,9 @@ error[E0605]: non-primitive cast: `Kind2` as `u8` --> $DIR/issue-88621.rs:9:13 | LL | let _ = Kind2::Foo() as u8; - | ^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + | ^^^^^^^^^^^^^^^^^^ an `as` expression can be used to convert enum types to numeric types only if the enum type is unit-only or field-less + | + = note: see https://doc.rust-lang.org/reference/items/enumerations.html#casting for more information error: aborting due to 1 previous error diff --git a/tests/ui/tag-variant-cast-non-nullary.stderr b/tests/ui/tag-variant-cast-non-nullary.stderr index 560dd7e8164f1..2e1dde27d0f98 100644 --- a/tests/ui/tag-variant-cast-non-nullary.stderr +++ b/tests/ui/tag-variant-cast-non-nullary.stderr @@ -4,7 +4,8 @@ error[E0605]: non-primitive cast: `NonNullary` as `isize` LL | let val = v as isize; | ^^^^^^^^^^ help: consider using the `From` trait instead: `isize::from(v)` | - = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + = note: an `as` expression can be used to convert enum types to numeric types only if the enum type is unit-only or field-less + = note: see https://doc.rust-lang.org/reference/items/enumerations.html#casting for more information error: aborting due to 1 previous error