Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Impl Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Default for closures #62815

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions src/libcore/closure.rs
Original file line number Diff line number Diff line change
@@ -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<T: Closure> 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<T: Closure> Debug for T
where <T as Closure>::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<T>
// marker that has no heterogeneous PartialOrd implementation

#[stable(feature = "closure_traits", since = "1.38.0")]
impl<A: Closure, B: Closure> PartialEq<B> for A
where <A as Closure>::Inner: PartialEq<<B as Closure>::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<A: Closure, B: Closure> PartialOrd<B> for A
where <A as Closure>::Inner: PartialOrd<<B as Closure>::Inner> {
#[inline]
fn partial_cmp(&self, other: &B) -> Option<Ordering> {
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<T: Closure> Ord for T
where <T as Closure>::Inner: Ord {
#[inline]
fn cmp(&self, other: &Self) -> Ordering { Ord::cmp(self.get(), other.get()) }
}

#[stable(feature = "closure_traits", since = "1.38.0")]
impl<T: Closure> Eq for T
where <T as Closure>::Inner: Eq {
}

#[stable(feature = "closure_traits", since = "1.38.0")]
impl<T: Closure> Hash for T
where <T as Closure>::Inner: Hash {
fn hash<H: Hasher>(&self, state: &mut H) {
self.get().hash(state);
}
}

#[stable(feature = "closure_traits", since = "1.38.0")]
impl<T: Closure> Default for T
where <T as Closure>::Inner: Default {
fn default() -> Self {
<T as ClosureExt>::from_inner(Default::default())
}
}
2 changes: 2 additions & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
80 changes: 80 additions & 0 deletions src/libcore/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -693,3 +693,83 @@ mod copy_impls {
impl<T: ?Sized> 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<T: ?Sized>(super::PhantomData<T>);

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<T:?Sized> Hash for LayoutAs<T> {
#[inline]
fn hash<H: Hasher>(&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<T:?Sized> cmp::PartialEq for LayoutAs<T> {
fn eq(&self, _other: &LayoutAs<T>) -> bool {
true
}
}

#[stable(feature = "closure_traits", since = "1.38.0")]
impl<T:?Sized> cmp::Eq for LayoutAs<T> {
}

#[stable(feature = "closure_traits", since = "1.38.0")]
impl<T:?Sized> cmp::PartialOrd for LayoutAs<T> {
fn partial_cmp(&self, _other: &LayoutAs<T>) -> Option<cmp::Ordering> {
Option::Some(cmp::Ordering::Equal)
}
}

#[stable(feature = "closure_traits", since = "1.38.0")]
impl<T:?Sized> cmp::Ord for LayoutAs<T> {
fn cmp(&self, _other: &LayoutAs<T>) -> cmp::Ordering {
cmp::Ordering::Equal
}
}

#[stable(feature = "closure_traits", since = "1.38.0")]
impl<T:?Sized> Copy for LayoutAs<T> { }

#[stable(feature = "closure_traits", since = "1.38.0")]
impl<T:?Sized> Clone for LayoutAs<T> {
fn clone(&self) -> LayoutAs<T> {
LayoutAs(super::PhantomData)
}
}

#[stable(feature = "closure_traits", since = "1.38.0")]
impl<T:?Sized> Default for LayoutAs<T> {
fn default() -> LayoutAs<T> {
LayoutAs(super::PhantomData)
}
}

#[stable(feature = "closure_traits", since = "1.38.0")]
impl<T: ?Sized> Debug for LayoutAs<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("LayoutAs<")?;
f.write_str(unsafe {type_name::<T>()})?;
f.write_char('>')
}
}
}
3 changes: 3 additions & 0 deletions src/librustc/middle/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
7 changes: 6 additions & 1 deletion src/librustc/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,12 @@ pub fn trait_ref_is_knowable<'tcx>(
trait_ref: ty::TraitRef<'tcx>,
) -> Option<Conflict> {
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.

Expand Down
40 changes: 36 additions & 4 deletions src/librustc/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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)
}
Expand Down
48 changes: 39 additions & 9 deletions src/librustc/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
8 changes: 8 additions & 0 deletions src/librustc/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,14 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
}

ty::Tuple(tys) => {
// tuples that end with LayoutAs<T> 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 {
Expand Down
Loading