Revert "Rollup merge of #122661 - estebank:assert-macro-span, r=petrochenkov"

This reverts commit 1eeb8e8b15, reversing
changes made to 324bf2b9fd.

Unfortunately the assert desugaring change is not backwards compatible,
see RUST-145770.

Code such as

```rust
#[derive(Debug)]
struct F {
    data: bool
}

impl std::ops::Not for F {
  type Output = bool;
  fn not(self) -> Self::Output { !self.data }
}

fn main() {
  let f = F { data: true };

  assert!(f);
}
```

would be broken by the assert desugaring change. We may need to land
the change over an edition boundary, or limit the editions that the
desugaring change impacts.
This commit is contained in:
Jieyou Xu
2025-09-11 09:00:01 +08:00
parent fc58d8f5cc
commit b38a86f4d7
17 changed files with 53 additions and 150 deletions

View File

@@ -1,8 +1,8 @@
mod context;
use rustc_ast::token::{self, Delimiter};
use rustc_ast::token::Delimiter;
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment};
use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment, UnOp, token};
use rustc_ast_pretty::pprust;
use rustc_errors::PResult;
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
@@ -29,7 +29,7 @@ pub(crate) fn expand_assert<'cx>(
// `core::panic` and `std::panic` are different macros, so we use call-site
// context to pick up whichever is currently in scope.
let call_site_span = cx.with_call_site_ctxt(cond_expr.span);
let call_site_span = cx.with_call_site_ctxt(span);
let panic_path = || {
if use_panic_2021(span) {
@@ -63,7 +63,7 @@ pub(crate) fn expand_assert<'cx>(
}),
})),
);
assert_cond_check(cx, call_site_span, cond_expr, then)
expr_if_not(cx, call_site_span, cond_expr, then, None)
}
// If `generic_assert` is enabled, generates rich captured outputs
//
@@ -88,33 +88,26 @@ pub(crate) fn expand_assert<'cx>(
)),
)],
);
assert_cond_check(cx, call_site_span, cond_expr, then)
expr_if_not(cx, call_site_span, cond_expr, then, None)
};
ExpandResult::Ready(MacEager::expr(expr))
}
/// `assert!($cond_expr, $custom_message)`
struct Assert {
cond_expr: Box<Expr>,
custom_message: Option<TokenStream>,
}
/// `match <cond> { true => {} _ => <then> }`
fn assert_cond_check(cx: &ExtCtxt<'_>, span: Span, cond: Box<Expr>, then: Box<Expr>) -> Box<Expr> {
// Instead of expanding to `if !<cond> { <then> }`, we expand to
// `match <cond> { true => {} _ => <then> }`.
// This allows us to always complain about mismatched types instead of "cannot apply unary
// operator `!` to type `X`" when passing an invalid `<cond>`, while also allowing `<cond>` to
// be `&true`.
let els = cx.expr_block(cx.block(span, thin_vec![]));
let mut arms = thin_vec![];
arms.push(cx.arm(span, cx.pat_lit(span, cx.expr_bool(span, true)), els));
arms.push(cx.arm(span, cx.pat_wild(span), then));
// We wrap the `match` in a statement to limit the length of any borrows introduced in the
// condition.
cx.expr_block(cx.block(span, [cx.stmt_expr(cx.expr_match(span, cond, arms))].into()))
// if !{ ... } { ... } else { ... }
fn expr_if_not(
cx: &ExtCtxt<'_>,
span: Span,
cond: Box<Expr>,
then: Box<Expr>,
els: Option<Box<Expr>>,
) -> Box<Expr> {
cx.expr_if(span, cx.expr(span, ExprKind::Unary(UnOp::Not, cond)), then, els)
}
fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> {

View File

@@ -1618,18 +1618,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
{
let e = self.tcx.erase_and_anonymize_regions(e);
let f = self.tcx.erase_and_anonymize_regions(f);
let mut expected = with_forced_trimmed_paths!(e.sort_string(self.tcx));
let mut found = with_forced_trimmed_paths!(f.sort_string(self.tcx));
if let ObligationCauseCode::Pattern { span, .. } = cause.code()
&& let Some(span) = span
&& !span.from_expansion()
&& cause.span.from_expansion()
{
// When the type error comes from a macro like `assert!()`, and we are pointing at
// code the user wrote the cause and effect are reversed as the expected value is
// what the macro expanded to.
(found, expected) = (expected, found);
}
let expected = with_forced_trimmed_paths!(e.sort_string(self.tcx));
let found = with_forced_trimmed_paths!(f.sort_string(self.tcx));
if expected == found {
label_or_note(span, terr.to_string(self.tcx));
} else {
@@ -2152,9 +2142,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
) -> Option<(DiagStyledString, DiagStyledString)> {
match values {
ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found),
ValuePairs::Terms(exp_found) => {
self.expected_found_str_term(cause, exp_found, long_ty_path)
}
ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found, long_ty_path),
ValuePairs::Aliases(exp_found) => self.expected_found_str(exp_found),
ValuePairs::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found),
ValuePairs::ExistentialProjection(exp_found) => self.expected_found_str(exp_found),
@@ -2193,7 +2181,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
fn expected_found_str_term(
&self,
cause: &ObligationCause<'tcx>,
exp_found: ty::error::ExpectedFound<ty::Term<'tcx>>,
long_ty_path: &mut Option<PathBuf>,
) -> Option<(DiagStyledString, DiagStyledString)> {
@@ -2201,27 +2188,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
if exp_found.references_error() {
return None;
}
let (mut expected, mut found) = (exp_found.expected, exp_found.found);
if let ObligationCauseCode::Pattern { span, .. } = cause.code()
&& let Some(span) = span
&& !span.from_expansion()
&& cause.span.from_expansion()
{
// When the type error comes from a macro like `assert!()`, and we are pointing at
// code the user wrote, the cause and effect are reversed as the expected value is
// what the macro expanded to. So if the user provided a `Type` when the macro is
// written in such a way that a `bool` was expected, we want to print:
// = note: expected `bool`
// found `Type`"
// but as far as the compiler is concerned, after expansion what was expected was `Type`
// = note: expected `Type`
// found `bool`"
// so we reverse them here to match user expectation.
(expected, found) = (found, expected);
}
Some(match (expected.kind(), found.kind()) {
Some(match (exp_found.expected.kind(), exp_found.found.kind()) {
(ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => {
let (mut exp, mut fnd) = self.cmp(expected, found);
// Use the terminal width as the basis to determine when to compress the printed

View File

@@ -11,7 +11,7 @@ use rustc_ast::{BinOpKind, LitKind, RangeLimits};
use rustc_data_structures::packed::Pu128;
use rustc_data_structures::unhash::UnindexMap;
use rustc_errors::{Applicability, Diag};
use rustc_hir::{Body, Expr, ExprKind};
use rustc_hir::{Block, Body, Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::source_map::Spanned;
@@ -135,12 +135,12 @@ fn assert_len_expr<'hir>(
cx: &LateContext<'_>,
expr: &'hir Expr<'hir>,
) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> {
let (cmp, asserted_len, slice_len) = if let Some(
higher::IfLetOrMatch::Match(cond, [_, then], _)
) = higher::IfLetOrMatch::parse(cx, expr)
&& let ExprKind::Binary(bin_op, left, right) = &cond.kind
let (cmp, asserted_len, slice_len) = if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr)
&& let ExprKind::Unary(UnOp::Not, condition) = &cond.kind
&& let ExprKind::Binary(bin_op, left, right) = &condition.kind
// check if `then` block has a never type expression
&& cx.typeck_results().expr_ty(then.body).is_never()
&& let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind
&& cx.typeck_results().expr_ty(then_expr).is_never()
{
len_comparison(bin_op.node, left, right)?
} else if let Some((macro_call, bin_op)) = first_node_macro_backtrace(cx, expr).find_map(|macro_call| {

View File

@@ -196,7 +196,6 @@ fn issue_13106() {
const {
assert!(EMPTY_STR.is_empty());
//~^ const_is_empty
}
const {

View File

@@ -158,16 +158,10 @@ LL | let _ = val.is_empty();
| ^^^^^^^^^^^^^^
error: this expression always evaluates to true
--> tests/ui/const_is_empty.rs:198:17
|
LL | assert!(EMPTY_STR.is_empty());
| ^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to true
--> tests/ui/const_is_empty.rs:203:9
--> tests/ui/const_is_empty.rs:202:9
|
LL | EMPTY_STR.is_empty();
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to 28 previous errors
error: aborting due to 27 previous errors

View File

@@ -1,6 +1,6 @@
#![warn(clippy::incompatible_msrv)]
#![feature(custom_inner_attributes)]
#![allow(stable_features, clippy::diverging_sub_expression)]
#![allow(stable_features)]
#![feature(strict_provenance)] // For use in test
#![clippy::msrv = "1.3.0"]

View File

@@ -1,16 +1,4 @@
fn main() {
assert!("foo"); //~ ERROR mismatched types
//~^ NOTE expected `bool`, found `str`
//~| NOTE in this expansion of assert!
let x = Some(&1);
assert!(x); //~ ERROR mismatched types
//~^ NOTE expected `bool`, found `Option<&{integer}>`
//~| NOTE expected enum `bool`
//~| NOTE in this expansion of assert!
//~| NOTE in this expansion of assert!
assert!(x, ""); //~ ERROR mismatched types
//~^ NOTE expected `bool`, found `Option<&{integer}>`
//~| NOTE expected enum `bool`
//~| NOTE in this expansion of assert!
//~| NOTE in this expansion of assert!
assert!("foo");
//~^ ERROR cannot apply unary operator `!`
}

View File

@@ -1,27 +1,9 @@
error[E0308]: mismatched types
--> $DIR/issue-28308.rs:2:13
error[E0600]: cannot apply unary operator `!` to type `&'static str`
--> $DIR/issue-28308.rs:2:5
|
LL | assert!("foo");
| ^^^^^ expected `bool`, found `str`
| ^^^^^^^^^^^^^^ cannot apply unary operator `!`
error[E0308]: mismatched types
--> $DIR/issue-28308.rs:6:13
|
LL | assert!(x);
| ^ expected `bool`, found `Option<&{integer}>`
|
= note: expected enum `bool`
found type `Option<&{integer}>`
error: aborting due to 1 previous error
error[E0308]: mismatched types
--> $DIR/issue-28308.rs:11:13
|
LL | assert!(x, "");
| ^ expected `bool`, found `Option<&{integer}>`
|
= note: expected enum `bool`
found type `Option<&{integer}>`
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0308`.
For more information about this error, try `rustc --explain E0600`.

View File

@@ -1,8 +1,8 @@
error[E0080]: evaluation panicked: assertion failed: false
--> $DIR/assert.rs:5:23
--> $DIR/assert.rs:5:15
|
LL | const _: () = assert!(false);
| ^^^^^ evaluation of `_` failed here
| ^^^^^^^^^^^^^^ evaluation of `_` failed here
error: aborting due to 1 previous error

View File

@@ -1,8 +1,8 @@
error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::<T>() == 0
--> $DIR/post_monomorphization_error_backtrace.rs:6:31
--> $DIR/post_monomorphization_error_backtrace.rs:6:23
|
LL | const V: () = assert!(std::mem::size_of::<T>() == 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::<u32>::V` failed here
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::<u32>::V` failed here
note: erroneous constant encountered
--> $DIR/post_monomorphization_error_backtrace.rs:14:5
@@ -17,10 +17,10 @@ LL | assert_zst::<U>()
| ^^^^^^^^^^^^^^^^^
error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::<T>() == 0
--> $DIR/post_monomorphization_error_backtrace.rs:6:31
--> $DIR/post_monomorphization_error_backtrace.rs:6:23
|
LL | const V: () = assert!(std::mem::size_of::<T>() == 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::<i32>::V` failed here
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::<i32>::V` failed here
note: erroneous constant encountered
--> $DIR/post_monomorphization_error_backtrace.rs:14:5

View File

@@ -1,8 +1,8 @@
error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::<T>() == 0
--> $DIR/const-expr-generic-err.rs:4:21
--> $DIR/const-expr-generic-err.rs:4:13
|
LL | const { assert!(std::mem::size_of::<T>() == 0); }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `foo::<i32>::{constant#0}` failed here
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `foo::<i32>::{constant#0}` failed here
note: erroneous constant encountered
--> $DIR/const-expr-generic-err.rs:4:5

View File

@@ -4,6 +4,8 @@
//! backwards-incompatible, and may need to be done over an edition boundary or limit editions for
//! which the desguaring change impacts.
//@ check-pass
#[derive(Debug)]
struct F {
data: bool
@@ -18,5 +20,4 @@ fn main() {
let f = F { data: true };
assert!(f);
//~^ ERROR mismatched types
}

View File

@@ -1,14 +0,0 @@
error[E0308]: mismatched types
--> $DIR/assert-desugaring-145770.rs:20:11
|
LL | assert!(f);
| ^ expected `bool`, found `F`
|
help: you might have meant to use field `data` whose type is `bool`
|
LL | assert!(f.data);
| +++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0308`.

View File

@@ -1,8 +0,0 @@
//@ check-pass
#[derive(PartialEq, Eq, Hash)]
struct S;
fn main() {
let foo = std::rc::Rc::new(std::cell::RefCell::new(std::collections::HashMap::<S, S>::new()));
// Ensure that the lifetimes of the borrow do not leak past the end of `main`.
assert!(matches!(foo.borrow().get(&S).unwrap(), S))
}

View File

@@ -12,7 +12,7 @@ fn main() {
// Test that we can use addr_of! to get the address of a packed member which according to its
// type is not aligned, but because it is a projection from a packed type is a valid place.
let ptr0 = std::ptr::addr_of!(memory[0].tail);
let ptr1 = std::ptr::addr_of!(memory[1].tail);
let ptr1 = std::ptr::addr_of!(memory[0].tail);
// Even if ptr0 happens to be aligned by chance, ptr1 is not.
assert!(!ptr0.is_aligned() || !ptr1.is_aligned());

View File

@@ -1,8 +1,8 @@
error[E0080]: evaluation panicked: assertion failed: LANE < 4
--> $DIR/const-err-trumps-simd-err.rs:17:21
--> $DIR/const-err-trumps-simd-err.rs:17:13
|
LL | const { assert!(LANE < 4); } // the error should be here...
| ^^^^^^^^ evaluation of `get_elem::<4>::{constant#0}` failed here
| ^^^^^^^^^^^^^^^^^ evaluation of `get_elem::<4>::{constant#0}` failed here
note: erroneous constant encountered
--> $DIR/const-err-trumps-simd-err.rs:17:5

View File

@@ -41,10 +41,10 @@ LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0080]: evaluation panicked: assertion failed: false
--> $DIR/uninhabited.rs:41:17
--> $DIR/uninhabited.rs:41:9
|
LL | assert!(false);
| ^^^^^ evaluation of `yawning_void_struct::_` failed here
| ^^^^^^^^^^^^^^ evaluation of `yawning_void_struct::_` failed here
error[E0277]: `()` cannot be safely transmuted into `yawning_void_enum::Void`
--> $DIR/uninhabited.rs:71:41
@@ -68,10 +68,10 @@ LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0080]: evaluation panicked: assertion failed: false
--> $DIR/uninhabited.rs:63:17
--> $DIR/uninhabited.rs:63:9
|
LL | assert!(false);
| ^^^^^ evaluation of `yawning_void_enum::_` failed here
| ^^^^^^^^^^^^^^ evaluation of `yawning_void_enum::_` failed here
error[E0277]: `u128` cannot be safely transmuted into `DistantVoid`
--> $DIR/uninhabited.rs:92:43
@@ -95,10 +95,10 @@ LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0080]: evaluation panicked: assertion failed: false
--> $DIR/uninhabited.rs:87:17
--> $DIR/uninhabited.rs:87:9
|
LL | assert!(false);
| ^^^^^ evaluation of `distant_void::_` failed here
| ^^^^^^^^^^^^^^ evaluation of `distant_void::_` failed here
error[E0277]: `Src` cannot be safely transmuted into `issue_126267::Error`
--> $DIR/uninhabited.rs:108:42