Auto merge of #119053 - matthiaskrgr:rollup-hky3ld3, r=matthiaskrgr

Rollup of 4 pull requests

Successful merges:

 - #118880 (More expressions correctly are marked to end with curly braces)
 - #118928 (fix: Overlapping spans in delimited meta-vars)
 - #119022 (Remove unnecessary constness from ProjectionCandidate)
 - #119052 (Avoid overflow in GVN constant indexing.)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-12-17 20:57:02 +00:00
commit 6a62871320
14 changed files with 739 additions and 27 deletions

View File

@ -40,15 +40,44 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
| Range(_, Some(e), _)
| Ret(Some(e))
| Unary(_, e)
| Yield(Some(e)) => {
| Yield(Some(e))
| Yeet(Some(e))
| Become(e) => {
expr = e;
}
Closure(closure) => {
expr = &closure.body;
}
Gen(..) | Block(..) | ForLoop(..) | If(..) | Loop(..) | Match(..) | Struct(..)
| TryBlock(..) | While(..) => break Some(expr),
_ => break None,
| TryBlock(..) | While(..) | ConstBlock(_) => break Some(expr),
// FIXME: These can end in `}`, but changing these would break stable code.
InlineAsm(_) | OffsetOf(_, _) | MacCall(_) | IncludedBytes(_) | FormatArgs(_) => {
break None;
}
Break(_, None)
| Range(_, None, _)
| Ret(None)
| Yield(None)
| Array(_)
| Call(_, _)
| MethodCall(_)
| Tup(_)
| Lit(_)
| Cast(_, _)
| Type(_, _)
| Await(_, _)
| Field(_, _)
| Index(_, _, _)
| Underscore
| Path(_, _)
| Continue(_)
| Repeat(_, _)
| Paren(_)
| Try(_)
| Yeet(None)
| Err => break None,
}
}
}

View File

