diff --git a/src/librustc_middle/traits/mod.rs b/src/librustc_middle/traits/mod.rs index 1254174a7a5f9..cb23054d8710e 100644 --- a/src/librustc_middle/traits/mod.rs +++ b/src/librustc_middle/traits/mod.rs @@ -409,6 +409,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>), @@ -427,6 +430,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, } } @@ -441,6 +445,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[..], } } @@ -482,6 +487,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, @@ -558,6 +566,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 822fbfa8ca669..a12f5910b14b9 100644 --- a/src/librustc_middle/traits/select.rs +++ b/src/librustc_middle/traits/select.rs @@ -132,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 668c84ad5e6df..c457d311b4093 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), @@ -273,6 +275,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_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/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!(