Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upObjects should be upcastable to supertraits #5665
Comments
This comment has been minimized.
This comment has been minimized.
|
Reproduced with 64963d6. Nominating for milestone 5, production-ready |
This comment has been minimized.
This comment has been minimized.
|
The error as of 6c2dc3c is:
which is a bit weird, why shouldn't you be able to cast a trait object to the same trait object? |
This comment has been minimized.
This comment has been minimized.
|
IINM there's two ways you could interpret this: one is that it's the identity function (casting something to its own type), the other is that you're looking for an |
This comment has been minimized.
This comment has been minimized.
|
I would have assumed it's the identity function |
This comment has been minimized.
This comment has been minimized.
|
Updating title to reflect the larger issue (casting from |
This comment has been minimized.
This comment has been minimized.
|
accepted for feature-complete milestone |
This comment has been minimized.
This comment has been minimized.
|
This doesn't seem like the identity function to me, as the vtables can be subsets of each other in weird ways (e.g. with multiple supertraits). Doing this properly will require new codegen. |
thestinger
referenced this issue
Oct 14, 2013
Closed
Trait objects cannot be cast to parent traits #9840
This comment has been minimized.
This comment has been minimized.
|
A simpler testcase:
|
This comment has been minimized.
This comment has been minimized.
|
this need not block 1.0. Assigning P-high as it is important for some use cases. |
pnkfelix
added
P-high
and removed
P-high-untriaged
labels
Feb 13, 2014
pnkfelix
added this to the 1.0 milestone
Feb 13, 2014
This comment has been minimized.
This comment has been minimized.
|
To be clear: this is not a subtyping rule but a coercion one. As @bblum says, it may require substituting a new vtable. |
This comment has been minimized.
This comment has been minimized.
|
This is on the 1.0 milestone currently, due to @pnkfelix's comment above, I'm removing the milestone assuming that it was an accident. |
alexcrichton
modified the milestone:
1.0
Apr 1, 2014
thestinger
removed
the
I-completion
label
Sep 16, 2014
This comment has been minimized.
This comment has been minimized.
little-arhat
commented
Oct 21, 2014
|
I'm curious, how we could call supertrait method on subtrait object [1], without being able to upcast such object to supertrait? Does vtable of subtrait include all methods from supertrait? Or how does it work? |
This comment has been minimized.
This comment has been minimized.
little-arhat
commented
Oct 22, 2014
|
Is there any way to work around this issue in current rust?
What is layout of trait objects? I think it should be possible to "upcast" subtrait object to supertrait with unsafe manipulation of vtables. |
This comment has been minimized.
This comment has been minimized.
little-arhat
commented
Oct 22, 2014
|
For future references: https://github.com/rust-lang/rust/blob/master/src/librustc/driver/pretty.rs#L137 — here is workaround for this issue. |
This comment has been minimized.
This comment has been minimized.
|
Sub. |
This comment has been minimized.
This comment has been minimized.
kFYatek
commented
Feb 22, 2015
|
My workaround: http://stackoverflow.com/a/28664881/403742 |
This comment has been minimized.
This comment has been minimized.
|
Triage: #5665 (comment) seems to be a decent test-case, and this is still a non-scalar cast today. |
This comment has been minimized.
This comment has been minimized.
|
I've started looking at this. I figured I'd try to clear up the design first. Here's my understanding: BasicsTrait objects (spelled as Smart pointersSimilar conversions should probably be possible for ImplementationTrait objects consist of a vptr and a self-pointer. The vptr points to a vtable, which contains a static array of function addresses. Each method is associated with a statically known offset in that table. To call a method on a trait object, add the offset to the vptr and call the function whose address is stored in that slot, passing the self-pointer as the first argument. (The vtable also stores some metadata, which currently includes size, alignment, and the address of the destructor. The destructor is treated specially, presumably because any concrete type may implement Currently, there is one vtable for each impl of an object-safe trait. There is no particular relationship between different vtables for a type that implements multiple traits. Supertrait method addresses are, however, duplicated into the subtrait's vtable. To support upcasting, we need a way to get from the subtrait's vtable to the supertrait's vtable. I'm planning on storing supertrait vtables next to subtraits vtables, in a recursive pattern. This would let us use statically known offsets for upcasting, because the offsets are the same for every subtrait impl. The drawback is that superclass vtables would need to be duplicated in a 'diamond' inheritance situation. (Another consequence is that two trait objects of the same trait could have different vptrs even if they were created from the same concrete type, depending on what path they took up the inheritance chain. This shouldn't matter in practice, since we don't make any guarantees about trait object equivalence.) An alternative solution would be to store offsets next to each method list, one for each supertrait we can upcast to. This representation is more compact, but I expect it would be slower because of the dependent read. Questions
|
This comment has been minimized.
This comment has been minimized.
|
Looks like RFC 401 answered my first question: this is supposed to be an implicit coercion. See also: #18469, the tracking issue for RFC 401, and #18600, which deals with subtrait coercions and therefore may be a duplicate of this issue. RFC 982 (DST coercions) also looks to be related. Tracking issue is #18598. |
This comment has been minimized.
This comment has been minimized.
|
RFC pull request 250 (postponed) dealt with this, but alongside proposals for associated fields on traits, opt-in internal vtables, and other features. The upcasting section of that proposal is basically what I had in mind. |
This comment has been minimized.
This comment has been minimized.
|
@typelist sorry for not getting back to you sooner! I have some thoughts about how this should work, but I think the problem can also be subdivided. Likely we should start by writing up an RFC, perhaps just for a subset of the total problem. Let me just leave various notes and let's try to sort it out a bit. Multiple traits, multiple vtablesFirst of all, this is related to the topic of supporting trait objects of multiple traits. For example, I would like to support This is orthogonal from supertrait upcasting, just wanted to throw it out there. In particular, if you have I bring it up though because the implementation strategy here may want to be shared with other trait combinations. Originally, the way I had thought to support this was to have a trait object potentially have >1 vtable (i.e., one for Note that if we adopted the strategy of making one coallesced vtable, this will affect how supertraits are stored in vtables. If we said that Supertrait upcastingOK, so, with that out of the way, I think it makes sense to focus squarely on the case of supertrait upcasting where exactly one vtable is needed. We can then extend (orthogonally) to the idea of multiple vtables. So in that case I think the basic strategy you outlined makes sense. You already cited this, but I think that the strategy outlined by @kimundi and @eddyb in RFC 250 is basically correct. Interaction with the unsize coercionYou are also already onto this one, but yes this is clearly related to the idea of coercing a Because the vtable must be adjusted, it's clear that this has to be a coercion and not an extension of subtyping, since subtyping would require that there are no repesentation changes. Moreover, by attaching it to the existing unsizing coercion, we don't have to address questions about just when it triggers -- same time as unsizing triggers. Do we need an RFC to go forward here?It seems like it can't hurt, but I'd basically just extract the relevant text from RFC 250, which I think was essentially correct. If we're just sticking to supporting single trait object to other trait object, seems harmless enough, and I can't imagine many objections. I would be very happy to work with you and draft it together! |
This comment has been minimized.
This comment has been minimized.
|
Thanks for the response! I started drafting an RFC based on part of RFC 250. Comments or revisions welcome. (Should we keep communicating through this issue, or some other way?) For the multi-trait case, increasing the size of the trait object does seem like the path of least resistance. It certainly makes partial upcasts (and upcasts like |
This comment has been minimized.
This comment has been minimized.
|
@typelist this RFC looks great! I appreciate that you took the time to talk about the motivation, alternatives and downsides. (We can keep talking over the issue, or feel free to e-mail me or ping me on IRC. I try to keep up with GH notifications, but it can be challenging.) |
Mark-Simulacrum
referenced this issue
Apr 30, 2017
Closed
Trait inherits from other traits, but cannot be casted back to them #26918
This comment has been minimized.
This comment has been minimized.
|
@typelist @nikomatsakis Any updates on this? I'd like to push this forward. I took a stat of vtable usage in rustc. Perhaps it is useful when predicting code-size/performance effects of upcasting support. https://gist.github.com/qnighy/20184bd34d8b4c70a302fac86c7bde91 |
Mark-Simulacrum
added
C-feature-request
T-lang
labels
Jul 19, 2017
This comment has been minimized.
This comment has been minimized.
ctaggart
commented
Oct 10, 2017
|
If this was implemented, would this compile? If so, this would be extremely helpful. For example, with TypeScript interop, I could avoid all the ugly trait Statement {}
trait IfStatement: Statement {}
struct IfStatementImpl;
impl Statement for IfStatementImpl {}
impl IfStatement for IfStatementImpl {}
fn print_statement(_statement: &Statement){
println!("print_statement");
}
fn print_if_statement(_if_statement: &IfStatement){
println!("print_if_statement");
}
fn main() {
// How come this compiles?
let ref a = IfStatementImpl {};
print_statement(a);
print_if_statement(a);
// but this does not?
let ref b: IfStatement = IfStatementImpl {};
print_statement(b);
print_if_statement(b);
} |
rkruppe
referenced this issue
Nov 28, 2017
Closed
Can't create trait object whose referent is another trait object #46337
This comment has been minimized.
This comment has been minimized.
|
@ctaggart I think 50% yes. |
ctaggart
added a commit
to ctaggart/typescript_ts
that referenced
this issue
Mar 13, 2018
mitsuhiko
referenced this issue
Jun 7, 2018
Merged
Added improved failure interoperability with downcasting #285
d33a94975ba60d59
referenced this issue
Oct 29, 2018
Merged
Add a test for the reordering attack on BinaryAgreement #270
This comment has been minimized.
This comment has been minimized.
|
@rust-lang/lang, IRC suggests that this is something that should be closed or moved to the RFCs repo. |
jdm commentedApr 1, 2013
error: failed to find an implementation of trait @T for T