-
Notifications
You must be signed in to change notification settings - Fork 804
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
cfg features for enum variants #4509
Conversation
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.
Thanks!
I like the implementation, I just have a couple of points that need addressing:
- This PR uses
attrs
as variable and field names. Existing code usescfg_attrs
. This code should too, for consistency. - Please add a test to ensure that something like
#[pyclass]
enum Empty{
#[cfg(any())]
Disabled,
}
does not compile. You can add it to tests/ui/invalid_pyclass_enum.rs
So in the case where all of the variants get disabled, like #[pyclass]
enum Empty{
#[cfg(any())]
Disabled,
} it does fail to compile, but not with the nice Instead we get:
Which I suppose is related to the reason why we have the first error message but I don't think we can know at this time which features are enabled and can't test for empty enums from disabled variants. The error message we do get is because the 2 generated match arms in fn __pyo3__repr__(&self) -> &'static str {
match self {
#(#variants_repr)*
_ => unreachable!(),
}
} and the generated code does compile but I don't think that could lead to a helpful way to report an invalid enum. The resulting python module silently does not have the empty enum class. I'm not sure what to do. I could see a user defining an enum where every variant is covered by a cfg attribute and triggering this. It will still fail to compile but for the wrong reason so I'm not sure how critical this is. I suppose we could work out the annihilating combination of variant cfg attributes and if they're met trigger a compile error? For example, with an enum like enum MyEnum {
#[cfg(feature = "x")]
VariantOne,
#[cfg(feature = "y")]
VariantTwo,
} collecting the cfg features and generating this: #[cfg(all(not(feature = "x"), not(feature = "y")))]
compile_error!("All variants of enum MyEnum have been disabled by cfg attributes"); Or is this just an edge case. |
In this case, just dereference the scrutinee - maybe we should do this to avoid emitting this particular error fn __pyo3__repr__(&self) -> &'static str {
match *self {
#(#variants_repr)*
}
} Anyway, we should not rely on
Yes, I think this is a good idea.
I can believe that someone would hit this eventually and waste a lot of time debugging ;) Maybe we could reconsider allowing empty enums. I wasn't involved with that feature so I'm not sure there is a compelling reason to disallow it. |
I've pushed a draft solution that constructs a If there is at least one variant that is not annotated with a cfg attribute or if there are zero variants then this token stream is quote! {
#[cfg(all(#(#conditions),*))]
::core::compile_error!(concat!("All variants of enum `", stringify!(#cls), "` have been disabled by cfg attributes"));
} Here's an example: #[pyclass(eq, eq_int)]
#[derive(PartialEq)]
enum MyEnum {
#[cfg(not(any(feature = "x", feature="y")))]
VariantOne,
#[cfg(all(feature = "y", feature="optional_feature"))]
VariantTwo,
}
I've also dereferenced the scrutinees so that the body of the generated class is syntactically valid and the
I don't have any input on what adding unconstructable types to python bindings would look like. I think python's Is this style consistent with pyo3? I will continue with making tests |
let cfg_attrs = &variant.cfg_attrs; | ||
|
||
if cfg_attrs.is_empty() { | ||
return quote! {}; |
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.
return quote! {}; | |
// There's at least one variant of the enum without cfg attributes, | |
// so the check is not necessary | |
return quote! {}; |
tests/ui/invalid_pyclass_enum.stderr
Outdated
@@ -66,6 +66,14 @@ error: The `ord` option requires the `eq` option. | |||
83 | #[pyclass(ord)] | |||
| ^^^ | |||
|
|||
error: All variants of enum `AllEnumVariantsDisabled` have been disabled by cfg attributes |
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.
We should probably mention why it's not allowed:
error: All variants of enum `AllEnumVariantsDisabled` have been disabled by cfg attributes | |
error: #[pyclass] can't be used on enums without any variants - all variants of enum `AllEnumVariantsDisabled` have been configured out by cfg attributes |
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.
Ok, good. I see we don't put #[pyclass] in backticks elsewhere in this module's compiler errors, too, so I haven't added them.
tests/ui/invalid_pyclass_enum.stderr
Outdated
96 | #[pyclass(eq)] | ||
| ^^^^^^^^^^^^^^ |
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.
It would be nice to have a better span for this error, cls.span()
should do.
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.
Thanks!
This is a fix for #4411.
#[cfg]
attributes are propagated to the generated python classes as described in this comment: #4411 (comment)It only covers the case for simple enums but a similar solution could be extended for complex enums.