Auto merge of #125415 - fmease:rollup-n2bg7q5, r=fmease

Rollup of 5 pull requests

Successful merges:

 - #124896 (miri: rename intrinsic_fallback_checks_ub to intrinsic_fallback_is_spec)
 - #125015 (Pattern types: Prohibit generic args on const params)
 - #125049 (Disallow cast with trailing braced macro in let-else)
 - #125259 (An async closure may implement `FnMut`/`Fn` if it has no self-borrows)
 - #125296 (Fix `unexpected_cfgs` lint on std)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-05-22 19:31:53 +00:00
commit 93e7cb835a
23 changed files with 407 additions and 354 deletions

View File

@ -81,8 +81,17 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
}
}
pub enum TrailingBrace<'a> {
/// Trailing brace in a macro call, like the one in `x as *const brace! {}`.
/// We will suggest changing the macro call to a different delimiter.
MacCall(&'a ast::MacCall),
/// Trailing brace in any other expression, such as `a + B {}`. We will
/// suggest wrapping the innermost expression in parentheses: `a + (B {})`.
Expr(&'a ast::Expr),
}
/// If an expression ends with `}`, returns the innermost expression ending in the `}`
pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
loop {
match &expr.kind {
AddrOf(_, _, e)
@ -111,10 +120,14 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
| Struct(..)
| TryBlock(..)
| While(..)
| ConstBlock(_) => break Some(expr),
| ConstBlock(_) => break Some(TrailingBrace::Expr(expr)),
Cast(_, ty) => {
break type_trailing_braced_mac_call(ty).map(TrailingBrace::MacCall);
}
MacCall(mac) => {
break (mac.args.delim == Delimiter::Brace).then_some(expr);
break (mac.args.delim == Delimiter::Brace).then_some(TrailingBrace::MacCall(mac));
}
InlineAsm(_) | OffsetOf(_, _) | IncludedBytes(_) | FormatArgs(_) => {
@ -131,7 +144,6 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
| MethodCall(_)
| Tup(_)
| Lit(_)
| Cast(_, _)
| Type(_, _)
| Await(_, _)
| Field(_, _)
@ -148,3 +160,78 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
}
}
}
/// If the type's last token is `}`, it must be due to a braced macro call, such
/// as in `*const brace! { ... }`. Returns that trailing macro call.
fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
loop {
match &ty.kind {
ast::TyKind::MacCall(mac) => {
break (mac.args.delim == Delimiter::Brace).then_some(mac);
}
ast::TyKind::Ptr(mut_ty) | ast::TyKind::Ref(_, mut_ty) => {
ty = &mut_ty.ty;
}
ast::TyKind::BareFn(fn_ty) => match &fn_ty.decl.output {
ast::FnRetTy::Default(_) => break None,
ast::FnRetTy::Ty(ret) => ty = ret,
},
ast::TyKind::Path(_, path) => match path_return_type(path) {
Some(trailing_ty) => ty = trailing_ty,
None => break None,
},
ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds, _) => {
match bounds.last() {
Some(ast::GenericBound::Trait(bound, _)) => {
match path_return_type(&bound.trait_ref.path) {
Some(trailing_ty) => ty = trailing_ty,
None => break None,
}
}
Some(ast::GenericBound::Outlives(_)) | None => break None,
}
}
ast::TyKind::Slice(..)
| ast::TyKind::Array(..)
| ast::TyKind::Never
| ast::TyKind::Tup(..)
| ast::TyKind::Paren(..)
| ast::TyKind::Typeof(..)
| ast::TyKind::Infer
| ast::TyKind::ImplicitSelf
| ast::TyKind::CVarArgs
| ast::TyKind::Pat(..)
| ast::TyKind::Dummy
| ast::TyKind::Err(..) => break None,
// These end in brace, but cannot occur in a let-else statement.
// They are only parsed as fields of a data structure. For the
// purpose of denying trailing braces in the expression of a
// let-else, we can disregard these.
ast::TyKind::AnonStruct(..) | ast::TyKind::AnonUnion(..) => break None,
}
}
}
/// Returns the trailing return type in the given path, if it has one.
///
/// ```ignore (illustrative)
/// ::std::ops::FnOnce(&str) -> fn() -> *const c_void
/// ^^^^^^^^^^^^^^^^^^^^^
/// ```
fn path_return_type(path: &ast::Path) -> Option<&ast::Ty> {
let last_segment = path.segments.last()?;
let args = last_segment.args.as_ref()?;
match &**args {
ast::GenericArgs::Parenthesized(args) => match &args.output {
ast::FnRetTy::Default(_) => None,
ast::FnRetTy::Ty(ret) => Some(ret),
},
ast::GenericArgs::AngleBracketed(_) => None,
}
}

