Skip to content

Commit 07a5b21

Browse files
committed
Accept T in assert! where <T as Not<Output = bool> in <=2021 editions
1 parent 0d358fd commit 07a5b21

File tree

8 files changed

+77
-40
lines changed

8 files changed

+77
-40
lines changed

compiler/rustc_builtin_macros/src/assert.rs

+43-33
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use rustc_ast_pretty::pprust;
1111
use rustc_errors::PResult;
1212
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
1313
use rustc_parse::parser::Parser;
14+
use rustc_span::edition::Edition;
1415
use rustc_span::symbol::{sym, Ident, Symbol};
1516
use rustc_span::{Span, DUMMY_SP};
1617
use thin_vec::thin_vec;
@@ -172,45 +173,54 @@ fn expand_cond(cx: &ExtCtxt<'_>, parser: Parser<'_>, cond_expr: P<Expr>) -> P<Ex
172173
// talk about traits, we'll just state the appropriate type error.
173174
// `let assert_macro: bool = $expr;`
174175
let ident = Ident::new(sym::assert_macro, DUMMY_SP);
175-
let local = P(ast::Local {
176-
ty: Some(P(ast::Ty {
177-
kind: ast::TyKind::Path(None, ast::Path::from_ident(Ident::new(sym::bool, DUMMY_SP))),
178-
id: ast::DUMMY_NODE_ID,
179-
span: DUMMY_SP,
180-
tokens: None,
181-
})),
182-
pat: parser.mk_pat_ident(DUMMY_SP, ast::BindingAnnotation::NONE, ident),
183-
kind: ast::LocalKind::Init(cond_expr),
184-
id: ast::DUMMY_NODE_ID,
185-
span,
186-
colon_sp: None,
187-
attrs: Default::default(),
188-
tokens: None,
189-
});
190-
// `{ let assert_macro: bool = $expr; assert_macro }`
191-
parser.mk_expr(
192-
span,
193-
ast::ExprKind::Block(
194-
parser.mk_block(
195-
thin_vec![
196-
parser.mk_stmt(span, ast::StmtKind::Let(local)),
197-
parser.mk_stmt(
198-
span,
199-
ast::StmtKind::Expr(parser.mk_expr(
200-
span,
201-
ast::ExprKind::Path(None, ast::Path::from_ident(ident))
202-
)),
203-
),
204-
],
205-
ast::BlockCheckMode::Default,
206-
span,
176+
177+
let expr = if use_assert_2024(span) {
178+
// `{ let assert_macro: bool = $expr; assert_macro }`
179+
cond_expr
180+
} else {
181+
// In <=2021, we allow anything that can be negated to `bool`, not just `bool`s. We use the
182+
// "double not" trick to coerce the expression to `bool`. We still assign it to a new `bool`
183+
// binding so that in the case of a type that implements `Not` but doesn't return `bool`,
184+
// like `i32`, we still point at the condition and not at the whole macro.
185+
// `{ let assert_macro: bool = !!$expr; assert_macro }`
186+
let not = |expr| parser.mk_expr(span, ast::ExprKind::Unary(ast::UnOp::Not, expr));
187+
not(not(cond_expr))
188+
};
189+
let block = thin_vec![
190+
cx.stmt_let_ty(
191+
DUMMY_SP,
192+
false,
193+
ident,
194+
Some(cx.ty_ident(span, Ident::new(sym::bool, DUMMY_SP))),
195+
expr,
196+
),
197+
parser.mk_stmt(
198+
span,
199+
ast::StmtKind::Expr(
200+
parser.mk_expr(span, ast::ExprKind::Path(None, ast::Path::from_ident(ident)))
207201
),
208-
None,
209202
),
203+
];
204+
parser.mk_expr(
205+
span,
206+
ast::ExprKind::Block(parser.mk_block(block, ast::BlockCheckMode::Default, span), None),
210207
)
211208
}
212209

213210
fn parse_custom_message(parser: &mut Parser<'_>) -> Option<TokenStream> {
214211
let ts = parser.parse_tokens();
215212
if !ts.is_empty() { Some(ts) } else { None }
216213
}
214+
215+
pub fn use_assert_2024(mut span: Span) -> bool {
216+
// To determine the edition, we check the first span up the expansion
217+
// stack that isn't internal.
218+
loop {
219+
let expn = span.ctxt().outer_expn_data();
220+
if let Some(_features) = expn.allow_internal_unstable {
221+
span = expn.call_site;
222+
continue;
223+
}
224+
break expn.edition >= Edition::Edition2024;
225+
}
226+
}

compiler/rustc_hir_typeck/src/demand.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
679679
) {
680680
match (self.tcx.parent_hir_node(expr.hir_id), error) {
681681
(hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. }), _)
682-
if init.hir_id == expr.hir_id && !ty.span.is_dummy() =>
682+
if init.hir_id == expr.hir_id && !ty.span.source_equal(init.span) =>
683683
{
684684
// Point at `let` assignment type.
685685
err.span_label(ty.span, "expected due to this");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0600]: cannot apply unary operator `!` to type `&'static str`
2+
--> $DIR/issue-28308.rs:6:13
3+
|
4+
LL | assert!("foo");
5+
| ^^^^^ cannot apply unary operator `!`
6+
|
7+
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
8+
9+
error: aborting due to 1 previous error
10+
11+
For more information about this error, try `rustc --explain E0600`.

tests/ui/codemap_tests/issue-28308.stderr renamed to tests/ui/codemap_tests/issue-28308.edition2024.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0308]: mismatched types
2-
--> $DIR/issue-28308.rs:2:13
2+
--> $DIR/issue-28308.rs:6:13
33
|
44
LL | assert!("foo");
55
| ^^^^^ expected `bool`, found `&str`

