diff --git a/src/doc/unstable-book/src/language-features/lang-items.md b/src/doc/unstable-book/src/language-features/lang-items.md index 250824321920d..20c7d7dcec8d6 100644 --- a/src/doc/unstable-book/src/language-features/lang-items.md +++ b/src/doc/unstable-book/src/language-features/lang-items.md @@ -287,6 +287,7 @@ the source code. - `unsize`: `libcore/marker.rs` - `sync`: `libcore/marker.rs` - `phantom_data`: `libcore/marker.rs` + - `discriminant_kind`: `libcore/marker.rs` - `freeze`: `libcore/marker.rs` - `debug_trait`: `libcore/fmt/mod.rs` - `non_zero`: `libcore/nonzero.rs` diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index a60380137e11e..9006e4cfaf7bb 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -54,6 +54,8 @@ )] #![allow(missing_docs)] +#[cfg(not(bootstrap))] +use crate::marker::DiscriminantKind; use crate::mem; #[stable(feature = "drop_in_place", since = "1.8.0")] @@ -1912,6 +1914,10 @@ extern "rust-intrinsic" { /// The stabilized version of this intrinsic is /// [`std::mem::discriminant`](../../std/mem/fn.discriminant.html) #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] + #[cfg(not(bootstrap))] + pub fn discriminant_value(v: &T) -> ::Discriminant; + #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] + #[cfg(bootstrap)] pub fn discriminant_value(v: &T) -> u64; /// Rust's "try catch" construct which invokes the function pointer `try_fn` diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 339b07119c6d5..c0c0f66aff908 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -8,6 +8,7 @@ use crate::cell::UnsafeCell; use crate::cmp; +use crate::fmt::Debug; use crate::hash::Hash; use crate::hash::Hasher; @@ -679,6 +680,37 @@ mod impls { unsafe impl Send for &mut T {} } +/// Compiler-internal trait used to indicate the type of enum discriminants. +/// +/// This trait is automatically implemented for every type and does not add any +/// guarantees to [`mem::Discriminant`]. It is **undefined behavior** to transmute +/// between `DiscriminantKind::Discriminant` and `mem::Discriminant`. +/// +/// [`mem::Discriminant`]: https://doc.rust-lang.org/stable/core/mem/struct.Discriminant.html +#[unstable( + feature = "discriminant_kind", + issue = "none", + reason = "this trait is unlikely to ever be stabilized, use `mem::discriminant` instead" +)] +#[cfg_attr(not(bootstrap), lang = "discriminant_kind")] +pub trait DiscriminantKind { + /// The type of the dicriminant, which must satisfy the trait + /// bounds required by `mem::Discriminant`. + type Discriminant: Clone + Copy + Debug + Eq + PartialEq + Hash + Send + Sync + Unpin; +} + +// Manually implement `DiscriminantKind` for all types during bootstrap +// to reduce the required amount of conditional compilation. +#[unstable( + feature = "discriminant_kind", + issue = "none", + reason = "this trait is unlikely to ever be stabilized, use `mem::discriminant` instead" +)] +#[cfg(bootstrap)] +impl DiscriminantKind for T { + type Discriminant = u64; +} + /// Compiler-internal trait used to determine whether a type contains /// any `UnsafeCell` internally, but not through an indirection. /// This affects, for example, whether a `static` of that type is diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs index b1bbcaeab8def..010f2958e36b9 100644 --- a/src/libcore/mem/mod.rs +++ b/src/libcore/mem/mod.rs @@ -10,7 +10,7 @@ use crate::cmp; use crate::fmt; use crate::hash; use crate::intrinsics; -use crate::marker::{Copy, PhantomData, Sized}; +use crate::marker::{Copy, DiscriminantKind, Sized}; use crate::ptr; mod manually_drop; @@ -930,7 +930,7 @@ pub unsafe fn transmute_copy(src: &T) -> U { /// /// [`discriminant`]: fn.discriminant.html #[stable(feature = "discriminant_value", since = "1.21.0")] -pub struct Discriminant(u64, PhantomData T>); +pub struct Discriminant(::Discriminant); // N.B. These trait implementations cannot be derived because we don't want any bounds on T. @@ -995,5 +995,5 @@ impl fmt::Debug for Discriminant { #[stable(feature = "discriminant_value", since = "1.21.0")] #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] pub const fn discriminant(v: &T) -> Discriminant { - Discriminant(intrinsics::discriminant_value(v), PhantomData) + Discriminant(intrinsics::discriminant_value(v)) } diff --git a/src/librustc_builtin_macros/deriving/generic/mod.rs b/src/librustc_builtin_macros/deriving/generic/mod.rs index 0ba9df08383a4..a9567f20d6925 100644 --- a/src/librustc_builtin_macros/deriving/generic/mod.rs +++ b/src/librustc_builtin_macros/deriving/generic/mod.rs @@ -187,7 +187,6 @@ use rustc_ast::ptr::P; use rustc_attr as attr; use rustc_data_structures::map_in_place::MapInPlace; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_session::parse::ParseSess; use rustc_span::source_map::respan; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -437,14 +436,7 @@ impl<'a> TraitDef<'a> { // This can only cause further compilation errors // downstream in blatantly illegal code, so it // is fine. - self.expand_enum_def( - cx, - enum_def, - &item.attrs, - item.ident, - generics, - from_scratch, - ) + self.expand_enum_def(cx, enum_def, item.ident, generics, from_scratch) } ast::ItemKind::Union(ref struct_def, ref generics) => { if self.supports_unions { @@ -769,7 +761,6 @@ impl<'a> TraitDef<'a> { &self, cx: &mut ExtCtxt<'_>, enum_def: &'a EnumDef, - type_attrs: &[ast::Attribute], type_ident: Ident, generics: &Generics, from_scratch: bool, @@ -801,7 +792,6 @@ impl<'a> TraitDef<'a> { cx, self, enum_def, - type_attrs, type_ident, self_args, &nonself_args[..], @@ -816,38 +806,6 @@ impl<'a> TraitDef<'a> { } } -fn find_repr_type_name(sess: &ParseSess, type_attrs: &[ast::Attribute]) -> &'static str { - let mut repr_type_name = "isize"; - for a in type_attrs { - for r in &attr::find_repr_attrs(sess, a) { - repr_type_name = match *r { - attr::ReprPacked(_) - | attr::ReprSimd - | attr::ReprAlign(_) - | attr::ReprTransparent - | attr::ReprNoNiche => continue, - - attr::ReprC => "i32", - - attr::ReprInt(attr::SignedInt(ast::IntTy::Isize)) => "isize", - attr::ReprInt(attr::SignedInt(ast::IntTy::I8)) => "i8", - attr::ReprInt(attr::SignedInt(ast::IntTy::I16)) => "i16", - attr::ReprInt(attr::SignedInt(ast::IntTy::I32)) => "i32", - attr::ReprInt(attr::SignedInt(ast::IntTy::I64)) => "i64", - attr::ReprInt(attr::SignedInt(ast::IntTy::I128)) => "i128", - - attr::ReprInt(attr::UnsignedInt(ast::UintTy::Usize)) => "usize", - attr::ReprInt(attr::UnsignedInt(ast::UintTy::U8)) => "u8", - attr::ReprInt(attr::UnsignedInt(ast::UintTy::U16)) => "u16", - attr::ReprInt(attr::UnsignedInt(ast::UintTy::U32)) => "u32", - attr::ReprInt(attr::UnsignedInt(ast::UintTy::U64)) => "u64", - attr::ReprInt(attr::UnsignedInt(ast::UintTy::U128)) => "u128", - } - } - } - repr_type_name -} - impl<'a> MethodDef<'a> { fn call_substructure_method( &self, @@ -1148,20 +1106,11 @@ impl<'a> MethodDef<'a> { cx: &mut ExtCtxt<'_>, trait_: &TraitDef<'b>, enum_def: &'b EnumDef, - type_attrs: &[ast::Attribute], type_ident: Ident, self_args: Vec>, nonself_args: &[P], ) -> P { - self.build_enum_match_tuple( - cx, - trait_, - enum_def, - type_attrs, - type_ident, - self_args, - nonself_args, - ) + self.build_enum_match_tuple(cx, trait_, enum_def, type_ident, self_args, nonself_args) } /// Creates a match for a tuple of all `self_args`, where either all @@ -1181,11 +1130,11 @@ impl<'a> MethodDef<'a> { /// ```{.text} /// let __self0_vi = unsafe { - /// std::intrinsics::discriminant_value(&self) } as i32; + /// std::intrinsics::discriminant_value(&self) }; /// let __self1_vi = unsafe { - /// std::intrinsics::discriminant_value(&arg1) } as i32; + /// std::intrinsics::discriminant_value(&arg1) }; /// let __self2_vi = unsafe { - /// std::intrinsics::discriminant_value(&arg2) } as i32; + /// std::intrinsics::discriminant_value(&arg2) }; /// /// if __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... { /// match (...) { @@ -1204,7 +1153,6 @@ impl<'a> MethodDef<'a> { cx: &mut ExtCtxt<'_>, trait_: &TraitDef<'b>, enum_def: &'b EnumDef, - type_attrs: &[ast::Attribute], type_ident: Ident, mut self_args: Vec>, nonself_args: &[P], @@ -1392,21 +1340,18 @@ impl<'a> MethodDef<'a> { // if variants.len() > 1 && self_args.len() > 1 { // Build a series of let statements mapping each self_arg - // to its discriminant value. If this is a C-style enum - // with a specific repr type, then casts the values to - // that type. Otherwise casts to `i32` (the default repr - // type). + // to its discriminant value. // // i.e., for `enum E { A, B(1), C(T, T) }`, and a deriving // with three Self args, builds three statements: // // ``` // let __self0_vi = unsafe { - // std::intrinsics::discriminant_value(&self) } as i32; + // std::intrinsics::discriminant_value(&self) }; // let __self1_vi = unsafe { - // std::intrinsics::discriminant_value(&arg1) } as i32; + // std::intrinsics::discriminant_value(&arg1) }; // let __self2_vi = unsafe { - // std::intrinsics::discriminant_value(&arg2) } as i32; + // std::intrinsics::discriminant_value(&arg2) }; // ``` let mut index_let_stmts: Vec = Vec::with_capacity(vi_idents.len() + 1); @@ -1414,17 +1359,12 @@ impl<'a> MethodDef<'a> { // discriminant_test = __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... let mut discriminant_test = cx.expr_bool(sp, true); - let target_type_name = find_repr_type_name(&cx.parse_sess, type_attrs); - let mut first_ident = None; for (&ident, self_arg) in vi_idents.iter().zip(&self_args) { let self_addr = cx.expr_addr_of(sp, self_arg.clone()); let variant_value = deriving::call_intrinsic(cx, sp, "discriminant_value", vec![self_addr]); - - let target_ty = cx.ty_ident(sp, cx.ident_of(target_type_name, sp)); - let variant_disr = cx.expr_cast(sp, variant_value, target_ty); - let let_stmt = cx.stmt_let(sp, false, ident, variant_disr); + let let_stmt = cx.stmt_let(sp, false, ident, variant_value); index_let_stmts.push(let_stmt); match first_ident { diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index ddf21ff6338eb..1e6d2e3dbb74e 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -188,11 +188,11 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { } "size_of" | "pref_align_of" | "min_align_of" | "needs_drop" | "type_id" | "type_name" => { - let ty_name = self + let value = self .tcx .const_eval_instance(ty::ParamEnv::reveal_all(), instance, None) .unwrap(); - OperandRef::from_const(self, ty_name, ret_ty).immediate_or_packed_pair(self) + OperandRef::from_const(self, value, ret_ty).immediate_or_packed_pair(self) } // Effectively no-op "forget" => { @@ -549,7 +549,13 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { } } - "discriminant_value" => args[0].deref(self.cx()).codegen_get_discr(self, ret_ty), + "discriminant_value" => { + if ret_ty.is_integral() { + args[0].deref(self.cx()).codegen_get_discr(self, ret_ty) + } else { + span_bug!(span, "Invalid discriminant type for `{:?}`", arg_tys[0]) + } + } name if name.starts_with("simd_") => { match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) { diff --git a/src/librustc_hir/lang_items.rs b/src/librustc_hir/lang_items.rs index 53f72804a848d..04fe3b60b6a87 100644 --- a/src/librustc_hir/lang_items.rs +++ b/src/librustc_hir/lang_items.rs @@ -163,6 +163,7 @@ language_item_table! { CopyTraitLangItem, "copy", copy_trait, Target::Trait; CloneTraitLangItem, "clone", clone_trait, Target::Trait; SyncTraitLangItem, "sync", sync_trait, Target::Trait; + DiscriminantKindTraitLangItem,"discriminant_kind", discriminant_kind_trait, Target::Trait; FreezeTraitLangItem, "freeze", freeze_trait, Target::Trait; DropTraitLangItem, "drop", drop_trait, Target::Trait; diff --git a/src/librustc_middle/lib.rs b/src/librustc_middle/lib.rs index d0f627d8bc576..7c433574d1843 100644 --- a/src/librustc_middle/lib.rs +++ b/src/librustc_middle/lib.rs @@ -32,6 +32,7 @@ #![feature(const_panic)] #![feature(const_transmute)] #![feature(core_intrinsics)] +#![feature(discriminant_kind)] #![feature(drain_filter)] #![feature(never_type)] #![feature(exhaustive_patterns)] diff --git a/src/librustc_middle/traits/mod.rs b/src/librustc_middle/traits/mod.rs index adbeac29bdd75..9afab5a4d2fe9 100644 --- a/src/librustc_middle/traits/mod.rs +++ b/src/librustc_middle/traits/mod.rs @@ -411,6 +411,9 @@ pub enum Vtable<'tcx, N> { /// Same as above, but for a function pointer type with the given signature. VtableFnPointer(VtableFnPointerData<'tcx, N>), + /// Vtable for a builtin `DeterminantKind` trait implementation. + VtableDiscriminantKind(VtableDiscriminantKindData), + /// Vtable automatically generated for a generator. VtableGenerator(VtableGeneratorData<'tcx, N>), @@ -429,6 +432,7 @@ impl<'tcx, N> Vtable<'tcx, N> { VtableGenerator(c) => c.nested, VtableObject(d) => d.nested, VtableFnPointer(d) => d.nested, + VtableDiscriminantKind(VtableDiscriminantKindData) => Vec::new(), VtableTraitAlias(d) => d.nested, } } @@ -443,6 +447,7 @@ impl<'tcx, N> Vtable<'tcx, N> { VtableGenerator(c) => &c.nested[..], VtableObject(d) => &d.nested[..], VtableFnPointer(d) => &d.nested[..], + VtableDiscriminantKind(VtableDiscriminantKindData) => &[], VtableTraitAlias(d) => &d.nested[..], } } @@ -484,6 +489,9 @@ impl<'tcx, N> Vtable<'tcx, N> { fn_ty: p.fn_ty, nested: p.nested.into_iter().map(f).collect(), }), + VtableDiscriminantKind(VtableDiscriminantKindData) => { + VtableDiscriminantKind(VtableDiscriminantKindData) + } VtableTraitAlias(d) => VtableTraitAlias(VtableTraitAliasData { alias_def_id: d.alias_def_id, substs: d.substs, @@ -560,6 +568,10 @@ pub struct VtableFnPointerData<'tcx, N> { pub nested: Vec, } +// FIXME(@lcnr): This should be refactored and merged with other builtin vtables. +#[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct VtableDiscriminantKindData; + #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] pub struct VtableTraitAliasData<'tcx, N> { pub alias_def_id: DefId, diff --git a/src/librustc_middle/traits/select.rs b/src/librustc_middle/traits/select.rs index d316d7659e222..a12f5910b14b9 100644 --- a/src/librustc_middle/traits/select.rs +++ b/src/librustc_middle/traits/select.rs @@ -34,7 +34,7 @@ impl<'tcx> SelectionCache<'tcx> { /// clauses, and so forth that might resolve an obligation. Sometimes /// we'll be able to say definitively that (e.g.) an impl does not /// apply to the obligation: perhaps it is defined for `usize` but the -/// obligation is for `int`. In that case, we drop the impl out of the +/// obligation is for `i32`. In that case, we drop the impl out of the /// list. But the other cases are considered *candidates*. /// /// For selection to succeed, there must be exactly one matching @@ -54,12 +54,14 @@ impl<'tcx> SelectionCache<'tcx> { /// will always be satisfied) picking the blanket impl will be wrong /// for at least *some* substitutions. To make this concrete, if we have /// -/// trait AsDebug { type Out : fmt::Debug; fn debug(self) -> Self::Out; } -/// impl AsDebug for T { -/// type Out = T; -/// fn debug(self) -> fmt::Debug { self } -/// } -/// fn foo(t: T) { println!("{:?}", ::debug(t)); } +/// ```rust, ignore +/// trait AsDebug { type Out: fmt::Debug; fn debug(self) -> Self::Out; } +/// impl AsDebug for T { +/// type Out = T; +/// fn debug(self) -> fmt::Debug { self } +/// } +/// fn foo(t: T) { println!("{:?}", ::debug(t)); } +/// ``` /// /// we can't just use the impl to resolve the `` obligation /// -- a type from another crate (that doesn't implement `fmt::Debug`) could @@ -79,14 +81,16 @@ impl<'tcx> SelectionCache<'tcx> { /// inference variables. The can lead to inference making "leaps of logic", /// for example in this situation: /// -/// pub trait Foo { fn foo(&self) -> T; } -/// impl Foo<()> for T { fn foo(&self) { } } -/// impl Foo for bool { fn foo(&self) -> bool { *self } } +/// ```rust, ignore +/// pub trait Foo { fn foo(&self) -> T; } +/// impl Foo<()> for T { fn foo(&self) { } } +/// impl Foo for bool { fn foo(&self) -> bool { *self } } /// -/// pub fn foo(t: T) where T: Foo { -/// println!("{:?}", >::foo(&t)); -/// } -/// fn main() { foo(false); } +/// pub fn foo(t: T) where T: Foo { +/// println!("{:?}", >::foo(&t)); +/// } +/// fn main() { foo(false); } +/// ``` /// /// Here the obligation `>` can be matched by both the blanket /// impl and the where-clause. We select the where-clause and unify `$0=bool`, @@ -128,6 +132,9 @@ pub enum SelectionCandidate<'tcx> { /// types generated for a fn pointer type (e.g., `fn(int) -> int`) FnPointerCandidate, + /// Builtin implementation of `DiscriminantKind`. + DiscriminantKindCandidate, + TraitAliasCandidate(DefId), ObjectCandidate, diff --git a/src/librustc_middle/traits/structural_impls.rs b/src/librustc_middle/traits/structural_impls.rs index 56744283b0c47..74f7441529499 100644 --- a/src/librustc_middle/traits/structural_impls.rs +++ b/src/librustc_middle/traits/structural_impls.rs @@ -19,6 +19,8 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::Vtable<'tcx, N> { super::VtableFnPointer(ref d) => write!(f, "VtableFnPointer({:?})", d), + super::VtableDiscriminantKind(ref d) => write!(f, "{:?}", d), + super::VtableObject(ref d) => write!(f, "{:?}", d), super::VtableParam(ref n) => write!(f, "VtableParam({:?})", n), @@ -274,6 +276,9 @@ impl<'a, 'tcx> Lift<'tcx> for traits::Vtable<'a, ()> { traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, nested }) }) } + traits::VtableDiscriminantKind(traits::VtableDiscriminantKindData) => { + Some(traits::VtableDiscriminantKind(traits::VtableDiscriminantKindData)) + } traits::VtableParam(n) => Some(traits::VtableParam(n)), traits::VtableBuiltin(n) => Some(traits::VtableBuiltin(n)), traits::VtableObject(traits::VtableObjectData { diff --git a/src/librustc_middle/ty/codec.rs b/src/librustc_middle/ty/codec.rs index cbbc937ed7d31..1cd4af45f2956 100644 --- a/src/librustc_middle/ty/codec.rs +++ b/src/librustc_middle/ty/codec.rs @@ -15,8 +15,10 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder}; use rustc_span::Span; +use std::convert::{TryFrom, TryInto}; use std::hash::Hash; use std::intrinsics; +use std::marker::DiscriminantKind; /// The shorthand encoding uses an enum's variant index `usize` /// and is offset by this value so it never matches a real variant. @@ -60,6 +62,7 @@ where E: TyEncoder, M: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap, T: EncodableWithShorthand, + ::Discriminant: Ord + TryFrom, { let existing_shorthand = cache(encoder).get(value).cloned(); if let Some(shorthand) = existing_shorthand { @@ -75,7 +78,8 @@ where // The shorthand encoding uses the same usize as the // discriminant, with an offset so they can't conflict. let discriminant = intrinsics::discriminant_value(variant); - assert!(discriminant < SHORTHAND_OFFSET as u64); + assert!(discriminant < SHORTHAND_OFFSET.try_into().ok().unwrap()); + let shorthand = start + SHORTHAND_OFFSET; // Get the number of bits that leb128 could fit diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index c8bf328ce8eee..42b969c99917f 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -2,8 +2,6 @@ //! looking at their MIR. Intrinsics/functions supported here are shared by CTFE //! and miri. -use std::convert::TryFrom; - use rustc_hir::def_id::DefId; use rustc_middle::mir::{ self, @@ -220,7 +218,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::discriminant_value => { let place = self.deref_operand(args[0])?; let discr_val = self.read_discriminant(place.into())?.0; - self.write_scalar(Scalar::from_u64(u64::try_from(discr_val).unwrap()), dest)?; + let scalar = match dest.layout.ty.kind { + ty::Int(_) => Scalar::from_int(discr_val as i128, dest.layout.size), + ty::Uint(_) => Scalar::from_uint(discr_val, dest.layout.size), + _ => bug!("invalid `discriminant_value` return layout: {:?}", dest.layout), + }; + self.write_scalar(scalar, dest)?; } sym::unchecked_shl | sym::unchecked_shr diff --git a/src/librustc_trait_selection/traits/project.rs b/src/librustc_trait_selection/traits/project.rs index c4cb72fa08c08..f102f34c744de 100644 --- a/src/librustc_trait_selection/traits/project.rs +++ b/src/librustc_trait_selection/traits/project.rs @@ -12,7 +12,10 @@ use super::Selection; use super::SelectionContext; use super::SelectionError; use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; -use super::{VtableClosureData, VtableFnPointerData, VtableGeneratorData, VtableImplData}; +use super::{ + VtableClosureData, VtableDiscriminantKindData, VtableFnPointerData, VtableGeneratorData, + VtableImplData, +}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; @@ -23,6 +26,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::lang_items::{FnOnceTraitLangItem, GeneratorTraitLangItem}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness}; use rustc_span::symbol::{sym, Ident}; use rustc_span::DUMMY_SP; @@ -1043,6 +1047,46 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( } } } + super::VtableDiscriminantKind(..) => { + // While `DiscriminantKind` is automatically implemented for every type, + // the concrete discriminant may not be known yet. + // + // Any type with multiple potential discriminant types is therefore not eligible. + let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty()); + + match self_ty.kind { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(..) + | ty::Foreign(_) + | ty::Str + | ty::Array(..) + | ty::Slice(_) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::Never + | ty::Tuple(..) + // Integers and floats always have `u8` as their discriminant. + | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, + + ty::Projection(..) + | ty::Opaque(..) + | ty::Param(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Infer(..) + | ty::Error => false, + } + } super::VtableParam(..) => { // This case tell us nothing about the value of an // associated type. Consider: @@ -1124,13 +1168,15 @@ fn confirm_select_candidate<'cx, 'tcx>( super::VtableGenerator(data) => confirm_generator_candidate(selcx, obligation, data), super::VtableClosure(data) => confirm_closure_candidate(selcx, obligation, data), super::VtableFnPointer(data) => confirm_fn_pointer_candidate(selcx, obligation, data), + super::VtableDiscriminantKind(data) => { + confirm_discriminant_kind_candidate(selcx, obligation, data) + } super::VtableObject(_) => confirm_object_candidate(selcx, obligation, obligation_trait_ref), super::VtableAutoImpl(..) | super::VtableParam(..) | super::VtableBuiltin(..) - | super::VtableTraitAlias(..) => - // we don't create Select candidates with this kind of resolution - { + | super::VtableTraitAlias(..) => { + // we don't create Select candidates with this kind of resolution span_bug!( obligation.cause.span, "Cannot project an associated type from `{:?}`", @@ -1259,6 +1305,37 @@ fn confirm_generator_candidate<'cx, 'tcx>( .with_addl_obligations(obligations) } +fn confirm_discriminant_kind_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + _: VtableDiscriminantKindData, +) -> Progress<'tcx> { + let tcx = selcx.tcx(); + + let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty()); + let substs = tcx.mk_substs([self_ty.into()].iter()); + + let assoc_items = tcx.associated_items(tcx.lang_items().discriminant_kind_trait().unwrap()); + // FIXME: emit an error if the trait definition is wrong + let discriminant_def_id = assoc_items.in_definition_order().next().unwrap().def_id; + + let discriminant_ty = match self_ty.kind { + // Use the discriminant type for enums. + ty::Adt(adt, _) if adt.is_enum() => adt.repr.discr_type().to_ty(tcx), + // Default to `i32` for generators. + ty::Generator(..) => tcx.types.i32, + // Use `u8` for all other types. + _ => tcx.types.u8, + }; + + let predicate = ty::ProjectionPredicate { + projection_ty: ty::ProjectionTy { substs, item_def_id: discriminant_def_id }, + ty: discriminant_ty, + }; + + confirm_param_env_candidate(selcx, obligation, ty::Binder::bind(predicate)) +} + fn confirm_fn_pointer_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, diff --git a/src/librustc_trait_selection/traits/select.rs b/src/librustc_trait_selection/traits/select.rs index 70c6cbef102c5..d903779e5075f 100644 --- a/src/librustc_trait_selection/traits/select.rs +++ b/src/librustc_trait_selection/traits/select.rs @@ -24,12 +24,13 @@ use super::{ObjectCastObligation, Obligation}; use super::{ObligationCause, PredicateObligation, TraitObligation}; use super::{OutputTypeParameterMismatch, Overflow, SelectionError, Unimplemented}; use super::{ - VtableAutoImpl, VtableBuiltin, VtableClosure, VtableFnPointer, VtableGenerator, VtableImpl, - VtableObject, VtableParam, VtableTraitAlias, + VtableAutoImpl, VtableBuiltin, VtableClosure, VtableDiscriminantKind, VtableFnPointer, + VtableGenerator, VtableImpl, VtableObject, VtableParam, VtableTraitAlias, }; use super::{ - VtableAutoImplData, VtableBuiltinData, VtableClosureData, VtableFnPointerData, - VtableGeneratorData, VtableImplData, VtableObjectData, VtableTraitAliasData, + VtableAutoImplData, VtableBuiltinData, VtableClosureData, VtableDiscriminantKindData, + VtableFnPointerData, VtableGeneratorData, VtableImplData, VtableObjectData, + VtableTraitAliasData, }; use crate::infer::{CombinedSnapshot, InferCtxt, InferOk, PlaceholderMap, TypeFreshener}; @@ -1382,6 +1383,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // For other types, we'll use the builtin rules. let copy_conditions = self.copy_clone_conditions(obligation); self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates)?; + } else if lang_items.discriminant_kind_trait() == Some(def_id) { + // `DiscriminantKind` is automatically implemented for every type. + candidates.vec.push(DiscriminantKindCandidate); } else if lang_items.sized_trait() == Some(def_id) { // Sized is never implementable by end-users, it is // always automatically computed. @@ -1995,11 +1999,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let is_global = |cand: &ty::PolyTraitRef<'_>| cand.is_global() && !cand.has_late_bound_regions(); + // (*) Prefer `BuiltinCandidate { has_nested: false }` and `DiscriminantKindCandidate` + // to anything else. + // + // This is a fix for #53123 and prevents winnowing from accidentally extending the + // lifetime of a variable. match other.candidate { - // Prefer `BuiltinCandidate { has_nested: false }` to anything else. - // This is a fix for #53123 and prevents winnowing from accidentally extending the - // lifetime of a variable. - BuiltinCandidate { has_nested: false } => true, + // (*) + BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => true, ParamCandidate(ref cand) => match victim.candidate { AutoImplCandidate(..) => { bug!( @@ -2007,10 +2014,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { when there are other valid candidates" ); } - // Prefer `BuiltinCandidate { has_nested: false }` to anything else. - // This is a fix for #53123 and prevents winnowing from accidentally extending the - // lifetime of a variable. - BuiltinCandidate { has_nested: false } => false, + // (*) + BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => false, ImplCandidate(..) | ClosureCandidate | GeneratorCandidate @@ -2038,10 +2043,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { when there are other valid candidates" ); } - // Prefer `BuiltinCandidate { has_nested: false }` to anything else. - // This is a fix for #53123 and prevents winnowing from accidentally extending the - // lifetime of a variable. - BuiltinCandidate { has_nested: false } => false, + // (*) + BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => false, ImplCandidate(..) | ClosureCandidate | GeneratorCandidate @@ -2486,6 +2489,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(VtableFnPointer(data)) } + DiscriminantKindCandidate => Ok(VtableDiscriminantKind(VtableDiscriminantKindData)), + TraitAliasCandidate(alias_def_id) => { let data = self.confirm_trait_alias_candidate(obligation, alias_def_id); Ok(VtableTraitAlias(data)) diff --git a/src/librustc_ty/instance.rs b/src/librustc_ty/instance.rs index 2a99bb1aed954..a793031d4025b 100644 --- a/src/librustc_ty/instance.rs +++ b/src/librustc_ty/instance.rs @@ -236,7 +236,10 @@ fn resolve_associated_item<'tcx>( None } } - traits::VtableAutoImpl(..) | traits::VtableParam(..) | traits::VtableTraitAlias(..) => None, + traits::VtableAutoImpl(..) + | traits::VtableParam(..) + | traits::VtableTraitAlias(..) + | traits::VtableDiscriminantKind(..) => None, }) } diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index db6c30adb04cc..bded2c695c9db 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -283,14 +283,20 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { "likely" => (0, vec![tcx.types.bool], tcx.types.bool), "unlikely" => (0, vec![tcx.types.bool], tcx.types.bool), - "discriminant_value" => ( - 1, - vec![tcx.mk_imm_ref( - tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0))), - param(0), - )], - tcx.types.u64, - ), + "discriminant_value" => { + let assoc_items = + tcx.associated_items(tcx.lang_items().discriminant_kind_trait().unwrap()); + let discriminant_def_id = assoc_items.in_definition_order().next().unwrap().def_id; + + ( + 1, + vec![tcx.mk_imm_ref( + tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0))), + param(0), + )], + tcx.mk_projection(discriminant_def_id, tcx.mk_substs([param(0).into()].iter())), + ) + } "try" => { let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8); diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index 05d5a81217ce1..a45a44a6801e8 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -48,7 +48,20 @@ fn enforce_trait_manually_implementable( let did = Some(trait_def_id); let li = tcx.lang_items(); - // Disallow *all* explicit impls of `Sized` and `Unsize` for now. + // Disallow *all* explicit impls of `DiscriminantKind`, `Sized` and `Unsize` for now. + if did == li.discriminant_kind_trait() { + let span = impl_header_span(tcx, impl_def_id); + struct_span_err!( + tcx.sess, + span, + E0322, + "explicit impls for the `DiscriminantKind` trait are not permitted" + ) + .span_label(span, "impl of 'DiscriminantKind' not allowed") + .emit(); + return; + } + if did == li.sized_trait() { let span = impl_header_span(tcx, impl_def_id); struct_span_err!( diff --git a/src/test/ui/enum-discriminant/actually_not_an_enum-discriminant.rs b/src/test/ui/enum-discriminant/actually_not_an_enum-discriminant.rs new file mode 100644 index 0000000000000..6a566ab3a3d88 --- /dev/null +++ b/src/test/ui/enum-discriminant/actually_not_an_enum-discriminant.rs @@ -0,0 +1,49 @@ +// run-pass +#![feature(core_intrinsics)] + +use std::intrinsics::discriminant_value; + +struct Zst; + +struct Struct { + _a: u32, +} + +union Union { + _a: u32, +} + +fn check(v: u8) { + assert_eq!(v, 0); +} + +pub fn generic() +where + for<'a> T: Fn(&'a isize), +{ + let v: Vec = Vec::new(); + let _: u8 = discriminant_value(&v); +} + +fn main() { + // check that we use `u8` as the discriminant value + // for everything that is not an enum. + check(discriminant_value(&true)); + check(discriminant_value(&'a')); + check(discriminant_value(&7)); + check(discriminant_value(&7.0)); + check(discriminant_value(&Zst)); + check(discriminant_value(&Struct { _a: 7 })); + check(discriminant_value(&Union { _a: 7 })); + check(discriminant_value(&[7, 77])); + check(discriminant_value(&(7 as *const ()))); + check(discriminant_value(&(7 as *mut ()))); + check(discriminant_value(&&7)); + check(discriminant_value(&&mut 7)); + check(discriminant_value(&check)); + let fn_ptr: fn(u8) = check; + check(discriminant_value(&fn_ptr)); + let hrtb: for<'a> fn(&'a str) -> &'a str = |x| x; + check(discriminant_value(&hrtb)); + check(discriminant_value(&(7, 77, 777))); +} diff --git a/src/test/ui/enum-discriminant/discriminant_size.rs b/src/test/ui/enum-discriminant/discriminant_size.rs new file mode 100644 index 0000000000000..4cede8c2a2ded --- /dev/null +++ b/src/test/ui/enum-discriminant/discriminant_size.rs @@ -0,0 +1,53 @@ +// run-pass +#![feature(core_intrinsics, repr128)] + +use std::intrinsics::discriminant_value; + +enum E1 { + A, + B, +} + +#[repr(i8)] +enum E2 { + A = 7, + B = -2, +} + +#[repr(C)] +enum E3 { + A = 42, + B = 100, +} + +#[repr(i128)] +enum E4 { + A = 0x1223_3445_5667_7889, + B = -0x1223_3445_5667_7889, +} + +fn main() { + let mut target: [isize; 3] = [0, 0, 0]; + target[1] = discriminant_value(&E1::A); + assert_eq!(target, [0, 0, 0]); + target[1] = discriminant_value(&E1::B); + assert_eq!(target, [0, 1, 0]); + + let mut target: [i8; 3] = [0, 0, 0]; + target[1] = discriminant_value(&E2::A); + assert_eq!(target, [0, 7, 0]); + target[1] = discriminant_value(&E2::B); + assert_eq!(target, [0, -2, 0]); + + let mut target: [isize; 3] = [0, 0, 0]; + target[1] = discriminant_value(&E3::A); + assert_eq!(target, [0, 42, 0]); + target[1] = discriminant_value(&E3::B); + assert_eq!(target, [0, 100, 0]); + + let mut target: [i128; 3] = [0, 0, 0]; + target[1] = discriminant_value(&E4::A); + assert_eq!(target, [0, 0x1223_3445_5667_7889, 0]); + target[1] = discriminant_value(&E4::B); + assert_eq!(target, [0, -0x1223_3445_5667_7889, 0]); +} diff --git a/src/test/ui/enum-discriminant/discriminant_value.rs b/src/test/ui/enum-discriminant/discriminant_value.rs index 32d2d40241c82..eb60aaf4b2d04 100644 --- a/src/test/ui/enum-discriminant/discriminant_value.rs +++ b/src/test/ui/enum-discriminant/discriminant_value.rs @@ -51,31 +51,31 @@ enum Mixed { } pub fn main() { - assert_eq!(discriminant_value(&CLike1::A), 0); + assert_eq!(discriminant_value(&CLike1::A), 0isize); assert_eq!(discriminant_value(&CLike1::B), 1); assert_eq!(discriminant_value(&CLike1::C), 2); assert_eq!(discriminant_value(&CLike1::D), 3); - assert_eq!(discriminant_value(&CLike2::A), 5); + assert_eq!(discriminant_value(&CLike2::A), 5isize); assert_eq!(discriminant_value(&CLike2::B), 2); assert_eq!(discriminant_value(&CLike2::C), 19); assert_eq!(discriminant_value(&CLike2::D), 20); - assert_eq!(discriminant_value(&CLike3::A), 5); + assert_eq!(discriminant_value(&CLike3::A), 5i8); assert_eq!(discriminant_value(&CLike3::B), 6); - assert_eq!(discriminant_value(&CLike3::C), -1_i8 as u64); + assert_eq!(discriminant_value(&CLike3::C), -1); assert_eq!(discriminant_value(&CLike3::D), 0); - assert_eq!(discriminant_value(&ADT::First(0,0)), 0); + assert_eq!(discriminant_value(&ADT::First(0,0)), 0isize); assert_eq!(discriminant_value(&ADT::Second(5)), 1); - assert_eq!(discriminant_value(&NullablePointer::Nothing), 1); + assert_eq!(discriminant_value(&NullablePointer::Nothing), 1isize); assert_eq!(discriminant_value(&NullablePointer::Something(&CONST)), 0); - assert_eq!(discriminant_value(&10), 0); - assert_eq!(discriminant_value(&"test"), 0); + assert_eq!(discriminant_value(&10), 0u8); + assert_eq!(discriminant_value(&"test"), 0u8); - assert_eq!(3, discriminant_value(&Mixed::Unit)); - assert_eq!(2, discriminant_value(&Mixed::Tuple(5))); - assert_eq!(1, discriminant_value(&Mixed::Struct{a: 7, b: 11})); + assert_eq!(discriminant_value(&Mixed::Unit), 3isize); + assert_eq!(discriminant_value(&Mixed::Tuple(5)), 2); + assert_eq!(discriminant_value(&Mixed::Struct{a: 7, b: 11}), 1); } diff --git a/src/test/ui/enum-discriminant/forbidden-discriminant-kind-impl.rs b/src/test/ui/enum-discriminant/forbidden-discriminant-kind-impl.rs new file mode 100644 index 0000000000000..4760ca5482a07 --- /dev/null +++ b/src/test/ui/enum-discriminant/forbidden-discriminant-kind-impl.rs @@ -0,0 +1,14 @@ +#![feature(discriminant_kind)] + +use std::marker::DiscriminantKind; + +enum Uninhabited {} + +struct NewType; + +impl DiscriminantKind for NewType { + //~^ ERROR explicit impls for the `DiscriminantKind` trait are not permitted + type Discriminant = Uninhabited; +} + +fn main() {} diff --git a/src/test/ui/enum-discriminant/forbidden-discriminant-kind-impl.stderr b/src/test/ui/enum-discriminant/forbidden-discriminant-kind-impl.stderr new file mode 100644 index 0000000000000..54360c4f47b3e --- /dev/null +++ b/src/test/ui/enum-discriminant/forbidden-discriminant-kind-impl.stderr @@ -0,0 +1,9 @@ +error[E0322]: explicit impls for the `DiscriminantKind` trait are not permitted + --> $DIR/forbidden-discriminant-kind-impl.rs:9:1 + | +LL | impl DiscriminantKind for NewType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of 'DiscriminantKind' not allowed + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0322`. diff --git a/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs index 05911a9a3036e..5a528379b0414 100644 --- a/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs +++ b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs @@ -13,5 +13,5 @@ enum MyWeirdOption { fn main() { assert_eq!(discriminant_value(&MyWeirdOption::<()>::None), 0); - assert_eq!(discriminant_value(&MyWeirdOption::Some(())), core::mem::size_of::() as u64); + assert_eq!(discriminant_value(&MyWeirdOption::Some(())), core::mem::size_of::()); } diff --git a/src/test/ui/enum-discriminant/issue-70509-partial_eq.rs b/src/test/ui/enum-discriminant/issue-70509-partial_eq.rs new file mode 100644 index 0000000000000..e9c6104e3875a --- /dev/null +++ b/src/test/ui/enum-discriminant/issue-70509-partial_eq.rs @@ -0,0 +1,17 @@ +// run-pass +#![feature(repr128, arbitrary_enum_discriminant)] + +#[derive(PartialEq, Debug)] +#[repr(i128)] +enum Test { + A(Box) = 0, + B(usize) = u64::max_value() as i128 + 1, +} + +fn main() { + assert_ne!(Test::A(Box::new(2)), Test::B(0)); + // This previously caused a segfault. + // + // See https://github.com/rust-lang/rust/issues/70509#issuecomment-620654186 + // for a detailed explanation. +} diff --git a/src/test/ui/enum-discriminant/repr128.rs b/src/test/ui/enum-discriminant/repr128.rs new file mode 100644 index 0000000000000..420b6007c6d85 --- /dev/null +++ b/src/test/ui/enum-discriminant/repr128.rs @@ -0,0 +1,44 @@ +// run-pass +#![feature(repr128, core_intrinsics, discriminant_kind)] + +use std::intrinsics::discriminant_value; +use std::marker::DiscriminantKind; + +#[repr(i128)] +enum Signed { + Zero = 0, + Staircase = 0x01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f, + U64Limit = u64::max_value() as i128 + 1, + SmallNegative = -1, + BigNegative = i128::min_value(), + Next, +} + +#[repr(u128)] +enum Unsigned { + Zero = 0, + Staircase = 0x01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f, + U64Limit = u64::max_value() as u128 + 1, + Next, +} + +fn discr(v: T, value: U) +where + ::Discriminant: PartialEq, +{ + assert!(discriminant_value(&v) == value); +} + +fn main() { + discr(Signed::Zero, 0); + discr(Signed::Staircase, 0x01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f); + discr(Signed::U64Limit, u64::max_value() as i128 + 1); + discr(Signed::SmallNegative, -1); + discr(Signed::BigNegative, i128::min_value()); + discr(Signed::Next, i128::min_value() + 1); + + discr(Unsigned::Zero, 0); + discr(Unsigned::Staircase, 0x01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f); + discr(Unsigned::U64Limit, u64::max_value() as u128 + 1); + discr(Unsigned::Next, u64::max_value() as u128 + 2); +} diff --git a/src/test/ui/generator/discriminant.rs b/src/test/ui/generator/discriminant.rs index 8a0f8a380ab59..3d0930da42243 100644 --- a/src/test/ui/generator/discriminant.rs +++ b/src/test/ui/generator/discriminant.rs @@ -3,10 +3,10 @@ // run-pass -#![feature(generators, generator_trait, core_intrinsics)] +#![feature(generators, generator_trait, core_intrinsics, discriminant_kind)] use std::intrinsics::discriminant_value; -use std::marker::Unpin; +use std::marker::{Unpin, DiscriminantKind}; use std::mem::size_of_val; use std::{cmp, ops::*}; @@ -65,7 +65,10 @@ macro_rules! yield250 { }; } -fn cycle(gen: impl Generator<()> + Unpin, expected_max_discr: u64) { +fn cycle( + gen: impl Generator<()> + Unpin + DiscriminantKind, + expected_max_discr: i32 +) { let mut gen = Box::pin(gen); let mut max_discr = 0; loop {