Auto merge of #70705 - lcnr:generic_discriminant, r=nikomatsakis

Use `T`'s discriminant type in `mem::Discriminant<T>` instead of `u64`.

fixes #70509

Adds the lang-item `discriminant_kind`.
Updates the function signature of `intrinsics::discriminant_value`.
Adds the *probably permanently unstable* trait `DiscriminantKind`.
`mem::Discriminant` should now be smaller in some cases.

r? @ghost
This commit is contained in:
bors 2020-05-21 03:48:47 +00:00
commit 963bf52829
27 changed files with 450 additions and 139 deletions

View File

@ -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`

View File

@ -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<T>(v: &T) -> <T as DiscriminantKind>::Discriminant;
#[rustc_const_unstable(feature = "const_discriminant", issue = "69821")]
#[cfg(bootstrap)]
pub fn discriminant_value<T>(v: &T) -> u64;
/// Rust's "try catch" construct which invokes the function pointer `try_fn`

View File

@ -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<T: Send + ?Sized> 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<T: ?Sized> 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

View File

@ -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<T, U>(src: &T) -> U {
///
/// [`discriminant`]: fn.discriminant.html
#[stable(feature = "discriminant_value", since = "1.21.0")]
pub struct Discriminant<T>(u64, PhantomData<fn() -> T>);
pub struct Discriminant<T>(<T as DiscriminantKind>::Discriminant);
// N.B. These trait implementations cannot be derived because we don't want any bounds on T.
@ -995,5 +995,5 @@ impl<T> fmt::Debug for Discriminant<T> {
#[stable(feature = "discriminant_value", since = "1.21.0")]
#[rustc_const_unstable(feature = "const_discriminant", issue = "69821")]
pub const fn discriminant<T>(v: &T) -> Discriminant<T> {
Discriminant(intrinsics::discriminant_value(v), PhantomData)
Discriminant(intrinsics::discriminant_value(v))
}

View File

@ -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<P<Expr>>,
nonself_args: &[P<Expr>],
) -> P<Expr> {
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<P<Expr>>,
nonself_args: &[P<Expr>],
@ -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<T> { 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<ast::Stmt> = 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 {

View File

@ -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) {

View File

@ -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;

View File

@ -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)]

View File

@ -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<N>,
}
// 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,

View File

@ -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<T: fmt::Debug> AsDebug for T {
/// type Out = T;
/// fn debug(self) -> fmt::Debug { self }
/// }
/// fn foo<T: AsDebug>(t: T) { println!("{:?}", <T as AsDebug>::debug(t)); }
/// ```rust, ignore
/// trait AsDebug { type Out: fmt::Debug; fn debug(self) -> Self::Out; }
/// impl<T: fmt::Debug> AsDebug for T {
/// type Out = T;
/// fn debug(self) -> fmt::Debug { self }
/// }
/// fn foo<T: AsDebug>(t: T) { println!("{:?}", <T as AsDebug>::debug(t)); }
/// ```
///
/// we can't just use the impl to resolve the `<T as AsDebug>` 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<T> { fn foo(&self) -> T; }
/// impl<T> Foo<()> for T { fn foo(&self) { } }
/// impl Foo<bool> for bool { fn foo(&self) -> bool { *self } }
/// ```rust, ignore
/// pub trait Foo<T> { fn foo(&self) -> T; }
/// impl<T> Foo<()> for T { fn foo(&self) { } }
/// impl Foo<bool> for bool { fn foo(&self) -> bool { *self } }
///
/// pub fn foo<T>(t: T) where T: Foo<bool> {
/// println!("{:?}", <T as Foo<_>>::foo(&t));
/// }
/// fn main() { foo(false); }
/// pub fn foo<T>(t: T) where T: Foo<bool> {
/// println!("{:?}", <T as Foo<_>>::foo(&t));
/// }
/// fn main() { foo(false); }
/// ```
///
/// Here the obligation `<T as Foo<$0>>` 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,

View File

@ -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 {

View File

@ -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, usize>,
T: EncodableWithShorthand,
<T::Variant as DiscriminantKind>::Discriminant: Ord + TryFrom<usize>,
{
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

View File

@ -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

View File

@ -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>,

View File

@ -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))

View File

@ -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,
})
}

View File

@ -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);

View File

@ -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!(

View File

@ -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<T>()
where
for<'a> T: Fn(&'a isize),
{
let v: Vec<T> = 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)));
}

View File

@ -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]);
}

View File

@ -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);
}

View File

@ -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() {}

View File

@ -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`.

View File

@ -13,5 +13,5 @@ enum MyWeirdOption<T> {
fn main() {
assert_eq!(discriminant_value(&MyWeirdOption::<()>::None), 0);
assert_eq!(discriminant_value(&MyWeirdOption::Some(())), core::mem::size_of::<usize>() as u64);
assert_eq!(discriminant_value(&MyWeirdOption::Some(())), core::mem::size_of::<usize>());
}

View File

@ -0,0 +1,17 @@
// run-pass
#![feature(repr128, arbitrary_enum_discriminant)]
#[derive(PartialEq, Debug)]
#[repr(i128)]
enum Test {
A(Box<u64>) = 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.
}

View File

@ -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<T, U>(v: T, value: U)
where
<T as DiscriminantKind>::Discriminant: PartialEq<U>,
{
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);
}

View File

@ -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<Discriminant = i32>,
expected_max_discr: i32
) {
let mut gen = Box::pin(gen);
let mut max_discr = 0;
loop {