From e2d5489cb6c53b259e002a38dc4d87714e1bc243 Mon Sep 17 00:00:00 2001 From: Luca Barbieri Date: Sat, 20 Jul 2019 02:05:40 +0200 Subject: [PATCH] Impl Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Default for closures --- src/libcore/closure.rs | 127 +++++++++++++++++++++++++++ src/libcore/lib.rs | 2 + src/libcore/marker.rs | 80 +++++++++++++++++ src/librustc/middle/lang_items.rs | 3 + src/librustc/traits/coherence.rs | 7 +- src/librustc/traits/project.rs | 40 ++++++++- src/librustc/traits/select.rs | 48 ++++++++-- src/librustc/ty/layout.rs | 8 ++ src/librustc_typeck/coherence/mod.rs | 10 +++ src/librustc_typeck/error_codes.rs | 5 ++ src/libsyntax/feature_gate.rs | 2 + src/libsyntax_pos/symbol.rs | 2 + 12 files changed, 320 insertions(+), 14 deletions(-) create mode 100644 src/libcore/closure.rs diff --git a/src/libcore/closure.rs b/src/libcore/closure.rs new file mode 100644 index 0000000000000..b7a1026712d7c --- /dev/null +++ b/src/libcore/closure.rs @@ -0,0 +1,127 @@ +//! `Closure` trait for examining closure captured variables, and trait implementations for closures +#![stable(feature = "closure_traits", since = "1.38.0")] + +use crate::mem::{transmute, transmute_copy, forget}; +use crate::fmt::{self, Debug}; +use crate::cmp::Ordering; +use crate::hash::{Hash, Hasher}; + +/// `Closure` is a trait automatically implemented for closures by the compiler +#[stable(feature = "closure_traits", since = "1.38.0")] +#[lang = "closure_trait"] +#[fundamental] +pub unsafe trait Closure: Sized { + /// The tuple that has equivalent layout to this closure + #[stable(feature = "closure_traits", since = "1.38.0")] + type Inner; +} + +/// `ClosureExt` is a trait that allows easier use of `Closure` +#[stable(feature = "closure_traits", since = "1.38.0")] +pub trait ClosureExt: Closure { + /// Get a reference to the tuple that has same layout as this closure + #[stable(feature = "closure_traits", since = "1.38.0")] + fn get(&self) -> &Self::Inner; + + /// Get a mutable reference to the tuple that has the same layout as this closure + #[stable(feature = "closure_traits", since = "1.38.0")] + fn get_mut(&mut self) -> &mut Self::Inner; + + /// Convert self to the tuple that has same layout as this closure + #[stable(feature = "closure_traits", since = "1.38.0")] + fn into_inner(self) -> Self::Inner; + + /// Create a closure from the tuple that has same layout as this closure + #[stable(feature = "closure_traits", since = "1.38.0")] + fn from_inner(x: Self::Inner) -> Self; +} + +#[stable(feature = "closure_traits", since = "1.38.0")] +impl ClosureExt for T { + fn get(&self) -> &Self::Inner { + unsafe {transmute(self)} + } + + fn get_mut(&mut self) -> &mut Self::Inner { + unsafe {transmute(self)} + } + + fn into_inner(self) -> Self::Inner { + let r = unsafe {transmute_copy(&self)}; + forget(self); + r + } + + fn from_inner(x: Self::Inner) -> Self { + let r = unsafe {transmute_copy(&x)}; + forget(x); + r + } +} + +#[stable(feature = "closure_traits", since = "1.38.0")] +impl Debug for T + where ::Inner: Debug { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // the type name is already printed by the Debug impl for LayoutAs + Debug::fmt(self.get(), f) + } +} + +// we allow comparisons between versions of the same closure with different +// type parameters, but not between different closures, thanks to the LayoutAs +// marker that has no heterogeneous PartialOrd implementation + +#[stable(feature = "closure_traits", since = "1.38.0")] +impl PartialEq for A + where ::Inner: PartialEq<::Inner> { + #[inline] + fn eq(&self, other: &B) -> bool { PartialEq::eq(self.get(), other.get()) } + #[inline] + fn ne(&self, other: &B) -> bool { PartialEq::ne(self.get(), other.get()) } +} + +#[stable(feature = "closure_traits", since = "1.38.0")] +impl PartialOrd for A + where ::Inner: PartialOrd<::Inner> { + #[inline] + fn partial_cmp(&self, other: &B) -> Option { + PartialOrd::partial_cmp(self.get(), other.get()) + } + #[inline] + fn lt(&self, other: &B) -> bool { PartialOrd::lt(self.get(), other.get()) } + #[inline] + fn le(&self, other: &B) -> bool { PartialOrd::le(self.get(), other.get()) } + #[inline] + fn ge(&self, other: &B) -> bool { PartialOrd::ge(self.get(), other.get()) } + #[inline] + fn gt(&self, other: &B) -> bool { PartialOrd::gt(self.get(), other.get()) } +} + +#[stable(feature = "closure_traits", since = "1.38.0")] +impl Ord for T + where ::Inner: Ord { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { Ord::cmp(self.get(), other.get()) } +} + +#[stable(feature = "closure_traits", since = "1.38.0")] +impl Eq for T + where ::Inner: Eq { +} + +#[stable(feature = "closure_traits", since = "1.38.0")] +impl Hash for T + where ::Inner: Hash { + fn hash(&self, state: &mut H) { + self.get().hash(state); + } +} + +#[stable(feature = "closure_traits", since = "1.38.0")] +impl Default for T + where ::Inner: Default { + fn default() -> Self { + ::from_inner(Default::default()) + } +} diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 04253858bf704..554effe807582 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -198,6 +198,8 @@ pub mod ascii; pub mod sync; pub mod cell; pub mod char; +#[cfg(not(bootstrap))] +pub mod closure; pub mod panic; pub mod panicking; pub mod pin; diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 39c390b4df6d3..5b8626886de32 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -693,3 +693,83 @@ mod copy_impls { impl Copy for &T {} } + +#[cfg(not(bootstrap))] +mod layout +{ + /// When this is the last element of a tuple, specifies that the tuple + /// should be laid out in memory like the closure with the rest of the + /// tuple types as upvars. Currently for internal use in implementations + /// of the `Closure` trait + #[stable(feature = "closure_traits", since = "1.38.0")] + #[lang = "layout_as"] + pub struct LayoutAs(super::PhantomData); + + use crate::cmp; + use crate::hash::Hash; + use crate::hash::Hasher; + use crate::fmt::{self, Debug, Write}; + use crate::intrinsics::type_name; + + #[stable(feature = "closure_traits", since = "1.38.0")] + impl Hash for LayoutAs { + #[inline] + fn hash(&self, _: &mut H) { + } + } + + // we don't want to implement an heterogeneous PartialOrd/PartialEq + // here since it would allow to compare distinct types that + // happen to have the same sequence of field types + + #[stable(feature = "closure_traits", since = "1.38.0")] + impl cmp::PartialEq for LayoutAs { + fn eq(&self, _other: &LayoutAs) -> bool { + true + } + } + + #[stable(feature = "closure_traits", since = "1.38.0")] + impl cmp::Eq for LayoutAs { + } + + #[stable(feature = "closure_traits", since = "1.38.0")] + impl cmp::PartialOrd for LayoutAs { + fn partial_cmp(&self, _other: &LayoutAs) -> Option { + Option::Some(cmp::Ordering::Equal) + } + } + + #[stable(feature = "closure_traits", since = "1.38.0")] + impl cmp::Ord for LayoutAs { + fn cmp(&self, _other: &LayoutAs) -> cmp::Ordering { + cmp::Ordering::Equal + } + } + + #[stable(feature = "closure_traits", since = "1.38.0")] + impl Copy for LayoutAs { } + + #[stable(feature = "closure_traits", since = "1.38.0")] + impl Clone for LayoutAs { + fn clone(&self) -> LayoutAs { + LayoutAs(super::PhantomData) + } + } + + #[stable(feature = "closure_traits", since = "1.38.0")] + impl Default for LayoutAs { + fn default() -> LayoutAs { + LayoutAs(super::PhantomData) + } + } + + #[stable(feature = "closure_traits", since = "1.38.0")] + impl Debug for LayoutAs { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("LayoutAs<")?; + f.write_str(unsafe {type_name::()})?; + f.write_char('>') + } + } +} diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index bdd48b3447498..7fc349c05f7c4 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -402,6 +402,9 @@ language_item_table! { Arc, "arc", arc, Target::Struct; Rc, "rc", rc, Target::Struct; + + ClosureTraitLangItem, "closure_trait", closure_trait, Target::Trait; + LayoutAsLangitem, "layout_as", layout_as_marker, Target::Struct; } impl<'tcx> TyCtxt<'tcx> { diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs index b6f0addd77107..acd8b67882ca1 100644 --- a/src/librustc/traits/coherence.rs +++ b/src/librustc/traits/coherence.rs @@ -188,7 +188,12 @@ pub fn trait_ref_is_knowable<'tcx>( trait_ref: ty::TraitRef<'tcx>, ) -> Option { debug!("trait_ref_is_knowable(trait_ref={:?})", trait_ref); - if orphan_check_trait_ref(tcx, trait_ref, InCrate::Remote).is_ok() { + + // internal traits that the user is not allowed to implement + let is_internal_sealed_trait = Some(trait_ref.def_id) == tcx.lang_items().closure_trait(); + + if !is_internal_sealed_trait + && orphan_check_trait_ref(tcx, trait_ref, InCrate::Remote).is_ok() { // A downstream or cousin crate is allowed to implement some // substitution of this trait-ref. diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 20acf44340690..029e57d14b8d2 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -12,6 +12,7 @@ use super::SelectionError; use super::{VtableImplData, VtableClosureData, VtableGeneratorData, VtableFnPointerData}; use super::util; +use core::iter; use crate::hir::def_id::DefId; use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -1384,10 +1385,41 @@ fn confirm_closure_candidate<'cx, 'tcx>( closure_sig, obligations); - confirm_callable_candidate(selcx, - obligation, - closure_sig, - util::TupleArgumentsFlag::No) + let trait_ref = obligation.predicate.trait_ref(tcx); + let closure_def_id = tcx.lang_items().closure_trait(); + + let progress = if Some(trait_ref.def_id) == closure_def_id { + // we rely on the fact that closures have the same layout as tuples + // made of their upvars with an () at the end + // the () at the end is because the last field is not reordered + // in tuples since it may be coerced to unsized + if let Some(layout_as_did) = tcx.lang_items().layout_as_marker() { + let marker = tcx.mk_adt(tcx.adt_def(layout_as_did), + tcx.intern_substs(&[obligation.predicate.self_ty().into()])); + let inner_type = tcx.mk_tup(vtable.substs.upvar_tys(vtable.closure_def_id, tcx) + .chain(iter::once(marker))); + + let predicate = ty::Binder::bind(ty::ProjectionPredicate { + projection_ty: ty::ProjectionTy::from_ref_and_name( + tcx, + trait_ref, + Ident::with_empty_ctxt(sym::Inner), + ), + ty: inner_type + }); + + confirm_param_env_candidate(selcx, obligation, predicate) + } else { + Progress::error(tcx) + } + } else { + confirm_callable_candidate(selcx, + obligation, + closure_sig, + util::TupleArgumentsFlag::No) + }; + + progress .with_addl_obligations(vtable.nested) .with_addl_obligations(obligations) } diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 798a25fe7b1bc..1a018e1d13204 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -1748,6 +1748,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates)?; } else if lang_items.unsize_trait() == Some(def_id) { self.assemble_candidates_for_unsizing(obligation, &mut candidates); + } else if lang_items.closure_trait() == Some(def_id) { + self.assemble_closure_trait_candidates(obligation, &mut candidates)?; } else { if lang_items.clone_trait() == Some(def_id) { // Same builtin conditions as `Copy`, i.e., every type which has builtin support @@ -2045,6 +2047,33 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(()) } + /// Checks for the artificial `Closure` trait on closures. + fn assemble_closure_trait_candidates( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + // Okay to skip binder because the substs on closure types never + // touch bound regions, they just capture the in-scope + // type/region parameters + match obligation.self_ty().skip_binder().sty { + ty::Closure(..) => { + debug!( + "assemble_closure_trait_candidates: obligation={:?}", + obligation + ); + candidates.vec.push(ClosureCandidate); + } + ty::Infer(ty::TyVar(_)) => { + debug!("assemble_closure_trait_candidates: ambiguous self-type"); + candidates.ambiguous = true; + } + _ => {} + } + + Ok(()) + } + /// Implement one of the `Fn()` family for a fn pointer. fn assemble_fn_pointer_candidates( &mut self, @@ -3301,8 +3330,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let kind = self.tcx() .lang_items() - .fn_trait_kind(obligation.predicate.def_id()) - .unwrap_or_else(|| bug!("closure candidate for non-fn trait {:?}", obligation)); + .fn_trait_kind(obligation.predicate.def_id()); // Okay to skip binder because the substs on closure types never // touch bound regions, they just capture the in-scope @@ -3337,13 +3365,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { trait_ref, )?); - // FIXME: chalk - if !self.tcx().sess.opts.debugging_opts.chalk { - obligations.push(Obligation::new( - obligation.cause.clone(), - obligation.param_env, - ty::Predicate::ClosureKind(closure_def_id, substs, kind), - )); + if let Some(kind) = kind { + // FIXME: chalk + if !self.tcx().sess.opts.debugging_opts.chalk { + obligations.push(Obligation::new( + obligation.cause.clone(), + obligation.param_env, + ty::Predicate::ClosureKind(closure_def_id, substs, kind), + )); + } } Ok(VtableClosureData { diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 4af26e19b370c..438bafb5d0c07 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -644,6 +644,14 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { } ty::Tuple(tys) => { + // tuples that end with LayoutAs where T is a closure + // and where the previous types are the closure T's upvars + // must be laid out exactly like the closure T + + // this is currently the case without any explicit action, but we'll need + // to explicitly do this if we ever do profile-guided layout or some other + // layout algorithm that doesn't only depend on a closure's upvar types + let kind = if tys.len() == 0 { StructKind::AlwaysSized } else { diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index 1d0e433f07b3a..0fa30f7422c4a 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -66,6 +66,16 @@ fn enforce_trait_manually_implementable(tcx: TyCtxt<'_>, impl_def_id: DefId, tra return; } + if did == li.closure_trait() { + struct_span_err!(tcx.sess, + span, + E0330, + "explicit impls for the `Closure` trait are not permitted") + .span_label(span, "impl of `Closure` not allowed") + .emit(); + return; + } + if tcx.features().unboxed_closures { // the feature gate allows all Fn traits return; diff --git a/src/librustc_typeck/error_codes.rs b/src/librustc_typeck/error_codes.rs index 19d5e8b3e8447..ae750d8e06349 100644 --- a/src/librustc_typeck/error_codes.rs +++ b/src/librustc_typeck/error_codes.rs @@ -2709,6 +2709,11 @@ fn get_bar_good() -> f64 { "##, */ +E0330: r##" +The Closure trait should not be implemented directly. All implementations of +Closure are provided automatically by the compiler. +"##, + E0366: r##" An attempt was made to implement `Drop` on a concrete specialization of a generic type. An example is shown below: diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index e3628d908fb1e..5aeb91fc0fd84 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -866,6 +866,8 @@ declare_features! ( (accepted, repr_align_enum, "1.37.0", Some(57996), None), // Allows `const _: TYPE = VALUE`. (accepted, underscore_const_names, "1.37.0", Some(54912), None), + // Implements Debug, Hash, PartialEq, Eq, PartialOrd, Ord for closures + (accepted, closure_traits, "1.38.0", None, None), // ------------------------------------------------------------------------- // feature-group-end: accepted features diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index a983180ac01e1..110917672cf13 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -183,6 +183,7 @@ symbols! { clone_closures, clone_from, closure_to_fn_coercion, + closure_traits, cmp, cmpxchg16b_target_feature, cold, @@ -353,6 +354,7 @@ symbols! { infer_outlives_requirements, infer_static_outlives_requirements, inline, + Inner, intel, into_iter, IntoIterator,