Skip to content

Commit e2d5489

Browse files
committed
Impl Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Default for closures
1 parent a9c7feb commit e2d5489

File tree

12 files changed

+320
-14
lines changed

12 files changed

+320
-14
lines changed

src/libcore/closure.rs

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
//! `Closure` trait for examining closure captured variables, and trait implementations for closures
2+
#![stable(feature = "closure_traits", since = "1.38.0")]
3+
4+
use crate::mem::{transmute, transmute_copy, forget};
5+
use crate::fmt::{self, Debug};
6+
use crate::cmp::Ordering;
7+
use crate::hash::{Hash, Hasher};
8+
9+
/// `Closure` is a trait automatically implemented for closures by the compiler
10+
#[stable(feature = "closure_traits", since = "1.38.0")]
11+
#[lang = "closure_trait"]
12+
#[fundamental]
13+
pub unsafe trait Closure: Sized {
14+
/// The tuple that has equivalent layout to this closure
15+
#[stable(feature = "closure_traits", since = "1.38.0")]
16+
type Inner;
17+
}
18+
19+
/// `ClosureExt` is a trait that allows easier use of `Closure`
20+
#[stable(feature = "closure_traits", since = "1.38.0")]
21+
pub trait ClosureExt: Closure {
22+
/// Get a reference to the tuple that has same layout as this closure
23+
#[stable(feature = "closure_traits", since = "1.38.0")]
24+
fn get(&self) -> &Self::Inner;
25+
26+
/// Get a mutable reference to the tuple that has the same layout as this closure
27+
#[stable(feature = "closure_traits", since = "1.38.0")]
28+
fn get_mut(&mut self) -> &mut Self::Inner;
29+
30+
/// Convert self to the tuple that has same layout as this closure
31+
#[stable(feature = "closure_traits", since = "1.38.0")]
32+
fn into_inner(self) -> Self::Inner;
33+
34+
/// Create a closure from the tuple that has same layout as this closure
35+
#[stable(feature = "closure_traits", since = "1.38.0")]
36+
fn from_inner(x: Self::Inner) -> Self;
37+
}
38+
39+
#[stable(feature = "closure_traits", since = "1.38.0")]
40+
impl<T: Closure> ClosureExt for T {
41+
fn get(&self) -> &Self::Inner {
42+
unsafe {transmute(self)}
43+
}
44+
45+
fn get_mut(&mut self) -> &mut Self::Inner {
46+
unsafe {transmute(self)}
47+
}
48+
49+
fn into_inner(self) -> Self::Inner {
50+
let r = unsafe {transmute_copy(&self)};
51+
forget(self);
52+
r
53+
}
54+
55+
fn from_inner(x: Self::Inner) -> Self {
56+
let r = unsafe {transmute_copy(&x)};
57+
forget(x);
58+
r
59+
}
60+
}
61+
62+
#[stable(feature = "closure_traits", since = "1.38.0")]
63+
impl<T: Closure> Debug for T
64+
where <T as Closure>::Inner: Debug {
65+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66+
// the type name is already printed by the Debug impl for LayoutAs
67+
Debug::fmt(self.get(), f)
68+
}
69+
}
70+
71+
// we allow comparisons between versions of the same closure with different
72+
// type parameters, but not between different closures, thanks to the LayoutAs<T>
73+
// marker that has no heterogeneous PartialOrd implementation
74+
75+
#[stable(feature = "closure_traits", since = "1.38.0")]
76+
impl<A: Closure, B: Closure> PartialEq<B> for A
77+
where <A as Closure>::Inner: PartialEq<<B as Closure>::Inner> {
78+
#[inline]
79+
fn eq(&self, other: &B) -> bool { PartialEq::eq(self.get(), other.get()) }
80+
#[inline]
81+
fn ne(&self, other: &B) -> bool { PartialEq::ne(self.get(), other.get()) }
82+
}
83+
84+
#[stable(feature = "closure_traits", since = "1.38.0")]
85+
impl<A: Closure, B: Closure> PartialOrd<B> for A
86+
where <A as Closure>::Inner: PartialOrd<<B as Closure>::Inner> {
87+
#[inline]
88+
fn partial_cmp(&self, other: &B) -> Option<Ordering> {
89+
PartialOrd::partial_cmp(self.get(), other.get())
90+
}
91+
#[inline]
92+
fn lt(&self, other: &B) -> bool { PartialOrd::lt(self.get(), other.get()) }
93+
#[inline]
94+
fn le(&self, other: &B) -> bool { PartialOrd::le(self.get(), other.get()) }
95+
#[inline]
96+
fn ge(&self, other: &B) -> bool { PartialOrd::ge(self.get(), other.get()) }
97+
#[inline]
98+
fn gt(&self, other: &B) -> bool { PartialOrd::gt(self.get(), other.get()) }
99+
}
100+
101+
#[stable(feature = "closure_traits", since = "1.38.0")]
102+
impl<T: Closure> Ord for T
103+
where <T as Closure>::Inner: Ord {
104+
#[inline]
105+
fn cmp(&self, other: &Self) -> Ordering { Ord::cmp(self.get(), other.get()) }
106+
}
107+
108+
#[stable(feature = "closure_traits", since = "1.38.0")]
109+
impl<T: Closure> Eq for T
110+
where <T as Closure>::Inner: Eq {
111+
}
112+
113+
#[stable(feature = "closure_traits", since = "1.38.0")]
114+
impl<T: Closure> Hash for T
115+
where <T as Closure>::Inner: Hash {
116+
fn hash<H: Hasher>(&self, state: &mut H) {
117+
self.get().hash(state);
118+
}
119+
}
120+
121+
#[stable(feature = "closure_traits", since = "1.38.0")]
122+
impl<T: Closure> Default for T
123+
where <T as Closure>::Inner: Default {
124+
fn default() -> Self {
125+
<T as ClosureExt>::from_inner(Default::default())
126+
}
127+
}

