-
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
Assert that the first assert!
expression is bool
#122661
base: master
Are you sure you want to change the base?
Conversation
rust-analyzer is developed in its own repository. If possible, consider making this change to rust-lang/rust-analyzer instead. cc @rust-lang/rust-analyzer The Miri subtree was changed cc @rust-lang/miri |
assert!((() <= ())); | ||
assert!((!(() > ()))); | ||
assert!((() >= ())); | ||
assert!(!(() != ())); |
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.
All of these are because now the unnecessary parentheses lint (correctly) triggers on assert!((expr))
.
error[E0600]: cannot apply unary operator `!` to type `BytePos` | ||
--> $DIR/issue-14091-2.rs:15:5 | ||
error[E0308]: mismatched types | ||
--> $DIR/issue-14091-2.rs:15:13 | ||
| | ||
LL | assert!(x, x); | ||
| ^^^^^^^^^^^^^ cannot apply unary operator `!` | ||
| | ||
note: an implementation of `Not` might be missing for `BytePos` | ||
--> $DIR/issue-14091-2.rs:6:1 | ||
| | ||
LL | pub struct BytePos(pub u32); | ||
| ^^^^^^^^^^^^^^^^^^ must implement `Not` | ||
note: the trait `Not` must be implemented | ||
--> $SRC_DIR/core/src/ops/bit.rs:LL:COL | ||
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) |
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 is the best example of how this is an improvement.
This comment has been minimized.
This comment has been minimized.
ead1593
to
eb411c1
Compare
This comment has been minimized.
This comment has been minimized.
Sigh, clippy shows at least one test where a suggestion causes there to be a condition that isn't a |
Some changes occurred in src/tools/clippy cc @rust-lang/clippy |
This comment has been minimized.
This comment has been minimized.
Isn't this technically a breaking change for e.g. (playground): struct Booly(i32);
impl std::ops::Not for Booly {
type Output = bool;
fn not(self) -> Self::Output {
self.0 == 0
}
}
fn main() {
assert!(Booly(1), "booly booly!")
} |
At the very least, we might need to tie such a change to an edition. I am not certain whether this decision would be a T-lang matter or a T-libs-api one. I'll nominate for T-lang for now. (Namely: The question is whether we can start enforcing a rule that the first expression to @rustbot label +I-lang-nominated |
src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0035_weird_exprs.rs
Outdated
Show resolved
Hide resolved
@pnkfelix we can keep the current (undocumented) behavior by making the desugaring be {
let x: bool = !!condition;
x
} instead of what this PR does: {
let x: bool = condition;
x
} I believe that would still cause errors to complain about Edit: an option would be to have an internal marker trait: use std::ops::Not;
trait CanAssert {}
impl<T: Not<Output = bool>> CanAssert for T {}
fn main() {
let _ = Box::new(true) as Box<dyn CanAssert>;
let _ = Box::new(42) as Box<dyn CanAssert>;
}
|
@estebank what about making the expansion edition-dependent? Is there precedent for that? Then, editions >= 2024 would expand to what you have proposed in the code of this PR, and editions < 2024 could expand to the |
(to answer my own question, |
c8185ea
to
07a5b21
Compare
Some changes occurred in coverage tests. cc @Zalathar |
I tried the marker trait approach for <=2021, and it kind of worked, but the diagnostics were actually worse than just doing |
This comment has been minimized.
This comment has been minimized.
Since I don't think it's been acknowledged above, for the record, this breaks the following code:
Because |
@compiler-errors that is indeed the case for 2024 onwards, not for previous editions. |
I think the critical point is whether an edition-dependent expansion is worth breaking that case (of Update: I don't know whether it is worth going through this exercise explicitly, but there is a design space here. E.g. one set of options is:
(And then there's variations thereof about how to handle editions < 2024, but that's a separate debate IMO.) |
(this is waiting for a decision from T-lang and/or T-libs regarding what interface we want to commit to for @rustbot label: +S-waiting-on-team -S-waiting-on-review |
Nominated for libs-api, because for things that are hypothetically macros written in core I'd mostly lean on them. Personally this makes sense to me. |
DUMMY_SP, | ||
false, | ||
ident, | ||
Some(cx.ty_ident(span, Ident::new(sym::bool, DUMMY_SP))), |
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 hygiene is not sufficient here. This example now breaks:
fn main() {
type bool = i32;
assert!(true);
}
error[E0308]: mismatched types
--> src/main.rs:3:13
|
3 | assert!(true);
| ^^^^ expected `i32`, found `bool`
|
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's probably better and easier to just use if <expr> {} else { panic!(..) }
, without making any block expressions or local bindings, and without making any changes to the expansion in previous editions.
// `{ let assert_macro: bool = !!$expr; assert_macro }` | ||
let not = |expr| parser.mk_expr(span, ast::ExprKind::Unary(ast::UnOp::Not, expr)); | ||
not(not(cond_expr)) |
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 !!
solution adds more issues: this is now accepted:
use std::ops::Not;
struct A;
struct B;
impl Not for A {
type Output = B;
fn not(self) -> B { B }
}
impl Not for B {
type Output = bool;
fn not(self) -> bool { true }
}
fn main() {
assert!(A);
}
We briefly discussed this in the lib-api meeting. This seems fine, but we thought it is important to consider if this could just be a non-edition change, considering how rare using However, in the meeting we failed to realize that that includes Note to self: For the next libs-api meeting, we should look at what Felix wrote here: #122661 (comment) |
We discussed this again in the libs-api meeting. We think it's fine if |
It just dawned on me that changing the macro from expanding to |
2a145b7
to
767615e
Compare
This comment has been minimized.
This comment has been minimized.
☔ The latest upstream changes (presumably #125379) made this pull request unmergeable. Please resolve the merge conflicts. |
(I'm personally happy with the usual "if it could be written on stable, then it's a libs-api question, not lang" here.) |
In the desugaring of `assert!`, assign the condition expression to a `bool` biding in order to provide better type errors when passed the wrong thing. The span will point only at the expression, and not the whole `assert!` invokation. ``` error[E0308]: mismatched types --> $DIR/issue-14091.rs:2:13 | LL | assert!(1,1); | ^ | | | expected `bool`, found integer | expected due to this ``` We no longer mention the expression needing to implement the `Not` trait. ``` error[E0308]: mismatched types --> $DIR/issue-14091-2.rs:15:13 | LL | assert!(x, x); | ^ | | | expected `bool`, found `BytePos` | expected due to this ``` Fix rust-lang#122159.
This approach works on *every* edition and emits the desired output consistently: ``` error[E0308]: mismatched types --> $DIR/issue-28308.rs:6:13 | LL | assert!("foo"); | ^^^^^ expected `bool`, found `&str` ```
767615e
to
3e43705
Compare
The job Click to see the possible cause of the failure (guessed by this bot)
|
In the desugaring of
assert!
in 2024 edition, assign the condition expression to abool
biding in order to provide better type errors when passed the wrong thing.The span will point only at the expression, and not the whole
assert!
invocation.We no longer mention the expression needing to implement the
Not
trait.In <=2021 edition, we still accept any type that implements
Not<Output = bool>
.Fix #122159.