-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Reflect trait #23712
Reflect trait #23712
Conversation
r? @pnkfelix (rust_highfive has picked a reviewer for you, use r? to override) |
@@ -82,20 +82,61 @@ use marker::Sized; | |||
// Any trait | |||
/////////////////////////////////////////////////////////////////////////////// | |||
|
|||
/// A marker trait indicates a type that can be reflected over. This | |||
/// trait is implemented for all types. It's purpose is to ensure that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/It's/Its/
r? @flaper87 I'm not sure how is best to review this to be honest. @flaper87 knows the code well, so he's a good start, but I'd like @aturon and @glaebhoerl to look over the logic (and anyone else who would like to do so). The basic idea is that you can say that |
Does this also need to add the |
Since we want to stabilize |
/// [1]: http://en.wikipedia.org/wiki/Parametricity | ||
#[rustc_reflect_like] | ||
#[stable(feature = "rust1", since = "1.0.0")] | ||
pub trait Reflect: 'static { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this not need some sort of marker bound to make Self
used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, because where Self: 'static
is itself a use of Self
.
Ah, yes. Thanks. |
Note: make check revealed some minor bugs, I'll mop those up tomorrow. |
} | ||
|
||
#[cfg(stage0)] | ||
impl<T: 'static> Reflect for T { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh mmh, I thought we already had a snapshot for defaulted traits.
Oh, yay! I didn't think there was still hope for improving this. :) Just to be explicit about it, the solution I had been envisioning was that we would have something like:
and for every type that is declared, an
and so on. (This gets hairier with HKTs, because then you need either Three inter-connected thoughts I don't know how to paragraphise together:
In connection to the above points GHC's new All of that throat-clearing out of the way: I'm not really familiar with OIBIT so I'm not sure to what extent it matches up with the above, but I suspect that it probably does. With respect to traits, just to make sure I'm understanding correctly, the idea is that given a non-trait type viewed as a trait object, If that's the case, then I still have to think harder about it to be sure :), but the proposed rules feel like they probably have the right idea. |
Another thing that's occurred to me is that we could potentially generalize the above scheme to lift the
(This might need the auto-generated |
8846f0d
to
286bc22
Compare
OK, this passes make check now. I had to cleanup some of the binder logic, there's still some questionable bits (I left a FIXME...) but good enough for now I guess. |
semantics that tests the *interface* of trait objects, rather than what they close over.
286bc22
to
710af04
Compare
I also significantly revised the design of the trait. The Reflect trait is now basically a marker trait you can use to tag other reflection mechanisms, but not interesting to users on its own. Even better, it can be marked unstable. This means that functions that wish to use reflections over generic types should bound those types with The reason for this change (among other things):
|
I'm quite happy with how this turned out! r=me from the library side at least |
This PR introduces a `Reflect` marker trait which is a supertrait of `Any`. The idea is that `Reflect` is defined for all concrete types, but is not defined for type parameters unless there is a `T:Reflect` bound. This is intended to preserve the parametricity property. This allows the `Any` interface to be stabilized without committing us to unbounded reflection that is not easily detectable by the caller. The implementation of `Reflect` relies on an experimental variant of OIBIT. This variant behaves differently for objects, since it requires that all types exposed as part of the object's *interface* are `Reflect`, but isn't concerned about other types that may be closed over. In other words, you don't have to write `Foo+Reflect` in order for `Foo: Reflect` to hold (where `Foo` is a trait). Given that `Any` is slated to stabilization and hence that we are committed to some form of reflection, the goal of this PR is to leave our options open with respect to parametricity. I see the options for full stabilization as follows (I think an RFC would be an appropriate way to confirm whichever of these three routes we take): 1. We make `Reflect` a lang-item. 2. We stabilize some version of the OIBIT variation I implemented as a general mechanism that may be appropriate for other use cases. 3. We give up on preserving parametricity here and just have `impl<T> Reflect for T` instead. In that case, `Reflect` is a harmless but not especially useful trait going forward. cc @aturon cc @alexcrichton cc @glaebhoerl (this is more-or-less your proposal, as I understood it) cc @reem (this is more-or-less what we discussed on IRC at some point) cc @flaper87 (vaguely pertains to OIBIT)
what is implemented matches what you wrote fairly closely, but with the exception that it is automatically lifted over structural types and so forth. In terms of objects, there is (currently) no mechanism for testing what type an object has, but yes if you wanted to do that you'd presumably need something like |
This PR introduces a `Reflect` marker trait which is a supertrait of `Any`. The idea is that `Reflect` is defined for all concrete types, but is not defined for type parameters unless there is a `T:Reflect` bound. This is intended to preserve the parametricity property. This allows the `Any` interface to be stabilized without committing us to unbounded reflection that is not easily detectable by the caller. The implementation of `Reflect` relies on an experimental variant of OIBIT. This variant behaves differently for objects, since it requires that all types exposed as part of the object's *interface* are `Reflect`, but isn't concerned about other types that may be closed over. In other words, you don't have to write `Foo+Reflect` in order for `Foo: Reflect` to hold (where `Foo` is a trait). Given that `Any` is slated to stabilization and hence that we are committed to some form of reflection, the goal of this PR is to leave our options open with respect to parametricity. I see the options for full stabilization as follows (I think an RFC would be an appropriate way to confirm whichever of these three routes we take): 1. We make `Reflect` a lang-item. 2. We stabilize some version of the OIBIT variation I implemented as a general mechanism that may be appropriate for other use cases. 3. We give up on preserving parametricity here and just have `impl<T> Reflect for T` instead. In that case, `Reflect` is a harmless but not especially useful trait going forward. cc @aturon cc @alexcrichton cc @glaebhoerl (this is more-or-less your proposal, as I understood it) cc @reem (this is more-or-less what we discussed on IRC at some point) cc @flaper87 (vaguely pertains to OIBIT)
/// [1]: http://en.wikipedia.org/wiki/Parametricity | ||
#[rustc_reflect_like] | ||
#[unstable(feature = "core", reason = "requires RFC and more experience")] | ||
pub trait Reflect : MarkerTrait { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is totally tangential and has probably been raised before, but isn't this saying trait Reflect where Self: MarkerTrait
, whereas what we might actually want (and what might actually make sense) would be impl MarkerTrait for Reflect
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This exists solely because Self
and type params have to be used by methods or supertraits.
(Is there no interest in lifting the |
@glaebhoerl it's impossible (the set of possible lifetime instantiations is almost always unbounded at compile time), unless you have some scheme in mind for reflecting lifetimes. |
@eddyb I was thinking that |
@glaebhoerl interesting thought; also seems like a possible future extension if we permitted defaulted lifetime parameters. |
I believe the [new] std::marker::[Reflect] trait made it necessary to specify a Reflect bound, but it's advised to use an Any bound instead. Adding this bound makes it compile. I ran the tests and got an error saying it couldn't determine the type of one of the expressions so I introduced a scope to bind the result of the expression and annotate its type. I also removed the `convert` feature that was no longer needed. [new]: rust-lang/rust#23712 [Reflect]: http://doc.rust-lang.org/std/marker/trait.Reflect.html
This PR introduces a
Reflect
marker trait which is a supertrait ofAny
. The idea is thatReflect
is defined for all concrete types, but is not defined for type parameters unless there is aT:Reflect
bound. This is intended to preserve the parametricity property. This allows theAny
interface to be stabilized without committing us to unbounded reflection that is not easily detectable by the caller.The implementation of
Reflect
relies on an experimental variant of OIBIT. This variant behaves differently for objects, since it requires that all types exposed as part of the object's interface areReflect
, but isn't concerned about other types that may be closed over. In other words, you don't have to writeFoo+Reflect
in order forFoo: Reflect
to hold (whereFoo
is a trait).Given that
Any
is slated to stabilization and hence that we are committed to some form of reflection, the goal of this PR is to leave our options open with respect to parametricity. I see the options for full stabilization as follows (I think an RFC would be an appropriate way to confirm whichever of these three routes we take):Reflect
a lang-item.impl<T> Reflect for T
instead. In that case,Reflect
is a harmless but not especially useful trait going forward.cc @aturon
cc @alexcrichton
cc @glaebhoerl (this is more-or-less your proposal, as I understood it)
cc @reem (this is more-or-less what we discussed on IRC at some point)
cc @flaper87 (vaguely pertains to OIBIT)