src/libcore/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ pub mod ascii;
198198
pub mod sync;
199199
pub mod cell;
200200
pub mod char;
201+
#[cfg(not(bootstrap))]
202+
pub mod closure;
201203
pub mod panic;
202204
pub mod panicking;
203205
pub mod pin;

src/libcore/marker.rs

+80
Original file line numberDiff line numberDiff line change
@@ -693,3 +693,83 @@ mod copy_impls {
693693
impl<T: ?Sized> Copy for &T {}
694694

695695
}
696+
697+
#[cfg(not(bootstrap))]
698+
mod layout
699+
{
700+
/// When this is the last element of a tuple, specifies that the tuple
701+
/// should be laid out in memory like the closure with the rest of the
702+
/// tuple types as upvars. Currently for internal use in implementations
703+
/// of the `Closure` trait
704+
#[stable(feature = "closure_traits", since = "1.38.0")]
705+
#[lang = "layout_as"]
706+
pub struct LayoutAs<T: ?Sized>(super::PhantomData<T>);
707+
708+
use crate::cmp;
709+
use crate::hash::Hash;
710+
use crate::hash::Hasher;
711+
use crate::fmt::{self, Debug, Write};
712+
use crate::intrinsics::type_name;
713+
714+
#[stable(feature = "closure_traits", since = "1.38.0")]
715+
impl<T:?Sized> Hash for LayoutAs<T> {
716+
#[inline]
717+
fn hash<H: Hasher>(&self, _: &mut H) {
718+
}
719+
}
720+
721+
// we don't want to implement an heterogeneous PartialOrd/PartialEq
722+
// here since it would allow to compare distinct types that
723+
// happen to have the same sequence of field types
724+
725+
#[stable(feature = "closure_traits", since = "1.38.0")]
726+
impl<T:?Sized> cmp::PartialEq for LayoutAs<T> {
727+
fn eq(&self, _other: &LayoutAs<T>) -> bool {
728+
true
729+
}
730+
}
731+
732+
#[stable(feature = "closure_traits", since = "1.38.0")]
733+
impl<T:?Sized> cmp::Eq for LayoutAs<T> {
734+
}
735+
736+
#[stable(feature = "closure_traits", since = "1.38.0")]
737+
impl<T:?Sized> cmp::PartialOrd for LayoutAs<T> {
738+
fn partial_cmp(&self, _other: &LayoutAs<T>) -> Option<cmp::Ordering> {
739+
Option::Some(cmp::Ordering::Equal)
740+
}
741+
}
742+
743+
#[stable(feature = "closure_traits", since = "1.38.0")]
744+
impl<T:?Sized> cmp::Ord for LayoutAs<T> {
745+
fn cmp(&self, _other: &LayoutAs<T>) -> cmp::Ordering {
746+
cmp::Ordering::Equal
747+
}
748+
}
749+
750+
#[stable(feature = "closure_traits", since = "1.38.0")]
751+
impl<T:?Sized> Copy for LayoutAs<T> { }
752+
753+
#[stable(feature = "closure_traits", since = "1.38.0")]
754+
impl<T:?Sized> Clone for LayoutAs<T> {
755+
fn clone(&self) -> LayoutAs<T> {
756+
LayoutAs(super::PhantomData)
757+
}
758+
}
759+
760+
#[stable(feature = "closure_traits", since = "1.38.0")]
761+
impl<T:?Sized> Default for LayoutAs<T> {
762+
fn default() -> LayoutAs<T> {
763+
LayoutAs(super::PhantomData)
764+
}
765+
}
766+
767+
#[stable(feature = "closure_traits", since = "1.38.0")]
768+
impl<T: ?Sized> Debug for LayoutAs<T> {
769+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
770+
f.write_str("LayoutAs<")?;
771+
f.write_str(unsafe {type_name::<T>()})?;
772+
f.write_char('>')
773+
}
774+
}
775+
}

