-
Notifications
You must be signed in to change notification settings - Fork 14k
enum variant support #89745
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
enum variant support #89745
Conversation
|
Some changes occured to the CTFE / Miri engine cc @rust-lang/miri |
|
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @michaelwoerister (or someone else) soon. Please see the contribution instructions for more information. |
a5f1cef to
43f9467
Compare
This comment has been minimized.
This comment has been minimized.
|
r? @oli-obk |
oli-obk
left a comment
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.
First of all, this is amazing. I thought an MVP would be significantly more involved, but you even got surface syntax working from the start.
I left a lot of comments, I do realize this is a draft and as a first iteration the copy paste is expected to get things running quickly. Please let me know how nit-picky you want the review to be. I'm also available on zulip for chatting if you prefer, just open a thread in the compiler team stream.
One general thing is that I think we should also change TyLayout::for_variant to return a ty::Variant in the type field instead of just returning the original type.
| ty::Variant(ty, _) => match ty.kind() { | ||
| ty::Adt(adt_def, _substs) if adt_def.is_enum() => { | ||
| if index.as_usize() >= adt_def.variants.len() { | ||
| PlaceTy::from_ty(span_mirbug_and_err!( | ||
| self, | ||
| place, | ||
| "cast to variant #{:?} but enum only has {:?}", | ||
| index, | ||
| adt_def.variants.len() | ||
| )) | ||
| } else { | ||
| PlaceTy { ty: *ty, variant_index: Some(index) } | ||
| } | ||
| } | ||
| _ => bug!("unexpected type: {:?}", ty.kind()), | ||
| }, |
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.
is downcasting a variant type an operation we would ever encounter?
I think for now this arm can just mir-bug if index != var_index where var_index is the second field of ty::Variant.
that would allow re-downcasting to the same variant, but nothing else
| adt_def.variants.len() | ||
| )) | ||
| } else { | ||
| PlaceTy { ty: base_ty, variant_index: Some(index) } |
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.
Ideally we'd remove the variant_index field and replace ty here with the variant type
| PlaceTy { ty, variant_index: Some(variant_index) } => match *ty.kind() { | ||
| ty::Adt(adt_def, substs) => (&adt_def.variants[variant_index], substs), | ||
| ty::Variant(ty, _) => match ty.kind() { | ||
| ty::Adt(adt_def, substs) => { | ||
| (adt_def.variants.get(variant_index).expect(""), *substs) | ||
| } | ||
| _ => bug!("unexpected type: {:?}", ty.kind()), | ||
| }, |
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.
if we get rid of variant_index as mentioned earlier, then we can grab the variant index from the ty::Variant
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.
If we remove variant_index how would we handle the Adt match arm above?
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.
If the above comment is implemented, then we should never encounter that arm, it should always be ty::Variant if we would have had a variant index in the PlaceTy
| ty::Variant(ty, _) => match ty.kind() { | ||
| ty::Adt(adt, _) => adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits), | ||
| _ => bug!("unexpected type: {:?}", ty.kind()), | ||
| } |
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.
| ty::Variant(ty, _) => match ty.kind() { | |
| ty::Adt(adt, _) => adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits), | |
| _ => bug!("unexpected type: {:?}", ty.kind()), | |
| } | |
| ty::Variant(_ty, idx) => idx, |
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 should return (VariantIdx, Discr) not just the index so this wouldn't work?
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.
ah, yea, that makes sense. we should probably still assert somewhere that the index matches the expected one here
| if tag_field == field { | ||
| return match layout.ty.kind() { | ||
| ty::Adt(def, ..) if def.is_enum() => PathElem::EnumTag, | ||
| ty::Variant(ty, ..) => match ty.kind() { |
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.
Instead of any of the changes in this function, we could do a
if let ty::Variant(ty, ..) = *layout.ty.kind() {
return self.aggregate_field_path_elem(self.ecx.layout_of(ty), field),
}at the start
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 in a Variants::Multiple match arm; we should never see ty::Variant here I think.
| self.check_expr_closure(expr, capture, &decl, body_id, gen, expected) | ||
| } | ||
| ExprKind::Block(body, _) => self.check_block_with_expected(&body, expected), | ||
| ExprKind::Call(callee, args) => self.check_call(expr, &callee, args, expected), |
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.
Same here, we can probably make the type of tuple variant constructors be fn(args) -> Enum::Variant instead of fn(args) -> Enum
| while let Some((base_t, _)) = autoderef.next() { | ||
| debug!("base_t: {:?}", base_t); | ||
| match base_t.kind() { | ||
| ty::Variant(ty, idx) => match ty.kind() { |
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.
I think as a first prototype we could start without this, and require let Foo::Bar { x, y } = ... in order to get the fields out
| = note: expected enum variant `Foo::Variant1` | ||
| found enum variant `Foo::Variant2` |
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.
nice!
| } | ||
|
|
||
| fn main() { | ||
| let f: Foo::Variant1 = Foo::Variant1(3, "test".to_string()); |
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.
will this work without f having a type specified?
| } | ||
|
|
||
| fn main() { | ||
| let f: Foo::Variant2 = Foo::Variant2(9); |
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.
Will the following also work with the current impl?
let y = Foo::Variant2(9);
foo(y);
fn foo(z: Foo::Variant2) {}
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.
Not yet, currently everything must have the type explicitly set.
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.
Ah, ok. so that's coupled with the ExpectedTy situation I commented on earlier in this PR.
If you want you can also commit failing tests like this and others you come up with, then we can see the test changes as you touch the logic
jackh726
left a comment
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.
Oli beat me to the review here, but I'm just as excited :)
Just one comment for now!
| Infer(InferTy), | ||
|
|
||
| /// An enum (TyKind::Adt) and its variant | ||
| Variant(Ty<'tcx>, VariantIdx), |
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.
So, is the Ty here ever going to be anything other than an Adt? If not, maybe we should just have (&'tcx AdtDef, SubstsRef<'tcx>, VariantIdx)?
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.
I personally think it should some day allow more than just enums. E.g. if we allowed multiple variant indices at some point, we could also think about ranged integers. As long as there are some assertions right now limiting it to adts, that's fine with me, we don't need to check everywhere though. I mean, not all Adts make sense here either, so it's not like limiting to Adts will get us freedom from misuse by construction. E.g. structs don't really make sense inside a ty::Variant right now.
And we should def re-use it for generators, if just for keeping the special cases and repetition down.
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.
Using Adt could cut down a lot of the matches that need to get the underlying AdtDef for the enum this is a variant of.
I wouldn't complicate the code now for extremely hypothetical extensions such as ranged integers.
| } | ||
|
|
||
| ty::Variant(ty, ..) => match ty.kind() { | ||
| ty::Adt(def, ..) if def.is_enum() => { |
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 seems to be a very common pattern to get the adtdef of the enum for a ty::Variant; should maybe ty_adt_def have support for ty::Variant so it could be used instead? That would also avoid all the rightwards drift.
| } | ||
| ty::Variant(ty, _) => match ty.kind() { | ||
| ty::Adt(def, _) => { | ||
| if def.is_box() { |
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.
Box is not an enum, this should be impossible. I think this can be handled together with the Adt fallback case below.
| ) -> InterpResult<'tcx> { | ||
| let name = match old_op.layout.ty.kind() { | ||
| ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].ident.name), | ||
| ty::Variant(ty, ..) => match ty.kind() { |
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.
Is this reachable? We should only reach this function for types with a multi-variant layout, which ty::Variant is not.
This comment has been minimized.
This comment has been minimized.
14a70a9 to
ccf569f
Compare
|
The job Click to see the possible cause of the failure (guessed by this bot) |
| new_op: &OpTy<'tcx, M::PointerTag>, | ||
| ) -> InterpResult<'tcx> { | ||
| let name = match old_op.layout.ty.kind() { | ||
| let ty = old_op.layout.ty.strip_variant_type(); |
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.
Variant types don't have variants, so something seems wrong if we see a variant type here.
| Variants::Multiple { tag_field, .. } => { | ||
| if tag_field == field { | ||
| return match layout.ty.kind() { | ||
| return match layout.ty.strip_variant_type().kind() { |
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.
Variant types should never have Variants::Multiple layout so this should be unreachable.
Generally I am not very happy with strip_variant_type, from a conceptual perspective this rarely seems like the right thing to do -- but maybe my intuition is leading me astray here.
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.
strip_variant_type is the correct thing wherever we don't actually handle variant things on generators or adts right now, so here it is indeed wrong, we should not ever encounter adt/generator anymore, but for that Layout::for_variant needs to be fixed to return ty::Variant.
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.
layout is the outer layout of the enum here, why should we not encounter adt/generators here?
It's the other match below in the same function where we should not encounter them any more.
|
So... a general point about this PR. It's a huge amount of work to implement this all at once. In order to get this mergeable (and reviewable) in small steps, how about the following plan:
|
|
Thanks for taking the time to lay out the next steps, and looking over this PR. I'd be more than happy to start on 1, but it does seem like this is skipping a few steps that are outlined here. |
|
1 and 2 are invisible to users. They are essentially cleanups of the compiler's logic. For 3 we'd at least need an accepted MCP, from the zulip discussion it sounded like there was nothing speaking against it, so maybe just asking for a "second" to be able to experiment is all that is needed at this stage? |
|
Would zulip be the best place to discuss this going forward? |
|
Yea a zulip thread in the T-compiler stream is probably the best place to discuss this |
|
I'll start a thread and tag you in it, whats your zulip name? |
|
|
|
☔ The latest upstream changes (presumably #89939) made this pull request unmergeable. Please resolve the merge conflicts. |
Proof of concept for enum variant types.