diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 438168f4fcc5c..7f7c6f01831fc 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2338,6 +2338,18 @@ pub struct ForeignMod { pub struct EnumDef { pub variants: Vec, } + +impl EnumDef { + /// Whether all variants have only constant constructors + /// (i.e. there are no tuple or struct variants). + pub fn is_c_like(&self) -> bool { + self.variants.iter().all(|variant| match variant.data { + VariantData::Struct(..) | VariantData::Tuple(..) => false, + VariantData::Unit(..) => true, + }) + } +} + /// Enum variant. #[derive(Clone, Encodable, Decodable, Debug)] pub struct Variant { diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index bab50df3dd543..4d3dbd9e516b5 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -985,7 +985,7 @@ pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec { acc } -fn int_type_of_word(s: Symbol) -> Option { +pub fn int_type_of_word(s: Symbol) -> Option { use IntType::*; match s { diff --git a/compiler/rustc_builtin_macros/src/deriving/enums.rs b/compiler/rustc_builtin_macros/src/deriving/enums.rs new file mode 100644 index 0000000000000..a017edbcf9009 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/enums.rs @@ -0,0 +1,364 @@ +use rustc_ast::ast::DUMMY_NODE_ID; +use rustc_ast::attr::mk_list_item; +use rustc_ast::ptr::P; +use rustc_ast::{ + AngleBracketedArg, AngleBracketedArgs, Arm, AssocItem, AssocItemKind, BinOpKind, BindingMode, + Block, Const, Defaultness, EnumDef, Expr, Fn, FnHeader, FnRetTy, FnSig, GenericArg, + GenericArgs, Generics, Impl, ImplPolarity, Item, ItemKind, LitIntType, LitKind, MetaItem, + Mutability, NestedMetaItem, PatKind, Path, PathSegment, Stmt, StmtKind, Ty, Unsafe, Variant, + VariantData, Visibility, VisibilityKind, +}; +use rustc_attr::{find_repr_attrs, int_type_of_word}; +use rustc_data_structures::thin_vec::ThinVec; +use rustc_errors::struct_span_err; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::{Span, Symbol, DUMMY_SP}; + +pub fn expand_from_repr( + cx: &mut ExtCtxt<'_>, + derive_span: Span, + _mitem: &MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), +) { + if let Some(ctx) = Ctx::extract(cx, item, derive_span) { + push(make_from_repr(cx, ctx.enum_def, ctx.ty, ctx.repr_ty)); + } +} + +struct Ctx<'enumdef> { + enum_def: &'enumdef EnumDef, + ty: P, + repr_ty: P, +} + +impl<'enumdef> Ctx<'enumdef> { + fn extract( + cx: &mut ExtCtxt<'_>, + item: &'enumdef Annotatable, + derive_span: Span, + ) -> Option> { + match *item { + Annotatable::Item(ref annitem) => match annitem.kind { + ItemKind::Enum(ref enum_def, _) => { + let repr = extract_repr(cx, annitem, enum_def, derive_span)?; + + let ty = cx.ty_ident(DUMMY_SP, annitem.ident); + let repr_ty = cx.ty_ident(DUMMY_SP, Ident { name: repr, span: DUMMY_SP }); + + return Some(Self { enum_def, ty, repr_ty }); + } + _ => {} + }, + _ => {} + } + struct_span_err!( + &cx.sess.parse_sess.span_diagnostic, + derive_span, + E0789, + "`FromRepr` can only be derived for enums", + ) + .span_label(item.span(), "not an enum") + .emit(); + None + } +} + +fn extract_repr( + cx: &mut ExtCtxt<'_>, + annitem: &P, + def: &EnumDef, + derive_span: Span, +) -> Option { + let reprs: Vec<_> = annitem + .attrs + .iter() + .flat_map(|attr| find_repr_attrs(&cx.sess, attr)) + .filter_map(|r| { + use rustc_attr::*; + match r { + ReprInt(rustc_attr::IntType::UnsignedInt(int_type)) => Some(int_type.name()), + ReprInt(rustc_attr::IntType::SignedInt(int_type)) => Some(int_type.name()), + ReprC | ReprPacked(..) | ReprSimd | ReprTransparent | ReprAlign(..) + | ReprNoNiche => None, + } + }) + .collect(); + + if reprs.len() != 1 { + let mut err_spans = Vec::new(); + + if reprs.is_empty() { + err_spans.push(derive_span); + } else { + for attr in &annitem.attrs { + if attr.has_name(sym::repr) { + err_spans.push(attr.span); + } + } + } + + let mut err = struct_span_err!( + &cx.sess.parse_sess.span_diagnostic, + err_spans, + E0789, + "`FromRepr` can only be derived for enums with exactly one explicit integer representation", + ); + if reprs.is_empty() { + err.span_label(annitem.span, "enum missing numeric `repr` annotation"); + } + let too_many_reprs = reprs.len() > 1; + for (i, attr) in annitem.attrs.iter().enumerate() { + if attr.has_name(sym::repr) { + if too_many_reprs { + if i == 0 { + err.span_label( + attr.span, + "multiple `repr` annotations on the same item are not allowed", + ); + } else { + err.span_label(attr.span, ""); + } + } + if let Some(items) = attr.meta_item_list() { + for item in items { + if int_type_of_word(item.name_or_empty()).is_none() { + err.span_label(attr.span, "Non-integer representation"); + } + } + } + } + } + err.emit(); + return None; + } + + if !def.is_c_like() { + let mut err = struct_span_err!( + &cx.sess.parse_sess.span_diagnostic, + derive_span, + E0789, + "`FromRepr` can only be derived for C-like enums", + ); + err.span_label(derive_span, "derive from `FromRepr` trait"); + for variant in &def.variants { + match variant.data { + VariantData::Struct(..) | VariantData::Tuple(..) => { + err.span_label(variant.span, "variant is not data-free"); + } + VariantData::Unit(..) => {} + } + } + err.note("only unit variants, like `Foo,` or `Bar = 3,` are data-free"); + err.emit(); + return None; + } + return Some(reprs[0]); +} + +fn make_from_repr(cx: &mut ExtCtxt<'_>, def: &EnumDef, ty: P, repr_ty: P) -> Annotatable { + let param_ident = Ident::from_str("value"); + let result_type = make_result_type(cx, ty.clone(), repr_ty.clone()); + + let try_from_repr = { + let decl = cx.fn_decl( + vec![cx.param(DUMMY_SP, param_ident.clone(), repr_ty.clone())], + FnRetTy::Ty(result_type), + ); + + assoc_item( + "try_from_repr", + AssocItemKind::Fn(Box::new(Fn { + defaultness: Defaultness::Final, + generics: Generics::default(), + sig: FnSig { header: FnHeader::default(), decl, span: DUMMY_SP }, + body: Some(make_match_block(cx, def, repr_ty.clone(), param_ident)), + })), + ) + }; + + Annotatable::Item(cx.item( + DUMMY_SP, + Ident::empty(), + vec![], + ItemKind::Impl(Box::new(Impl { + unsafety: Unsafe::Yes(DUMMY_SP), + polarity: ImplPolarity::Positive, + defaultness: Defaultness::Final, + constness: Const::No, + generics: Generics::default(), + of_trait: Some(cx.trait_ref(std_path_from_ident_symbols( + cx, + &[Symbol::intern("enums"), sym::FromRepr], + ))), + self_ty: ty.clone(), + items: vec![try_from_repr], + })), + )) +} + +fn make_match_block( + cx: &mut ExtCtxt<'_>, + def: &EnumDef, + repr_ty: P, + value_ident: Ident, +) -> P { + let ok = std_path_from_ident_symbols(cx, &[sym::result, sym::Result, sym::Ok]); + let ok = cx.expr_path(ok); + + let mut prev_explicit_disr: Option> = None; + let mut count_since_prev_explicit_disr = 0; + + let mut stmts = Vec::with_capacity(def.variants.len() + 1); + let mut arms = Vec::with_capacity(def.variants.len() + 1); + + for Variant { ident, disr_expr, .. } in &def.variants { + let expr = match (disr_expr, &prev_explicit_disr) { + (Some(disr), _) => { + prev_explicit_disr = Some(disr.value.clone()); + count_since_prev_explicit_disr = 0; + disr.value.clone() + } + (None, None) => { + let expr = cx.expr_lit( + DUMMY_SP, + LitKind::Int(count_since_prev_explicit_disr, LitIntType::Unsuffixed), + ); + count_since_prev_explicit_disr += 1; + expr + } + (None, Some(prev_expr)) => { + count_since_prev_explicit_disr += 1; + cx.expr_binary( + DUMMY_SP, + BinOpKind::Add, + prev_expr.clone(), + cx.expr_lit( + DUMMY_SP, + LitKind::Int(count_since_prev_explicit_disr, LitIntType::Unsuffixed), + ), + ) + } + }; + + let const_ident = Ident::from_str(&format!("DISCIMINANT_FOR_{}", arms.len())); + stmts.push( + cx.stmt_item(DUMMY_SP, cx.item_const(DUMMY_SP, const_ident, repr_ty.clone(), expr)), + ); + + arms.push(cx.arm( + DUMMY_SP, + cx.pat_ident(DUMMY_SP, const_ident), + cx.expr_call( + DUMMY_SP, + ok.clone(), + vec![cx.expr_path(cx.path(DUMMY_SP, vec![Ident::from_str("Self"), ident.clone()]))], + ), + )); + } + + let err = std_path_from_ident_symbols(cx, &[sym::result, sym::Result, sym::Err]); + let try_from_int_error = std_path_from_ident_symbols( + cx, + &[Symbol::intern("enums"), Symbol::intern("TryFromReprError")], + ); + + let other_match = Ident::from_str("other_value"); + + // Rather than having to know how many variants could fit into the repr, + // just allow the catch-all to be superfluous. + arms.push(Arm { + attrs: ThinVec::from(vec![cx.attribute(mk_list_item( + Ident::new(sym::allow, DUMMY_SP), + vec![NestedMetaItem::MetaItem( + cx.meta_word(DUMMY_SP, Symbol::intern("unreachable_patterns")), + )], + ))]), + + pat: cx.pat( + DUMMY_SP, + PatKind::Ident(BindingMode::ByValue(Mutability::Not), other_match.clone(), None), + ), + guard: Option::None, + body: cx.expr_call( + DUMMY_SP, + cx.expr_path(err), + vec![cx.expr_call( + DUMMY_SP, + cx.expr_path(try_from_int_error), + vec![cx.expr_ident(DUMMY_SP, other_match.clone())], + )], + ), + span: DUMMY_SP, + id: DUMMY_NODE_ID, + is_placeholder: false, + }); + + stmts.push(Stmt { + id: DUMMY_NODE_ID, + span: DUMMY_SP, + kind: StmtKind::Expr(cx.expr_match(DUMMY_SP, cx.expr_ident(DUMMY_SP, value_ident), arms)), + }); + + cx.block(DUMMY_SP, stmts) +} + +fn assoc_item(name: &str, kind: AssocItemKind) -> P { + P(AssocItem { + attrs: vec![], + id: DUMMY_NODE_ID, + span: DUMMY_SP, + vis: Visibility { kind: VisibilityKind::Inherited, span: DUMMY_SP, tokens: None }, + ident: Ident::from_str(name), + kind, + tokens: None, + }) +} + +fn make_result_type(cx: &mut ExtCtxt<'_>, ty: P, repr_ty: P) -> P { + std_path_with_generics(cx, &[sym::result], sym::Result, vec![ty, error_type(cx, repr_ty)]) +} + +fn std_path_with_generics( + cx: &ExtCtxt<'_>, + symbols: &[Symbol], + ty: Symbol, + generics: Vec>, +) -> P { + let mut path = std_path_from_ident_symbols(cx, symbols); + path.segments.push(path_segment_with_generics(ty, generics)); + cx.ty_path(path) +} + +fn std_path_from_ident_symbols(cx: &ExtCtxt<'_>, symbols: &[Symbol]) -> Path { + Path { + span: DUMMY_SP, + segments: cx.std_path(symbols).into_iter().map(PathSegment::from_ident).collect(), + tokens: None, + } +} + +fn error_type(cx: &ExtCtxt<'_>, repr_ty: P) -> P { + let mut error_type = std_path_from_ident_symbols(cx, &[Symbol::intern("enums")]); + + error_type + .segments + .push(path_segment_with_generics(Symbol::intern("TryFromReprError"), vec![repr_ty])); + + cx.ty_path(error_type) +} + +fn path_segment_with_generics(symbol: Symbol, generic_types: Vec>) -> PathSegment { + PathSegment { + ident: Ident { name: symbol, span: DUMMY_SP }, + id: DUMMY_NODE_ID, + args: Some(P(GenericArgs::AngleBracketed(AngleBracketedArgs { + span: DUMMY_SP, + args: generic_types + .into_iter() + .map(|ty| AngleBracketedArg::Arg(GenericArg::Type(ty))) + .collect(), + }))), + } +} diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 367a5aa732370..851aef960d63b 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -25,6 +25,7 @@ pub mod debug; pub mod decodable; pub mod default; pub mod encodable; +pub mod enums; pub mod hash; #[path = "cmp/eq.rs"] diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 6c16c28549241..00ab0bd23de99 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -104,6 +104,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { Debug: debug::expand_deriving_debug, Default: default::expand_deriving_default, Eq: eq::expand_deriving_eq, + FromRepr: enums::expand_from_repr, Hash: hash::expand_deriving_hash, Ord: ord::expand_deriving_ord, PartialEq: partial_eq::expand_deriving_partial_eq, diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index d4b12d00f1ff6..d627c3597ba41 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -487,6 +487,8 @@ E0784: include_str!("./error_codes/E0784.md"), E0785: include_str!("./error_codes/E0785.md"), E0786: include_str!("./error_codes/E0786.md"), E0787: include_str!("./error_codes/E0787.md"), +E0788: include_str!("./error_codes/E0788.md"), +E0789: include_str!("./error_codes/E0789.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard diff --git a/compiler/rustc_error_codes/src/error_codes/E0788.md b/compiler/rustc_error_codes/src/error_codes/E0788.md new file mode 100644 index 0000000000000..c627e428a068f --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0788.md @@ -0,0 +1,35 @@ +The `AsRepr` trait was implemented explicitly. + +Erroneous code example: + +```compile_fail,E0788 +#![feature(enum_as_repr)] + +enum Number { + Zero, + One, + Other(u8), +} + +impl core::enums::AsRepr for Number {} // error! +``` + +The `AsRepr` trait is a special trait built-in to the compiler for data-free +enum types. This trait is automatically implemented for types as needed by the +compiler, and it is currently disallowed to explicitly implement it for a type. + +If you need a type to implement `AsRepr`, you should ensure that it is an enum +type, that it has an explicit numeric `repr` attribute, and that all of its +variants are data-free. + +Working example: + +``` +#![feature(enum_as_repr)] + +#[repr(u8)] +enum Number { + Zero, + One, +} +``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0789.md b/compiler/rustc_error_codes/src/error_codes/E0789.md new file mode 100644 index 0000000000000..b44a8005a1960 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0789.md @@ -0,0 +1,33 @@ +A trait which can only be derived for data-free enums was being derived for a +type other than a data-free enum. + +Erroneous code example: + +```compile_fail,E0789 +#![feature(enum_as_repr)] + +#[derive(core::enums::FromRepr)] +enum Number { + Zero, + NonZero(core::num::NonZeroU8), +} +``` + +Types may only have this trait derived if all of the following are true: +1. The type must be an enum. +1. The enum must be data-free (i.e. have no variants which are struct-like or + tuple-like). +1. The type must have an explicit repr of an integer type. + +Working example: + +``` +#![feature(enum_as_repr)] + +#[derive(core::enums::FromRepr)] +#[repr(u8)] +enum Number { + Zero, + One, +} +``` diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 0b65a5ff3ecc3..7ff07704591b2 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -366,6 +366,10 @@ declare_features! ( (active, doc_cfg_hide, "1.57.0", Some(43781), None), /// Allows `#[doc(masked)]`. (active, doc_masked, "1.21.0", Some(44027), None), + /// Allows using doc(primitive) without a future-incompat warning + (active, doc_primitive, "1.56.0", Some(88070), None), + /// Allows converting enums to/from their repr. + (active, enum_as_repr, "1.60.0", Some(86772), None), /// Allows `X..Y` patterns. (active, exclusive_range_pattern, "1.11.0", Some(37854), None), /// Allows exhaustive pattern matching on types that contain uninhabited types. diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index be4849d0b846e..9c97511676627 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -206,6 +206,8 @@ language_item_table! { DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None; /// The associated item of the [`DiscriminantKind`] trait. Discriminant, sym::discriminant_type, discriminant_type, Target::AssocTy, GenericRequirement::None; + AsRepr, sym::AsRepr, as_repr_trait, Target::Trait, GenericRequirement::None; + HasAsReprImpl, sym::HasAsReprImpl, has_as_repr_impl_trait, Target::Trait, GenericRequirement::None; PointeeTrait, sym::pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None; Metadata, sym::metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None; diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index de5beffb5c541..b2df33c15efea 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -500,11 +500,6 @@ pub type SelectionResult<'tcx, T> = Result, SelectionError<'tcx>>; /// // Case A: ImplSource points at a specific impl. Only possible when /// // type is concretely known. If the impl itself has bounded /// // type parameters, ImplSource will carry resolutions for those as well: -/// concrete.clone(); // ImpleSource(Impl_1, [ImplSource(Impl_2, [ImplSource(Impl_3)])]) -/// -/// // Case A: ImplSource points at a specific impl. Only possible when -/// // type is concretely known. If the impl itself has bounded -/// // type parameters, ImplSource will carry resolutions for those as well: /// concrete.clone(); // ImplSource(Impl_1, [ImplSource(Impl_2, [ImplSource(Impl_3)])]) /// /// // Case B: ImplSource must be provided by caller. This applies when diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 6cec75d36e2c2..dcde1aeb617aa 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -52,6 +52,9 @@ bitflags! { /// Indicates whether the variant list of this ADT is `#[non_exhaustive]`. /// (i.e., this flag is never set unless this ADT is an enum). const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8; + /// Indicates whether any particular variant of this ADT is `#[non_exhaustive]`. + /// (i.e., this flag is never set unless this ADT is an enum). + const HAS_NON_EXHAUSTIVE_VARIANT = 1 << 9; } } @@ -194,6 +197,12 @@ impl<'tcx> AdtDef { flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE; } + for variant in &variants { + if tcx.has_attr(variant.def_id, sym::non_exhaustive) { + flags = flags | AdtFlags::HAS_NON_EXHAUSTIVE_VARIANT; + } + } + flags |= match kind { AdtKind::Enum => AdtFlags::IS_ENUM, AdtKind::Union => AdtFlags::IS_UNION, @@ -239,12 +248,20 @@ impl<'tcx> AdtDef { self.flags.contains(AdtFlags::IS_ENUM) } - /// Returns `true` if the variant list of this ADT is `#[non_exhaustive]`. + /// Returns `true` if the variant list of this ADT is `#[non_exhaustive]` + /// (i.e. it may get additional variants in the future). #[inline] pub fn is_variant_list_non_exhaustive(&self) -> bool { self.flags.contains(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE) } + /// Returns `true` if any of the variants of this ADT is `#[non_exhaustive]` + /// (i.e. it may get additional fields on the variant in the future). + #[inline] + pub fn has_non_exhaustive_variant(&self) -> bool { + self.flags.contains(AdtFlags::HAS_NON_EXHAUSTIVE_VARIANT) + } + /// Returns the kind of the ADT. #[inline] pub fn adt_kind(&self) -> AdtKind { @@ -356,6 +373,20 @@ impl<'tcx> AdtDef { self.variants.iter().all(|v| v.fields.is_empty()) } + /// Whether all variants have only constant constructors + /// (i.e. there are no tuple or struct variants). + /// This is distinct from `is_payloadfree` specifically for the case of + /// empty tuple constructors, e.g. for: + /// ``` + /// enum Number { + /// Zero(), + /// } + /// ``` + /// this function returns false, where `is_payloadfree` returns true. + pub fn is_c_like_enum(&self) -> bool { + self.is_enum() && self.variants.iter().all(|v| v.ctor_kind == CtorKind::Const) + } + /// Return a `VariantDef` given a variant id. pub fn variant_with_id(&self, vid: DefId) -> &VariantDef { self.variants.iter().find(|v| v.def_id == vid).expect("variant_with_id: unknown variant") diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 3294f2cf64172..6be4ad11a06d4 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -382,9 +382,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // all the arm blocks will rejoin here let end_block = self.cfg.start_new_block(); - let end_brace = self.source_info( - outer_source_info.span.with_lo(outer_source_info.span.hi() - BytePos::from_usize(1)), - ); + // Don't underflow on dummy spans. + let end_brace_span = if outer_source_info.span.hi() > BytePos::from_usize(0) { + outer_source_info.span.with_lo(outer_source_info.span.hi() - BytePos::from_usize(1)) + } else { + outer_source_info.span + }; + let end_brace = self.source_info(end_brace_span); for arm_block in arm_end_blocks { let block = &self.cfg.basic_blocks[arm_block.0]; let last_location = block.statements.last().map(|s| s.source_info); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9870c90f2ec94..3c768b894b840 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -132,6 +132,7 @@ symbols! { Arguments, AsMut, AsRef, + AsRepr, AtomicBool, AtomicI128, AtomicI16, @@ -184,10 +185,12 @@ symbols! { Formatter, From, FromIterator, + FromRepr, Future, FxHashMap, FxHashSet, GlobalAlloc, + HasAsReprImpl, Hash, HashMap, HashMapEntry, @@ -611,6 +614,7 @@ symbols! { enclosing_scope, encode, end, + enum_as_repr, env, env_macro, eprint_macro, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index b573c4b43906c..00279cb73eb2f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -314,6 +314,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // `~const Drop` when we are not in a const context has no effect. candidates.vec.push(ConstDropCandidate) } + } else if lang_items.has_as_repr_impl_trait() == Some(def_id) { + if let ty::Adt(def, ..) = + self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()).kind() + { + // Automatically implement AsRepr for C-like enums which have an explicit numeric repr. + if def.is_c_like_enum() + && def.repr.int.is_some() + && !def.is_variant_list_non_exhaustive() + && !def.has_non_exhaustive_variant() + { + candidates.vec.push(BuiltinCandidate { has_nested: false }); + } + } } else { if lang_items.clone_trait() == Some(def_id) { // Same builtin conditions as `Copy`, i.e., every type which has builtin support diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_typeck/src/coherence/mod.rs index 055818f55f0c8..6d9a3887a02ee 100644 --- a/compiler/rustc_typeck/src/coherence/mod.rs +++ b/compiler/rustc_typeck/src/coherence/mod.rs @@ -75,6 +75,35 @@ fn enforce_trait_manually_implementable( return; } + // Only allow AsRepr to be implemented via blanket impls inside `core`. + if did == li.as_repr_trait() && !trait_def_id.is_local() { + let span = impl_header_span(tcx, impl_def_id); + struct_span_err!( + tcx.sess, + span, + E0788, + "explicit impls for the `AsRepr` trait are not permitted" + ) + .span_label(span, "impl of `AsRepr` not allowed, it is automatically implemented for C-like enum types with explicit numeric reprs") + .emit(); + return; + } + + // HasAsReprImpl is only implemented via custom logic in trait selection. + // Manual impls are not allowed anywhere. + if did == li.has_as_repr_impl_trait() { + let span = impl_header_span(tcx, impl_def_id); + struct_span_err!( + tcx.sess, + span, + E0788, + "explicit impls for the `HasAsReprImpl` trait are not permitted" + ) + .span_label(span, "impl of `HasAsReprImpl` not allowed, it is automatically implemented for C-like enum types with explicit numeric reprs") + .emit(); + return; + } + if did == li.sized_trait() { let span = impl_header_span(tcx, impl_def_id); struct_span_err!( diff --git a/library/core/src/enums.rs b/library/core/src/enums.rs new file mode 100644 index 0000000000000..60d8eb6959828 --- /dev/null +++ b/library/core/src/enums.rs @@ -0,0 +1,82 @@ +//! For introspecting and converting between C-like enums and numbers. + +use core::marker::DiscriminantKind; +use core::mem::Discriminant; + +/// Converts an enum to its underlying discriminant. +/// +/// This trait is automatically implemented for all C-like enum types which have an explicit +/// numeric repr. It may not be manually implemented. +#[unstable(feature = "enum_as_repr", issue = "86772")] +#[cfg_attr(not(bootstrap), lang = "AsRepr")] +pub trait AsRepr: Sized { + /// The underlying repr type of the enum. + type Repr; + + /// Convert the enum to its underlying discriminant. + fn as_repr(&self) -> Self::Repr; +} + +/// Marker trait used by the trait solver to enable implementations of AsRepr. +/// This trait cannot be manually implemented outside of `core`. +#[unstable(feature = "enum_as_repr", issue = "86772")] +#[cfg_attr(not(bootstrap), lang = "HasAsReprImpl")] +pub trait HasAsReprImpl {} + +impl AsRepr for T { + type Repr = ::Discriminant; + + fn as_repr(&self) -> Self::Repr { + core::intrinsics::discriminant_value(self) + } +} + +impl AsRepr for Discriminant { + type Repr = ::Discriminant; + + fn as_repr(&self) -> Self::Repr { + self.discriminant() + } +} + +/// Converts an enum from its underlying repr. +/// +/// # Safety +/// +/// This trait must only be implemented for types which are transmutable from their repr for +/// inhabited repr values. Callers may assume that it is safe to transmute an instance of `Repr` +/// into its associated `Repr` type if that value is inhabited for this type. +#[unstable(feature = "enum_as_repr", issue = "86772")] +pub unsafe trait FromRepr: AsRepr { + /// Tries to convert an enum from its underlying repr type. + fn try_from_repr(from: Self::Repr) -> Result>; + + /// Converts from the enum's underlying repr type to this enum. + /// + /// # Safety + /// + /// This is only safe to call if it is known that the value being converted has a matching + /// variant of this enum. Attempting to convert a value which doesn't correspond to an enum + /// variant causes undefined behavior. + unsafe fn from_repr(from: Self::Repr) -> Self { + // SAFETY: Guaranteed to be safe from the safety constraints of the unsafe trait itself. + let value = unsafe { crate::mem::transmute_copy(&from) }; + drop(from); + value + } +} + +/// Derive macro generating an impl of the trait `FromRepr` for enums. +#[cfg(not(bootstrap))] +#[rustc_builtin_macro] +#[unstable(feature = "enum_as_repr", issue = "86772")] +pub macro FromRepr($item:item) { + /* compiler built-in */ +} + +/// The error type returned when a checked integral type conversion fails. +/// Ideally this would be the same as `core::num::TryFromIntError` but it's not publicly +/// constructable. +#[unstable(feature = "enum_as_repr", issue = "86772")] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct TryFromReprError(pub T); diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index d8ac816fb15a0..8c329f34b0a51 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -281,6 +281,9 @@ pub mod f64; #[macro_use] pub mod num; +#[unstable(feature = "enum_as_repr", issue = "86772")] +pub mod enums; + /* The libcore prelude, not as all-encompassing as the libstd prelude */ pub mod prelude; diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 989ec0639cd6b..49e4a10c01181 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -972,6 +972,12 @@ pub const unsafe fn transmute_copy(src: &T) -> U { #[stable(feature = "discriminant_value", since = "1.21.0")] pub struct Discriminant(::Discriminant); +impl Discriminant { + pub(crate) fn discriminant(&self) -> ::Discriminant { + self.0 + } +} + // N.B. These trait implementations cannot be derived because we don't want any bounds on T. #[stable(feature = "discriminant_value", since = "1.21.0")] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 489d362be4276..1a6aa48212ade 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -277,6 +277,7 @@ #![feature(duration_checked_float)] #![feature(duration_constants)] #![feature(edition_panic)] +#![feature(enum_as_repr)] #![feature(exact_size_is_empty)] #![feature(exhaustive_patterns)] #![feature(extend_one)] @@ -489,6 +490,9 @@ pub use core::u8; #[allow(deprecated, deprecated_in_future)] pub use core::usize; +#[unstable(feature = "enum_as_repr", issue = "86772")] +pub use core::enums; + pub mod f32; pub mod f64; diff --git a/src/doc/unstable-book/src/library-features/enum-as-repr.md b/src/doc/unstable-book/src/library-features/enum-as-repr.md new file mode 100644 index 0000000000000..5346469c03781 --- /dev/null +++ b/src/doc/unstable-book/src/library-features/enum-as-repr.md @@ -0,0 +1,10 @@ +# `enum_as_repr` + +The tracking issue for this feature is [#86772] + +[#86772]: https://github.com/rust-lang/rust/issues/86772 + +------------------------ + +This feature allows converting between enums and their repr types using the +`core::enums::AsRepr` and `core::enums::FromRepr` traits. diff --git a/src/test/ui/deriving/deriving-enum-from-repr-bad.rs b/src/test/ui/deriving/deriving-enum-from-repr-bad.rs new file mode 100644 index 0000000000000..6e6ef02aa7c94 --- /dev/null +++ b/src/test/ui/deriving/deriving-enum-from-repr-bad.rs @@ -0,0 +1,61 @@ +// Test that FromRepr cannot be derived other than for enums with an explicit int repr and no data. + +// gate-test-enum_as_repr + +#![feature(enum_as_repr)] +#![allow(unused)] + +use std::enums::FromRepr; + +#[derive(FromRepr)] +//~^ ERROR `FromRepr` can only be derived for enums [E0789] +struct Struct {} + +#[derive(FromRepr)] +//~^ ERROR `FromRepr` can only be derived for enums with exactly one explicit integer representation [E0789] +#[repr(C)] +enum NumberC { + Zero, + One, +} + +#[derive(FromRepr)] +//~^ ERROR `FromRepr` can only be derived for enums with exactly one explicit integer representation [E0789] +enum NumberNoRepr { + Zero, + One, +} + +#[derive(FromRepr)] +//~^ ERROR `FromRepr` can only be derived for C-like enums [E0789] +#[repr(u8)] +enum NumberTuple { + Zero, + NonZero(u8), +} + +#[derive(FromRepr)] +//~^ ERROR `FromRepr` can only be derived for C-like enums [E0789] +#[repr(u8)] +enum NumberStruct { + Zero, + NonZero { value: u8 }, +} + +#[derive(FromRepr)] +//~^ ERROR `FromRepr` can only be derived for C-like enums [E0789] +#[repr(u8)] +enum NumberEmptyTuple { + Zero(), + NonZero, +} + +#[derive(FromRepr)] +//~^ ERROR `FromRepr` can only be derived for C-like enums [E0789] +#[repr(u8)] +enum NumberEmptyStruct { + Zero {}, + NonZero, +} + +fn main() {} diff --git a/src/test/ui/deriving/deriving-enum-from-repr-bad.stderr b/src/test/ui/deriving/deriving-enum-from-repr-bad.stderr new file mode 100644 index 0000000000000..3c2bd02523f0b --- /dev/null +++ b/src/test/ui/deriving/deriving-enum-from-repr-bad.stderr @@ -0,0 +1,92 @@ +error[E0789]: `FromRepr` can only be derived for enums + --> $DIR/deriving-enum-from-repr-bad.rs:10:10 + | +LL | #[derive(FromRepr)] + | ^^^^^^^^ +LL | +LL | struct Struct {} + | ---------------- not an enum + | + = note: this error originates in the derive macro `FromRepr` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0789]: `FromRepr` can only be derived for enums with exactly one explicit integer representation + --> $DIR/deriving-enum-from-repr-bad.rs:14:10 + | +LL | #[derive(FromRepr)] + | ^^^^^^^^ +LL | +LL | #[repr(C)] + | ---------- Non-integer representation +LL | / enum NumberC { +LL | | Zero, +LL | | One, +LL | | } + | |_- enum missing numeric `repr` annotation + | + = note: this error originates in the derive macro `FromRepr` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0789]: `FromRepr` can only be derived for enums with exactly one explicit integer representation + --> $DIR/deriving-enum-from-repr-bad.rs:22:10 + | +LL | #[derive(FromRepr)] + | ^^^^^^^^ +LL | +LL | / enum NumberNoRepr { +LL | | Zero, +LL | | One, +LL | | } + | |_- enum missing numeric `repr` annotation + | + = note: this error originates in the derive macro `FromRepr` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0789]: `FromRepr` can only be derived for C-like enums + --> $DIR/deriving-enum-from-repr-bad.rs:29:10 + | +LL | #[derive(FromRepr)] + | ^^^^^^^^ derive from `FromRepr` trait +... +LL | NonZero(u8), + | ----------- variant is not data-free + | + = note: only unit variants, like `Foo,` or `Bar = 3,` are data-free + = note: this error originates in the derive macro `FromRepr` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0789]: `FromRepr` can only be derived for C-like enums + --> $DIR/deriving-enum-from-repr-bad.rs:37:10 + | +LL | #[derive(FromRepr)] + | ^^^^^^^^ derive from `FromRepr` trait +... +LL | NonZero { value: u8 }, + | --------------------- variant is not data-free + | + = note: only unit variants, like `Foo,` or `Bar = 3,` are data-free + = note: this error originates in the derive macro `FromRepr` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0789]: `FromRepr` can only be derived for C-like enums + --> $DIR/deriving-enum-from-repr-bad.rs:45:10 + | +LL | #[derive(FromRepr)] + | ^^^^^^^^ derive from `FromRepr` trait +... +LL | Zero(), + | ------ variant is not data-free + | + = note: only unit variants, like `Foo,` or `Bar = 3,` are data-free + = note: this error originates in the derive macro `FromRepr` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0789]: `FromRepr` can only be derived for C-like enums + --> $DIR/deriving-enum-from-repr-bad.rs:53:10 + | +LL | #[derive(FromRepr)] + | ^^^^^^^^ derive from `FromRepr` trait +... +LL | Zero {}, + | ------- variant is not data-free + | + = note: only unit variants, like `Foo,` or `Bar = 3,` are data-free + = note: this error originates in the derive macro `FromRepr` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0789`. diff --git a/src/test/ui/deriving/deriving-enum-from-repr.rs b/src/test/ui/deriving/deriving-enum-from-repr.rs new file mode 100644 index 0000000000000..ae400e05deeb6 --- /dev/null +++ b/src/test/ui/deriving/deriving-enum-from-repr.rs @@ -0,0 +1,303 @@ +// Test that FromRepr can be derived to convert an int-repr'd enum into its repr. + +// run-pass +// gate-test-enum_as_repr + +#![feature(enum_as_repr)] + +#![deny(unreachable_patterns)] + +use std::enums::{FromRepr, TryFromReprError}; + +#[derive(FromRepr, Debug, PartialEq, Eq)] +#[repr(u8)] +enum PositiveNumber { + Zero, + One, +} + +#[derive(FromRepr, Debug, PartialEq, Eq)] +#[repr(i8)] +enum Number { + MinusOne = -1, + Zero, + One, + Four = 4, +} + +fn main() { + let n = PositiveNumber::try_from_repr(0_u8); + assert_eq!(n, Ok(PositiveNumber::Zero)); + let n = PositiveNumber::try_from_repr(1_u8); + assert_eq!(n, Ok(PositiveNumber::One)); + let n = PositiveNumber::try_from_repr(3_u8); + assert_eq!(n, Err(TryFromReprError(3_u8))); + + unsafe { + let n = PositiveNumber::from_repr(0_u8); + assert_eq!(n, PositiveNumber::Zero); + let n = PositiveNumber::from_repr(1_u8); + assert_eq!(n, PositiveNumber::One); + } +} + +#[derive(FromRepr)] +#[repr(u8)] +enum ExhaustiveVariants { + Variant0, + Variant1, + Variant2, + Variant3, + Variant4, + Variant5, + Variant6, + Variant7, + Variant8, + Variant9, + Variant10, + Variant11, + Variant12, + Variant13, + Variant14, + Variant15, + Variant16, + Variant17, + Variant18, + Variant19, + Variant20, + Variant21, + Variant22, + Variant23, + Variant24, + Variant25, + Variant26, + Variant27, + Variant28, + Variant29, + Variant30, + Variant31, + Variant32, + Variant33, + Variant34, + Variant35, + Variant36, + Variant37, + Variant38, + Variant39, + Variant40, + Variant41, + Variant42, + Variant43, + Variant44, + Variant45, + Variant46, + Variant47, + Variant48, + Variant49, + Variant50, + Variant51, + Variant52, + Variant53, + Variant54, + Variant55, + Variant56, + Variant57, + Variant58, + Variant59, + Variant60, + Variant61, + Variant62, + Variant63, + Variant64, + Variant65, + Variant66, + Variant67, + Variant68, + Variant69, + Variant70, + Variant71, + Variant72, + Variant73, + Variant74, + Variant75, + Variant76, + Variant77, + Variant78, + Variant79, + Variant80, + Variant81, + Variant82, + Variant83, + Variant84, + Variant85, + Variant86, + Variant87, + Variant88, + Variant89, + Variant90, + Variant91, + Variant92, + Variant93, + Variant94, + Variant95, + Variant96, + Variant97, + Variant98, + Variant99, + Variant100, + Variant101, + Variant102, + Variant103, + Variant104, + Variant105, + Variant106, + Variant107, + Variant108, + Variant109, + Variant110, + Variant111, + Variant112, + Variant113, + Variant114, + Variant115, + Variant116, + Variant117, + Variant118, + Variant119, + Variant120, + Variant121, + Variant122, + Variant123, + Variant124, + Variant125, + Variant126, + Variant127, + Variant128, + Variant129, + Variant130, + Variant131, + Variant132, + Variant133, + Variant134, + Variant135, + Variant136, + Variant137, + Variant138, + Variant139, + Variant140, + Variant141, + Variant142, + Variant143, + Variant144, + Variant145, + Variant146, + Variant147, + Variant148, + Variant149, + Variant150, + Variant151, + Variant152, + Variant153, + Variant154, + Variant155, + Variant156, + Variant157, + Variant158, + Variant159, + Variant160, + Variant161, + Variant162, + Variant163, + Variant164, + Variant165, + Variant166, + Variant167, + Variant168, + Variant169, + Variant170, + Variant171, + Variant172, + Variant173, + Variant174, + Variant175, + Variant176, + Variant177, + Variant178, + Variant179, + Variant180, + Variant181, + Variant182, + Variant183, + Variant184, + Variant185, + Variant186, + Variant187, + Variant188, + Variant189, + Variant190, + Variant191, + Variant192, + Variant193, + Variant194, + Variant195, + Variant196, + Variant197, + Variant198, + Variant199, + Variant200, + Variant201, + Variant202, + Variant203, + Variant204, + Variant205, + Variant206, + Variant207, + Variant208, + Variant209, + Variant210, + Variant211, + Variant212, + Variant213, + Variant214, + Variant215, + Variant216, + Variant217, + Variant218, + Variant219, + Variant220, + Variant221, + Variant222, + Variant223, + Variant224, + Variant225, + Variant226, + Variant227, + Variant228, + Variant229, + Variant230, + Variant231, + Variant232, + Variant233, + Variant234, + Variant235, + Variant236, + Variant237, + Variant238, + Variant239, + Variant240, + Variant241, + Variant242, + Variant243, + Variant244, + Variant245, + Variant246, + Variant247, + Variant248, + Variant249, + Variant250, + Variant251, + Variant252, + Variant253, + Variant254, + Variant255, +} diff --git a/src/test/ui/enum/enum-as-repr-manual-impl.rs b/src/test/ui/enum/enum-as-repr-manual-impl.rs new file mode 100644 index 0000000000000..168ce3e8638dd --- /dev/null +++ b/src/test/ui/enum/enum-as-repr-manual-impl.rs @@ -0,0 +1,47 @@ +// Test that AsRepr cannot be manually derived, as it is an auto-derived lang item. + +// gate-test-enum_as_repr + +#![feature(enum_as_repr)] + +use std::enums::{AsRepr, HasAsReprImpl}; + +#[repr(u8)] +enum HasExplicitRepr { + Zero, + One, +} + +impl AsRepr for HasExplicitRepr { +//~^ ERROR explicit impls for the `AsRepr` trait are not permitted [E0788] +//~| ERROR conflicting implementations of trait `std::enums::AsRepr` for type `HasExplicitRepr` [E0119] + type Repr = u8; + + fn as_repr(&self) -> Self::Repr { + 0 + } +} + +enum HasNoExplicitRepr { + Zero, + One, +} + +impl AsRepr for HasNoExplicitRepr { +//~^ ERROR explicit impls for the `AsRepr` trait are not permitted [E0788] + type Repr = u8; + + fn as_repr(&self) -> Self::Repr { + 0 + } +} + +enum IndirectEnum { + Zero, + One, +} + +impl HasAsReprImpl for IndirectEnum {} +//~^ ERROR explicit impls for the `HasAsReprImpl` trait are not permitted [E0788] + +fn main() {} diff --git a/src/test/ui/enum/enum-as-repr-manual-impl.stderr b/src/test/ui/enum/enum-as-repr-manual-impl.stderr new file mode 100644 index 0000000000000..28f1db8c75eaa --- /dev/null +++ b/src/test/ui/enum/enum-as-repr-manual-impl.stderr @@ -0,0 +1,32 @@ +error[E0119]: conflicting implementations of trait `std::enums::AsRepr` for type `HasExplicitRepr` + --> $DIR/enum-as-repr-manual-impl.rs:15:1 + | +LL | impl AsRepr for HasExplicitRepr { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: conflicting implementation in crate `core`: + - impl AsRepr for T + where T: HasAsReprImpl; + +error[E0788]: explicit impls for the `AsRepr` trait are not permitted + --> $DIR/enum-as-repr-manual-impl.rs:15:1 + | +LL | impl AsRepr for HasExplicitRepr { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `AsRepr` not allowed, it is automatically implemented for C-like enum types with explicit numeric reprs + +error[E0788]: explicit impls for the `AsRepr` trait are not permitted + --> $DIR/enum-as-repr-manual-impl.rs:30:1 + | +LL | impl AsRepr for HasNoExplicitRepr { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `AsRepr` not allowed, it is automatically implemented for C-like enum types with explicit numeric reprs + +error[E0788]: explicit impls for the `HasAsReprImpl` trait are not permitted + --> $DIR/enum-as-repr-manual-impl.rs:44:1 + | +LL | impl HasAsReprImpl for IndirectEnum {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `HasAsReprImpl` not allowed, it is automatically implemented for C-like enum types with explicit numeric reprs + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0119, E0788. +For more information about an error, try `rustc --explain E0119`. diff --git a/src/test/ui/enum/enum-as-repr-non-exhaustive.rs b/src/test/ui/enum/enum-as-repr-non-exhaustive.rs new file mode 100644 index 0000000000000..4ab8f9867e311 --- /dev/null +++ b/src/test/ui/enum/enum-as-repr-non-exhaustive.rs @@ -0,0 +1,31 @@ +// Test that AsRepr is not implemented for enums with non_exhaustive attributes. + +// gate-test-enum_as_repr + +#![feature(enum_as_repr)] + +use std::enums::AsRepr; + +#[repr(u8)] +#[non_exhaustive] +enum AttrOnEnum { + Zero, + One, +} + +#[repr(u8)] +enum AttrOnVariant { + Zero, + #[non_exhaustive] + One, +} + +fn main() { + consumes_asrepr(AttrOnEnum::Zero); + //~^ ERROR the trait bound `AttrOnEnum: HasAsReprImpl` is not satisfied [E0277] + + consumes_asrepr(AttrOnVariant::Zero); + //~^ ERROR the trait bound `AttrOnVariant: HasAsReprImpl` is not satisfied [E0277] +} + +fn consumes_asrepr(_v: V) {} diff --git a/src/test/ui/enum/enum-as-repr-non-exhaustive.stderr b/src/test/ui/enum/enum-as-repr-non-exhaustive.stderr new file mode 100644 index 0000000000000..e631f989c78ec --- /dev/null +++ b/src/test/ui/enum/enum-as-repr-non-exhaustive.stderr @@ -0,0 +1,33 @@ +error[E0277]: the trait bound `AttrOnEnum: HasAsReprImpl` is not satisfied + --> $DIR/enum-as-repr-non-exhaustive.rs:24:21 + | +LL | consumes_asrepr(AttrOnEnum::Zero); + | --------------- ^^^^^^^^^^^^^^^^ the trait `HasAsReprImpl` is not implemented for `AttrOnEnum` + | | + | required by a bound introduced by this call + | + = note: required because of the requirements on the impl of `AsRepr` for `AttrOnEnum` +note: required by a bound in `consumes_asrepr` + --> $DIR/enum-as-repr-non-exhaustive.rs:31:23 + | +LL | fn consumes_asrepr(_v: V) {} + | ^^^^^^ required by this bound in `consumes_asrepr` + +error[E0277]: the trait bound `AttrOnVariant: HasAsReprImpl` is not satisfied + --> $DIR/enum-as-repr-non-exhaustive.rs:27:21 + | +LL | consumes_asrepr(AttrOnVariant::Zero); + | --------------- ^^^^^^^^^^^^^^^^^^^ the trait `HasAsReprImpl` is not implemented for `AttrOnVariant` + | | + | required by a bound introduced by this call + | + = note: required because of the requirements on the impl of `AsRepr` for `AttrOnVariant` +note: required by a bound in `consumes_asrepr` + --> $DIR/enum-as-repr-non-exhaustive.rs:31:23 + | +LL | fn consumes_asrepr(_v: V) {} + | ^^^^^^ required by this bound in `consumes_asrepr` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/enum/enum-as-repr.rs b/src/test/ui/enum/enum-as-repr.rs new file mode 100644 index 0000000000000..896811a24e4f8 --- /dev/null +++ b/src/test/ui/enum/enum-as-repr.rs @@ -0,0 +1,80 @@ +// Test that AsRepr is automatically implemented to convert an int-repr'd enum into its +// discriminant. + +// run-pass +// gate-test-enum_as_repr + +#![feature(enum_as_repr)] + +use std::sync::atomic::{AtomicUsize, Ordering}; + +#[derive(Debug, PartialEq, Eq)] +#[repr(u8)] +enum PositiveNumber { + Zero, + One, +} + +#[derive(Debug, PartialEq, Eq)] +#[repr(i8)] +enum Number { + MinusOne = -1, + Zero, + One, + Four = 4, +} + +static DROP_COUNT: AtomicUsize = AtomicUsize::new(0); + +#[derive(Debug, PartialEq, Eq)] +#[repr(usize)] +enum DroppableNumber { + Zero, + One, +} + +impl Drop for DroppableNumber { + fn drop(&mut self) { + DROP_COUNT.fetch_add(1, Ordering::SeqCst); + } +} + +fn main() { + use std::enums::AsRepr; + + let n = PositiveNumber::Zero.as_repr(); + assert_eq!(n, 0); + let n = PositiveNumber::One.as_repr(); + assert_eq!(n, 1); + + let n = std::mem::discriminant(&PositiveNumber::Zero).as_repr(); + assert_eq!(n, 0_u8); + let n = std::mem::discriminant(&PositiveNumber::One).as_repr(); + assert_eq!(n, 1_u8); + + let n = Number::MinusOne.as_repr(); + assert_eq!(n, -1); + let n = Number::Zero.as_repr(); + assert_eq!(n, 0); + let n = Number::One.as_repr(); + assert_eq!(n, 1); + let n = Number::Four.as_repr(); + assert_eq!(n, 4); + + let n = std::mem::discriminant(&Number::Zero).as_repr(); + assert_eq!(n, 0_i8); + let n = std::mem::discriminant(&Number::One).as_repr(); + assert_eq!(n, 1_i8); + + assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 0); + { + let n = DroppableNumber::Zero; + assert_eq!(n.as_repr(), 0); + } + assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 1); + { + let n = DroppableNumber::One; + assert_eq!(n.as_repr(), 1); + } + assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 2); +}