tests/ui/codemap_tests/issue-28308.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
//@revisions: edition2024 edition2021
2+
//@[edition2024] edition:2024
3+
//@[edition2024] compile-flags: -Z unstable-options
4+
//@[edition2021] edition:2021
15
fn main() {
26
assert!("foo");
3-
//~^ ERROR mismatched types
7+
//[edition2024]~^ ERROR mismatched types
8+
//[edition2021]~^^ ERROR cannot apply unary operator `!` to type `&'static str`
49
}

tests/ui/issues/issue-14091-2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ fn main() {
1313
let x = BytePos(1);
1414

1515
assert!(x, x);
16-
//~^ ERROR mismatched types
16+
//~^ ERROR cannot apply unary operator `!` to type `BytePos`
1717
}

tests/ui/issues/issue-14091-2.stderr

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1-
error[E0308]: mismatched types
1+
error[E0600]: cannot apply unary operator `!` to type `BytePos`
22
--> $DIR/issue-14091-2.rs:15:13
33
|
44
LL | assert!(x, x);
5-
| ^ expected `bool`, found `BytePos`
5+
| ^ cannot apply unary operator `!`
6+
|
7+
note: an implementation of `Not` might be missing for `BytePos`
8+
--> $DIR/issue-14091-2.rs:6:1
9+
|
10+
LL | pub struct BytePos(pub u32);
11+
| ^^^^^^^^^^^^^^^^^^ must implement `Not`
12+
note: the trait `Not` must be implemented
13+
--> $SRC_DIR/core/src/ops/bit.rs:LL:COL
14+
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
615

716
error: aborting due to 1 previous error
817

9-
For more information about this error, try `rustc --explain E0308`.
18+
For more information about this error, try `rustc --explain E0600`.

tests/ui/issues/issue-14091.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ error[E0308]: mismatched types
33
|
44
LL | assert!(1,1);
55
| ^ expected `bool`, found integer
6+
|
7+
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
68

79
error: aborting due to 1 previous error
810

0 commit comments

Comments
 (0)