-
Notifications
You must be signed in to change notification settings - Fork 765
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
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.