-
-
Notifications
You must be signed in to change notification settings - Fork 14.3k
add unreachable_cfg_select_predicates lint
#149960
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
base: main
Are you sure you want to change the base?
Conversation
|
Some changes occurred in compiler/rustc_attr_parsing |
|
rustbot has assigned @WaffleLapkin. Use |
|
The proposed name for this lint seems quite unfortunate to me. When I first read it I though it had something to do with the I think it would be better include the word "arm" or "cfg_select" in it, to clearly disambiguate it from the other places where cfgs can appear. Maybe |
|
There is some reasoning for then name at #149783 (comment). cc @traviscross was there any particular reason for you to leave out the select or arm/branch parts? |
|
The reasoning was: For Here that would suggest the name But we have an existing lint, To your point about detecting Sitting with it now, though, I think |
This comment was marked as resolved.
This comment was marked as resolved.
542a992 to
20f27e5
Compare
This comment was marked as resolved.
This comment was marked as resolved.
unreachable_cfgs lintunreachable_cfg_select_predicates lint
|
I do like |
| if let Some((underscore, _, _)) = branches.wildcard | ||
| && features.map_or(false, |f| f.enabled(rustc_span::sym::cfg_select)) | ||
| { | ||
| for (predicate, _, _) in &branches.unreachable { | ||
| let span = predicate.span(); | ||
| p.psess.buffer_lint( | ||
| UNREACHABLE_CFG_SELECT_PREDICATES, | ||
| span, | ||
| lint_node_id, | ||
| BuiltinLintDiag::UnreachableCfg { span, wildcard_span: underscore.span }, | ||
| ); | ||
| } | ||
| } |
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 doesn't seem to warn, unless there is a wildcard, why so? Does it not warn in case of
cfg_select! {
unix => true,
not(unix) => false,
windows => false,
}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.
That is correct, it's hard to do better than that. we'd need to actually have some sort of logic solver to do so in general, and even then I think it might misfire if there is some sort of feature flag implication that the checker does not know about.
So the current imlementation is basic but reliable.
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 have similar checks for unreachable match arms, so it can't be that bad, right?
I'm really concerned that this lint in its current form is just misleading. I think a user that sees #![deny(unreachable_cfg_select_predicates) will assume that rust will detect all unreachable predicates (since rust is normally reliable like that), and then will not notice that there is an unreachable predicate.
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 have similar checks for unreachable match arms, so it can't be that bad, right?
It seems feasible, but it really is adding a SMT solver, so, not straightforward.
https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/attrs/data_structures/enum.CfgEntry.html
We currently store
pub enum CfgEntry {
All(ThinVec<CfgEntry>, Span),
Any(ThinVec<CfgEntry>, Span),
Not(Box<CfgEntry>, Span),
Bool(bool, Span),
NameValue {
name: Symbol,
name_span: Span,
value: Option<(Symbol, Span)>,
span: Span,
},
Version(Option<RustcVersion>, Span),
}Meaning that e.g. for target_endian we'd need to encode that covering big/little is exhaustive. We need target feature implications to be taken into account, and cargo feature implication to be taken into account. This all seems useful, but it's complex. I'd rather not block cfg_select! on it.
@traviscross do you have more details of what T-lang had in mind here (and whether we want to block cfg_select! itself on that?
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.
Unfortunately, I do not have more details on hand. I've noted the question, though, and we'll cover this when we pick up the nomination on #149783.
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.
Question for you. I've been working on a minimal SAT solver that fits in around 600 lines of Rust code (while still being impressively fast). Let's say we had this. How much of the problem is that, and how much of the problem is encoding the needed invariants in CNF? I.e., if we had a good solver, is this still hard due to e.g. not knowing what we need to know in the right place to add the clauses, needing hard circuits such as for string operations, etc.? Or, if we had a solver, would it be straightforward to encode the problem in CNF?
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 honestly have no idea, this is not really my area of expertise.
The NameValue variant might need some design around when that is exhaustive. Version seems hard, maybe that is just never exhaustive?
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.
Basically, a solver is going to offer an interface like this:
/// Adds a clause to the formula.
///
/// Literals are specified in DIMACS style: positive literal for
/// variable `v` is `(v + 1)`, negative is `-(v + 1)`. Variables are
/// 0-indexed internally.
///
/// # Panics
///
/// Panics if any literal refers to a variable outside `[0, num_vars)`.
pub fn add_clause(&mut self, lits: &[i32]);That is, we have a set of 1 asserts that a variable is true; literal -1 asserts that it is false.
A clause is a set of literals that are ORed together (e.g., [1, -2] means v1 OR !v2). The solver ensures all added clauses are true.
If the formula is satisfiable (SAT), the solver returns an assignment of each variable. If it's not, it returns UNSAT.
The game is to assert all of the predicates of the earlier cfg_select arms as false (negated) and the predicate of the current arm as true. If it's SAT, then the arm is reachable. If it's UNSAT, then it's not.
We'd need to map every unique atomic cfg predicate (e.g., unix, target_pointer_width="32") to a unique SAT variable index. The tooling with the solver then provides a mechanism for encoding arbitrary boolean trees (e.g. not(all(a, any(b, c)))) into the right form (using a Tseitin transformation).
One design question is whether it'd be straightforward to produce the needed mapping and clauses.
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.
The
NameValuevariant might need some design around when that is exhaustive.
Yes, and we'd want to think about what we expect of the interaction with --cfg-check.
Versionseems hard, maybe that is just never exhaustive?
Sounds right. We'd give each version a variable, then assert that at most one of these can be true.
|
@rustbot ready |
20f27e5 to
288443c
Compare
This comment has been minimized.
This comment has been minimized.
This is emitted on branches of a `cfg_select!` that are statically known to be unreachable.
288443c to
d333d5e
Compare
tracking issue: #115585
Split out from #149783. This lint is emitted on branches of a
cfg_select!that are statically known to be unreachable. The lint is only emitted when the feature is enabled, so this change specifically does not need an FCP, and the lint will be stabilized alongside the feature (see #149783 (comment)).