src/librustc/middle/lang_items.rs

+3
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,9 @@ language_item_table! {
402402

403403
Arc, "arc", arc, Target::Struct;
404404
Rc, "rc", rc, Target::Struct;
405+
406+
ClosureTraitLangItem, "closure_trait", closure_trait, Target::Trait;
407+
LayoutAsLangitem, "layout_as", layout_as_marker, Target::Struct;
405408
}
406409

407410
impl<'tcx> TyCtxt<'tcx> {

src/librustc/traits/coherence.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,12 @@ pub fn trait_ref_is_knowable<'tcx>(
188188
trait_ref: ty::TraitRef<'tcx>,
189189
) -> Option<Conflict> {
190190
debug!("trait_ref_is_knowable(trait_ref={:?})", trait_ref);
191-
if orphan_check_trait_ref(tcx, trait_ref, InCrate::Remote).is_ok() {
191+
192+
// internal traits that the user is not allowed to implement
193+
let is_internal_sealed_trait = Some(trait_ref.def_id) == tcx.lang_items().closure_trait();
194+
195+
if !is_internal_sealed_trait
196+
&& orphan_check_trait_ref(tcx, trait_ref, InCrate::Remote).is_ok() {
192197
// A downstream or cousin crate is allowed to implement some
193198
// substitution of this trait-ref.
194199

src/librustc/traits/project.rs

+36-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use super::SelectionError;
1212
use super::{VtableImplData, VtableClosureData, VtableGeneratorData, VtableFnPointerData};
1313
use super::util;
1414

15+
use core::iter;
1516
use crate::hir::def_id::DefId;
1617
use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
1718
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@@ -1384,10 +1385,41 @@ fn confirm_closure_candidate<'cx, 'tcx>(
13841385
closure_sig,
13851386
obligations);
13861387

1387-
confirm_callable_candidate(selcx,
1388-
obligation,
1389-
closure_sig,
1390-
util::TupleArgumentsFlag::No)
1388+
let trait_ref = obligation.predicate.trait_ref(tcx);
1389+
let closure_def_id = tcx.lang_items().closure_trait();
1390+
1391+
let progress = if Some(trait_ref.def_id) == closure_def_id {
1392+
// we rely on the fact that closures have the same layout as tuples
1393+
// made of their upvars with an () at the end
1394+
// the () at the end is because the last field is not reordered
1395+
// in tuples since it may be coerced to unsized
1396+
if let Some(layout_as_did) = tcx.lang_items().layout_as_marker() {
1397+
let marker = tcx.mk_adt(tcx.adt_def(layout_as_did),
1398+
tcx.intern_substs(&[obligation.predicate.self_ty().into()]));
1399+
let inner_type = tcx.mk_tup(vtable.substs.upvar_tys(vtable.closure_def_id, tcx)
1400+
.chain(iter::once(marker)));
1401+
1402+
let predicate = ty::Binder::bind(ty::ProjectionPredicate {
1403+
projection_ty: ty::ProjectionTy::from_ref_and_name(
1404+
tcx,
1405+
trait_ref,
1406+
Ident::with_empty_ctxt(sym::Inner),
1407+
),
1408+
ty: inner_type
1409+
});
1410+
1411+
confirm_param_env_candidate(selcx, obligation, predicate)
1412+
} else {
1413+
Progress::error(tcx)
1414+
}
1415+
} else {
1416+
confirm_callable_candidate(selcx,
1417+
obligation,
1418+
closure_sig,
1419+
util::TupleArgumentsFlag::No)
1420+
};
1421+
1422+
progress
13911423
.with_addl_obligations(vtable.nested)
13921424
.with_addl_obligations(obligations)
13931425
}

src/librustc/traits/select.rs

+39-9
Original file line numberDiff line numberDiff line change
@@ -1748,6 +1748,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
17481748
self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates)?;
17491749
} else if lang_items.unsize_trait() == Some(def_id) {
17501750
self.assemble_candidates_for_unsizing(obligation, &mut candidates);
1751+
} else if lang_items.closure_trait() == Some(def_id) {
1752+
self.assemble_closure_trait_candidates(obligation, &mut candidates)?;
17511753
} else {
17521754
if lang_items.clone_trait() == Some(def_id) {
17531755
// Same builtin conditions as `Copy`, i.e., every type which has builtin support
@@ -2045,6 +2047,33 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
20452047
Ok(())
20462048
}
20472049

2050+
/// Checks for the artificial `Closure` trait on closures.
2051+
fn assemble_closure_trait_candidates(
2052+
&mut self,
2053+
obligation: &TraitObligation<'tcx>,
2054+
candidates: &mut SelectionCandidateSet<'tcx>,
2055+
) -> Result<(), SelectionError<'tcx>> {
2056+
// Okay to skip binder because the substs on closure types never
2057+
// touch bound regions, they just capture the in-scope
2058+
// type/region parameters
2059+
match obligation.self_ty().skip_binder().sty {
2060+
ty::Closure(..) => {
2061+
debug!(
2062+
"assemble_closure_trait_candidates: obligation={:?}",
2063+
obligation
2064+
);
2065+
candidates.vec.push(ClosureCandidate);
2066+
}
2067+
ty::Infer(ty::TyVar(_)) => {
2068+
debug!("assemble_closure_trait_candidates: ambiguous self-type");
2069+
candidates.ambiguous = true;
2070+
}
2071+
_ => {}
2072+
}
2073+
2074+
Ok(())
2075+
}
2076+
20482077
/// Implement one of the `Fn()` family for a fn pointer.
20492078
fn assemble_fn_pointer_candidates(
20502079
&mut self,
@@ -3301,8 +3330,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
33013330

33023331
let kind = self.tcx()
33033332
.lang_items()
3304-
.fn_trait_kind(obligation.predicate.def_id())
3305-
.unwrap_or_else(|| bug!("closure candidate for non-fn trait {:?}", obligation));
3333+
.fn_trait_kind(obligation.predicate.def_id());
33063334

33073335
// Okay to skip binder because the substs on closure types never
33083336
// touch bound regions, they just capture the in-scope
@@ -3337,13 +3365,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
33373365
trait_ref,
33383366
)?);
33393367

