Repository navigation
Reject cfg on expressions that cannot be safely removed - #159580
Conversation
|
rustbot has assigned @petrochenkov. Use Why was this reviewer chosen?The reviewer was selected based on:
|
|
I don't think we should do this.
|
|
@petrochenkov should the toolchain provide guidance for things that are likely to cause trouble because they are always a footgun regardless? Code like Edit: even if we don't emit an error, shouldn't this case in particular be at least deny-by-default lint, given that it is syntactically correct, but unlikely to be what the user intended? |
#159581 won't stabilize attribute macros on expressions, so should we move Also if we ever get a way for macros to overload based on what position (type, expr, etc) they're in (perhaps similar to |
Yes, this just looks like a lint material to me. Are you sure |
For an example, see https://github.com/PyO3/pyo3/blob/09b8d484b3f79b8519cac63be9d71a107cb14952/pyo3-ffi/examples/string-sum/src/lib.rs#L36 where the value of an array length depends on conditional compilation. It would be quite tempting to try to write that as... const SLOTS_LEN: usize = 1 + #[cfg(Py_3_12)] 1 + #[cfg(Py_GIL_DISABLED)] 1 + #[cfg(Py_3_15)] 4);...and end up in one of those situations where it looks like something that does what you expect, only for it to turn out you're wrong. It would just be another gotcha that most people are going to run into at some point. |
This is technically a breaking change, because it closes a stability hole. macro_rules! mac {
($expr:expr) => { $expr.clone() }
}
fn main() {
let _ = mac!(#[cfg(true)] 10);
} |
|
Let's land this, it will simplify the stabilization of |
|
Reminder, once the PR becomes ready for a review, use |
|
Could you also add this as a test case? #[derive(Clone)]
enum E {
V1 = #[cfg(true)] 1,
V2 = #[cfg_attr(true, cfg(true))] 2,
}Both variants should produce errors. |
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
Report the same error for true and false cfg predicates when the surrounding expression cannot be removed. Apply it consistently during normal expansion and cfg_eval. Signed-off-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
|
Thanks! |
|
📋 This PR cannot be approved because it currently has the following label: |
|
@bors r+ |
Reject cfg on expressions that cannot be safely removed ## Change description This PR changes how `#[cfg]` behaves in expression positions where removing the expression would result in invalid syntax. For example: ```rust let _ = 1 + #[cfg(unix)] 2; ``` This currently works on Unix because the condition is `true`, but becomes invalid on non-Unix targets when the expression is removed. The proposed change rejects `cfg` in these positions regardless of whether the condition is `true` or `false`. The goal is to avoid code whose validity depends on the target and to make the behavior more predictable as `stmt_expr_attributes` is stabilized. ## Summary of the discussion @petrochenkov pointed out that this differs from the usual token-based model for macros: `#[cfg(true)] expr` produces `expr`, while `#[cfg(false)] expr` produces no tokens. From that perspective, only the latter naturally results in an error. On the other hand, @estebank pointed out that these cases are easy to introduce accidentally and may only be discovered when the code is compiled for another target. This becomes more important with the stabilization of expression attributes. We also discussed that this is technically a breaking change, since some currently accepted code such as `#[cfg(true)]` inside a macro invocation would stop compiling (rust-lang#159580 (comment)). The conclusion was that this is effectively closing a stability hole, and the amount of affected code is expected to be very small. The current implementation also uses `RemoveNodeNotSupported` for these cases and adds coverage for both `#[cfg(true)]` and `#[cfg(false)]`.
Reject cfg on expressions that cannot be safely removed ## Change description This PR changes how `#[cfg]` behaves in expression positions where removing the expression would result in invalid syntax. For example: ```rust let _ = 1 + #[cfg(unix)] 2; ``` This currently works on Unix because the condition is `true`, but becomes invalid on non-Unix targets when the expression is removed. The proposed change rejects `cfg` in these positions regardless of whether the condition is `true` or `false`. The goal is to avoid code whose validity depends on the target and to make the behavior more predictable as `stmt_expr_attributes` is stabilized. ## Summary of the discussion @petrochenkov pointed out that this differs from the usual token-based model for macros: `#[cfg(true)] expr` produces `expr`, while `#[cfg(false)] expr` produces no tokens. From that perspective, only the latter naturally results in an error. On the other hand, @estebank pointed out that these cases are easy to introduce accidentally and may only be discovered when the code is compiled for another target. This becomes more important with the stabilization of expression attributes. We also discussed that this is technically a breaking change, since some currently accepted code such as `#[cfg(true)]` inside a macro invocation would stop compiling (rust-lang#159580 (comment)). The conclusion was that this is effectively closing a stability hole, and the amount of affected code is expected to be very small. The current implementation also uses `RemoveNodeNotSupported` for these cases and adds coverage for both `#[cfg(true)]` and `#[cfg(false)]`.
…uwer Rollup of 22 pull requests Successful merges: - #159580 (Reject cfg on expressions that cannot be safely removed) - #162597 (raise ambiguity error on attribute macro that could be tool attribute) - #162808 (Enable `--generate-link-to-definition` rustdoc option on std docs) - #163059 (Recover invalid return type until `{`, `}` or `;`) - #163672 (Use runCodeGen Abstraction for new LLVM) - #164028 (Add a future incompat warning for #[no_link]) - #164188 (Suggest adding lifetime for `dyn Trait` in return type) - #164345 (fix: handle `false` in root contexts) - #164494 (drop ands which are a superset of other ands) - #164766 (Unstably introduce MVP edition migration lint `unmarked_unchecked_type_aliases` together with bikeshed attribute `#[rustc_unchecked_type_alias]`) - #164785 (delegation: emit error when there is no field for `Self` mapping) - #164799 (Don't use `IntoIterator` in parallel API) - #164871 (Refactor call argument codegen) - #164648 (parse inline and rustc_force_inline with same parser) - #164811 (fix: Prevent ICE when comparing EII impl against generic declaration) - #164900 (make tool_attrs method to retrieve tool attributes.) - #164993 (copy_nonoverlapping: dont skip ovelrap check when src is fully uninit) - #164996 (miri: add missing alignment check in write_bytes) - #165012 (Remove Span::eq_ctxt()) - #165013 ([tiny] Add missing backticks around lifetime name) - #165024 (rustdoc-search: rank re-export lower) - #165028 (Update enzyme to fix one bug related to higher-order derivatives) > [!WARNING] > This rollup conflicts with pending auto build #165042 and may need to be recreated if the pending build succeeds.
…uwer Rollup of 21 pull requests Successful merges: - #159580 (Reject cfg on expressions that cannot be safely removed) - #162597 (raise ambiguity error on attribute macro that could be tool attribute) - #162808 (Enable `--generate-link-to-definition` rustdoc option on std docs) - #163059 (Recover invalid return type until `{`, `}` or `;`) - #163672 (Use runCodeGen Abstraction for new LLVM) - #164028 (Add a future incompat warning for #[no_link]) - #164188 (Suggest adding lifetime for `dyn Trait` in return type) - #164345 (fix: handle `false` in root contexts) - #164494 (drop ands which are a superset of other ands) - #164785 (delegation: emit error when there is no field for `Self` mapping) - #164799 (Don't use `IntoIterator` in parallel API) - #164871 (Refactor call argument codegen) - #164648 (parse inline and rustc_force_inline with same parser) - #164811 (fix: Prevent ICE when comparing EII impl against generic declaration) - #164900 (make tool_attrs method to retrieve tool attributes.) - #164993 (copy_nonoverlapping: dont skip ovelrap check when src is fully uninit) - #164996 (miri: add missing alignment check in write_bytes) - #165012 (Remove Span::eq_ctxt()) - #165013 ([tiny] Add missing backticks around lifetime name) - #165024 (rustdoc-search: rank re-export lower) - #165028 (Update enzyme to fix one bug related to higher-order derivatives)
…uwer Rollup of 21 pull requests Successful merges: - #159580 (Reject cfg on expressions that cannot be safely removed) - #162597 (raise ambiguity error on attribute macro that could be tool attribute) - #162808 (Enable `--generate-link-to-definition` rustdoc option on std docs) - #163059 (Recover invalid return type until `{`, `}` or `;`) - #163672 (Use runCodeGen Abstraction for new LLVM) - #164028 (Add a future incompat warning for #[no_link]) - #164188 (Suggest adding lifetime for `dyn Trait` in return type) - #164345 (fix: handle `false` in root contexts) - #164494 (drop ands which are a superset of other ands) - #164785 (delegation: emit error when there is no field for `Self` mapping) - #164799 (Don't use `IntoIterator` in parallel API) - #164871 (Refactor call argument codegen) - #164648 (parse inline and rustc_force_inline with same parser) - #164811 (fix: Prevent ICE when comparing EII impl against generic declaration) - #164900 (make tool_attrs method to retrieve tool attributes.) - #164993 (copy_nonoverlapping: dont skip ovelrap check when src is fully uninit) - #164996 (miri: add missing alignment check in write_bytes) - #165012 (Remove Span::eq_ctxt()) - #165013 ([tiny] Add missing backticks around lifetime name) - #165024 (rustdoc-search: rank re-export lower) - #165028 (Update enzyme to fix one bug related to higher-order derivatives)
…uwer Rollup of 21 pull requests Successful merges: - #159580 (Reject cfg on expressions that cannot be safely removed) - #162597 (raise ambiguity error on attribute macro that could be tool attribute) - #162808 (Enable `--generate-link-to-definition` rustdoc option on std docs) - #163059 (Recover invalid return type until `{`, `}` or `;`) - #163672 (Use runCodeGen Abstraction for new LLVM) - #164028 (Add a future incompat warning for #[no_link]) - #164188 (Suggest adding lifetime for `dyn Trait` in return type) - #164345 (fix: handle `false` in root contexts) - #164494 (drop ands which are a superset of other ands) - #164785 (delegation: emit error when there is no field for `Self` mapping) - #164799 (Don't use `IntoIterator` in parallel API) - #164871 (Refactor call argument codegen) - #164648 (parse inline and rustc_force_inline with same parser) - #164811 (fix: Prevent ICE when comparing EII impl against generic declaration) - #164900 (make tool_attrs method to retrieve tool attributes.) - #164993 (copy_nonoverlapping: dont skip ovelrap check when src is fully uninit) - #164996 (miri: add missing alignment check in write_bytes) - #165012 (Remove Span::eq_ctxt()) - #165013 ([tiny] Add missing backticks around lifetime name) - #165024 (rustdoc-search: rank re-export lower) - #165028 (Update enzyme to fix one bug related to higher-order derivatives)
…uwer Rollup of 21 pull requests Successful merges: - #159580 (Reject cfg on expressions that cannot be safely removed) - #162597 (raise ambiguity error on attribute macro that could be tool attribute) - #162808 (Enable `--generate-link-to-definition` rustdoc option on std docs) - #163059 (Recover invalid return type until `{`, `}` or `;`) - #163672 (Use runCodeGen Abstraction for new LLVM) - #164028 (Add a future incompat warning for #[no_link]) - #164188 (Suggest adding lifetime for `dyn Trait` in return type) - #164345 (fix: handle `false` in root contexts) - #164494 (drop ands which are a superset of other ands) - #164785 (delegation: emit error when there is no field for `Self` mapping) - #164799 (Don't use `IntoIterator` in parallel API) - #164871 (Refactor call argument codegen) - #164648 (parse inline and rustc_force_inline with same parser) - #164811 (fix: Prevent ICE when comparing EII impl against generic declaration) - #164900 (make tool_attrs method to retrieve tool attributes.) - #164993 (copy_nonoverlapping: dont skip ovelrap check when src is fully uninit) - #164996 (miri: add missing alignment check in write_bytes) - #165012 (Remove Span::eq_ctxt()) - #165013 ([tiny] Add missing backticks around lifetime name) - #165024 (rustdoc-search: rank re-export lower) - #165028 (Update enzyme to fix one bug related to higher-order derivatives)
Rollup merge of #159580 - Unique-Usman:ua/cfg, r=petrochenkov Reject cfg on expressions that cannot be safely removed ## Change description This PR changes how `#[cfg]` behaves in expression positions where removing the expression would result in invalid syntax. For example: ```rust let _ = 1 + #[cfg(unix)] 2; ``` This currently works on Unix because the condition is `true`, but becomes invalid on non-Unix targets when the expression is removed. The proposed change rejects `cfg` in these positions regardless of whether the condition is `true` or `false`. The goal is to avoid code whose validity depends on the target and to make the behavior more predictable as `stmt_expr_attributes` is stabilized. ## Summary of the discussion @petrochenkov pointed out that this differs from the usual token-based model for macros: `#[cfg(true)] expr` produces `expr`, while `#[cfg(false)] expr` produces no tokens. From that perspective, only the latter naturally results in an error. On the other hand, @estebank pointed out that these cases are easy to introduce accidentally and may only be discovered when the code is compiled for another target. This becomes more important with the stabilization of expression attributes. We also discussed that this is technically a breaking change, since some currently accepted code such as `#[cfg(true)]` inside a macro invocation would stop compiling (#159580 (comment)). The conclusion was that this is effectively closing a stability hole, and the amount of affected code is expected to be very small. The current implementation also uses `RemoveNodeNotSupported` for these cases and adds coverage for both `#[cfg(true)]` and `#[cfg(false)]`.
Reject cfg on expressions that cannot be safely removed ## Change description This PR changes how `#[cfg]` behaves in expression positions where removing the expression would result in invalid syntax. For example: ```rust let _ = 1 + #[cfg(unix)] 2; ``` This currently works on Unix because the condition is `true`, but becomes invalid on non-Unix targets when the expression is removed. The proposed change rejects `cfg` in these positions regardless of whether the condition is `true` or `false`. The goal is to avoid code whose validity depends on the target and to make the behavior more predictable as `stmt_expr_attributes` is stabilized. ## Summary of the discussion @petrochenkov pointed out that this differs from the usual token-based model for macros: `#[cfg(true)] expr` produces `expr`, while `#[cfg(false)] expr` produces no tokens. From that perspective, only the latter naturally results in an error. On the other hand, @estebank pointed out that these cases are easy to introduce accidentally and may only be discovered when the code is compiled for another target. This becomes more important with the stabilization of expression attributes. We also discussed that this is technically a breaking change, since some currently accepted code such as `#[cfg(true)]` inside a macro invocation would stop compiling (rust-lang/rust#159580 (comment)). The conclusion was that this is effectively closing a stability hole, and the amount of affected code is expected to be very small. The current implementation also uses `RemoveNodeNotSupported` for these cases and adds coverage for both `#[cfg(true)]` and `#[cfg(false)]`.
…uwer Rollup of 21 pull requests Successful merges: - rust-lang/rust#159580 (Reject cfg on expressions that cannot be safely removed) - rust-lang/rust#162597 (raise ambiguity error on attribute macro that could be tool attribute) - rust-lang/rust#162808 (Enable `--generate-link-to-definition` rustdoc option on std docs) - rust-lang/rust#163059 (Recover invalid return type until `{`, `}` or `;`) - rust-lang/rust#163672 (Use runCodeGen Abstraction for new LLVM) - rust-lang/rust#164028 (Add a future incompat warning for #[no_link]) - rust-lang/rust#164188 (Suggest adding lifetime for `dyn Trait` in return type) - rust-lang/rust#164345 (fix: handle `false` in root contexts) - rust-lang/rust#164494 (drop ands which are a superset of other ands) - rust-lang/rust#164785 (delegation: emit error when there is no field for `Self` mapping) - rust-lang/rust#164799 (Don't use `IntoIterator` in parallel API) - rust-lang/rust#164871 (Refactor call argument codegen) - rust-lang/rust#164648 (parse inline and rustc_force_inline with same parser) - rust-lang/rust#164811 (fix: Prevent ICE when comparing EII impl against generic declaration) - rust-lang/rust#164900 (make tool_attrs method to retrieve tool attributes.) - rust-lang/rust#164993 (copy_nonoverlapping: dont skip ovelrap check when src is fully uninit) - rust-lang/rust#164996 (miri: add missing alignment check in write_bytes) - rust-lang/rust#165012 (Remove Span::eq_ctxt()) - rust-lang/rust#165013 ([tiny] Add missing backticks around lifetime name) - rust-lang/rust#165024 (rustdoc-search: rank re-export lower) - rust-lang/rust#165028 (Update enzyme to fix one bug related to higher-order derivatives)
View all comments
Change description
This PR changes how
#[cfg]behaves in expression positions where removing the expression would result in invalid syntax.For example:
This currently works on Unix because the condition is
true, but becomes invalid on non-Unix targets when the expression is removed.The proposed change rejects
cfgin these positions regardless of whether the condition istrueorfalse. The goal is to avoid code whose validity depends on the target and to make the behavior more predictable asstmt_expr_attributesis stabilized.Summary of the discussion
@petrochenkov pointed out that this differs from the usual token-based model for macros:
#[cfg(true)] exprproducesexpr, while#[cfg(false)] exprproduces no tokens. From that perspective, only the latter naturally results in an error.On the other hand, @estebank pointed out that these cases are easy to introduce accidentally and may only be discovered when the code is compiled for another target. This becomes more important with the stabilization of expression attributes.
We also discussed that this is technically a breaking change, since some currently accepted code such as
#[cfg(true)]inside a macro invocation would stop compiling (#159580 (comment)). The conclusion was that this is effectively closing a stability hole, and the amount of affected code is expected to be very small.The current implementation also uses
RemoveNodeNotSupportedfor these cases and adds coverage for both#[cfg(true)]and#[cfg(false)].