@ -236,6 +236,13 @@ fn expand_macro<'cx>(
target_sp.open = source_sp.open.with_ctxt(ctxt);
target_sp.close = source_sp.close.with_ctxt(ctxt);
}
(
TokenTree::Delimited(target_sp, ..),
mbe::TokenTree::MetaVar(source_sp, ..),
) => {
target_sp.open = source_sp.with_ctxt(ctxt);
target_sp.close = source_sp.with_ctxt(ctxt).shrink_to_hi();
}
_ => {
let sp = rhs_tt.span().with_ctxt(ctxt);
tt.set_span(sp);

View File

@ -125,10 +125,8 @@ pub enum SelectionCandidate<'tcx> {
/// This is a trait matching with a projected type as `Self`, and we found
/// an applicable bound in the trait definition. The `usize` is an index
/// into the list returned by `tcx.item_bounds`. The constness is the
/// constness of the bound in the trait.
// FIXME(effects) do we need this constness
ProjectionCandidate(usize, ty::BoundConstness),
/// into the list returned by `tcx.item_bounds`.
ProjectionCandidate(usize),
/// Implementation of a `Fn`-family trait by one of the anonymous types
/// generated for an `||` expression.

View File

@ -644,12 +644,10 @@ fn simplify_place_projection(&mut self, place: &mut Place<'tcx>, location: Locat
{
if let Some(offset) = self.evaluated[idx].as_ref()
&& let Ok(offset) = self.ecx.read_target_usize(offset)
&& let Some(min_length) = offset.checked_add(1)
{
projection.to_mut()[i] = ProjectionElem::ConstantIndex {
offset,
min_length: offset + 1,
from_end: false,
};
projection.to_mut()[i] =
ProjectionElem::ConstantIndex { offset, min_length, from_end: false };
} else if let Some(new_idx) = self.try_as_local(idx, location) {
projection.to_mut()[i] = ProjectionElem::Index(new_idx);
self.reused_locals.insert(new_idx);

View File

@ -154,10 +154,7 @@ fn assemble_candidates_from_projected_tys(
.infcx
.probe(|_| self.match_projection_obligation_against_definition_bounds(obligation));
// FIXME(effects) proper constness needed?
candidates.vec.extend(
result.into_iter().map(|idx| ProjectionCandidate(idx, ty::BoundConstness::NotConst)),
);
candidates.vec.extend(result.into_iter().map(|idx| ProjectionCandidate(idx)));
}
/// Given an obligation like `<SomeTrait for T>`, searches the obligations that the caller
@ -585,7 +582,7 @@ fn assemble_candidates_from_auto_impls(
}
ty::Alias(ty::Opaque, _) => {
if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate(..))) {
if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate(_))) {
// We do not generate an auto impl candidate for `impl Trait`s which already
// reference our auto trait.
//

View File

@ -71,7 +71,7 @@ pub(super) fn confirm_candidate(
ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
ProjectionCandidate(idx, _) => {
ProjectionCandidate(idx) => {
let obligations = self.confirm_projection_candidate(obligation, idx)?;
ImplSource::Param(obligations)
}
@ -1313,7 +1313,6 @@ fn confirm_const_destruct_candidate(
// If we have a projection type, make sure to normalize it so we replace it
// with a fresh infer variable
ty::Alias(ty::Projection | ty::Inherent, ..) => {
// FIXME(effects) this needs constness
let predicate = normalize_with_depth_to(
self,
obligation.param_env,
@ -1344,7 +1343,6 @@ fn confirm_const_destruct_candidate(
// since it's either not `const Drop` (and we raise an error during selection),
// or it's an ADT (and we need to check for a custom impl during selection)
_ => {
// FIXME(effects) this needs constness
let predicate = self_ty.rebind(ty::TraitPredicate {
trait_ref: ty::TraitRef::from_lang_item(
self.tcx(),

View File

@ -1883,7 +1883,7 @@ fn candidate_should_be_dropped_in_favor_of(
| BuiltinCandidate { .. }
| TraitAliasCandidate
| ObjectCandidate(_)
| ProjectionCandidate(..),
| ProjectionCandidate(_),
) => {
// We have a where clause so don't go around looking
// for impls. Arbitrarily give param candidates priority
@ -1893,7 +1893,7 @@ fn candidate_should_be_dropped_in_favor_of(
// here (see issue #50825).
DropVictim::drop_if(!is_global(other_cand))
}
(ObjectCandidate(_) | ProjectionCandidate(..), ParamCandidate(ref victim_cand)) => {
(ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref victim_cand)) => {
// Prefer these to a global where-clause bound
// (see issue #50825).
if is_global(victim_cand) { DropVictim::Yes } else { DropVictim::No }
@ -1921,20 +1921,20 @@ fn candidate_should_be_dropped_in_favor_of(
)
}
(ProjectionCandidate(i, _), ProjectionCandidate(j, _))
(ProjectionCandidate(i), ProjectionCandidate(j))
| (ObjectCandidate(i), ObjectCandidate(j)) => {
// Arbitrarily pick the lower numbered candidate for backwards
// compatibility reasons. Don't let this affect inference.
DropVictim::drop_if(i < j && !has_non_region_infer)
}
(ObjectCandidate(_), ProjectionCandidate(..))
| (ProjectionCandidate(..), ObjectCandidate(_)) => {
(ObjectCandidate(_), ProjectionCandidate(_))
| (ProjectionCandidate(_), ObjectCandidate(_)) => {
bug!("Have both object and projection candidate")
}
// Arbitrarily give projection and object candidates priority.
(
ObjectCandidate(_) | ProjectionCandidate(..),
ObjectCandidate(_) | ProjectionCandidate(_),
ImplCandidate(..)
| AutoImplCandidate
| ClosureCandidate { .. }
@ -1964,7 +1964,7 @@ fn candidate_should_be_dropped_in_favor_of(
| TraitUpcastingUnsizeCandidate(_)
| BuiltinCandidate { .. }
| TraitAliasCandidate,
ObjectCandidate(_) | ProjectionCandidate(..),
ObjectCandidate(_) | ProjectionCandidate(_),
) => DropVictim::No,
(&ImplCandidate(other_def), &ImplCandidate(victim_def)) => {

View File

@ -0,0 +1,104 @@
- // MIR for `constant_index_overflow` before GVN
+ // MIR for `constant_index_overflow` after GVN
fn constant_index_overflow(_1: &[T]) -> () {
debug x => _1;
let mut _0: ();
let _2: usize;
let mut _4: bool;
let mut _5: usize;
let mut _6: usize;
let mut _7: &[T];
let _8: usize;
let mut _9: usize;
let mut _10: bool;
let _11: usize;
let mut _12: usize;
let mut _13: bool;
let mut _14: T;
scope 1 {
debug a => _2;
let _3: T;
scope 2 {
debug b => _3;
}
}
bb0: {
- StorageLive(_2);
- _2 = const _ as usize (IntToInt);
+ nop;
+ _2 = const usize::MAX;
StorageLive(_3);
StorageLive(_4);
StorageLive(_5);
- _5 = _2;
+ _5 = const usize::MAX;
StorageLive(_6);
StorageLive(_7);
_7 = &(*_1);
_6 = core::slice::<impl [T]>::len(move _7) -> [return: bb1, unwind unreachable];
}
bb1: {
StorageDead(_7);
- _4 = Lt(move _5, move _6);
+ _4 = Lt(const usize::MAX, move _6);
switchInt(move _4) -> [0: bb4, otherwise: bb2];
}
bb2: {
StorageDead(_6);
StorageDead(_5);
StorageLive(_8);
- _8 = _2;
+ _8 = const usize::MAX;
_9 = Len((*_1));
- _10 = Lt(_8, _9);
- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind unreachable];
+ _10 = Lt(const usize::MAX, _9);
+ assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, const usize::MAX) -> [success: bb3, unwind unreachable];
}
bb3: {
- _3 = (*_1)[_8];
+ _3 = (*_1)[_2];
StorageDead(_8);
goto -> bb6;
}
bb4: {
StorageDead(_6);
StorageDead(_5);
StorageLive(_11);
_11 = const 0_usize;
_12 = Len((*_1));
- _13 = Lt(_11, _12);
- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb5, unwind unreachable];
+ _13 = Lt(const 0_usize, _12);
+ assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 0_usize) -> [success: bb5, unwind unreachable];
}
bb5: {
- _3 = (*_1)[_11];
+ _3 = (*_1)[0 of 1];
StorageDead(_11);
goto -> bb6;
}
bb6: {
StorageDead(_4);
StorageLive(_14);
_14 = _3;
_0 = opaque::<T>(move _14) -> [return: bb7, unwind unreachable];
}
bb7: {
StorageDead(_14);
StorageDead(_3);
- StorageDead(_2);
+ nop;
return;
}
}

View File

@ -0,0 +1,104 @@
- // MIR for `constant_index_overflow` before GVN
+ // MIR for `constant_index_overflow` after GVN
fn constant_index_overflow(_1: &[T]) -> () {
debug x => _1;
let mut _0: ();
let _2: usize;
let mut _4: bool;
let mut _5: usize;
let mut _6: usize;
let mut _7: &[T];
let _8: usize;
let mut _9: usize;
let mut _10: bool;
let _11: usize;
let mut _12: usize;
let mut _13: bool;
let mut _14: T;
scope 1 {
debug a => _2;
let _3: T;
scope 2 {
debug b => _3;
}
}
bb0: {
- StorageLive(_2);
- _2 = const _ as usize (IntToInt);
+ nop;
+ _2 = const usize::MAX;
StorageLive(_3);
StorageLive(_4);
StorageLive(_5);
- _5 = _2;
+ _5 = const usize::MAX;
StorageLive(_6);
StorageLive(_7);
_7 = &(*_1);
_6 = core::slice::<impl [T]>::len(move _7) -> [return: bb1, unwind continue];
}
bb1: {
StorageDead(_7);
- _4 = Lt(move _5, move _6);
+ _4 = Lt(const usize::MAX, move _6);
switchInt(move _4) -> [0: bb4, otherwise: bb2];
}
bb2: {
StorageDead(_6);
StorageDead(_5);
StorageLive(_8);
- _8 = _2;
+ _8 = const usize::MAX;
_9 = Len((*_1));
- _10 = Lt(_8, _9);
- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind continue];
+ _10 = Lt(const usize::MAX, _9);
+ assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, const usize::MAX) -> [success: bb3, unwind continue];
}
bb3: {
- _3 = (*_1)[_8];
+ _3 = (*_1)[_2];
StorageDead(_8);
goto -> bb6;
}
bb4: {
StorageDead(_6);
StorageDead(_5);
StorageLive(_11);
_11 = const 0_usize;
_12 = Len((*_1));
- _13 = Lt(_11, _12);
- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb5, unwind continue];
+ _13 = Lt(const 0_usize, _12);
+ assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 0_usize) -> [success: bb5, unwind continue];
}
bb5: {
- _3 = (*_1)[_11];
+ _3 = (*_1)[0 of 1];
StorageDead(_11);
goto -> bb6;
}
bb6: {
StorageDead(_4);
StorageLive(_14);
_14 = _3;
_0 = opaque::<T>(move _14) -> [return: bb7, unwind continue];
}
bb7: {
StorageDead(_14);
StorageDead(_3);
- StorageDead(_2);
+ nop;
return;
}
}

View File

@ -609,6 +609,22 @@ fn indirect_static() {
})
}
/// Verify that having constant index `u64::MAX` does not yield to an overflow in rustc.
fn constant_index_overflow<T: Copy>(x: &[T]) {
// CHECK-LABEL: fn constant_index_overflow(
// CHECK: debug a => [[a:_.*]];
// CHECK: debug b => [[b:_.*]];
// CHECK: [[a]] = const usize::MAX;
// CHECK-NOT: = (*_1)[{{.*}} of 0];
// CHECK: [[b]] = (*_1)[[[a]]];
// CHECK-NOT: = (*_1)[{{.*}} of 0];
// CHECK: [[b]] = (*_1)[0 of 1];
// CHECK-NOT: = (*_1)[{{.*}} of 0];
let a = u64::MAX as usize;
let b = if a < x.len() { x[a] } else { x[0] };
opaque(b)
}
fn main() {
subexpression_elimination(2, 4, 5);
wrap_unwrap(5);
@ -627,6 +643,7 @@ fn main() {
repeat();
fn_pointers();
indirect_static();
constant_index_overflow(&[5, 3]);
}
#[inline(never)]
@ -653,3 +670,4 @@ fn identity<T>(x: T) -> T {
// EMIT_MIR gvn.repeat.GVN.diff
// EMIT_MIR gvn.fn_pointers.GVN.diff
// EMIT_MIR gvn.indirect_static.GVN.diff
// EMIT_MIR gvn.constant_index_overflow.GVN.diff

View File

@ -0,0 +1,16 @@
// compile-flags: --crate-type lib -O -C debug-assertions=yes
// Regression test for issue 118786
macro_rules! make_macro {
($macro_name:tt) => {
macro_rules! $macro_name {
//~^ ERROR macros that expand to items must be delimited with braces or followed by a semicolon
//~| ERROR macro expansion ignores token `{` and any following
//~| ERROR cannot find macro `macro_rules` in this scope
() => {}
}
}
}
make_macro!((meow));

View File

@ -0,0 +1,47 @@
error: macros that expand to items must be delimited with braces or followed by a semicolon
--> $DIR/issue-118786.rs:7:22
|
LL | macro_rules! $macro_name {
| ^^^^^^^^^^^
|
help: change the delimiters to curly braces
|
LL | macro_rules! {} {
| ~ +
help: add a semicolon
|
LL | macro_rules! $macro_name; {
| +
error: macro expansion ignores token `{` and any following
--> $DIR/issue-118786.rs:7:34
|
LL | macro_rules! $macro_name {
| ^
...
LL | make_macro!((meow));
| ------------------- caused by the macro expansion here
|
= note: the usage of `make_macro!` is likely invalid in item context
error: cannot find macro `macro_rules` in this scope
--> $DIR/issue-118786.rs:7:9
|
LL | macro_rules! $macro_name {
| ^^^^^^^^^^^
...
LL | make_macro!((meow));
| ------------------- in this macro invocation
|
note: maybe you have forgotten to define a name for this `macro_rules!`
--> $DIR/issue-118786.rs:7:9
|
LL | macro_rules! $macro_name {
| ^^^^^^^^^^^
...
LL | make_macro!((meow));
| ------------------- in this macro invocation
= note: this error originates in the macro `make_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 3 previous errors

View File

@ -0,0 +1,164 @@
#![feature(inline_const)]
#![feature(yeet_expr)]
#![allow(incomplete_features)] // Necessary for now, while explicit_tail_calls is incomplete
#![feature(explicit_tail_calls)]
fn a() {
let foo = {
1
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
return;
};
}
fn b() {
let foo = for i in 1..2 {
break;
} else {
//~^ ERROR `for...else` loops are not supported
return;
};
}
fn c() {
let foo = if true {
1
} else {
0
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
return;
};
}
fn d() {
let foo = loop {
break;
} else {
//~^ ERROR loop...else` loops are not supported
return;
};
}
fn e() {
let foo = match true {
true => 1,
false => 0
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
return;
};
}
struct X {a: i32}
fn f() {
let foo = X {
a: 1
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
return;
};
}
fn g() {
let foo = while false {
break;
} else {
//~^ ERROR `while...else` loops are not supported
return;
};
}
fn h() {
let foo = const {
1
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
return;
};
}
fn i() {
let foo = &{
1
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
return;
};
}
fn j() {
let bar = 0;
let foo = bar = {
1
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
return;
};
}
fn k() {
let foo = 1 + {
1
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
return;
};
}
fn l() {
let foo = 1..{
1
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
return;
};
}
fn m() {
let foo = return {
()
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
return;
};
}
fn n() {
let foo = -{
1
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
return;
};
}
fn o() -> Result<(), ()> {
let foo = do yeet {
()
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
return Ok(());
};
}
fn p() {
let foo = become {
()
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
return;
};
}
fn q() {
let foo = |x: i32| {
x
} else {
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
return;
};
}
fn main() {}

View File

@ -0,0 +1,232 @@
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:9:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = ({
LL | 1
LL ~ }) else {
|
error: `for...else` loops are not supported
--> $DIR/bad-let-else-statement.rs:18:7
|
LL | let foo = for i in 1..2 {
| --- `else` is attached to this loop
LL | break;
LL | } else {
| _______^
LL | |
LL | | return;
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:29:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = (if true {
LL | 1
LL | } else {
LL | 0
LL ~ }) else {
|
error: `loop...else` loops are not supported
--> $DIR/bad-let-else-statement.rs:38:7
|
LL | let foo = loop {
| ---- `else` is attached to this loop
LL | break;
LL | } else {
| _______^
LL | |
LL | | return;
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:48:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = (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:58:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = (X {
LL | a: 1
LL ~ }) else {
|
error: `while...else` loops are not supported
--> $DIR/bad-let-else-statement.rs:67:7
|
LL | let foo = while false {
| ----- `else` is attached to this loop
LL | break;
LL | } else {
| _______^
LL | |
LL | | return;
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:76:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = (const {
LL | 1
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:85:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = &({
LL | 1
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:95:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = bar = ({
LL | 1
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:104:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = 1 + ({
LL | 1
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:113:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = 1..({
LL | 1
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:122:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = return ({
LL | ()
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:131:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = -({
LL | 1
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:140:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = do yeet ({
LL | ()
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:149:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = become ({
LL | ()
LL ~ }) else {
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
--> $DIR/bad-let-else-statement.rs:158:5
|
LL | } else {
| ^
|
help: wrap the expression in parentheses
|
LL ~ let foo = |x: i32| ({
LL | x
LL ~ }) else {
|
error: aborting due to 17 previous errors