View File

@ -371,9 +371,9 @@ hir_analysis_pass_to_variadic_function = can't pass `{$ty}` to variadic function
.suggestion = cast the value to `{$cast_ty}`
.help = cast the value to `{$cast_ty}`
hir_analysis_pattern_type_non_const_range = "range patterns must have constant range start and end"
hir_analysis_pattern_type_wild_pat = "wildcard patterns are not permitted for pattern types"
.label = "this type is the same as the inner type without a pattern"
hir_analysis_pattern_type_non_const_range = range patterns must have constant range start and end
hir_analysis_pattern_type_wild_pat = wildcard patterns are not permitted for pattern types
.label = this type is the same as the inner type without a pattern
hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind}
.label = not allowed in type signatures

View File

@ -1382,7 +1382,7 @@ pub enum GenericsArgsErrExtend<'tcx> {
span: Span,
},
SelfTyParam(Span),
TyParam(DefId),
Param(DefId),
DefVariant,
None,
}
@ -1498,11 +1498,11 @@ fn generics_args_err_extend<'a>(
GenericsArgsErrExtend::DefVariant => {
err.note("enum variants can't have type parameters");
}
GenericsArgsErrExtend::TyParam(def_id) => {
if let Some(span) = tcx.def_ident_span(def_id) {
let name = tcx.item_name(def_id);
err.span_note(span, format!("type parameter `{name}` defined here"));
}
GenericsArgsErrExtend::Param(def_id) => {
let span = tcx.def_ident_span(def_id).unwrap();
let kind = tcx.def_descr(def_id);
let name = tcx.item_name(def_id);
err.span_note(span, format!("{kind} `{name}` defined here"));
}
GenericsArgsErrExtend::SelfTyParam(span) => {
err.span_suggestion_verbose(

View File

@ -1758,7 +1758,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
assert_eq!(opt_self_ty, None);
let _ = self.prohibit_generic_args(
path.segments.iter(),
GenericsArgsErrExtend::TyParam(def_id),
GenericsArgsErrExtend::Param(def_id),
);
self.lower_ty_param(hir_id)
}
@ -2191,10 +2191,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
hir::ExprKind::Path(hir::QPath::Resolved(
_,
&hir::Path {
res: Res::Def(DefKind::ConstParam, def_id), ..
path @ &hir::Path {
res: Res::Def(DefKind::ConstParam, def_id),
..
},
)) => {
let _ = self.prohibit_generic_args(
path.segments.iter(),
GenericsArgsErrExtend::Param(def_id),
);
let ty = tcx
.type_of(def_id)
.no_bound_vars()

View File

@ -401,6 +401,45 @@ impl<'tcx> CoroutineClosureArgs<'tcx> {
pub fn coroutine_witness_ty(self) -> Ty<'tcx> {
self.split().coroutine_witness_ty
}
pub fn has_self_borrows(&self) -> bool {
match self.coroutine_captures_by_ref_ty().kind() {
ty::FnPtr(sig) => sig
.skip_binder()
.visit_with(&mut HasRegionsBoundAt { binder: ty::INNERMOST })
.is_break(),
ty::Error(_) => true,
_ => bug!(),
}
}
}
/// Unlike `has_escaping_bound_vars` or `outermost_exclusive_binder`, this will
/// detect only regions bound *at* the debruijn index.
struct HasRegionsBoundAt {
binder: ty::DebruijnIndex,
}
// FIXME: Could be optimized to not walk into components with no escaping bound vars.
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasRegionsBoundAt {
type Result = ControlFlow<()>;
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
&mut self,
t: &ty::Binder<'tcx, T>,
) -> Self::Result {
self.binder.shift_in(1);
t.super_visit_with(self)?;
self.binder.shift_out(1);
ControlFlow::Continue(())
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
if let ty::ReBound(binder, _) = *r
&& self.binder == binder
{
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)]

View File

@ -15,7 +15,7 @@ use ast::Label;
use rustc_ast as ast;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, TokenKind};
use rustc_ast::util::classify;
use rustc_ast::util::classify::{self, TrailingBrace};
use rustc_ast::{AttrStyle, AttrVec, LocalKind, MacCall, MacCallStmt, MacStmtStyle};
use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Recovered, Stmt};
use rustc_ast::{StmtKind, DUMMY_NODE_ID};
@ -407,18 +407,24 @@ impl<'a> Parser<'a> {
fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
if let Some(trailing) = classify::expr_trailing_brace(init) {
let sugg = match &trailing.kind {
ExprKind::MacCall(mac) => errors::WrapInParentheses::MacroArgs {
left: mac.args.dspan.open,
right: mac.args.dspan.close,
},
_ => errors::WrapInParentheses::Expression {
left: trailing.span.shrink_to_lo(),
right: trailing.span.shrink_to_hi(),
},
let (span, sugg) = match trailing {
TrailingBrace::MacCall(mac) => (
mac.span(),
errors::WrapInParentheses::MacroArgs {
left: mac.args.dspan.open,
right: mac.args.dspan.close,
},
),
TrailingBrace::Expr(expr) => (
expr.span,
errors::WrapInParentheses::Expression {
left: expr.span.shrink_to_lo(),
right: expr.span.shrink_to_hi(),
},
),
};
self.dcx().emit_err(errors::InvalidCurlyInLetElse {
span: trailing.span.with_lo(trailing.span.hi() - BytePos(1)),
span: span.with_lo(span.hi() - BytePos(1)),
sugg,
});
}

View File

@ -300,14 +300,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
return Err(NoSolution);
}
// If `Fn`/`FnMut`, we only implement this goal if we
// have no captures.
let no_borrows = match args.tupled_upvars_ty().kind() {
ty::Tuple(tys) => tys.is_empty(),
ty::Error(_) => false,
_ => bug!("tuple_fields called on non-tuple"),
};
if closure_kind != ty::ClosureKind::FnOnce && !no_borrows {
// A coroutine-closure implements `FnOnce` *always*, since it may
// always be called once. It additionally implements `Fn`/`FnMut`
// only if it has no upvars referencing the closure-env lifetime,
// and if the closure kind permits it.
if closure_kind != ty::ClosureKind::FnOnce && args.has_self_borrows() {
return Err(NoSolution);
}

View File

@ -418,20 +418,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// Ambiguity if upvars haven't been constrained yet
&& !args.tupled_upvars_ty().is_ty_var()
{
let no_borrows = match args.tupled_upvars_ty().kind() {
ty::Tuple(tys) => tys.is_empty(),
ty::Error(_) => false,
_ => bug!("tuple_fields called on non-tuple"),
};
// A coroutine-closure implements `FnOnce` *always*, since it may
// always be called once. It additionally implements `Fn`/`FnMut`
// only if it has no upvars (therefore no borrows from the closure
// that would need to be represented with a lifetime) and if the
// closure kind permits it.
// FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut`
// if it takes all of its upvars by copy, and none by ref. This would
// require us to record a bit more information during upvar analysis.
if no_borrows && closure_kind.extends(kind) {
// only if it has no upvars referencing the closure-env lifetime,
// and if the closure kind permits it.
if closure_kind.extends(kind) && !args.has_self_borrows() {
candidates.vec.push(ClosureCandidate { is_const });
} else if kind == ty::ClosureKind::FnOnce {
candidates.vec.push(ClosureCandidate { is_const });

View File

@ -40,3 +40,15 @@ compiler-builtins-weak-intrinsics = ["compiler_builtins/weak-intrinsics"]
panic_immediate_abort = ["core/panic_immediate_abort"]
# Choose algorithms that are optimized for binary size instead of runtime performance
optimize_for_size = ["core/optimize_for_size"]
[lints.rust.unexpected_cfgs]
level = "warn"
# x.py uses beta cargo, so `check-cfg` entries do not yet take effect
# for rust-lang/rust. But for users of `-Zbuild-std` it does.
# The unused warning is waiting for rust-lang/cargo#13925 to reach beta.
check-cfg = [
'cfg(bootstrap)',
'cfg(no_global_oom_handling)',
'cfg(no_rc)',
'cfg(no_sync)',
]

View File

@ -36,3 +36,16 @@ optimize_for_size = []
# Make `RefCell` store additional debugging information, which is printed out when
# a borrow error occurs
debug_refcell = []
[lints.rust.unexpected_cfgs]
level = "warn"
# x.py uses beta cargo, so `check-cfg` entries do not yet take effect
# for rust-lang/rust. But for users of `-Zbuild-std` it does.
# The unused warning is waiting for rust-lang/cargo#13925 to reach beta.
check-cfg = [
'cfg(bootstrap)',
'cfg(no_fp_fmt_parse)',
'cfg(stdarch_intel_sde)',
# This matches `EXTRA_CHECK_CFGS` in `src/bootstrap/src/lib.rs`.
'cfg(feature, values(any()))',
]

View File

@ -987,7 +987,7 @@ pub const unsafe fn assume(b: bool) {
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_intrinsic]
#[rustc_nounwind]
#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)]
#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)]
pub const fn likely(b: bool) -> bool {
b
}
@ -1007,7 +1007,7 @@ pub const fn likely(b: bool) -> bool {
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_intrinsic]
#[rustc_nounwind]
#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)]
#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)]
pub const fn unlikely(b: bool) -> bool {
b
}
@ -2483,7 +2483,7 @@ extern "rust-intrinsic" {
#[rustc_nounwind]
#[rustc_do_not_const_check]
#[inline]
#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)]
#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)]
pub const fn ptr_guaranteed_cmp<T>(ptr: *const T, other: *const T) -> u8 {
(ptr == other) as u8
}
@ -2748,7 +2748,7 @@ pub const fn ub_checks() -> bool {
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_nounwind]
#[rustc_intrinsic]
#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)]
#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)]
pub const unsafe fn const_allocate(_size: usize, _align: usize) -> *mut u8 {
// const eval overrides this function, but runtime code for now just returns null pointers.
// See <https://github.com/rust-lang/rust/issues/93935>.
@ -2769,7 +2769,7 @@ pub const unsafe fn const_allocate(_size: usize, _align: usize) -> *mut u8 {
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_nounwind]
#[rustc_intrinsic]
#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)]
#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)]
pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {
// Runtime NOP
}

View File

@ -97,3 +97,13 @@ heap_size = 0x8000000
name = "stdbenches"
path = "benches/lib.rs"
test = true
[lints.rust.unexpected_cfgs]
level = "warn"
check-cfg = [
'cfg(bootstrap)',
'cfg(backtrace_in_libstd)',
'cfg(netbsd10)',
'cfg(target_arch, values("xtensa"))',
'cfg(feature, values("std", "as_crate"))',
]

View File

@ -84,6 +84,9 @@ const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
(Some(Mode::ToolRustc), "rust_analyzer", None),
(Some(Mode::ToolStd), "rust_analyzer", None),
(Some(Mode::Codegen), "parallel_compiler", None),
// NOTE: consider updating `check-cfg` entries in `std/Cargo.toml` too.
// cfg(bootstrap) remove these once the bootstrap compiler supports
// `lints.rust.unexpected_cfgs.check-cfg`
(Some(Mode::Std), "stdarch_intel_sde", None),
(Some(Mode::Std), "no_fp_fmt_parse", None),
(Some(Mode::Std), "no_global_oom_handling", None),

View File

@ -43,18 +43,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
if this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden {
throw_unsup_format!("unimplemented intrinsic: `{intrinsic_name}`")
}
let intrinsic_fallback_checks_ub = Symbol::intern("intrinsic_fallback_checks_ub");
let intrinsic_fallback_is_spec = Symbol::intern("intrinsic_fallback_is_spec");
if this
.tcx
.get_attrs_by_path(
instance.def_id(),
&[sym::miri, intrinsic_fallback_checks_ub],
&[sym::miri, intrinsic_fallback_is_spec],
)
.next()
.is_none()
{
throw_unsup_format!(
"miri can only use intrinsic fallback bodies that check UB. After verifying that `{intrinsic_name}` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that"
"Miri can only use intrinsic fallback bodies that exactly reflect the specification: they fully check for UB and are as non-deterministic as possible. After verifying that `{intrinsic_name}` does so, add the `#[miri::intrinsic_fallback_is_spec]` attribute to it; also ping @rust-lang/miri when you do that"
);
}
Ok(Some(ty::Instance {

View File

@ -1,14 +0,0 @@
error: unsupported operation: miri can only use intrinsic fallback bodies that check UB. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that
--> $DIR/intrinsic_fallback_checks_ub.rs:LL:CC
|
LL | ptr_guaranteed_cmp::<()>(std::ptr::null(), std::ptr::null());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ miri can only use intrinsic fallback bodies that check UB. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that
|
= help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
= note: BACKTRACE:
= note: inside `main` at $DIR/intrinsic_fallback_checks_ub.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View File

@ -10,5 +10,5 @@ pub const fn ptr_guaranteed_cmp<T>(ptr: *const T, other: *const T) -> u8 {
fn main() {
ptr_guaranteed_cmp::<()>(std::ptr::null(), std::ptr::null());
//~^ ERROR: can only use intrinsic fallback bodies that check UB.
//~^ ERROR: can only use intrinsic fallback bodies that exactly reflect the specification
}

View File

@ -0,0 +1,14 @@
error: unsupported operation: Miri can only use intrinsic fallback bodies that exactly reflect the specification: they fully check for UB and are as non-deterministic as possible. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_is_spec]` attribute to it; also ping @rust-lang/miri when you do that
--> $DIR/intrinsic_fallback_is_spec.rs:LL:CC
|
LL | ptr_guaranteed_cmp::<()>(std::ptr::null(), std::ptr::null());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Miri can only use intrinsic fallback bodies that exactly reflect the specification: they fully check for UB and are as non-deterministic as possible. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_is_spec]` attribute to it; also ping @rust-lang/miri when you do that
|
= help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
= note: BACKTRACE:
= note: inside `main` at $DIR/intrinsic_fallback_is_spec.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View File

@ -0,0 +1,23 @@
//@ check-pass
//@ edition: 2021
// Demonstrates that an async closure may implement `FnMut` (not just `async FnMut`!)
// if it has no self-borrows. In this case, `&Ty` is not borrowed from the closure env,
// since it's fine to reborrow it with its original lifetime. See the doc comment on
// `should_reborrow_from_env_of_parent_coroutine_closure` for more detail for when we
// must borrow from the closure env.
#![feature(async_closure)]
fn main() {}
fn needs_fn_mut<T>(x: impl FnMut() -> T) {}
fn hello(x: &Ty) {
needs_fn_mut(async || { x.hello(); });
}
struct Ty;
impl Ty {
fn hello(&self) {}
}

View File

@ -3,8 +3,7 @@
#![feature(explicit_tail_calls)]
fn a() {
let foo = {
//~^ WARN irrefutable `let...else` pattern
let 0 = {
1
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@ -22,8 +21,7 @@ fn b() {
}
fn c() {
let foo = if true {
//~^ WARN irrefutable `let...else` pattern
let 0 = if true {
1
} else {
0
@ -43,8 +41,7 @@ fn d() {
}
fn e() {
let foo = match true {
//~^ WARN irrefutable `let...else` pattern
let 0 = match true {
true => 1,
false => 0
} else {
@ -53,10 +50,12 @@ fn e() {
};
}
struct X {a: i32}
fn f() {
let foo = X {
//~^ WARN irrefutable `let...else` pattern
struct X {
a: i32,
}
let X { a: 0 } = X {
a: 1
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@ -74,8 +73,7 @@ fn g() {
}
fn h() {
let foo = const {
//~^ WARN irrefutable `let...else` pattern
let 0 = const {
1
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@ -84,8 +82,7 @@ fn h() {
}
fn i() {
let foo = &{
//~^ WARN irrefutable `let...else` pattern
let 0 = &{
1
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@ -94,8 +91,8 @@ fn i() {
}
fn j() {
let bar = 0;
let foo = bar = { //~ ERROR: cannot assign twice
let mut bar = 0;
let foo = bar = {
//~^ WARN irrefutable `let...else` pattern
1
} else {
@ -105,8 +102,7 @@ fn j() {
}
fn k() {
let foo = 1 + {
//~^ WARN irrefutable `let...else` pattern
let 0 = 1 + {
1
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@ -115,8 +111,8 @@ fn k() {
}
fn l() {
let foo = 1..{
//~^ WARN irrefutable `let...else` pattern
const RANGE: std::ops::Range<u8> = 0..0;
let RANGE = 1..{
1
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@ -125,8 +121,7 @@ fn l() {
}
fn m() {
let foo = return {
//~^ WARN irrefutable `let...else` pattern
let 0 = return {
()
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@ -135,8 +130,7 @@ fn m() {
}
fn n() {
let foo = -{
//~^ WARN irrefutable `let...else` pattern
let 0 = -{
1
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@ -145,8 +139,7 @@ fn n() {
}
fn o() -> Result<(), ()> {
let foo = do yeet {
//~^ WARN irrefutable `let...else` pattern
let 0 = do yeet {
()
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@ -155,8 +148,7 @@ fn o() -> Result<(), ()> {
}
fn p() {
let foo = become {
//~^ WARN irrefutable `let...else` pattern
let 0 = become {
()
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@ -185,22 +177,37 @@ fn r() {
fn s() {
macro_rules! a {
() => { {} }
//~^ WARN irrefutable `let...else` pattern
//~| WARN irrefutable `let...else` pattern
() => {
{ 1 }
};
}
macro_rules! b {
(1) => {
let x = a!() else { return; };
let 0 = a!() else { return; };
};
(2) => {
let x = a! {} else { return; };
let 0 = a! {} else { return; };
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
};
}
b!(1); b!(2);
b!(1);
b!(2);
}
fn t() {
macro_rules! primitive {
(8) => { u8 };
}
let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! {
//~^ WARN irrefutable `let...else` pattern
8
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
return;
};
}
fn main() {}

View File

@ -1,19 +1,18 @@
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:9:5
--> $DIR/bad-let-else-statement.rs:8:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = ({
LL |
LL ~ let 0 = ({
LL | 1
LL ~ }) else {
|
error: `for...else` loops are not supported
--> $DIR/bad-let-else-statement.rs:18:7
--> $DIR/bad-let-else-statement.rs:17:7
|
LL | let foo = for i in 1..2 {
| --- `else` is attached to this loop
@ -28,22 +27,22 @@ LL | | };
= note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:30:5
--> $DIR/bad-let-else-statement.rs:28:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = (if true {
LL |
...
LL ~ let 0 = (if true {
LL | 1
LL | } else {
LL | 0
LL ~ }) else {
|
error: `loop...else` loops are not supported
--> $DIR/bad-let-else-statement.rs:39:7
--> $DIR/bad-let-else-statement.rs:37:7
|
LL | let foo = loop {
| ---- `else` is attached to this loop
@ -58,36 +57,34 @@ LL | | };
= note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:50:5
--> $DIR/bad-let-else-statement.rs:47:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = (match true {
LL |
LL ~ let 0 = (match true {
LL | true => 1,
LL | false => 0
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:61:5
--> $DIR/bad-let-else-statement.rs:60:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = (X {
LL |
LL ~ let X { a: 0 } = (X {
LL | a: 1
LL ~ }) else {
|
error: `while...else` loops are not supported
--> $DIR/bad-let-else-statement.rs:70:7
--> $DIR/bad-let-else-statement.rs:69:7
|
LL | let foo = while false {
| ----- `else` is attached to this loop
@ -102,35 +99,33 @@ LL | | };
= note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:80:5
--> $DIR/bad-let-else-statement.rs:78:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = (const {
LL |
LL ~ let 0 = (const {
LL | 1
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:90:5
--> $DIR/bad-let-else-statement.rs:87:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = &({
LL |
LL ~ let 0 = &({
LL | 1
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:101:5
--> $DIR/bad-let-else-statement.rs:98:5
|
LL | } else {
| ^
@ -144,91 +139,85 @@ LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:111:5
--> $DIR/bad-let-else-statement.rs:107:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = 1 + ({
LL |
LL ~ let 0 = 1 + ({
LL | 1
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:121:5
--> $DIR/bad-let-else-statement.rs:117:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = 1..({
LL |
LL ~ let RANGE = 1..({
LL | 1
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:131:5
--> $DIR/bad-let-else-statement.rs:126:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = return ({
LL |
LL ~ let 0 = return ({
LL | ()
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:141:5
--> $DIR/bad-let-else-statement.rs:135:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = -({
LL |
LL ~ let 0 = -({
LL | 1
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:151:5
--> $DIR/bad-let-else-statement.rs:144:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = do yeet ({
LL |
LL ~ let 0 = do yeet ({
LL | ()
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:161:5
--> $DIR/bad-let-else-statement.rs:153:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = become ({
LL |
LL ~ let 0 = become ({
LL | ()
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:171:5
--> $DIR/bad-let-else-statement.rs:163:5
|
LL | } else {
| ^
@ -242,7 +231,7 @@ LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:181:31
--> $DIR/bad-let-else-statement.rs:173:31
|
LL | let bad = format_args! {""} else { return; };
| ^
@ -253,24 +242,38 @@ LL | let bad = format_args! ("") else { return; };
| ~ ~
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:198:25
--> $DIR/bad-let-else-statement.rs:207:5
|
LL | let x = a! {} else { return; };
LL | } else {
| ^
|
help: use parentheses instead of braces for this macro
|
LL ~ let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! (
LL |
LL | 8
LL ~ ) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:190:25
|
LL | let 0 = a! {} else { return; };
| ^
...
LL | b!(1); b!(2);
| ----- in this macro invocation
LL | b!(2);
| ----- in this macro invocation
|
= note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
help: use parentheses instead of braces for this macro
|
LL | let x = a! () else { return; };
LL | let 0 = a! () else { return; };
| ~~
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:6:5
--> $DIR/bad-let-else-statement.rs:95:5
|
LL | / let foo = {
LL | / let foo = bar = {
LL | |
LL | | 1
LL | | } else {
@ -281,169 +284,7 @@ LL | | } else {
= note: `#[warn(irrefutable_let_patterns)]` on by default
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:25:5
|
LL | / let foo = if true {
LL | |
LL | | 1
LL | | } else {
LL | | 0
LL | | } else {
| |_____^
|
= note: this pattern will always match, so the `else` clause is useless
= help: consider removing the `else` clause
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:46:5
|
LL | / let foo = match true {
LL | |
LL | | true => 1,
LL | | false => 0
LL | | } else {
| |_____^
|
= note: this pattern will always match, so the `else` clause is useless
= help: consider removing the `else` clause
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:58:5
|
LL | / let foo = X {
LL | |
LL | | a: 1
LL | | } else {
| |_____^
|
= note: this pattern will always match, so the `else` clause is useless
= help: consider removing the `else` clause
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:77:5
|
LL | / let foo = const {
LL | |
LL | | 1
LL | | } else {
| |_____^
|
= note: this pattern will always match, so the `else` clause is useless
= help: consider removing the `else` clause
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:87:5
|
LL | / let foo = &{
LL | |
LL | | 1
LL | | } else {
| |_____^
|
= note: this pattern will always match, so the `else` clause is useless
= help: consider removing the `else` clause
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:98:5
|
LL | / let foo = bar = {
LL | |
LL | | 1
LL | | } else {
| |_____^
|
= note: this pattern will always match, so the `else` clause is useless
= help: consider removing the `else` clause
error[E0384]: cannot assign twice to immutable variable `bar`
--> $DIR/bad-let-else-statement.rs:98:15
|
LL | let bar = 0;
| ---
| |
| first assignment to `bar`
| help: consider making this binding mutable: `mut bar`
LL | let foo = bar = {
| _______________^
LL | |
LL | | 1
LL | | } else {
| |_____^ cannot assign twice to immutable variable
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:108:5
|
LL | / let foo = 1 + {
LL | |
LL | | 1
LL | | } else {
| |_____^
|
= note: this pattern will always match, so the `else` clause is useless
= help: consider removing the `else` clause
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:118:5
|
LL | / let foo = 1..{
LL | |
LL | | 1
LL | | } else {
| |_____^
|
= note: this pattern will always match, so the `else` clause is useless
= help: consider removing the `else` clause
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:128:5
|
LL | / let foo = return {
LL | |
LL | | ()
LL | | } else {
| |_____^
|
= note: this pattern will always match, so the `else` clause is useless
= help: consider removing the `else` clause
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:138:5
|
LL | / let foo = -{
LL | |
LL | | 1
LL | | } else {
| |_____^
|
= note: this pattern will always match, so the `else` clause is useless
= help: consider removing the `else` clause
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:148:5
|
LL | / let foo = do yeet {
LL | |
LL | | ()
LL | | } else {
| |_____^
|
= note: this pattern will always match, so the `else` clause is useless
= help: consider removing the `else` clause
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:158:5
|
LL | / let foo = become {
LL | |
LL | | ()
LL | | } else {
| |_____^
|
= note: this pattern will always match, so the `else` clause is useless
= help: consider removing the `else` clause
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:168:5
--> $DIR/bad-let-else-statement.rs:160:5
|
LL | / let foo = |x: i32| {
LL | |
@ -455,7 +296,7 @@ LL | | } else {
= help: consider removing the `else` clause
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:178:5
--> $DIR/bad-let-else-statement.rs:170:5
|
LL | let ok = format_args!("") else { return; };
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@ -464,7 +305,7 @@ LL | let ok = format_args!("") else { return; };
= help: consider removing the `else` clause
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:181:5
--> $DIR/bad-let-else-statement.rs:173:5
|
LL | let bad = format_args! {""} else { return; };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -473,45 +314,16 @@ LL | let bad = format_args! {""} else { return; };
= help: consider removing the `else` clause
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:188:19
--> $DIR/bad-let-else-statement.rs:204:5
|
LL | () => { {} }
| ___________________^
LL | / let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! {
LL | |
LL | |
LL | | }
... |
LL | | (1) => {
LL | | let x = a!() else { return; };
| |____________^
...
LL | b!(1); b!(2);
| ----- in this macro invocation
LL | | 8
LL | | } else {
| |_____^
|
= note: this pattern will always match, so the `else` clause is useless
= help: consider removing the `else` clause
= note: this warning originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
warning: irrefutable `let...else` pattern
--> $DIR/bad-let-else-statement.rs:188:19
|
LL | () => { {} }
| ___________________^
LL | |
LL | |
LL | | }
... |
LL | | (2) => {
LL | | let x = a! {} else { return; };
| |____________^
...
LL | b!(1); b!(2);
| ----- in this macro invocation
|
= note: this pattern will always match, so the `else` clause is useless
= help: consider removing the `else` clause
= note: this warning originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 20 previous errors; 5 warnings emitted
error: aborting due to 20 previous errors; 18 warnings emitted
For more information about this error, try `rustc --explain E0384`.

View File

@ -0,0 +1,10 @@
#![feature(pattern_types, core_pattern_type)]
#![allow(internal_features)]
type Pat<const START: u32, const END: u32> =
std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>);
//~^ ERROR type and const arguments are not allowed on const parameter `START`
//~| ERROR type arguments are not allowed on const parameter `END`
//~| ERROR associated type bindings are not allowed here
fn main() {}

View File

@ -0,0 +1,38 @@
error[E0109]: type and const arguments are not allowed on const parameter `START`
--> $DIR/bad_const_generics_args_on_const_param.rs:5:44
|
LL | std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>);
| ----- ^^ ^^^ ^ type and const arguments not allowed
| |
| not allowed on const parameter `START`
|
note: const parameter `START` defined here
--> $DIR/bad_const_generics_args_on_const_param.rs:4:16
|
LL | type Pat<const START: u32, const END: u32> =
| ^^^^^
error[E0109]: type arguments are not allowed on const parameter `END`
--> $DIR/bad_const_generics_args_on_const_param.rs:5:64
|
LL | std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>);
| --- ^ type argument not allowed
| |
| not allowed on const parameter `END`
|
note: const parameter `END` defined here
--> $DIR/bad_const_generics_args_on_const_param.rs:4:34
|
LL | type Pat<const START: u32, const END: u32> =
| ^^^
error[E0229]: associated type bindings are not allowed here
--> $DIR/bad_const_generics_args_on_const_param.rs:5:67
|
LL | std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>);
| ^^^^^^^^^^ associated type not allowed here
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0109, E0229.
For more information about an error, try `rustc --explain E0109`.

View File

@ -14,7 +14,7 @@ LL | type Positive2 = pattern_type!(i32 is 0..=);
|
= note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
error: "wildcard patterns are not permitted for pattern types"
error: wildcard patterns are not permitted for pattern types
--> $DIR/bad_pat.rs:11:33
|
LL | type Wild = pattern_type!(() is _);