3340-
// FIXME: chalk
3341-
if !self.tcx().sess.opts.debugging_opts.chalk {
3342-
obligations.push(Obligation::new(
3343-
obligation.cause.clone(),
3344-
obligation.param_env,
3345-
ty::Predicate::ClosureKind(closure_def_id, substs, kind),
3346-
));
3368+
if let Some(kind) = kind {
3369+
// FIXME: chalk
3370+
if !self.tcx().sess.opts.debugging_opts.chalk {
3371+
obligations.push(Obligation::new(
3372+
obligation.cause.clone(),
3373+
obligation.param_env,
3374+
ty::Predicate::ClosureKind(closure_def_id, substs, kind),
3375+
));
3376+
}
33473377
}
33483378

33493379
Ok(VtableClosureData {

src/librustc/ty/layout.rs

+8
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,14 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
644644
}
645645

646646
ty::Tuple(tys) => {
647+
// tuples that end with LayoutAs<T> where T is a closure
648+
// and where the previous types are the closure T's upvars
649+
// must be laid out exactly like the closure T
650+
651+
// this is currently the case without any explicit action, but we'll need
652+
// to explicitly do this if we ever do profile-guided layout or some other
653+
// layout algorithm that doesn't only depend on a closure's upvar types
654+
647655
let kind = if tys.len() == 0 {
648656
StructKind::AlwaysSized
649657
} else {

0 commit comments

Comments
 (0)