-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Tracking issue for RFC 2523, #[cfg(accessible(::path::to::thing))]
#64797
Comments
@Centril I am interested in doing this, I have never contributed to rust code and would like to try. Any guidance? |
@pickfire Cool! I believe the logic here can be divided into 2 parts roughly:
|
Oh no, this RFC passed. |
We can reliably answer the For type-relative paths everything looks harder.
|
Agreed!
Can you elaborate on this point a bit? What specifically induces this requirement re. this crate and are there syntactic restrictions on the path we can impose to prevent the requirement from arising? (For example by preventing
Hmm; I don't think I follow how why we need to go from aliases to their bodies here... can you elaborate on this as well? |
use std::io::Write;
// The cfg result depends on whether the `io::Write` trait is in scope here
#[cfg(::std::fs::File::write)] There's no syntactic way to detect this.
trait Alias = std::io::Write;
// The cfg result depends on whether the `Alias` refers to `io::Write`
#[cfg(::std::fs::File::write)] |
For type-relative paths, I don't think we should do anything more than inherent impls (because otherwise it's really
There should be no generic arguments on such paths anyway, because there is no machinery to interpret them in any meaningful way. I'd suggest implementing only module-relative paths to begin with, and do more investigations for type-relative paths, since I suspect they're not as useful. |
The unpleasant part is that for some paths we may give an incorrect answer if we don't consider type-relative resolutions, e.g. using module-relative resolution we may return cfg(MyEnum::MaybeNonExistent) or cfg(MyTrait::maybe_non_existent) even if both paths exist if we apply type-relative resolution. For imports those would also return "false", but in that case it's a compilation error (unresolved import), rather than a silent un-configuration. |
Module-relative resolutions also have complications. #[cfg(::proc_macro::TokenStream)]
struct S;
macro_rules! m { () => { extern crate proc_macro; } }
m!(); If we expand the That means the path-based cfgs need to be enqueued until they are ready/resolved and only then expanded. The conclusion is that supporting paths from other crates is not easier than supporting local paths. |
@Centril Thanks for the explanation and the quick response, I do quite understand what you meant but not quite for the others. Based on my understanding, |
@Centril Does that mean that I need to add something like diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 8b967048848..9b6373bfb42 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -514,6 +514,10 @@ pub enum MetaItemKind {
///
/// E.g., `feature = "foo"` as in `#[feature = "foo"]`.
NameValue(Lit),
+ /// Value meta item.
+ ///
+ /// E.g., `path` as in `accessible(path)`.
+ Value(Lit),
}
/// A block (`{ .. }`). |
Will this support paths to modules, e.g. I don't have any more realistic use cases in mind yet; just curious. |
How would #[cfg(accessible(peach::Peach))]
mod banana {
crate struct Banana;
}
#[cfg(accessible(banana::Banana))]
mod peach {
crate struct Peach;
} work? Given comments from @petrochenkov can we restrict this feature for now (and maybe indefinitely) to support external paths only and nothing related to type-relative resolution? EDIT: I’ve been pointed to the RFC portion that mentions paths must refer to external crates. I think that at very least makes type-relative trait resolution that was raised as a concern in #64797 (comment) a non-problem. For a |
FWIW, I work on implementing a prototype for this in the most conservative variant (https://github.com/petrochenkov/rust/tree/cfgacc), but I've been constantly distracted by job and holiday trips since mid December. The prototype will work as a macro attribute rather than a |
Conditionally enables signals if they're available. This would make for an amazing use case of `#[cfg(accessible(...))]`. See rust-lang/rust#64797 for info on this.
PR submitted - #69870. |
expand: Implement something similar to `#[cfg(accessible(path))]` cc rust-lang#64797 The feature is implemented as a `#[cfg_accessible(path)]` attribute macro rather than as `#[cfg(accessible(path))]` because it needs to wait until `path` becomes resolvable, and `cfg` cannot wait, but macros can wait. Later we can think about desugaring or not desugaring `#[cfg(accessible(path))]` into `#[cfg_accessible(path)]`. This implementation is also incomplete in the sense that it never returns "false" from `cfg_accessible(path)`, it requires some tweaks to resolve, which is not quite ready to answer queries like this during early resolution. However, the most important part of this PR is not `cfg_accessible` itself, but expansion infrastructure for retrying expansions. Before this PR we could say "we cannot resolve this macro path, let's try it later", with this PR we can say "we cannot expand this macro, let's try it later" as well. This is a pre-requisite for - turning `#[derive(...)]` into a regular attribute macro, - properly supporting eager expansion for macros that cannot yet be resolved like ``` fn main() { println!(not_available_yet!()); } macro_rules! make_available { () => { #[macro_export] macro_rules! not_available_yet { () => { "Hello world!" } }} } make_available!(); ```
expand: Implement something similar to `#[cfg(accessible(path))]` cc rust-lang#64797 The feature is implemented as a `#[cfg_accessible(path)]` attribute macro rather than as `#[cfg(accessible(path))]` because it needs to wait until `path` becomes resolvable, and `cfg` cannot wait, but macros can wait. Later we can think about desugaring or not desugaring `#[cfg(accessible(path))]` into `#[cfg_accessible(path)]`. This implementation is also incomplete in the sense that it never returns "false" from `cfg_accessible(path)`, it requires some tweaks to resolve, which is not quite ready to answer queries like this during early resolution. However, the most important part of this PR is not `cfg_accessible` itself, but expansion infrastructure for retrying expansions. Before this PR we could say "we cannot resolve this macro path, let's try it later", with this PR we can say "we cannot expand this macro, let's try it later" as well. This is a pre-requisite for - turning `#[derive(...)]` into a regular attribute macro, - properly supporting eager expansion for macros that cannot yet be resolved like ``` fn main() { println!(not_available_yet!()); } macro_rules! make_available { () => { #[macro_export] macro_rules! not_available_yet { () => { "Hello world!" } }} } make_available!(); ```
Status:
|
Yeah, |
I'm not sure I agree that's a good use case for this feature. Using cfg(accessible) for that quickly results in hard-to-maintain code that changes behavior depending on whether a symbol exists, without any checks for typos or signatures. There would be all kind of very implicit contracts between crates, similar to what we'd have if we relied on duck-typing everywhere instead of well defined traits and types. |
What would your suggested alternative be? Currently, the only alternative I know of is to maintain a large list of cfgs for which targets support (e.g. |
Another thing I'm worried about this feature enabling or even encouraging code that depends on future versions of the standard library that don't even exist yet. For example: If we're experimenting with an unstable type #[cfg(accessible(std::X))]
pub type X = std::X;
#[cfg(not(accessible(std::X)))]
pub struct X { .. } The problem with this 'future compatible' code is that it assumes the future stable We could simply blame the author of this code for using cfg(accessible) like this, but if this is part of a popular library, all their users now have a reason to not upgrade to a new version of Rust, since we broke their dependency in the new Rust version. |
That's a good point, and might be a point in favor of something like a That said, I believe this kind of version checking is regarded as undesirable for various reasons as well (although it's widely used in crates with long MSRV requirements). |
Ideally, something that interacts well with our type and trait system, with where-clauses. E.g. an I realize that this requires some new language features, and probably some more to make it anywhere close to ergonomic. I really dislike conditional compilation, as it results in similar surprises and headaches as e.g. C++ templates (which are only type checked when instantiated) or type issues in dynamically typed scripting languages. I think conditional compilation is unavoidable in some cases, but I don't think we should encourage more of it. I'm not sure if I see cfg(accessible) as something to just improve existing |
I also have noted in the past that I also agree something like what I suggested probably wouldn't be the right fit for use inside libstd, but I don't think I take as hard of a line against conditional compilation. It does make code harder to maintain, but it also comes with a number of benefits (compile time, for example, even if just in terms of not needing to pull in things like I am in favor of ways to avoid the need for it, though, especially inside the stdlib. (I'm not sure I follow how your suggestion avoids the current situation, but am willing to accept that it may be slightly handwaved given the "this requires some new language features" bit) |
@m-ou-se I think we should document the implications of potential |
As some anecdata that this kind of thing is always hard and requires getting many details right: 1. A number of years ago, when working with C++, I wanted to use its This worked great as long as you constrained yourself to the common API of all of them, over different compiler versions. Unfortunately, there was no way to statically detect when you went outside that realm and that was quite easy (IIRC, the Now Another related thought: The same way
I'm not sure how relevant this will be in practice, just some food for thought :) 2. Much more recently, I was looking for which Rust nightly introduced a weird bug I was experiencing, which was degrading docs. At a certain point, I suddenly got compilation errors. This wasn't another nightly bug, it was lock_api doing version detection and making a constructor |
Would adding a |
|
It being technically allowed doesn't really mean we can ignore the concern, since if it breaks a large number of crates it still becomes a concern, as it can prevent people from updating. |
Is this currently awaiting a more complete implementation (as the status at the top suggests), or resolution of design questions (as the most recent questions suggest)? |
@alex a bit of column A and a bit of column B I think... Certainly there are some design questions still open, but there is also implementation work to be done in order to get this finished. The ideas by @m-ou-se are also interesting, although IMO they are a longer term project. One shouldn't let perfect be the enemy of good. Regarding the discussion about the danger of people depending on unstable APIs in |
This is obviously not the intended use-case, but one neat side-effect of getting this stabilized would be the ability to annotate code such that you'll get a warning when a feature stabilizes. For example, imagine you currently have some code that wants to use #[cfg(accessible(Box<MaybeUninit<T>>::assume_init))]
let todo_adopt_assume_init = (); which will trigger an unused variable warning when Now, what exactly goes in side |
This is a tracking issue for
#[cfg(accessible(::path::to::thing))]
(rust-lang/rfcs#2523).Steps
#[cfg(accessible(path))]
#69870Status
From this comment
#[cfg(accessible(path))]
#69870 as an attribute#[cfg_accessible(path)] item
. The attribute can configure or unconfigure theitem
and wait until the predicate "path
is accessible" becomes determinate.#[cfg(accessible)]
into#[cfg_accessible]
is not implemented, we need to consider doing or not doing it only when everything else is implemented.Unresolved questions:
None so far.
The text was updated successfully, but these errors were encountered: